Вы находитесь на странице: 1из 98

№8(21) август 2004

подписной индекс 81655


www.samag.ru
Кроссплатформенная
частная сеть: OpenVPN
Linux и NTFS
Особенности установки
антивируса ClamAV во FreeBSD
Обзор ОС Darwin 7.0
на платформе x86
Практика работы с NetBSD:
профилирование ядра
Создание и настройка IVR
для голосовых шлюзов Cisco Systems
Разгон и торможение Windows NT
Поисковая система своими руками
№8(21) август 2004

HTML-шаблоны для PHP и Perl


Обработка HTML-шаблонов off-line
оглавление

АДМИНИСТРИРОВАНИЕ Знакомство с Usergate


Андрей Уваров
OpenVPN, или Кроссплатформенная dashin@ua.fm 36
частная сеть
Андрей Бешков Создание и настройка IVR
tigrisha@sysadmins.ru 4 для голосовых шлюзов Cisco Systems
Михаил Заграевский
Запуск в VMWare гостевой оси, mike@megalog.ru 38
установленной на физическом диске
Николай Почабытов Разгон и торможение Windows NT
nikel@tsr.ru 13
Крис Касперски
kk@sendmail.ru 48
Linux и NTFS
Сергей Яремчук Поисковая система своими руками
grinder@ua.fm 18
Андрей Сапронов
duan@bk.ru 61
Еще раз о ClamAV:
особенности установки во FreeBSD
WEB
Сергей Супрунов
amsand@rambler.ru 24
HTML-шаблоны для PHP и Perl,
или Не делайте инструмент самоцелью
CrossOver и лицензионный вопрос
Дмитрий Горяинов
Андрей Бешков dg@webclub.ru 66
tigrisha@sysadmins.ru 26
Обработка HTML-шаблонов off-line.
В яблочко! Возможности и ограничения
Краткий обзор ОС Darwin 7.0
на платформе x86 (Mac OS X 10.3 Jaguar) Алексей Мичурин
alexey@office-a.mtu-net.ru 74
Антон Борисов
a.borisov@tesv.tmb.ru 28
ОБРАЗОВАНИЕ
Практика работы с NetBSD:
профилирование ядра Автоматизация процессов в сети
Александр Байрак Иван Коробко
x01mer@pisem.net 34 ikorobko@prosv.ru 84

Уважаемые системные администраторы!

Редакция журнала объявляет конкурс на лучшие статьи, описывающие успешный опыт в области системного
администрирования (решение нетривиальных проблем, успешная организация работы отдела и любые другие
полезные материалы, которые помогут вашим коллегам в работе). Самые интересные будут опубликованы
в ноябрьском и декабрьском номерах журнала «Системный администратор».

Условия участия смотрите на сайте журнала www.samag.ru в разделе «Конкурс».

№8(21), август 2004 1


администрирование

OpenVPN,
ИЛИ КРОССПЛАТФОРМЕННАЯ
ЧАСТНАЯ СЕТЬ
АНДРЕЙ БЕШКОВ
Рано или поздно большинство системных администрато- реализации функций IPSec приходится вносить в ядро опе-
ров сталкивается с необходимостью соединить свои тер- рационной системы и IP-стек довольно много изменений.
риториально удаленные сети с помощью надежного, дос- Как гласит старинная пословица: «Надежность заканчива-
таточно быстрого и в то же время защищенного от прослу- ется там, где начинается сложная механика», это значит,
шивания и вмешательства злоумышленников канала. Та- что одна-единственная ошибка в коде, реализующем IPSec,
ким образом, получается, что нам нужно создать частную приведет к огромной дыре в безопасности. Вдобавок, на-
виртуальную сеть (Virtual Private Network), или VPN. В этот сколько я знаю, на данный момент не существует легко
момент перед нами открывается развилка из нескольких настраиваемого свободного клиента IPSec для Windows.
путей для претворения задуманного в жизнь: Значит, либо придется разбираться в настройках тех кли-
1. Использовать свободную реализацию IPSec (Internet ентов, что распространяются под эгидой opensource, либо
Protocol Security). В качестве таковой могут выступать покупать коммерческий. Также стоит обратить внимание на
FreeSWAN или FreeBSD IPSec. несколько большую сложность установки и настройки та-
2. Купить и внедрить коммерческое решение. Например, кого комплекса по сравнению с остальными типами VPN.
Cisco VPN или Securepoint VPN Server, который также Еще одним из минусов является отсутствие технической
основан на IPSec, или взять решение от какого-либо дру- поддержки. Если что-то пойдет не так, как планировалось,
гого производителя, например Windows IPSec. В дан- то на квалифицированную помощь от производителя рас-
ном разделе я сознательно не делю решения на аппа- считывать трудно. Это ведь свободный продукт, поэтому
ратные и программные, потому что это всего лишь об- придется просить совета либо у своих более удачливых кол-
зор. лег, либо в форумах и списках, посвященных данному про-
3. Взять на вооружение свободные разработки, использу- граммному обеспечению. Впрочем, не каждый производи-
ющие криптографические алгоритмы собственного из- тель проприетарного софта может похвастаться квалифи-
готовления. Список таких приложений довольно велик. цированной службой поддержки. К тому же обычно у мно-
Поэтому перечислим только те, что у всех на слуху – гих свободных разработок существует внушительный ла-
cipe, vpnd, tinc, – этот список можно было бы продол- герь сторонников, которые будут рады обратить вас в свою
жать очень долго. веру и заодно помочь в настройке программы.
4. Самостоятельно написать программное обеспечение, Второй способ выглядит довольно привлекательным в
реализующее все механизмы VPN. случае, если у вас есть деньги. В обмен на довольно вну-
5. Использовать PPTP (Point to Point Tunneling Protocol). шительную сумму вы, скорее всего, получите более или ме-
нее качественную техническую поддержку, стабильно ра-
Давайте разберемся с достоинствами и недостатками ботающее оборудование и специалистов, которые самосто-
каждой из предлагаемых методик решения проблемы. ятельно или по вашим указаниям решат, как именно со-
Что же можно сказать по поводу первого решения. Пос- единить сети для достижения наилучшего результата. Да и
ледние несколько лет при создании VPN стандартом де- с технической документацией вероятнее всего дело будет
факто считается IPSec. Такая распространенность помога- обстоять очень хорошо. Но, как всегда в жизни, плюсов без
ет нам не слишком беспокоиться о совместимости VPN-сер- минусов не бывает. Выбрав решение от одного производи-
веров и клиентов. Все-таки единый стандарт – штука очень теля, вы будете впредь очень сильно привязаны к нему. Со-
удобная. Подобный способ хорош тем, что не потребует ответственно, если выбранный вендор не реализует те или
больших материальных затрат и в то же время предлагает иные возможности в своей продуктовой линейке, значит и
стойкую криптографическую защиту передаваемых данных. вы, скорее всего, не сможете ими воспользоваться.
Но на этом его преимущества заканчиваются, и на сцену Третий вариант наиболее подходит для тех, кому нужно
выходят недостатки. Во-первых, IPSec не обязательно смо- развернуть VPN без больших затрат времени, сил и денег.
жет мирно сосуществовать с вашим межсетевым экраном, В то же время нужно отдавать себе отчет, что обычно по-
особенно если тот выполняет над пакетами изуверские опе- добные программы пишут для собственного использования
рации, называемые в народе NAT. Опять же экраны с конт- те, кто не смог или не захотел разобраться с принципами
ролем состояния соединения (statefull firewall), находящие- работы и методикой настройки IPSec. Соответственно ос-
ся между двумя точками виртуального тоннеля и управляе- новной идеей разработки является удобство использова-
мые провайдером, могут не пропускать те или иные IPSec- ния и нетребовательность к ресурсам. Со временем такие
пакеты. О кроссплатформенности разных реализаций IPSec программы понемногу совершенствуются, но все же не сто-
пока что остается только мечтать в связи с тем, что для ит ожидать от них запредельной надежности и безопаснос-

4
администрирование
ти. Происходит это потому, что каждый автор использует Решив разработать свой собственный пакет, реализу-
свои собственные реализации криптоалгоритмов. Конечно, ющий функции VPN, он в отличие от других авторов не
они обеспечивают некоторую степень защищенности, но стал заниматься созданием своего собственного уникаль-
без проведения сторонней экспертизы точно сказать, на- ного криптоалгоритма с нуля. Вместо этого был исполь-
сколько надежно все это работает, довольно затруднитель- зован проверенный временем и неоднократно доказавший
но. Соответственно данный класс программ больше подхо- свою надежность стандарт SSL, для работы с которым
дит для защиты каналов, по которым передается инфор- можно было опереться на доступную для многих систем
мация с малым временем жизни, предназначенная для ог- библиотеку OpenSSL. Стоит отметить, что такая же идея
раниченного распространения. Внедрение такого решения используется и в программном обеспечении, называемом
позволит защитить наши данные от начинающих злоумыш- vtun.
ленников, но не от профессионалов в области подглядыва- Итак, давайте посмотрим поближе на возможности, пре-
ния. Впрочем, для некоторых предприятий вполне доста- доставляемые OpenVPN.
точно и таких возможностей. ! Официально OpenVPN успешно работает под управле-
Четвертый путь выглядит привлекательным только для нием следующих операционных систем: Linux, Solaris,
специалистов в области криптографии, обладающих боль- OpenBSD, FreeBSD, NetBSD, Mac OS X, Windows 2000/
шим количеством свободного времени. Но, к сожалению, XP. Это позволяет создавать сложные кроссплатфор-
таких людей в мире немного, а тех, кто позволит себя на- менные туннели. Впрочем, не составит никакого труда
нять, еще меньше. портировать OpenVPN в любую другую систему, для ко-
Вариант решения, основанный на PPT, большей частью торой существует драйвер tun/tap-устройств. К тому же
используется приверженцами Microsoft. Данный стандарт данная разработка независима от размера и старшин-
реализует довольно стойкое шифрование и аутентифика- ства байтов в машинном слове, что облегчает перенос
цию соединений. Созданный в недрах большой Редмондс- на новые операционные системы.
кой корпорации, он не получил особого распространения в ! Компрессия потока передаваемых данных и управление
мире Unix. Хотя свободное программное обеспечение для полосой пропускания производятся с помощью библио-
работы с ним все же существует и пользуется некоторым теки LZO. Процесс сжатия является адаптивным, то есть
спросом, все же решения на основе IPSec практикуют го- попытки упаковать передаваемые данные будут пред-
раздо чаще. Происходит это, видимо, потому, что IPSec приняты, только если есть смысл их упаковывать. Эта
имеет более надежные процедуры шифрования. Но поли- возможность может быть легко отключена по желанию
тические мотивы сопротивления со стороны юниксистов но- пользователя, как и любые другие компоненты в слу-
винкам, внедряемым Microsoft, тоже не стоит сбрасывать чае, если вам не удалось с ними поладить, или если вы
со счетов. хотите поступиться какой-то частью функционала вза-
Ну а большинству читателей, не воспользовавшихся ва- мен на быстродействие.
риантами, предлагаемыми выше, видимо, придется обра- ! Поддерживаются два типа туннелей: IP и Ethernet, соот-
титься к программе, которой собственно и посвящена дан- ветственно называемые routed и bridged. Таким обра-
ная статья. Между делом неплохо было бы посетить сайт зом, появляется возможность туннелировать как IP-под-
проекта http://openvpn.sourceforge.net. Ну а я продолжу свое сети, так и виртуальные Ethernet-адаптеры.
повествование. ! Отлично работает в сетях, где адреса распределяются
История разработки OpenVPN выглядит довольно забав- с помощью DHCP. Помогает подключаться к VPN-кли-
но и в то же время является хрестоматийной иллюстраци- ентам, попадающим в Интернет через dial-up.
ей принципов, типичных для движения открытых исходни- ! Позволяет создать туннели поверх NAT, несмотря на то
ков. В конце 2001 года Джеймс Йонан (James Yonan) ус- что NAT изменяет содержимое заголовков передавае-
пешно выполнил работу над большим проектом для компа- мых пакетов.
нии, сотрудником которой он является и по сей день. В ка- ! Дает возможность работать с любыми механизмами
честве благодарности работодатель разрешил ему не хо- шифрования, встроенными в OpenSSL для защиты пе-
дить каждый день на работу и выполнять свои обязанности редаваемого трафика. А это, в свою очередь, позволя-
удаленно. С этого момента жизнь Джеймса кардинально ет каждому клиенту выбрать тип, режим работы (CBC,
изменилась. Словно в калейдоскопе мелькали страны и кон- CFB, OFB) и размер ключа шифра в соответствии с ин-
тиненты: Египет, Кыргызстан, Монголия, Китай. Как обыч- дивидуальными предпочтениями.
но, без русских тут не обошлось. Уж очень автора OpenVPN ! Для аутентификации датаграмм, пересылаемых по тун-
беспокоил тот парадоксальный факт, что в России есть до- нелю, можно использовать HMAC-дайджест.
статочно большое количество безработных, но очень та- ! В случае если в передаваемых данных есть повторяю-
лантливых хакеров. А судя по его наблюдениям, некоторая щиеся последовательности, для их сокрытия будет ис-
часть трафика шла именно через российские сети. Объез- пользован алгоритм explicit IV.
див всю центральную Азию, Джеймс попутно продолжал ис- ! Каждая датаграмма помечается с помощью специаль-
кать решение, которое позволит ему работать с централь- ных ID, создаваемых на основе времени отправки и но-
ным офисом удобно, безопасно, надежно и в то же время мера последовательности. Таким образом предотвра-
не будет стоить бешеных денег. Испробовав на себе все щается возможность повторного проигрывания зло-
вышеперечисленные способы создания VPN, он понял, что умышленником последовательности записанных паке-
на данный момент идеал, к сожалению, недостижим. тов.

№8(21), август 2004 5


администрирование
! В качестве дополнительной меры безопасности может Более наглядно все вышеуказанное отображено на при-
быть использован протокол TLS, позволяющий аутен- веденной схеме.
тифицировать сессию с помощью динамического обме-
на сертификатами. Довольно большой оптимизации бы-
стродействия при динамическом обмене SSL/TLS-клю-
чами позволяет добиться использование мультипоточ-
ной библиотеки pthread. Таким образом, даже частый
обмен между сервером и клиентом ключами размером
более чем 2048 байт практически не влияет на скорость
передачи туннелируемых данных.
! Для увеличения безопасности OpenVPN позволяет пе-
реместить себя в chroot-окружение и снижает свои при-
вилегии после старта так, чтобы отлично работать от
имени самого бесправного пользователя системы.
! Еще одним полезным с точки зрения безопасности свой-
ством является наличие ключа --mlock. Он позволяет зап- Итак, начнем установку пакетов на ALT Linux. В систем-
ретить OpenVPN записывать в процессе работы на жест- ном репозитарии есть все, что нам может понадобиться.
кий диск какую-либо информацию, связанную с секрет-
ными ключами и данными, передаваемыми по туннелю. # apt-get update
# apt-get install iptables openssl liblzo liblzo-devel openvpn
! В связи с тем, что данная программа является всего лишь
обычным пользовательским приложением, а не частью После удачного завершения установки создаем пользо-
ядра, она может вполне мирно сосуществовать с други- вателя openvpn, входящего в группу openvpn. Процессы
ми приложениями, использующими tun/tap-туннели. openvpn, стартуя от имени пользователя root, будут сни-
! OpenVPN создавался для тесной интеграции с пользова- жать свои права, чтобы с наименьшими привилегиями
тельскими скриптами и другими высокоуровневыми при- работать от имени вышеуказанного пользователя. Согла-
ложениями. Что в свою очередь дает возможность по пер- ситесь, что дополнительная безопасность никому не по-
вому требованию легко создавать и уничтожать туннели. мешает.
! Позволяет удобно работать через межсетевые экраны
с контролем состояния соединения. В случае если по # useradd openvpn
туннелю не передаются данные, OpenVPN позволяет че-
рез определенные промежутки времени посылать ping, Теперь можно приступить к тестам общей работоспо-
для того чтобы не дать межсетевым экранам разорвать собности openvpn. Первым делом нужно посмотреть, какие
соединение из-за неактивности. из алгоритмов шифрования нам доступны:

В данной статье мы будем работать только с маршрути- # openvpn --show-ciphers


зируемыми (routed) туннелями, а разговор о туннелях типа
мост (bridge) отложим до следующего раза. Как я уже упо-
мянул ранее, маршрутизируемый туннель умеет работать
только с TCP/IP-трафиком. Соответственно широковеща-
тельный трафик через него не пройдет. А это значит, что
некоторые протоколы вроде Microsoft Netbios работать по-
верх нашего туннеля не смогут.
Задачей на сегодня будет соединение через VPN трех
удаленных филиалов одной организации. Будем считать,
что они называются магазин, склад и офис. В каждом из
филиалов стоит маршрутизатор, имеющий на борту по два
реальных и два виртуальных сетевых интерфейса. Первый
из реальных интерфейсов направлен во внутреннюю ло-
кальную сеть с серым адресным пространством, а второй –
в Интернет. Адреса для внешних интерфейсов выданы нам
провайдером и принадлежат к сети 80.80.20.0/24. Для вир- Довольно неплохо. Стоит обратить внимание на надпи-
туальных tun-интерфейсов выбрана подсеть 10.3.0.0/24. си variable и fixed возле каждого наименования шифра. Они
Òàáëèöà 1 указывают, существует ли возможность самостоятельно
выбирать размер ключа.
Для аутентификации каждой получаемой UDP-датаграм-
мы применяется так называемый механизм дайджеста.
Смотрим доступные дайджесты:

# openvpn --show-digests

6
администрирование
Теперь нужно создать конфигурационные файлы, кото-
рые опишут наши туннели. Первый из них описывает тун-
нель между машинами Linux и FreeBSD, а второй – между
Linux и Windows. Файл linux-freebsd.conf:

proto udp
dev tun
port 5000
comp-lzo
ping 15
verb 3
user openvpn
group openvpn
remote 80.80.20.129
ifconfig 10.3.0.9 10.3.0.10
route 10.10.120.0 255.255.255.0 10.3.0.10
secret /etc/openvpn/static.key
auth MD5
cipher DES-CBC
tun-mtu 1500

Файл linux-windows.conf:
Сначала нужно сгенерировать статический ключ, с по-
мощью которого в дальнейшем будет производиться шиф- proto udp
dev tun
рование потока. port 5002
comp-lzo
#openvpn --genkey --secret /etc/openvpn/static.key ping 15
verb 3
user openvpn
group openvpn
Внутри файла static.key должно появиться что-то похо- remote 80.80.20.128
жее на следующие данные: ifconfig 10.3.0.6 10.3.0.5
route 10.10.130.0 255.255.255.0 10.3.0.5
secret /etc/openvpn/static.key
tun-mtu 1500
auth MD5
cipher DES-CBC

Начинаем разбираться по порядку, что означает каж-


дая из этих опций.
! proto – протокол, используемый для передачи данных.
Может принимать значения udp, tcp-client, tcp-server. На-
стоятельно рекомендуется использовать udp, потому что
туннелирование поверх TCP создает слишком большие
накладные расходы. Впрочем, если нет другого выхода,
то и TCP сойдет. В таком случае нужно на одном конце
туннеля установить настройки этой переменной как tcp-
server, а на другом – как tcp-client. Соответственно от-
вечать за инициацию соединения будет машина со зна-
После этого можно провести тестирование криптосис- чением tcp-client. По умолчанию эта переменная имеет
темы с использованием только что созданного ключа. значение udp, поэтому если вы с этим согласны, то ее
определение можно в дальнейшем не использовать.
# openvpn --test-crypto --secret /etc/openvpn/static.key ! port – порт, на котором нужно ждать соединений. Дол-
жен быть одинаковым на обоих концах туннеля. Номе-
ра портов для разных туннелей не могут совпадать.
! dev – тип виртуального устройства туннеля. Может при-
нимать значения tun, tap, null. В этом случае OpenVPN
будет пытаться динамически выбрать и задействовать
первое свободное устройство данного типа. Если же ему
это не удается, то нужно будет четко указать имя устрой-
Судя по надписям, все работает вполне приемлемо. Ко- ства с помощью опции dev-node. Например, dev-node tun1.
нечно, не стоит забывать, что статический ключ имеет один ! comp-lzo – включение упаковки потока данных. В слу-
большой недостаток. Если злоумышленнику удастся его чае если поток все же не стоило упаковывать, размер
раздобыть, то он сможет расшифровать все данные, кото- передаваемых данных увеличится всего на один байт.
рые передавались по сети. С динамическим ключом такое Впрочем, за все время эксплуатации данного программ-
развитие событий нам бы не угрожало. В самом худшем ного комплекса я не встречал случая, когда система
случае можно было бы расшифровать только малую часть адаптивной компрессии ошиблась и стала пытаться при-
трафика. менить сжатие там, где делать этого не стоило.

№8(21), август 2004 7


администрирование
! ping – в случае если по VPN-каналу не передается ни- ! cipher – алгоритм, используемый для шифрования паке-
каких данных, приказывается отправлять ping каждые тов. Blowfish выбран потому, что является весьма стойким
n секунд, чтобы не позволить соединению разорваться и в то же время достаточно быстрым. В случае если хочет-
из-за простоя. Полезно в случае, если между конечны- ся повысить надежность шифрования, нужно использовать
ми точками vpn находятся межсетевые экраны с конт- переменную keysize и указывать большой размер ключа.
ролем состояния. По умолчанию Blowfish использует ключ длиной 128 бит,
! verb – уровень подробности выводимых сообщений. хотя максимально возможный размер – 448 бит.
Чем больше число, тем многословнее и педантичнее
программа будет рассказывать о том, что происходит На этом установку и первоначальную настройку пакета
у нее внутри. Верхним пределом для этой переменной на Linux-машине можно считать завершенной. Переходим
является число 11. Данная опция чаще всего исполь- на FreeBSD. Как человек рассудительный и здравомысля-
зуется при отладке и первоначальной настройке тун- щий, предпочитаю устанавливать сложное программное
нелей. обеспечение из портов.
! user, group – имя пользователя и группы, от имени ко-
торой будет работать программа. Первоначально # cd /usr/ports/archivers/lzo
# make install clean
openvpn стартует от имени root и, прочитав все инте-
ресующие файлы, снижает привилегии до указанного # cd ../../security/openssl
# make install clean
уровня.
! remote – IP-адрес хоста, представляющего из себя даль- # cd ../openvpn
# make install clean
нюю сторону туннеля. Если его не указать, то openvpn
будет пассивно принимать все входящие соединения, К сожалению, несмотря на то что перед началом инстал-
не пытаясь самостоятельно соединяться с удаленной ляции я обновил список портов, система упрямо не хотела
машиной. Затем все полученные соединения должны ставить openvpn-1.6.0, а вместо него подсовывала openvpn-
будут пройти авторизацию. Данный режим удобен для 1.4.0.
работы с dial-up системами, у которых постоянно меня- Дабы не наступать на грабли и впоследствии не ломать
ется IP-адрес. голову над совмещением разных версий, пришлось деин-
! ifconfig – назначает виртуальному tun/tap интерфейсу IP- сталлировать ранее установленную версию 1.4.0. Хотя в
адрес. Заодно указывает адрес удаленного виртуаль- документации описано, как «подружить» старые и новые
ного интерфейса. Это необходимо, потому что туннель версии, я решил рисковать.
работает как стандартное соединение «точка-точка».
! route – описывает маршрут, который должны пройти # make deinstall
пакеты, чтобы попасть в удаленную сеть. Можно обо-
значить маршрут двумя способами. Либо с помощью Отправляемся по адресу: http://www.freebsd.org/cgi/
этой настройки, либо переменной up, значением кото- ports.cgi?query=openvpn&stype=all и с помощью поиска вы-
рой необходимо указать имя командного скрипта, отве- ясняем, что исходные тексты самой свежей версии порта
чающего за выполнение правильной настройки марш- openvpn-1.6.0 не желают скачиваться. Ну на нет и суда нет.
рутизации. В случае если вы пользовались первым спо- Видимо, такова судьба, и нам придется снова пойти в об-
собом настройки маршрутизации, то openvpn самосто- ход. Берем отсюда бинарный пакет ftp://ftp.freebsd.org/pub/
ятельно удалит из таблицы маршрутов нужную запись, FreeBSD/ports/i386/packages-4-stable/All/openvpn-1.6.0.tgz и
когда пользователь попросит его завершить работу. Ну устанавливаем его с помощью утилиты pkg_add.
а если прибегали к услугам опции up, то необходимо
также описать опцию down, которая будет указывать на # pkg_add openvpn-1.6.0.tgz
скрипт, выполняющий самостоятельные действия по
удалению маршрута. Не забываем добавить в систему пользователя и груп-
! secret – указывает имя файла, в котором хранится ста- пу openvpn.
тический ключ, используемый для шифрования потока.
! tun-mtu – максимальный размер пакета, передаваемо- # adduser openvpn
го по виртуальному интерфейсу. Пакеты большего раз-
мера будут разбиваться на несколько кусков и переда- Затем создаем директорию /etc/openvpn, где у нас бу-
ваться последовательно отдельными датаграммами. дут храниться файлы с настройками и ключом. С помощью
Желательно, чтобы на обоих концах туннеля значение какого-либо безопасного транспорта переносим с Linux-
этой переменной было одинаково, иначе можно прове- машины файл static.key. В качестве средства, пригодного
сти несколько интересных часов в забавном поиске при- для этой цели, можно использовать scp, sftp, архив с паро-
чины, почему авторизация и передача первых пакетов лем или просто дискету, переданную доверенным лицом.
проходит нормально, а затем все начинает работать Теперь давайте посмотрим на содержимое конфигураци-
весьма медленно и нестабильно. онных файлов.
! auth – наименование алгоритма, используемого для Файл freebsd-linux.conf:
аутентификации приходящих пакетов. В моем случае это
MD5. Хотя никто не мешает взять любой другой. dev tun

8
администрирование
port 5000
comp-lzo
ping 15
verb 3
user openvpn
group openvpn
remote 80.80.20.131
ifconfig 10.3.0.10 10.3.0.9
route 10.10.140.0 255.255.255.0 10.3.0.9
secret /etc/openvpn/static.key
auth MD5
cipher DES-CBC
tun-mtu 1500
comp-lzo
Файл freebsd-windows.conf:
Как только увидите надпись «Peer Connection Initiated
dev tun with», считайте, что дело сделано. Теперь с помощью ко-
remote 80.80.20.128
port 5001 манды ping можно проверить, видны ли оконечные адреса
ifconfig 10.3.0.1 10.3.0.2 из соединяемых частных сетей. В нашем примере это
route 10.10.130.0 255.255.255.0 10.3.0.2
secret /etc/openvpn/static.key 10.10.120.1 и 10.10.140.1. Не забываем настроить компью-
ping 10 теры, находящиеся в наших локальных сетях так, чтобы они
verb 3
tun-mtu 1500 считали машины Linux и FreeBSD шлюзами по умолчанию.
user openvpn Кстати, стоит отметить, что openvpn может вполне ус-
group openvpn
auth MD5 пешно работать и без всяких конфигурационных файлов.
cipher DES-CBC Дело в том, что все параметры можно передать в програм-
comp-lzo
му и из командной строки. К примеру, для поднятия того же
Я думаю, что, пользуясь предыдущими объяснениями, самого туннеля можно было скомандовать вот так:
будет довольно легко понять все используемые настройки.
Права на директорию /etc/openvpn и файлы в ней должны # openvpn --remote 80.80.20.128 --dev tun --port 5001 ↵
--ifconfig 10.3.0.1 10.3.0.2 --route 10.10.130.0 ↵
позволять чтение только пользователю root и группе wheel. 255.255.255.0 10.3.0.2 --secret /etc/openvpn/static.key ↵
Если очень хочется, то можно выполнить проверку дос- --ping 10 --verb 3 --tun-mtu 1500 --user openvpn ↵
--group openvpn --auth MD5 --cipher DES-CBC
тупных шифров так же, как мы делали это для Linux.
Наконец-то мы готовы к запуску первого из наших тун- Хотя, с моей точки зрения, данный способ выглядит не-
нелей. На Linux-машине выполняем следующую команду: сколько громоздко.
Стоит отметить один интересный факт: дистрибутив
# openvpn --config /etc/openvpn/linux-freebsd.conf openvpn для Linux устанавливает в систему скрипт /etc/rc.d/
init.d/openvpn, который помогает удобно управлять нашими
И соответственно на FreeBSD делаем так: туннелями с помощью команды service openvpn. Главной
особенностью скрипта является способность поднять тунне-
# openvpn --config /etc/openvpn/freebsd-linux.conf ли для всех *.conf-файлов, находящихся в /etc/openvpn. Та-
ким образом, нам не надо придумывать, как автоматически
На терминале Linux должны появляться следующие над- запустить требуемое количество экземпляров openvpn с нуж-
писи: ными настройками после перезагрузки системы.
Во FreeBSD тоже есть скрипт подобного предназначе-
ния. Сразу после инсталляции он обычно находится в /usr/
local/etc/rc.d/openvpn.sh.sample. Посмотрев внутрь него,
понимаем, что он совершенно бесполезен, так как практи-
чески ничего не умеет делать. Поэтому нам придется смас-
терить свой собственный вариант такого скрипта. Для это-
го нужно создать файл openvpn.sh и внести в него следую-
щие данные:

#! /bin/sh

case x$1 in
xstart)
/usr/local/sbin/openvpn --config ↵
/etc/openvpn/freebsd-linux.conf &
/usr/local/sbin/openvpn --config ↵
/etc/openvpn/freebsd-windows.conf &
;;
Ну а FreeBSD-машина должна говорить что-то вроде это- xstop)
killall -SIGTERM openvpn
го: route delete 10.10.130.0
route delete 10.10.140.0
;;
*) echo >&2 "Usage: $0 {start|stop}"
esac

№8(21), август 2004 9


администрирование
Обязательно кладем файл в /usr/local/etc/rc.d/ и даем ему Тут стоит сделать одну важную оговорку: под Windows
право на выполнение. Стоит обратить внимание на нали- не существует различия между tun- и tap-устройствами. А
чие знака «&» после запуска каждого экземпляра openvpn. еще точнее было бы сказать, что такая реализация tap-драй-
Сделано это потому, что данная программа жестко привя- вера позволяет устройству работать в любом из двух ре-
зывается к терминалу, с которого была запущена. Соот- жимов.
ветственно при отсутствии этого знака второй процесс Для нас столь длинное название устройства неудобно,
openvpn не запустится до тех пор, пока терминал не осво- поэтому переименовываем его во что-нибудь более крат-
бодится, а этого при правильном развитии событий не дол- кое и информативное. Например, в Linux. Затем с помо-
жно произойти никогда. Вторым важным моментом явля- щью меню Пуск → Программы → Open → VPNAdd a new
ется наличие команд route, удаляющих записи о маршру- TAP-win32 virtual ethernet adapter создаем еще один вирту-
тах в той ветке скрипта, которая отвечает за выполнение альный интерфейс и переименовываем его во FreeBSD. Ду-
действия stop. Удалять маршруты приходится вручную, по- маю, название каждого из этих интерфейсов достаточно
тому что к моменту завершения работы openvpn выполня- красноречиво говорит об их предназначении. Надеюсь, что
ется от лица одноименного пользователя и группы, а изме- все еще помнят, зачем мы создавали файл static.key. Если
нять таблицу маршрутизации имеет право только root. Ко- это действительно так, то копируем его в C:\Program
нечно, можно было бы портировать под FreeBSD версию Files\OpenVPN\config\ под именем staic.txt. По непонятной
скрипта, поставляющуюся в комплекте с Linux-версией про- причине файлы с расширением .key openvpn, работающие
граммы, но сейчас заниматься этим как-то недосуг. Воз- под Windows, не воспринимаются как секретные ключи. За-
можно, я сделаю это в следующей статье. тем создаем конфигурационные файлы наших туннелей.
Настало время перейти к настройке Windows-системы. На Стоит обратить внимание на тот факт, что для успешной
первый взгляд здесь все довольно просто. Берем дистрибу- работы у этих файлов должно быть расширение .ovpn.
тив для этой платформы на родном сайте программы http:// Файл windows-freebsd.ovpn:
prdownloads.sourceforge.net/openvpn/openvpn-1.6.0-install.exe.
Затем запускаем только что скачанный инсталлятор и ме- remote 80.80.20.129
dev-node FreeBSD
тодично жмем на кнопки «Далее» и «ОК». После установ- dev tun
ки в систему будет добавлен новый сетевой интерфейс со port 5001
ifconfig 10.3.0.2 10.3.0.1
странным именем «Подключение по локальной сети 3». secret static.txt
ping 10
verb 3
route 10.10.120.0 255.255.255.0 10.3.0.1
auth MD5
cipher DES-CBC
comp-lzo

Файл windows-linux.ovpn:

remote 80.80.20.131
dev tun
dev-node Linux
port 5002
ifconfig 10.3.0.5 10.3.0.6
secret static.txt
ping 10
verb 3
Если присмотреться внимательно к свойствам данного route 10.10.140.0 255.255.255.0 10.3.0.6
подключения, то можно заметить что оно представляет со- tun-mtu 1500
comp-lzo
бой не что иное, как интерфейс tap.
В сущности, все тесты, которые мы использовали до
этого, отлично работают и под управлением Windows. По-
этому, если есть желание, можете выполнить openvpn с
параметрами --show-digests и --show-ciphers. Единственное,
на что стоит обратить внимание, – это обязательное нали-
чие в конфигурационном файле записи dev-node с именем
используемого устройства. К сожалению, Windows умеет
автоматически находить и использовать нужное виртуаль-
ное устройство туннеля только в том случае, если оно в
системе представлено в единственном числе. Кстати, сто-
ит отметить тот факт, что в случае работы устройства tun
под Windows использовать для туннеля какие попало адре-
са из подсети 10.3.0.0 не получится, придется выбирать из
таблицы, выводимой командой openvpn --show-valid-subnets.
Как и под UNIX-системами, можно поднять туннель вы-
зовом openvpn с указанием имени используемого конфи-
гурационного файла, а можно и без него, просто перечис-

10
администрирование
лив все нужные ключи в командной строке. Есть еще два
способа запуска туннеля. Первый из них состоит в том, что
нужно с помощью проводника перейти в директорию
C:\Program Files\OpenVPN\config\ и щелкнуть правой кла-
вишей на файле конфигурации. В ниспадающем меню выб-
рать «Start OpenVPN on this config file». А можно поступить
более умно и включить службу OpenVPN, которая самосто-
ятельно будет заботиться о том, чтобы запустить по одно-
му экземпляру программы для каждого конфигурационно-
го файла.

Ну и на закуску осталось посмотреть на интерфейсы


Windows:
Настройка протокола IP для Windows 2000:

На этом радостном этапе установку и настройку можно


считать завершенными. На обоих UNIX-машинах сначала
останавливаем, а затем снова запускаем сервисы openvpn.

# service openvpn stop Адаптер Ethernet Linux:


# service openvpn start
# /usr/local/etc/rc.d/openvpn.sh stop
# /usr/local/etc/rc.d/openvpn.sh start

То же самое проделываем и под управлением Windows.


После этого все вышеописанные туннели должны пра-
вильно подняться и заработать без сбоев. Соответственно
таблица интерфейсов, получаемая с помощью ifconfig, на
Linux должна выглядеть так:

Адаптер Ethernet FreeBSD:

Адаптер Ethernet Подключение по локальной сети 2:

Адаптер Ethernet Подключение по локальной сети:

А для FreeBSD будет характерна следующая картина:

№8(21), август 2004 11


администрирование

Для проверки того, как работает туннель, запускаем с


разных сторон ping, направленный в удаленные сети, и смот- А проходя через интерфейс lnc1, в Интернет они попа-
рим на результаты. дают уже зашифрованными:
Теперь хотелось бы разобраться, как обстоит дело с
шифрованием. Для этого начинаем прослушивание интер- # tcpdump –i lnc1 -lenx
фейсов tun0 – 10.3.0.10 и lnc1 – 80.80.20.129 и снова вы-
полняем ping 10.10.140.1. Таким образом, мы видим, что
через tun0 пакеты icmp идут в открытом виде.

# tcpdump –i tun0 -lenx

Таким образом, видно, что поставленную задачу мы ус-


пешно выполнили.

12
администрирование

ЗАПУСК В VMWare ГОСТЕВОЙ ОСИ,


УСТАНОВЛЕННОЙ НА ФИЗИЧЕСКОМ ДИСКЕ
В этой статье будет описана возможность запуска
гостевой системы, установленной на компьютере
при помощи VMWare. ОС Windows 2000 Professional,
Linux (Fedora Core и SuSe) установлены самым
обычным способом. Для более рационального
использования времени и ресурсов необходимо
одну из них (в данном случае Windows) запускать
либо на виртуальной машине, либо на реальном
компьютере, и чтобы при этом сохранялись
все изменения, сделанные в системе. Это также
необходимо на случай отсутствия администратора
на работе. Про установку и запуск этой программы
можно прочитать на http://www.onix.opennet.ru, где
выложены статьи Андрея Бешкова, либо в прошлых
номерах журнала [1, 2, 3]. Но в них не описан вопрос
запуска уже установленной системы, находящейся
на физическом диске. Данный материал должен
помочь в этом.

НИКОЛАЙ ПОЧАБЫТОВ
У системного администратора или программиста очень ча- что на виртуальном компьютере мы получаем возможность
сто бывают ситуации, когда необходимо проверить рабо- работать сразу, ничего не переустанавливая.
ту программы или что-то сделать в другой ОС. Для этого В данной статье в качестве базовой ОС рассматрива-
на компьютер ставятся несколько систем. Но для того что- ется Linux Fedore Core 2 test1, а в качестве гостевой –
бы проверить работоспособность новой программы, надо Windows 2000 Professional.
перегружать компьютер, а это не всегда хорошо. Поэто- Из-за того, что FC2 работает на ядре серии 2.6.*, нужно
му был выпущен такой продукт, как VMWare (http:// устанавливать VMWare 4.5.1, в которой учтена возможность
www.wmware.com). Его особенности можно не расписывать, работать с этой серией ядер.
он позволяет многое, главное – с его помощью можно за- Переходим к главному – запуску установленной системы.
пустить в качестве гостевой операционной системы уже ус- Сформулируем требования:
тановленную на жестком диске. Вся суть заключается в том, ! Должны быть установлены Windows и Linux.

№8(21), август 2004 13


администрирование
! Резервная копия системы (на случай, если что-то не по-
лучится с первого раза).
! Хороший boot-менеджер.

В самом начале необходимо загрузиться в Windows и


провести cледующие настройки. Выбираем: Свойства сис-
темы → Оборудование → Профили оборудования:

После чего получаем примерно такую картинку:

Далее щелкаем по кнопке, и на экран выводится окно,


в котором перечислены существующие профили оборудо-
вания. Нам надо скопировать текущий профиль:

Обязательно надо отметить пункт «Дождаться явного


указания от пользователя». Далее необходимо отправить
компьютер на перезагрузку и выбрать в меню загрузки
Windows. Теперь там появляется дополнительный пункт «Вы-
бор профиля для загрузки», нам нужен только что создан-
ный профиль для виртуальной машины. Эти действия надо
проделать только для систем на основе NT (Windows NT4,
Windows 2000, Windows XP, Windows 2003). В Windows 9*
система plug and play, поэтому необходимые драйвера ус-
В появившемся окне вводим название профиля: тановятся сами.

14
администрирование
После того как загрузилась система с выбранным про-
филем, заходим в «Диспетчер устройств» и начинаем там
хозяйничать. Сразу же необходимо заметить, что надо от-
казываться от предложений перегрузить систему.
В первую очередь удаляем видеоадаптер:

После этого находим звуковые устройства и отключаем


их, так же поступаем и с сетевыми устройствами, USB:
Теперь систему можно перегружать в Linux, первый этап
закончен.
Второй этап начинается с конфигурирования и подго-
товке VMWare для работы. Будем считать, что VMWare уже
установлена и сконфигурирована, поэтому просто ее за-
пускаем.
И начинаем создавать новую виртуальную машину:

Выбираем пункт «Сustom». На следующем экране ука-


зываем нужную гостевую операционную систему. Далее вы-
бираем имя виртуальной машины и каталог, где будут хра-
ниться основные конфигурационные файлы. На следующем
экране выделяем количество оперативной памяти для гос-
тевой ОС. После этого отмечаем тип сетевого подключе-
ния. Потом предстоит определиться с I/O adapter types. Пра-
вильнее было бы назвать этот пункт «Контроллеры для под-
ключения НЖМД и CD-ROM». Контроллер ATAPI выбран по
умолчанию, а вот со SCSI-контроллерами предоставлен
выбор. Лучше всего оставить настройки по умолчанию и
перейти к следующему экрану.
Здесь необходимо отметить один из трех вариантов:
! Создание нового виртуального диска.
! Использование существующего виртуального диска.
! Использование физического диска.

Для контроллера IDE нужно сменить драйвера на стан- Так как наша цель запустить на виртуальном компью-
дартные из поставки Windows: тере установленную систему, то выделяем последний пункт:

№8(21), август 2004 15


администрирование
На следующем экране выбираем имя конфигурацион-
ного файла и место его хранения. Возможно, будут еще
какие-то вопросы (зависит от оборудования и дистрибути-
ва Linux). Отвечаем на них утвердительно. VMWare практи-
чески готова к запуску:

При нажатии кнопки «Next» появится предупреждение,


соглашаемся с ним и видим следующий экран, на котором
предстоит выбрать устройство и использование какой-то
части или всего диска. Лучше остановиться на тех разде-
лах, на которых у вас установлена система Windows, и мо-
жет быть, какие-то дополнительные диски (если у вас су-
ществуют дополнительные разделы для Windows. Если на
компьютере существует два и более жестких диска, и сис-
темы установлены на разных физических дисках, то необ- Выбираем редактирование свойств виртуальной маши-
ходимо для нормальной работы подключать все жесткие ны. Надо поправить всего лишь один параметр – подклю-
диски, иначе система не будет загружаться): чение CD/DVD-ROM должно быть SCSI:

Запускаем виртуальную машину. И вот здесь вполне


возможно столкнуться с трудностью, если boot-менеджер –
GRUB, то Windows грузиться не будет. Решение пока не най-
дено. Такая же проблема может быть и с lilo. При попытке
запустить Windows появляется ошибка, говорящая о том,
что NTLoader не найден. Самым простым выходом из дан-
ной ситуации является установка другого boot-менеджера,
лучше понимающего файловые системы FAT и NTFS. В
данном случае был использован Acronis OS Selecter 8.0, так
как он работает с файловыми системи ext3 и reiserfs, впол-
не возможно, что и другие boot-менеджеры справятся с этой
задачей.
И вот появляется долгожданное меню выбора профиля
загрузки:

16
администрирование
Здесь необходимо сразу отметить несколько моментов
по работе:
! USB Flash-накопители работают очень хорошо и без до-
полнительных настроек.
! Может возникнуть серьезная проблема у тех, кто пользу-
ется USB-шнуром для связи мобильного телефона и ком-
пьютера. Драйвера и программное обеспечение уста-
навливаются без проблем и также нормально работа-
ют. Но при перезагрузке можно увидеть «синий экран
смерти». Возможно, он появляется из-за отсутствия под-
ключенного кабеля или телефона. Вариантов решения
данной проблемы я пока не нашел, поэтому нужно заг-
рузиться в Windows и там удалить драйвера и ПО от
этого кабеля.
! Возникает странная «плавающая ошибка» с интегриро-
ванным SCSI-контроллером. Непонятно почему, но от-
рубаются даже мышь с клавиатурой, что может потре-
Естественно, что надо выбирать профиль, созданный бовать переустановки системы.
для виртуальной машины. При загрузке Windows будет ис-
кать новые устройства и драйвера для них, поэтому после Также в VMWare можно загрузить установленные на же-
того, как будут введены логин и пароль, нужно проинстал- стком диске и другие системы, в частности Linux. Здесь си-
лировать VMWare Tools (VM → Install VMWare Tools). туация попроще и система спокойно может стартовать при
помощи стандартных линуксовых загрузчиков lilo или GRUB.
Для запуска Linux в качестве гостевой системы не требуется
никаких дополнительных заморочек и профилей. Достаточ-
но создать в VMWare новую гостевую систему и запустить
ее. В качестве гостевой системы бралась Red Hat Linux AS
3.0. Kudzu не был отключен, поэтому установка и удаление
новых устройств прошло без проблем, система запустилась
и начала работать. Возможно следующее: если в системе
есть SCSI-адаптер и SCSI-устройства, то они не примонти-
руются и не будут работать, соответственно в логах будут
ошибки монтирования этих устройств. Так же будет конф-
ликтовать Xfree86 и придется конфигурировать его заново.
Таким образом, получаем решение, с которым очень
удобно работать, и с которым можно вписаться в любую
После этого перезагружаемся, если возникнет необхо- сеть. Вы можете также ознакомиться с книгой В. Костро-
димость. Теперь можно полноценно работать с системой: мина [4], с той ее частью, где описывается работа с VMWare
(следует учесть, что в ней описана предыдущая версия (до
4.0) программы, и в качестве boot-менеджера выступает
NTLoader).

Литература:
1. Бешков А. Виртуальный полигон для администратора и
разработчика. – // Журнал «Системный администратор»,
№9(10), сентябрь 2003 г. – 08-23 с.
2. Бешков А. Виртуальный полигон для разработчика и ад-
министратора на основе Linux и VMWare. – // Журнал
«Системный администратор», №11(12), ноябрь 2003 г. –
04-17 с.
3. Бешков А. VMWare со всеми удобствами. – // Журнал
«Системный администратор», №6(19), июнь 2004 г. –
12-17 с.
4. Костромин В. Linux для пользователя. - «БХВ-Петер-
бург», 2002 г.

№8(21), август 2004 17


администрирование

LINUX И NTFS
«...ntfs not supported by kernel»
Сообщение системы об ошибках.

СЕРГЕЙ ЯРЕМЧУК
До недавнего времени острой необходимости в доступе к ются еще проблемы, например, драйвер не понимает за-
разделам с файловой системой NTFS, в общем-то, и не было. шифрованные файлы, квоты, игнорирует информацию бе-
Ругать разработчиков ядра Linux не за что, необходимые зопасности и ограничен по записи. Проблемы эти, в об-
работы ведутся уже давно и отнюдь не безуспешно. Уже в щем-то, существуют не по вине разработчиков. Все дело
1995 году для ядер серии 2.0 был доступен патч для работы в том, что Microsoft не выпустило никакой документации
с этой файловой системой, а с версии 2.2 (если быть точнее по внутреннему строению NTFS, и драйвера разрабаты-
2.1.74) подержка NTFS в ядро была включена стандартно. ваются фактически с пустого листа, методом научного
Но все равно разработки велись несколько вяло. Причин тому тыка и плясания с бубном вокруг компьютера. Строение
несколько. Семейство пользовательских операционных си- же NTFS довольно сложное и внутри напоминает базу дан-
стем Windows до Me включительно поддерживают исключи- ных, когда изменение в одном месте требует замен и во
тельно FAT, а NTFS использовалась на ОС корпоративно- многих других местах файловой системы, а иначе она по-
серверного уровня, где вряд ли кто-то додумается поставить просту будет разрушена. Поэтому новый драйвер пока
одновременно две операционные системы на одном компь- может только перезаписывать существующие файлы, но
ютере. Появление Windows 2000 практически ничего не из- не может изменять их длину, добавлять новые и удалять
менило, на домашних компьютерах ее устанавливали доволь- старые файлы, не доступны операции с каталогами.
но неохотно, и если посмотреть на форумах тех времен, то Теперь немного практики. В ядрах, начиная с 2.5.11 и,
можно отметить относительное спокойствие. А вот Windows конечно же, в серии 2.6, используется новый драйвер, по-
XP смог привлечь пользователя, этот момент и стоит счи- этому если у вас с поддержкой не сложилось, то просто
тать началом действительного интереса разработчиков к про- перекомпилируйте ядро, включив нужные пункты. Так как
блеме. Многие (да почти все) производители включили под- в народе еще популярны ядра серии 2.4, то для их владель-
держку NTFS в ядрах своих дистрибутивов. Проблем с дос- цев доступен патч.
тупом к разделам NTFS не обнаружат пользователи Текущую версию драйвера узнать очень просто:
Mandrake, SUSE, ALTLinux, ASPLinux, Slackware, Debian и
прочих популярных дистрибутивов. Только компания RedHat, # dmesg | grep -i ntfs
очевидно, руководствуясь лицензионной чистотой своего ди-
стрибутива, не включила поддержку данной ФС. Поэтому
пользователи RedHat и Fedora увидят при попытке монтиро- Или такой вариант:
вания раздела сообщение, вынесенное в эпиграф.
На данный момент имеются два свободных проекта, по- # grep -i ntfs /var/log/messages
своему решающие вопрос работы с разделами NTFS. Пер-
вый, проект Linux-NTFS, предлагает традиционный подход,
т.е. написание драйвера, который позволит нормально ра- Как видите, ядро Slaksware 9.1, которое использовалось
ботать с этой файловой системой. Второй, проект Captive во время написания статьи, поддерживает первую версию
NTFS, пробует решить все эмуляцией системы NT. драйверов, но это довольно легко исправить. Если вывод
этих команд ничего не дал, то дополнительно проверить
Проект Linux-NTFS поддерживаемые ядром файловые системы можно, введя:
Сейчас для GNU/Linux фактически существуют два драй-
вера NTFS. Первый используется в ядрах серии 2.4.х и в # cat /proc/filesystems
2.5.0-2.5.10, этот драйвер имеет ограниченные возможно-
сти по записи в раздел NTFS, и поэтому такой режим по- За драйверами, патчами, утилитами для работы с NTFS
мечен как опасный, т.е. может привести к потере данных. и документацией идем на сайт http://linux-ntfs.sourceforge.net.
Этот драйвер долго практически не развивался. Вторая Владельцам RedHat и Fedora можно идти сразу на стра-
версия драйвера была фактически переписана заново ницу http://linux-ntfs.sourceforge.net/rpm/index.html, где дос-
одним из разработчиков Антоном Алтапармаковым (Anton тупны прекомпилированные rpm-пакеты с необходимыми
Altaparmakov), причем записи в раздел NTFS уделялось модулями. Выбираем нужный и устанавливаем.
повышенное внимание, драйвер стал более легким, про-
стым и быстрым, поддерживаются NTFS версий 1.2, 3.0 и #rpm -ihv --noscripts kernel-ntfs-2.6.5-1.358.i586.rpm
#/sbin/depmod -a
3.1, Unicod, сжатые файлы. Хотя в настоящее время име-

18
администрирование
Например, мы хотим пересобрать ядро 2.4.25 с новым тема не сможет сама определить ее и выдаст примерно
драйвером. такое сообщение:
Скачиваем по ссылке патч к используемому ядру, в моем
случае это linux-2.4.25-ntfs-2.1.6a.patch.bz2, и распаковыва-
ем его.
Для того чтобы просмотреть опции по умолчанию, с ко-
# bunzip2 linux-2.4.25-ntfs-2.1.6a.patch.bz2 торыми монтируется раздел, вводим:

Теперь распаковываем ядро, взятое с www.kernel.org, # cat /proc/mounts | grep -i ntfs


можно использовать и имеющееся в вашем дистрибутиве,
только тогда возьмите и патч с соответствующим номером.
Как видите, раздел смонтирован в режиме «чтение-за-
#cd /usr/src пись», это можно изменить на «только чтение», явно задав
в командной строке опцию -r (или -o ro). Владельцем явля-
Накладываем патч: ется root (uid=0, gid=o), опция errors указывает, как будет
себя вести система при возникновении ошибок, поддержи-
#cd linux вается два варианта: continue (продолжает работу) и recover
#patch -p1 < ../linux-2.4.25-ntfs-2.1.6a.patch
(пытается восстановить), в настоящее время поддержива-
В последнем случае не должно быть сообщений об ется только замена boot-сектора резервным. Опции fmask
ошибках, иначе проверьте, правильно ли заданы пути. И и dmask задают параметры доступа к файлам и каталогам
теперь приступаем к конфигурации ядра. Набираем make соответственно, возможно использование общей опции
nenuconfig, в «File systems» необходимо включить пункты umask, задающей доступ к файлам и каталогам одновре-
драйвера NTFS (рис. 1). Сохраняем конфигурацию и выхо- менно. NTFS хранит имена в Unicode, но драйвер перево-
дим. После чего идет стандарная компиляция ядра и на- дит их в ASCII; чтобы указать на используемый язык, при-
стройка загрузчика. меняются две конструкции: -o iocharset= или в новом вари-
анте -o nls=. Для отображения русских имен используются
koi8-r, возможно задание utf8 (если ядро не поддерживает
Unicod, то дополнительно используйте utf8=true). В моем
случае nls был выбран автоматически, потому что при кон-
фигурировании ядра эта кодировка была прописана по
умолчанию (смотрите в File Systems – Native Language
Support – Default NLS Option). Параметр mft_zone_multiplier
указывает на размер зарезервированной в master file table
части, которая содержит информацию о файле. Примеча-
тельно, что маленькие файлы полностью помещаются в
MFT, что позволяет быстро его находить и избежать потерь
дискового пространства присущих FAT (в FAT файл разме-
ром 1 байт займет на диске минимум 4 Кб). Первоначально
задается при форматировании, но в процессе эксплуата-
Ðèñóíîê 1 ции может изменяться на лету. Цифра 1 является значени-
Выполнив все, перезагружаемся с новым ядром и про- ем по умолчанию и соответствует 12.5% зарезервирован-
веряем: ного объема, 2 – 25%, 3 – 37.5% и 4 – 50.0%. Полная опция
монтирования может быть указана в таком виде:
#grep -i ntfs /var/log/messages
#mount /dev/hda7 /mnt/temp/ -t ntfs -r -o nls=koi8-r -o ↵
uid=500,gid=winuser,umask=0222

Как видите, версия драйвера обновилась и соответству- часть после второй -o, вообще говоря, не нужна и дана для
ет установленной; пробуем примонтировать раздел, для того примера.
чтобы узнать, какой именно, вводим: После этого с чтением данных проблем быть не долж-
но, все имена будут отображаться нормально.
# fdisk -l | grep -i ntfs Дополнительно проект Linux-NTFS предоставляет и ряд
утилит для работы с NTFS из-под Linux, библиотеку libntfs,
обеспечивающую доступ к функциям NTFS программ, в том
Монтируем раздел. числе и других разработчиков, а также libntfs-gnomevfs –
модуль для Gnome VFS (virtual filesystem), обеспечивающий
# mount /dev/hda7 /mnt/temp/ -t ntfs универсальный доступ ко всем файловым системам. Боль-
шая часть утилит ориентирована скорее на разработчиков,
Обратите внимание, что ключ -t, указывающий на фай- но годом раньше вообще ничего подобного не было, про-
ловую систему, в данном случае обязательный, иначе сис- гресс налицо. Все они доступны в пакете ntfsprogs, который

№8(21), август 2004 19


администрирование
распространяется как в исходных кодах, так и имеются пре- ! ntfsinfo – выводит атрибуты по номеру inode или имени
компилированные пакеты. Установка из исходников проблем файла.
не вызывает, все те же стандартные ./configure; make; make ! ntfslabel – выведет или установит метку файловой сис-
install. В результате в системе появится еще 10 утилит: темы NTFS (метка до 128 Unicode-знаков).
! ntfsfix – для установки измененных драйвером разде-
лов NTFS, нечто вроде scandisk, который должен исполь- # ./ntfslabel -v /dev/hda7
зоваться после каждой записи (особенно со старым
драйвером) во избежание возможной потери данных,
для того чтобы привести файловую систему в непроти- ! ntfsls – аналог UNIX-утилиты ls (Windows – dir) – выво-
воречивое состояние. дит список файлов в разделе NTFS (монтировать нео-
! mkntfs – для создания NTFS 1.2 (поддерживается всеми бязательно).
Windows NT/2000/XP).
! ntfscat – является аналогом стандартной UNIX-утилиты # ./ntfsls -v -d /dev/hda7
cat, предназначен для чтения файлов в разделах NTFS.
! ntfsclone – предназначена для клонирования (копирова-
ния, сохранения, создания резервного образа и восста-
новления) раздела с файлововй системой NTFS. Рабо-
тает на уровне секторов диска и сохраняет только ис- ! ntfsresize – а вот это действительно полезная утилита,
пользуемые данные, неиспользуемые заполняются ну- предназначена для изменения размера файловой сис-
лями, что позволяет эффективно сжимать полученные темы NTFS без потерь данных. Примечание: утилита не
образы. Полезна для создания точных копий раздела и манипулирует размерами разделов, для этого необхо-
восстановления системы. Примеры: димо воспользоваться утилитой fdisk. Как работать с
! Создание копии раздела: ней, поговорим дальше.
! ntfsundelete – восстановление удаленных файлов на раз-
#ntfsclone --output system.img /dev/hda1 деле NTFS. Имеет три режима работы:
! scan (режим по умолчанию) – просмотр файловой
! Восстановили раздел: системы на предмет наличия удаленных файлов, при
нахождении выводит список таких файлов;
#ntfsclone --overwrite /dev/hda1 system.img ! undelete – пытается, насколько это возможно, восста-
новить утраченные данные (кроме сжатых и зашиф-
! Так можно заглянуть внутрь созданного образа: рованных файлов), файл сохраняется в другой раздел;
! copy – полезен большей частью при отладке, сохра-
#mount -t ntfs -o loop system.img /mnt/ntfsclone няет данные MFT в файл.

! ntfscluster – идентификация файлов в указанном разде- #./ntfsundelete /dev/hda7


ле или области NTFS. Работает в трех режимах:
! info (режим по умолчанию) – покажет общую инфор-
мацию об области NTFS; Сообщение «Volume is dirty» может возникнуть после за-
! sector – покажет список файлов в заданном диапа- писи в раздел, изменения размера раздела и других воз-
зоне секторов; можных операций, связанных с изменением данных, после
! cluster – то же, что и предыдущий, только выводит каждой такой операции во избежание несоответствия ре-
список файлов в группе. комендуется проверка раздела средствами Windows.

#./ntfscluster /dev/hda7 #./ntfsundelete /dev/hda7 --force

20
администрирование
Пробуем восстановить один из найденных файлов: но работает не со всеми версиями драйверов Windows (пол-
ный список проверенных в работе доступен на сайте), во
#./ntfsundelete /dev/hda7 -s -m home.jpg --force всяком случае со взятыми с русифицированной версии
Windows XP с первым сервис-паком работа у меня не по-
шла. В этом случае придется тащить файлы с сайта Microsoft,
где они находятся по адресу: http://www.microsoft.com/
WindowsXP/pro/downloads/servicepacks/sp1/checkedbuild.asp,
с которыми captive работает отменно.
Правда, ситуация с их использованием двоякая, с од-
В настоящее время начата разработка еще нескольких ной стороны, они лежат свободно, с другой – предназначе-
утилит, некоторые из них находятся в стадии альфы. Это ны для пользователей Windows, во всяком случае во всех
ntfswipe – позволит зачистить нулями свободные части дис- дистрибутивах, имеющих captive, их приходится добывать
ка, ntfsdefrag – дефрагментатор файлов, каталогов и MFT, самостоятельно. Для работы их следует положить в ката-
для проверки диска будет использоваться ntfsck, nttools лог /var/lib/captive. Так как драйверы Windows требуют осо-
позволит просмотреть/создать/изменить/копировать/найти бых привилегий вроде прямого доступа к железу, то пол-
требуемые значения (эквивалентны командам ntfscp, ную 100% эмуляцию реализовать не получится, т.к. UNIX-
ntfsgrep, ntfstouch, ntfsrm, ntfsrmdir, ntfsmkdir). И пока в пла- системы, естественно, будут защищаться от процессов, ко-
нах еще одна утилита ntfsdiskedit, которая позволит рабо- торые лезут не в свое дело, поэтому эмулируемая среда
тать с дисковыми структурами NTFS. отделена от остальной части UNIX и любой код, выполняю-
Начиная с Windows 2000, файловая система получила щийся в этой среде, не должен привести к краху системы.
новое понятие – динамический диск, который пришел на Для защиты от возможного краха используются несколько
смену стилю разделов, принятых еще в MS-DOS (Basic технологий. Так, для работы потребуется модуль ядра LUFS
Disks), и позволил снять все ограничения файловой систе- (Linux Userland File System), который, если будете собирать
мы, в том числе и создавать многодисковые тома. Инфор- из исходников, нужно взять с http://lufs.sourceforge.net/lufs.
мация хранится в журналируемой базе данных Logical Disk Во время установки создается новый пользователь и груп-
Manager (LDM) в последнем 1 Мб диска. Начиная с версии па captive, от имени которых и будут работать процессы, а
ядра 2.5.29, в него включаются драйвера для работы с LDM, сам процесс по умолчанию запускается в изолированой
для версий 2.4.19 и 2.4.20-pre1-ac1 доступен патч. Отдель- CORBA sandbox среде и chroot-окружении. К сожалению,
но в пакете linux-ldm доступны две утилиты для работы с эти ограничения сказались на невозможности работы од-
LDM. Первая ldminfo выводит детальную информацию о новременно сразу с несколькими разделами. Драйверы же
LDM-базе, ldmutil – предназначена для восстановления, ре- на сайте проекта captive доступны как в исходных кодах,
зервирования и изменения LDM-баз. Планируется в бли- так и в прекомпилированном виде со статической линков-
жайшее время начать работу над библиотекой ldmlib, в ко- кой, работают они одинаково, проблема может быть толь-
торую будет вынесена часть кода, и еще над несколькими ко с модулем ядра lufs.o, который должен быть собран под
утилитами, которые позволят работать с отдельными час- определенную версию ядра. После установки для первона-
тями раздела LDM и записывать/читать информацию с чального конфигурирования необходимо запустить утили-
базы. Как видите, хотя работа над новыми драйверами и ту captive-install-acquire (рис. 2), которая проверит наличие
утилитами идет полным ходом, но, увы, на данный момент необходимых библиотек и при необходимости закачает все
они не могут обеспечить пользователя требуемой (полной) нужное.
функциональностью, и, как говорится, нормальные герои
всегда идут в обход, что и сделали разработчики в проек-
те, о котором пойдет речь дальше.

Проект Captive NTFS


Тема эмуляции работы операционных систем, и в частно-
сти Windows, довольно популярна в среде Linux/UNIX, до-
статочно вспомнить проекты вроде Wine или Crossover
Office, поэтому не удивительно, что нашлись разработчи-
ки, попытавшиеся заставить работать Linux с разделами
NTFS средствами самой Windows.
Свободный проект, реализующий возможность запи-
си/чтения данных с разделов NTFS путем эмуляции всех
необходимых уровней Windows, называется Captive и на-
ходится по адресу: http://www.jankratochvil.net/project/
captive. Для работы captive потребуется драйвер ntfs.sys и
системный модуль ядра NT – ntoskrnl.exe. Если под рукой
имеются компьютеры с установленными Windows XP, то
их можно взять в C:\WINDOWS\system32\ntoskrnl.exe и
C:\WINDOWS\system32\drivers\ntfs.sys), но captive нормаль- Ðèñóíîê 2

№8(21), август 2004 21


администрирование
Если все есть, то можно монтировать раздел: источником, т.к. в некоторых дистрибутивах вам позволят
изменить раздел только с командной строки (например, заб-
# mount -t captive-ntfs /dev/hda7 /mnt/utils/ равшись во вторую консоль [Ctrl]-[Alt]-[F2]). Для работы
ntfsresize драйвер поддержки NTFS в ядре не нужен, ути-
В случае ошибок вся информация доступна /var/log/ лита обращается напрямую к диску. Также если не требу-
messages. ются все утилиты или хотите использовать ее в спасатель-
ной дискете, то можно взять статически слинкованную вер-
#cat /var/log/messages | grep captive-lufs сию ntfsresize по адресу: http://mlf.linux.rulez.org/mlf/ezaz/
ntfsresize-static-1.9.1.tgz.
Если требуется автоматическое монтирование при заг- Теперь посмотрим, что у нас есть:
рузке системы, используйте скрипт captive-install-fstab с па-
раметром -add, который автоматически добавит в файл /etc/ # ./ntfsresize -i /dev/hda7
fstab используемый раздел.
Еще пару слов хочу сказать о коммерческом драйвере
от Paragon Software Group, обеспечивающем прозрачный
доступ к разделам с файловой системой NTFS. Доступен
драйвер в двух версиях: персональной и профессиональ-
ной. Как и у предыдущих проектов, поддерживаются все
версии файловой системы NTFS, сжатые файлы и катало-
ги, размеры дисков до 127 Гб, в персональной версии ра-
бота возможна только в режиме чтения, а в професиональ-
ной возможна запись (что, в общем, и не должно вызывать
удивления; учитывая опыт работы этой компании на подоб- Команда выдала все о разделе NTFS и сообщила, что
ном поприще). Демо-версию драйвера, которую произво- можем уменьшить раздел вплоть до 5 Мб. Но перед реаль-
дитель разрешает использовать без регистрации в тече- ным изменением желательно прогнать тест.
ние 30 дней и поддерживающую только чтение, можно ска-
чать с http://www.ntfs-linux.com. # ./ntfsresize --no-action --size 500M /dev/hda7
Для установки потребуются исходники ядра. Установка
проста до безобразия: после распаковки архива запуска- Если в результате получим сообщение «The read-only
ем скрипт install.sh (возможен запуск в интерактивном ре- test run ended successfully.», то можно смело приступать к
жиме ./install.sh –interactive и при помощи –iocharset=koi8-r изменению размера. Если же команда выдала «ERROR:»,
возможно установить кодировку по умолчанию для разде- то лучше для начала исправить ошибки, спешка в данном
ла NTFS). случае ни к чему хорошему не приведет.
После этого, если не выбрано автоматическое монти-
рование при загрузке, можно примонтировать раздел вруч- # ./ntfsresize -s 500M /dev/hda7
ную:

#mount -t ufsd /dev/hda7 /mnt/test_ntfs


# mount -t ufsd -o iocharset=koi8-r /dev/hda7 /mnt/test_ntfs

Изменение размеров NTFS


из-под GNU/Linux
Теперь давайте попробуем разобраться с вопросом, мож-
но ли изменить размер раздела с файловой системой NTFS
прямо из-под GNU/Linux, не прибегая к посторонним ути-
литам вроде Partition Magic. По заверению разработчиков Если все данные сохранены, то соглашаемся:
проекта Linux-NTFS, утилита ntfsresize, доступная пользо-
вателям с июля 2002, позволит изменить размер раздела,
не разрушив при этом данных, причем нормально поддер-
живаются все версии NTFS и работает нормально со все-
ми версиями системы (Windows XP/2000/NT4, Windows
Server 2003 и даже беты Longhorn). Разработчиками сде-
лано все, чтобы свести риск потери данных к минимуму,
для подстраховки проводятся всевозможные проверки,
включая проверку на непротиворечивость данных, и, най-
дя проблемы и при возникновении подозрений, утилита
отказывается производить изменение размера. Давайте
проверим. Хотя уже появились графические средства, ис-
пользующие ntfsresize, но для начала разберемся с перво- Проверить работу можно, введя:

22
администрирование
#./ntfsresize --info --force /dev/hda7 Если же вся работа производилась в работающей с
жесткого диска системе, необходимо заново перечитать
Как видите, утилита свою работу проделала, но изме- данные о новом разбиении, для чего придется перезаг-
нила только размер самой файловой системы NTFS, раз- рузиться.
мер же дискового раздела остался неизменным (что мож- Но для нормальной работы обязательно нужно прове-
но легко проверить, введя: fdisk -l | grep -i ntfs), и далее, рить файловую систему средствами Windows.
следуя инструкции, необходимо проделать еще несколько Уже появились и графические утилиты, в том числе и
шагов. инсталляторы, позволяющие изменить раздел в наглядном
Неплохо бы для начала на всякий случай сохранить MBR. и понятном любому пользователю виде, которые использу-
ют код ntfsresize. К таким приложениям относятся DiskDrake
# dd if=/dev/hda of=hda.mbr bs=512 count=1 от MandrakeSoft, который может встретиться и в других
производных от Mandrake дистрибутивах, YaST от SUSE
Запускаем fdisk, конечно, было бы нагляднее восполь- доступный и после установки. Некоторые дистрибутивы
зоваться cfdisk, но если указать ему новое значение разде- используют коммерческие утилиты для разбиения разде-
ла в мегабайтах, то он округлит до ближайшего значения, лов ASPLinux – PartitionExpert, Xandros использует PQDisk.
которое, вполне возможно, будет меньше требуемой вели- Но самой известной, распространенной и, главное, свобод-
чины, что приведет к невозможности работать с разделом, ной графической утилитой, предназначенной для разбие-
fdisk же корректно обрабатывает ситуацию. ния диска, является QTParted (http://qtparted.sourceforge.net,
рис. 3). Те, кто пользовался PartitionMagic, трудностей с ос-
# fdisk /dev/hda воением не встретят. Выбираем нужный раздел, далее пра-
вый клик – Resize (рис. 4 ), полозком или цифрами выстав-
ляем новый размер и подтверждаем изменения File →
Commit.

Удаляем раздел, на котором размещается NTFS, в моем


случае это 7.

Создаем на его месте раздел с новым размером 500 Мб,


при этом начальный цилиндр обязательно должен совпа- Ðèñóíîê 3
дать, т.е. судя по выводу выше – 3522.

Устанавливаем тип раздела для NTFS – 7.

Записываем изменения и выходим.


Ðèñóíîê 4
К сожалению, можно сделать вывод, что работа GNU/
Linux с файловой системой NTFS еще далека от идеала, а
учитывая трудности написания вслепую драйвера и пробле-
Если все выше проделывалось с LiveCD или дискетного мы с полной эмуляцией, когда будет окончательное реше-
дистрибутива, то сразу же можно просмотреть информа- ние, сказать пока трудно. Но как видите, по сравнению с
цию о разделе. предыдущими годами, сдвиги есть, и работа ведется уси-
ленными темпами. Так, сайт Linux-NTFS уже давно не об-
# ./ntfsresize -i -f /dev/hda7 новлялся, потому что разработчики сосредоточились на
конечном результате. Будем надеяться, что он не заставит
себя долго ждать.

№8(21), август 2004 23


администрирование

ЕЩЕ РАЗ О ClamAV:


ОСОБЕННОСТИ УСТАНОВКИ ВО FreeBSD
СЕРГЕЙ СУПРУНОВ
Статья Сергея Яремчука «Свободный антивирус», опубли- Сборка пошла, но через несколько минут появилось
кованная в мартовском номере журнала «Системный ад- сообщение, что программа не будет нормально работать,
министратор» 2004 года, вдохновила меня на установку это- пока в системе не появится пользователь clamav. Пришлось
го пакета на вверенном мне сервере. Однако выяснилось, выполнить этот ультиматум вручную, хотя обычно порт со-
что под FreeBSD этот процесс происходит несколько ина- здает нужных ему пользователей самостоятельно.
че, чем под Linux, что и стало поводом для написания дан- Сборка завершилась успешно, инсталляция тоже особых
ной статьи. Впрочем, ничего сложного нет, поэтому мате- проблем не породила, не считая того, что с первого раза
риал ориентирован скорее на новичков. вывалилась ошибка, а повторный make install, запущенный
Антивирус ClamAV имеется в коллекции портов FreeBSD, для детального изучения проблемы, завершился без про-
а потому вполне разумным мне показалось выполнить ус- блем. Видимо, с первой попытки не удалось закачать ка-
тановку именно оттуда. Ряд проблем, с которыми пришлось кой-то вспомогательный пакет. На стадии инсталляции тоже
столкнуться, показал, что ClamAV портирован на эту ОС не частенько докачиваются и устанавливаются вспомогатель-
совсем корректно. Но обо всем по порядку… ные пакеты, которые для сборки не нужны, но используют-
Первым делом я отправился в /usr/ports/security/clamav. ся в дальнейшей работе. В частности, для clamav именно
Чтение Makefile позволило узнать, что предлагаемая вер- на этом этапе происходит установка портов unarj, unrar и т. д.,
сия – 0.73, что выглядит довольно хорошо (последняя на если их нет в системе, а проверка архивов включена.
тот момент была 0.74, но, как известно, «последнее» – не На первый взгляд все выглядело весьма респектабель-
значит «лучшее»). Там же узнаем опции, с которыми пакет но: конфигурационные файлы clamav.conf и freshclam.conf
будет сконфигурирован: чинно лежали в /usr/local/etc. В первом я включил заком-
ментированные по умолчанию опции LogTime, выводящую
CONFIGURE_ARGS= --with-dbdir=${DATADIR} ↵ в лог время события (лог при этом растет вдвое быстрее,
--disable-clamuko --disable-clamav ↵
--enable-bigstack --disable-dependency-tracking зато понятно, что и когда происходит), и ScanRAR, разре-
шающую проверку rar-архивов (проверка остальных по
Опцию --disable-clamav, отменяющую работу от имени умолчанию включена). Директория /usr/local/etc/rc.d обза-
пользователя clamav, я решил убрать – зачем пренебре- велась еще двумя сценариями, призванными автоматичес-
гать дополнительной безопасностью? Ну и поскольку сис- ки запускать при перезагрузке компьютера демон clamd и
темные администраторы – люди исключительно бескорыс- утилиту обновления баз freshclam.
тные, и все их заботы и труды – о пользователях и ради Запуск clamscan продемонстрировал его работоспособ-
пользователей, то была добавлена поддержка milter для пос- ность и вселил надежды на светлое будущее:
ледующей интеграции нашего антивируса с sendmail:

CONFIGURE_ARGS+= --enable-milter

Попутно я обратил внимание на следующую строчку:

OPTIONS= MILTER “Compile the milter interface” off

Попытка собрать систему вышеприведенной опцией и


без нее не обнаружила никаких различий. Более того, в
bsd.ports.mk упоминания параметра OPTIONS не нашлось. Однако сильно смущало время обработки – свыше 10
Возможно, во FreeBSD он не используется. секунд для 20 Кб проверенных файлов (конфигурация сер-
Полагая, что подготовительные работы закончены и все вера – Celeron-466/64Мб, load average ~ 0.1). Может, дело в
остальное инсталлятор сделает сам, была подана команда каких-то «подготовительных работах» вроде подгрузки
make. И тут случилась первая аномалия: утилита, как и по- базы данных, и на больших объемах дело пойдет лучше?
ложено, попыталась скачать дистрибутив пакета, но толь- Действительно, проверка на папке /usr/ports/distfiles пока-
ко с одного сайта – с ftp.freebsd.org, и заявила об отсут- зала некоторое увеличение скорости:
ствии там нужного ей файла clamav-0.73.tar.gz. Раньше я
такого не видел – на сервере операционной системы все-
гда в папке distfiles обнаруживался любой нужный портам
архив. Пришлось забирать требуемый файл с сайта проек-
та clamav.sourceforge.net.

24
администрирование
сокетов соответствуют друг другу в строке запуска clamav-
milter и в sendmail.mc.) С сокетом clamd.sock таких манипу-
ляций не понадобилось, поскольку демон успешно убивает
старый сокет сам, лишь сообщив в лог-файл об этом. Как
видно из файла, помимо демона clamd запускается и
clamav-milter. А вот в вопросе обновления баз я решил на
демонизацию freshclam не полагаться (хотя такая возмож-
ность предусмотрена) – зачем запускать еще один демон,
Почти 2 секунды на каждый мегабайт. Ну да ладно. На- когда с этим отлично справится cron:
грузка на мой сервер пока минимальна, будем надеяться,
что медлительность с лихвой окупится дотошностью. По 0 0,12 * * * /usr/local/bin/freshclam
крайней мере тестовые сигнатуры в собственных пакетах
антивирусу обнаружить удалось. Следующая неприятность Настройка sendmail была выполнена в полном соответ-
ждала меня при попытке запустить демон традиционным ствии с документацией и проблем не вызвала (см. статью
для FreeBSD способом: Сергея Яремчука). Нужно только не забыть перезапустить
сервер (make restart в /etc/mail).
/usr/local/etc/rc.d/clamav-clamd.sh start Итак, затратив немного усилий, удалось заставить
clamav работать так, как это принято во FreeBSD. Конечно,
Оказалось, что стартовые сценарии разработаны в сти- если бы порт был разработан более корректно, то и эти
ле Linux и для запуска из /usr/local/etc/rc.d не годятся. Пере- усилия не понадобились бы.
носить их в /etc/rc.d с добавлением соответствующих оп- В первые часы работы было «зарезано» около 50 зара-
ций (приведенных в начале стартовых скриптов) в файл женных писем (напомню, что лечить файлы ClamAV пока
/etc/rc.conf я посчитал неправильным. Как бы то ни было, не умеет), извещения о данном прискорбном факте были
но место всех программ, устанавливаемых пользователем, – добросовестно разосланы отправителям, получателям и ад-
в /usr/local. министратору. Огорчало одно – язык сообщений был анг-
Ну что ж, раз не хочешь по-хорошему, сделаем все по- лийским, а зная своих пользователей, нетрудно было спрог-
своему. Созданные при инсталляции стартовые сценарии нозировать раскаленный телефон службы технической под-
были безжалостно уничтожены, а их место занял следую- держки.
щий: Но и эта проблема оказалась более чем решаемой: все
сообщения, отсылаемые пользователям, были сосредото-
#!/bin/sh чены в исходном файле clamav-milter.c и насчитывали не
# Manager for ClamAV: clamd & clamav-milter
более пяти строк. Таким образом, «русификация» свелась
case $1 in к простому вбиванию новых слов взамен старых. Ну и в
start)
if [ -e /var/run/clamav/clamd.pid ]; then заголовок отсылаемого письма была добавлена (в том же
echo 'Ingored - clamd already started.' clamav-milter.c) строчка «Content-Type: text/plain; charset=
exit 0
fi ”koi8-r”», поскольку без нее мой Outlook упорно пытался
if [ -e /var/run/clamav/clmilter.sock ]; then подсунуть мне письма под видом win-1251. После правки
rm /var/run/clamav/clmilter.sock
fi исходника – повторная сборка и установка. Результат пред-
/usr/local/sbin/clamd ставлен на рисунке.
/usr/local/sbin/clamav-milter -blo ↵
/var/run/clamav/clmilter.sock
echo 'clamd & clamav-milter started'
;;
stop)
killall clamav-milter
kill -15 `cat /var/run/clamav/clamd.pid`
echo 'clamd & clav-milter stopped'
;;
restart)
kill -1 `cat /var/run/clamav/clamd.pid`
echo 'clamd restarted'
;;
fresh)
/usr/local/bin/freshclam
;;
*)
echo "Usage: `basename $0` ↵
{start|stop|restart|fresh}" >&2
;;
esac
exit 0
В заключение следует заметить, что проблемы, описан-
Удалять clmilter.sock я решил вручную, поскольку натол- ные в статье, вполне вероятны при попытке запустить на
кнулся на то, что иногда он удаляется с некоторой паузой FreeBSD и иные приложения, первоначально разработан-
(или не удаляется вообще) в результате уничтожения де- ные для другой ОС. Методы решения большинства из них
мона clamav-milter, и при повторном запуске milter возника- будут аналогичными. Главное – хорошо представлять себе,
ет ошибка. (Кстати, следует убедиться, что имена и пути что должно получиться в итоге.

№8(21), август 2004 25


администрирование

CROSSOVER
И
ЛИЦЕНЗИОННЫЙ ВОПРОС

АНДРЕЙ БЕШКОВ

26
администрирование
В ответ на публикацию трилогии о CrossOver Office в мой ухудшилось самочувствие той или иной программы и по
почтовый ящик посыпались читательские письма с раз- горячим следам провести корректировку кода системы.
нообразными вопросами. Сегодня мы займемся самым ча- Тут мы как раз и подходим к самому интересному. Бета-
сто встречающимся из них: «Объясните мне смысл поку- тестеров проекта называют адвокатами приложений по
пать этот продукт, если есть wine». той причине, что каждый из них следит за самочувстви-
Wine – это, конечно, хорошо, но в некоторых областях ем дорогих ему сердцу программ. Подробнее почитать о
CrossOver на данный момент впереди планеты всей. На- процессе адвокатства и получаемых от него выгодах
пример, продвинутая поддержка InstallShield, удобство ин- можно тут – http://www.codeweavers.com/site/compatibility/
терфейса, простота установки и самое главное – очень advocate_overview. Если говорить коротко, то адвокаты
дружелюбная служба технической поддержки. Впрочем, получают в свое распоряжение новейшие версии коммер-
это уже индивидуальные вкусовые предпочтения. Боль- ческих разработок CrossOver Office. Все отчеты от адво-
шинство читателей в силу особенностей менталитета бо- катов по поводу тех или иных проблем, возникающих в
лее всего интересует вариант, чтобы можно было и рыбку процессе эксплуатации программного продукта, попада-
съесть, и косточки сдать. То есть получить все бесплатно. ют на рассмотрение технической службы вне очереди.
Моралисты всех мастей сейчас начнут качать головой и Плюс ко всему возможность напрямую общаться с нуж-
говорить: «Ой, как нехорошо». Ну а мы посмотрим, как ными вам людьми из команды СodeWeavers. Также при-
добиться решения поставленной задачи. Предположим, ветствуется голосование за интересные для вас приложе-
что у нас установлена версия CrossOver Office c ограни- ния, таким образом можно стимулировать разработчиков
чением на 30 дней. Проявив немного любознательности, уделять больше внимания именно этой программе.
можно заметить, что после инсталляции у нас на жестком В обмен на такие услуги адвокат должен заниматься
диске появился файл /var/run/cxoffice/.evaluation. Хотя в не- тестированием своего приложения на новых версиях
которых дистрибутивах Linux этот файл может находить- CrossOver Office, помогать другим пользователям, у кото-
ся, к примеру, в домашней директории пользователя. Для рых возникли проблемы установки или использования дан-
того чтобы пробный период никогда не закончился, нужно ного приложения, активно поддерживать форум приложе-
всего лишь удалять его раз в несколько дней. Данный трюк ния, участвовать в дискуссиях по поводу тех или иных про-
работает на основе того, что начало тестового периода блем системы и, наконец, отправлять отчеты о найденных
всегда отсчитывается от даты создания вышеупомянуто- ошибках. Думаю, это небольшая плата за возможность
го файла. А если его на диске нет – значит программа счи- пользоваться коммерческой версией CrossOver Office, к
тает, что запущена в первый раз и пользователю полага- тому же все это происходит на добровольных началах, по-
ется законные тридцать пробных дней. Насколько я пони- этому не стоит ожидать, что кто-то будет стоять над вами
маю, с юридической точки зрения тут нет никаких проблем. с палкой и требовать отработать n-ое количество челове-
Владелец компьютера имеет полное право удалять любые ко-часов на благо проекта.
файлы, хранящиеся на жестком диске личного компьюте- Все, кто захотел стать адвокатом и тем самым помочь
ра. Ну а тот факт, что защита программы от этого будет проекту, могут сделать это прямо сейчас. В списке про-
неправильно работать, однозначно относится к личным грамм нужно выбрать то приложение, которое нравится
проблемам разработчика. вам: http://www.codeweavers.com/site/compatibility/browse/
В то же время можно пойти совсем другим путем и стать name.
официальным пользователем коммерческой версии Cross В качестве примера давайте используем 1C Предпри-
Over Office совершенно бесплатно. И самое главное, что ятие, у которого пока что нет адвокатов ht tp://
мы не только соблюдаем все законы, что само по себе www.codeweavers.com/site/compatibility/browse/
достойно всяческих похвал, но еще и помогаем развитию name?app_id=298. На страничке приложения нажать на
проекта. Для этого нужно, вооружившись именем пользо- ссылки «Want to help?» затем «help CodeWeavers» и, на-
вателя и паролем, которые нам выдали при первичной ре- конец, «Make Me an Advocate». В течение нескольких бли-
гистрации, войти на сайт http://www.codeweavers.com/login. жайших дней ваша заявка будет рассмотрена и, скорее
Затем ознакомиться с документацией по центру совмес- всего, удовлетворена. О назначении вас адвокатом мож-
тимости приложений http://www.codeweavers.com/site/ но узнать, периодически заходя на страницу приложения.
compatibility. Центр создавался с одной-единственной це- В случае если интересующих вас приложений в базе най-
лью облегчить взаимодействие между разработчиками и ти не удалось, есть возможность добавить их самому:
пользователями продукта. Соответственно посещать его http://www.codeweavers.com/site/compatibility/submit/ и уже
в любом случае полезно, но мы сейчас не об этом. В со- затем стать адвокатом. Кстати, стоит отметить, что ко-
ставе центра находится список приложений, которые так личество адвокатов, работающих над одним приложени-
или иначе работают под CrossOver Office. Посмотреть на ем, теоретически не ограничено, но на практике больше
его наполнение можно здесь: http://www.codeweavers.com/ пяти не бывает.
site/compatibility/browse/name. С выходом новых версий Став адвокатом, можно будет получить доступ к внут-
CrossOver Office из-за изменений, вносимых в код, одни ренним форумам и разделу с новейшим программным обес-
приложения начинают работать лучше, а другие наобо- печением: http://www.codeweavers.com/site/compatibility/
рот – хуже, соответственно, разработчики проекта заин- advocate_center/. Также стоит подписаться на списки рас-
тересованы в большом количестве бета-тестеров. Таким сылки: http://www.codeweavers.com/site/support, довольно
образом, можно будет очень быстро заметить, насколько часто там обсуждают интересные проблемы.

№8(21), август 2004 27


администрирование

В ЯБЛОЧКО!
КРАТКИЙ ОБЗОР ОС DARWIN 7.0
НА ПЛАТФОРМЕ X86 (MAC OS X 10.3 JAGUAR)
Полгода прошло с момента выхода в свет
последнего релиза упомянутого в заголовке
продукта. Президент Apple Стив Джобс
подтвердил, что MacOSX действительно
можно запустить на x86-платформе [1].
Давайте разберемся, что же может привлечь
потенциального пользователя/администратора,
помимо легендарного названия фирмы Apple
Computer? В первую очередь, ориентирование
компании на постепенное расширение своей
продукции. Как известно, на протяжении своей
истории операционные системы фирмы Apple
работали только на оборудовании самой Apple.
Отчасти это правильно. Приходится меньше
волноваться о вопросах совместимости
аппаратного и программного обеспечения.
В то же время это и откровенный тупик
в развитии.

АНТОН БОРИСОВ
Что же из себя представляет Darwin? Если коротко – это Дискутировать относительно открытости продуктов са-
некое ядро операционной системы MacOSX, оно состоит мой фирмы Apple можно долго, однако нас в первую оче-
из 5 главных компонентов: микроядро Mach, BSD-подсис- редь должно волновать несколько моментов:
тема, файловая система, сетевая подсистема и система ! списки совместимости с оборудованием (так называе-
ввода-вывода (I/O Kit). мые hardware compatibility list [3]);
! Микроядро Mach занимается распределением вычисли- ! знание структуры *BSD-систем;
тельных ресурсов, защитой памяти, обменом сообще- ! потребность в использовании данной ОС в обучающем
ний между процессами. процессе на производстве.
! Вокруг микроядра существует «обертка» из POSIX API,
абстрактная файловая система и сетевая подсистема. Насчет первого пункта сильно огорчаться не следует,
Многое в микроядре заимствовано из 4.4 BSD-Lite2, со- т.к. система стартует и работает на более-менее современ-
ответственно модель процессов, система безопаснос- ной x86 машине. В общем-то, Intel Celeron 366, 128 Мб па-
ти, потоковая поддержка покажутся знакомыми всем, мяти должно хватить для ознакомления. Также следует об-
кто работал с BSD-системами. завестись одной из следующих сетевых карт: 3Com 3c90x,
! Файловая система поддерживает как UNIX (UFS), так и Intel 8255x, Broadcom 570x, Realtek 8139, Dec 21x4 (она же
«родную» маковскую файловую систему (HFS). Tulip). Встроенные сетевые карты, к сожалению, не поддер-
! TCP/IP-стек основан на проверенном временем BSD- живаются. Видеокарта должна работать в VESA-режиме.
коде. Для начала возьмем дистрибутив с сайта OpenDarwin
! Объектно-ориентированная система ввода/вывода пред- [4]. Он из себя представляет упакованный gzip-образ, кото-
ставляет развитую инфраструктуру для управления рый потом надо записать на компакт-диск. Размер упако-
драйверами устройств. ванного образа составляет 430 Мб.

К сожалению, графическая подсистема MacOSX (Quick wget -ct0 http://opendarwin.org/downloads/darwin-701.iso.gz


gzip -d darwin-701.iso.gz
Time, OpenGL, Quartz), пользовательский интерфейс (Aqua) cdrecord -v darwin-701.iso
не присутствуют в системе. Хотя и без них Darwin является
уже полноценной ОС. Более подробно об истории возник- Для дальнейшей работы потребуется пустой жесткий
новения Darwin см. [2]. диск размером не более 2 Гб или такого же объема раздел.

28
администрирование
Установленная система занимает примерно 1 Гб. Ос- И запустив /sbin/lilo, перезаписываю MBR. Теперь при
тавшееся место будем использовать для сборки необходи- старте можно будет выбрать загрузку с раздела hdd2, где
мых пакетов. обитает OpenDarwin.
Настраиваем загрузку с компакт-диска, и после опре- Всё. Выставим флаг активности раздела (команда
деления оборудования ядром ОС мы увидим приглашение «active ID», где ID – опять-таки нужный нам номер разде-
выбрать тот диск, на который хотим поставить операцион- ла). И завершим разметку подав команду «exit».
ную систему. После этого осталось дождаться, чтобы установились
пакеты, входящие в состав компакт-диска. Как говорится,
возьмите чай, кофе – сахар по вкусу.
В ходе установки внимание обращает тот факт, что все
Пункт 1 предназначен для установки на пустой диск. В пакеты находятся в упакованном виде. Сжаты они с помо-
случае, когда не надо иметь на диске несколько систем, щью bzip2. После установки истинно «яблочных» пакетов,
выбирайте его. Пункт 3 предназначен для случая, когда таких как AppleUSBAudio, следом идут пакеты с до боли
место заранее выделено и размечено под операционную знакомыми названиями. Согласитесь, что такие строчки,
систему Darwin (ее идентификатор 0xA8, см. далее по тек- как apache-670tar.bz2, bind9-7tar.bz2, gcc-1495tar.bz2, вам
сту). о чем-то говорят. В этом нет ничего удивительного, т.к. си-
Если вам не подошли пункты 1 или 3, тогда вспомина- стема, как вы, наверное, слышали, базируется на BSD.
ем, что настоящие герои всегда идут в обход и выбирают Большинство утилит, присутствующих в системе, хоро-
самый легкий путь. В целом разметка вручную не так и шо знакомы людям, постоянно работающим в Open/Net/
страшна, как кажется на первый взгляд. FreeBSD, да и тем, кто когда-либо занимался сборкой про-
Выбираем пункт 2. Для начала следует ознакомиться с грамм под UNIX.
командами fdisk данной системы. Вернемся к нашему яблоку. Перегружаемся, не забы-
Правильно, нажимаем слово «help» и читаем, что есть ваем вытащить из привода компакт-диск. Далее загрузка
в меню. Команд немного, поэтому можно набирать не пол- будет происходить с нашего жесткого диска.
ную версию, а, например, сокращать до 2 начальных букв. Вот несколько интересных, на мой взгляд, строк, кото-
То есть вместо «help» писать «he». рые можно наблюдать при старте системы. Ваше аппарат-
Команды прочитаны, пора выделить место под наше ное обеспечение отличается от моего, но по аналогии мо-
сегодняшнее яблочко. Распечатаем содержимое MBR дис- жете сами проследить отличия.
ка, на который будем ставить ОС. Для этого подаем коман-
ду «print». Видим теперь, где находятся разделы и свобод-
ное место. Для редактирования свободного места подадим
команду «edit ID», где ID – это номер раздела, предполо-
жим под номером 2 (то есть на текущем диске первичный
раздел номер 2). Процедура выбора необходимого разме-
ра для нового раздела не составит, я надеюсь, особого тру-
да. Отмечу, что размер считается в секторах, а не в кило-
байтах. Идентификатор файловой системы для Darwin име-
ет код 0xA8 (Darwin UFS).
Затем следует обновить информацию в MBR (Master
Boot Record). Если вы ставите на чистый диск, то в MBR
никакого загрузчика нет. Поэтому следует записать в него
штатный, идущий в комплекте с OpenDarwin. В случае ког-
да вы устанавливаете на отдельный раздел, все равно при-
дется перезаписать MBR. В этом нет ничего страшного, т.к.
дальше в статье разъясняется на примере LILO, как сде- Итак, строка externalClock = 0x85 означает, что шина
лать загрузку нескольких операционных систем. работает на частоте 133 МГц (переведите цифры в деся-
Для этих целей предусмотрена команда «update». Пос- тичное представление). Строка currentClock = 0x5ba разъяс-
ле этого запишем в MBR непосредственно сведения о раз- няет, что текущая тактовая частота равна 1466 МГц, а мак-
деле, в котором будет жить наша «яблочная» операцион- симальная частота для этого типа процессора равна 2250
ная система. Сделаем это с помощью команды «write». МГц (maximumClock = 0x8ca).
Следует помнить, что в MBR записывается новый загруз- Стоит разъяснить, откуда появляется надпись «hfs_
чик, поэтому если у вас стоял LILO, GRUB или что-то ана- mount: invalid HFS+ sig 0x0000». Вы помните, когда мы ста-
логичное, то он будет просто-напросто переписан загруз- вили идентификатор раздела, то поставили 0xA8 (Darwin
чиком от Darwin. У меня используется LILO, поэтому я про- UFS). В связи с чем делаем вывод, что корневой раздел у
сто добавил в /etc/lilo.conf следующую строчку: нас отформатировался в UFS-формате. Ничего страшно-
го, кроме досадного факта, что файловая система у нас
# DarwinOS begins получилась нежурналируемая. Для того чтобы корневая
other = /dev/hdd2
label = DarwinOS файловая система была в HFS+ формате, при начальной
# DarwinOS ends разметке диска следует ставить идентификатор 0xAF (вме-

№8(21), август 2004 29


администрирование
сто 0xA8). Перевести систему из UFS в HFS+ пока не пред- else
ставляется возможным. sysctl -w net.inet.ip.forwarding=0 > /dev/null
fi
Процесс загрузки ОС кардинально не отличается от
процесса загрузки системы на FreeBSD. Знакомые конфи- ifconfig en0 10.0.0.10 netmask 255.255.255.0
гурационные файлы в /etc (rc, rc.boot, rc.common, fstab, }
syslog.conf и т. д.). StopService ()
Итак, перед нами приглашение на вход в систему. За- {
ходим под пользователем root (пароль изначально не уста- return 0
}
новлен). Что необходимо сделать на этом этапе? Попробу-
ем поднять сеть? Давайте сделаем! RestartService ()
{
Даем команду: return 0
}
# ifconfig RunService "$1"

Вот так выглядит файл Network на моей машине. До-


бавленная строка выделена красным цветом.
Небольшое уточнение, выясненное в ходе настройки.
При переходе на новую систему не надо забывать, что струк-
турно Darwin не отличается от FreeBSD или, скажем, Linux
в плане загрузки модулей. Что я хочу этим сказать. Если у
Ага, вот и наша сетевая карта (интерфейс en0). Есть вас сетевая карта опознана, то Darwin подгрузит для нее
два пути – правильный и не очень правильный. Чтобы про- модуль. Как только модуль подгружен, то можно выстав-
писать в Darwin IP-адрес для интерфейса, в /etc/ надо со- лять IP-адрес и маску. В общем, если выставляете IP-ад-
здать файл iftab. Структура его следующая: рес в один из rc-файлов, будьте внимательны.
Вставляйте строку после загрузки модулей ядра (они
en0 inet 10.0.0.10 netmask 255.255.255.0 up загружаются демоном kextd).
На этом этапе сеть у нас готова. Правда, развертка мо-
где 10.0.0.10 – IP-адрес, 255.255.255.0 – маска сети, «up» – нитора может раздражать. Мы ведь работаем в VESA-ре-
интерфейс при загрузке поднять. жиме. Вертикальная развертка 60 Hz.
Стоит отметить, что файла /etc/rc.conf в Darwin просто Давайте зададим текстовый режим консоли. Для этого
нет. Поэтому вписать информацию о сетевых адресах по редактируем файл /Library/Preferences/SystemConfiguration/
аналогии с FreeBSD не получится. com.apple.Boot.plist.
Так вот, правильный путь не работает. Поэтому либо В оригинале он выглядит так.
пропишем в /etc/rc.common наш сетевой адрес (для этого
найдите функцию CheckForNetwork() – в ее теле и надо про- <?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN"
писать), либо идем в системный каталог автозагрузки. Его "http://www.apple.com/DTDs/P
полный путь – /System/Library/StartupItems. Каталог, отве- ropertyList-1.0.dtd">
<plist version="1.0">
чающий за сеть, именуется Network. <dict>
Файл, который необходимо отредактировать, совпада- <key>Kernel</key>
<string>mach_kernel</string>
ет с названием каталога (его имя тоже Network). Обратите <key>Kernel Flags</key>
внимание, что на нем установлены биты на исполнение. <string> -v</string>
<key>Boot Graphics</key>
<string>Yes</string>
# cat Network <key>APM</key>
<string>Yes</string>
#!/bin/sh </dict>
</plist>
##
# Configure network interfaces and host name
## Да, забыл сказать. Почти все конфигурационные фай-
. /etc/rc.common лы в Darwin хранятся в xml-формате. Что мы сейчас и ви-
дим.
StartService ()
{ Для текстового режима необходимо поменять параметр
ConsoleMessage "Initializing network" Boot Graphics с Yes на No. Со следующей загрузки будет
ipconfig waitall > /dev/null 2>&1 использоваться текстовый режим.
Добавим теперь сервисы ftp, telnet. Редактируем файл
if [ "${IPV6:=-YES-}" = "-NO-" ]
then /etc/inetd.conf, удаляем ненужные символы, ставим коммен-
sysctl -w net.inet6.ip6.auto_on=0 > /dev/null тарии перед нужными нам службами.
ip6 -x
fi Посылаем сигнал, чтобы демон inetd перечитал свои
настройки.
if [ "${IPFORWARDING:=-NO-}" = "-YES-" ]
then
sysctl -w net.inet.ip.forwarding=1 > /dev/null killall -1 inetd

30
администрирование
Отлично. Службы появились. {
Теперь задумаемся, а может, нам еще нужен и веб-сер- Description = "smb file server";
Provides = ("Samba");
вер? А DNS-сервер? Что ж, пробовать, так все и сразу. Requires = ("Resolver");
Чтобы запускать указанные и некоторые другие служ- OrderPreference = "None";
Messages =
бы, при старте Darwin существует файл /etc/hostconfig: {
start = "Starting Samba";
stop = "Stopping Samba";
# cat /etc/hostconfig };
}
# /etc/hostconfig
## Вставляем в файл /etc/hostconfig строчку SMBSER-
# This file is maintained by the system control panels
## VER=-YES- и в дальнейшем надо будет выделить samba-
ресурсы (файл /etc/smb.conf).
# Network configuration
HOSTNAME=DarwinOS Так, со службами все понятно. Установим локальное
время в системе.
# Services
AUTOMOUNT=-NO-
CUPS=-NO- cd /etc
IPFORWARDING=-NO- ln -s /usr/share/zoneinfo/Europe/Moscow localtime
IPV6=-YES-
NETINFOSERVER=-NO- Для необходимого нам hostname следует подправить
NISDOMAIN=-NO-
RPCSERVER=-YES- файл /etc/rc.boot. Вместо строки по умолчанию «hostname
QTSSERVER=-YES- localhost» вставим «hostname DarwinOS».
WEBSERVER=-YES-
DNSSERVER=-NO- Теперь мы рассмотрим концепцию NetInfo, реализован-
COREDUMPS=-NO- ную в Darwin. Система может искать информацию как в
VPNSERVER=-NO-
файлах службы NetInfo, так и в «плоских» («plain text») фай-
Если параметр установлен в -YES-, тогда при старте лах (/etc/fstab, /etc/passwd и др.).
указанные службы будут запущены. Для добавления пользователя, существует утилита niutil
Запуском apache (строка WEBSERVER=-YES-) занима- (NetInfo Util). С помощью этой же утилиты настраивается
ется файл /System/Library/StartupItems/Apache/Apache. большая часть системы.
Для DNS-сервера выделен файл /System/Library/Startup
Items/BIND/BIND. В целом прослеживается аналогия со niutil -create / /users/Bob
niutil -createprop / /users/Bob shell /bin/tcsh
структурой системных файлов, например, как в Linux niutil -createprop / /users/Bob realname UncleBob
Slackware. В последней скрипты, запускающие определен- niutil -createprop / /users/Bob home /Users/Bob
niutil -createprop / /users/Bob _shadow_passwd
ный сервис, называются /etc/rc.d/rc.ServiceName.
Строка MAILSERVER предназначена для почтовой служ- Комментарии, надеюсь, излишни? Сначала мы создали
бы postfix, TIMESYNC – для синхронизации времени, CUPS – объект Bob в иерархии /users, а затем добавили этому
для сервиса, отвечающего за печать, SMBSERVER – samba- объекту свойства. Добавить добавили, но надо и создать
сервис, добавим самостоятельно чуть позднее. ему домашний каталог.
Картина вырисовывается следующая. Сначала отраба-
тываются файлы /etc/rc*, а затем – соответствующие фай- cd /Users
mkdir Bob
лы в /System/Library/StartupItems. chown -R Bob:wheel Bob
Чтобы запустить свой сервис, необходимо создать в /Sys-
tem/Library/StartupItems директорию с названием сервиса. Теперь у Bob домашняя директория, да и сам он полу-
В ней должны находиться: исполняемый файл, совпадаю- чился по умолчанию в группе wheel. Можно переделать,
щий с названием только что созданной директории и ин- добавив через niutil пользователю uid и gid.
формационный файл StartupParameters.plist. Для получения списка объектов в иерархии NetInfo вы-
Давайте посмотрим, как создать скрипт сервиса, отве- полним команду:
чающего за старт samba-сервера.
niutil -list . /
#!/bin/sh

# Include system wide configuration options Получим список корневых иерархий. Для просмотра
объекта resolver (аналог /etc/resolver):
. /etc/rc.common
# Start SMB services niutil -read . /locations/resolver

if [ "${SMBSERVER:=-NO-}" = "-YES-" ]; then


Создадим запись в объекте resolver:
ConsoleMessage "Starting SMB services"
/usr/sbin/smbd -D niutil -createprop . /locations/resolver ↵
/usr/sbin/nmbd -D nameserver 10.0.0.100
fi Всю базу NetInfo можно просмотреть:

Файл StartupParameters.plist: nidump -r / / > nidump.txt

№8(21), август 2004 31


администрирование
Приступаем к заключительной части нашей статьи. Го-
товимся к прыжку в окна X-сервера. ./configure --prefix=/usr/local/blackbox
В Darwin он называется XDarwin (это портированный в make && make install
Mac OS проект XFree86). В системе обнаруживается толь-
ко один оконный менеджер. Это twm. Меняем .xinitrc.
Для запуска twm создадим в домашней директории
файл .xinitrc: echo “/usr/local/blackbox/bin/blackbox” > ~/.xinitrc

# cat ~/.xinitrc Теперь, как мне кажется, намного симпатичнее.


/usr/X11R6/bin/xterm & Большую часть приложений придется собирать из исход-
/usr/X11R6/bin/twm ников. Кому нравится GNOME, нет проблем. Дело вкуса.
Для любителей клубнички даже существует проект darwine,
Не забываем, что он должен иметь установленным ис- запуск windows-приложений под Darwin (небезызвестный
полняемый бит. родоначальник – проект wine).
Пару слов о модулях, упомянутых в начале статьи. Про-
# chmod +x .xinitrc смотр информации о загруженных модулях:

А теперь в окна! То есть в X-сервер. # kextstat

# startx

В списке я оставил упоминания модулей, названия ко-


торых отчасти говорят о платформе и используемом обо-
рудовании.
! kextload kextName – загрузка модуля под именем
kextName (они находятся в /System/Library/Extensions).
Как-то непривычно, не правда ли? ! kextunload kextName – соответственно выгрузка из па-
Давайте поменяем twm на другой менеджер. Например, мяти модуля kextName.
blackbox. Сказано – сделано. ! kextxcache – подготовка базы модулей, хранящихся в
/System/Library/Extensions.

Apache-сервер, которым комплектуется система, иден-


тифицируется следующим образом:

wget -ct0 http://prdownloads.sf.net/blackboxwm/ ↵


blackbox-0.65.0.tar.gz
mkdir ~/sources
mv blackbox-0.65.0.tar.gz sources
tar xzvf blackbox-0.65.0.tar.gz
cd blackbox-0.65.0

32
администрирование
Настройка ничем оригинальным не отличается. Файловая система NTFS доступна только на чтение. Для
Интересно, как же идентифицируется система со сто- монтирования файловой системы из-под Linux:
роны. Сейчас поглядим. Заходим на удаленную машину. И
оттуда запускаем наш сканер портов nmap. # mount /dev/hdd2 /mnt/hd –t ufs –o, ufstype=44bsd

rootfuji:# /usr/local/nmap/bin/nmap -v -sS -O darwin На момент написания статьи поддержка из-под Linux
только на чтение.
Напоследок пара строк о средстве фильтрации трафи-
ка. Конечно, это ipfw. Полностью похож на своего *BSD-
собрата. Впрочем, это не собрат, а скомпилированный под
Darwin оригинальный ipfw. Правила задаются и убирают-
ся абсолютно так же, как и в FreeBSD. Более подробно
см. в статьях: «Ipfw и управление трафиком в FreeBSD»
(№6 журнала «Системный администратор» за 2003 год),
«Сам себе антихакер. Защита от хакерских атак с помо-
щью ipfw» (№1 журнала «Системный администратор» за
2004 год).
В целом из машины на основе OpenDarwin вполне воз-
можно сделать почтовый релей, систему доступа из Интер-
нета по ppp-соединению, веб-сервер. Основные компонен-
ты по созданию сетевой инфраструктуры уже присутству-
ют в системе: postfix, mysql, php, apache, bind, perl. Относи-
тельно сетевой безопасности – код стека TCP/IP, как гово-
рилось в начале статьи, основан на оригинальном BSD-коде.
Если каких-либо программ не хватает, то следует обратить-
Со стороны не отличишь, действительно ли это MacOSX ся на сайт [7]. Вполне возможно, что эти программы уже
на платформе PPC или x86. Жаль, что ни Aqua, ни Quartz портированы и доступны, как ports.
не предусмотрены. Несколько слов о компиляции программ под Darwin. В
Посмотрим на samba-ресурсы удаленной машины. системе используется GNU Compiler Collection. Поэтому
особых проблем при сборке возникнуть не должно. В Darwin
DarwinOS:~ root# smbclient -L fuji -I fuji 2> /dev/null я встретил такое понятие, как «толстые» файлы («fat»-files).
То есть при сборке файлы компилируются под несколько
архитектур, например, под PPC и под x86. И собираются в
один бинарный файл. При выполнении файла происходит
определение архитектуры и передается управление на не-
обходимый участок кода. В частности, ядро, идущее с сис-
темой, собрано с поддержкой как PowerPC (PPC) архитек-
туры, так и x86.
Конечно же, статья не претендует на полное освещение
всех нюансов «фруктовой» ОС. Она предназначена в пер-
вую очередь для тех, кто собирается расширить свой кру-
гозор, и тех, кто неравнодушен к самой компании Apple
Computer.
Мечты придуманы для того, чтобы из них делать реаль-
ность. И фруктовая компания не забывает об этом.
Посмотрим на геометрию диска из-под ОС Darwin.
Ссылки:
DarwinOS:~ root# fdisk /dev/rdisk1 1. http://developer.apple.com/darwin/history.html
2. http://news.com.com/2100-1045_3-5103279.html
3. http://www.opendarwin.org/hardware/
4. http://blackboxwm.sf.net
5. http://www.opendarwin.org/documentation/
6. http://www.mymac.ru
7. http://darwinports.opendarwin.org
8. Потемкин А. Mac OS X или то, что должен знать каждый
Наглядно и просто. Файловых систем не так уж и много. про Macintosh, Apple и операционные системы. – // Жур-
нал «Системный администратор», №7, июль, 2003 г. –
# ls /System/Library/Filesystems 68-77 с.

№8(21), август 2004 33


администрирование

ПРАКТИКА РАБОТЫ С NetBSD:


ПРОФИЛИРОВАНИЕ ЯДРА

АЛЕКСАНДР БАЙРАК
Перед началом рассмотрения процесса профилирования я # cp /netbsd /netbsd.old
предполагаю, что вы установили исходные тексты системы
и умеете перекомпилировать ядро. Как это сделать, было Копируем в корень новое:
описано в статье «Первые шаги в NetBSD. Часть 1», опуб-
ликованной в июньском номере журнала. # cp netbsd /netbsd
Целью и задачей профилирования ядра служит сравне-
ние производительности старого и нового ядра. Например, Перезагружаемся:
вы скомпилировали новое ядро, и по вашим расчетам оно
должно работать быстрее первого, но желаемого резуль- # reboot
тата получено не было. В чем дело? Где вы ошиблись? Все
это можно будет выяснить, используя профилирование Сразу после загрузки системы проверим, работает про-
ядра. Если вы собираете NetBSD для какой-либо встроен- филирование или нет.
ной системы, без профилирования вам точно не обойтись.
Ведь во встроенных системах аппаратные характеристики # kgmon – b
железа, как правило, очень ограниченны. А без использо-
вания профилирования вряд ли удастся настроить систему В ответ мы должны получить:
на оптимальную производительность.
Рассмотрим пример профилирования для NetBSD, ра-
ботающей на обычном x86-компьютере. Для примера мы Теперь отключим профилирование:
возьмем ядро GENERIC, поставляющееся с системой по
умолчанию, и сравним его по производительности с собран- # kgmon –h
ным вами новым ядром.
Соберем профилированное GENERIC-ядро. В ответ мы получим:
Переходим в каталог, где располагаются конфигураци-
онные файлы ядра:
Теперь нам нужно поместить данные kgmon в файл.
# cd /sys/arch/i386/conf/
# kgmon –p
Опция –p указывает, что собираемое ядро будет про-
филироваться: После этой команды мы получим файл gmon.out разме-
ром около 3 Мб.
# config –p GENERIC Далее нам нужно получить вывод gprof:

Запускаем сборку ядра: #gprof /netbsd > gprof.out

# cd ../compile/GENERIC.PROF/ Должен заметить, имя файла для вывода можно выб-


# make depend && make
рать самому. Примерно через 2-3 минуты в текущем ката-
Сохраняем старое ядро: логе появится заказанный нами файл gprof.out. После это-

34
администрирование
го переходим непосредственно к процессу анализа полу- Следующим разделом этого файла является список всех
ченных данных. Смотрим наш gprof.out (или как вы его на- функций, которые указывались выше, отсортированные в
звали). алфавитном порядке, и с указанием уникального номера,
Первым разделом идет Flat profile, это список всех выз- присвоенного в разделе Call graph profile. После того как
ванных функций, время и количество их вызовов. мы проанализировали полученные данные, давайте созда-
Вот пример части вывода: дим еще одно ядро системы. Отличие от «оригинального»
будет в заведомой «заторможенности» одной из функций.
Для примера (как и в NetBSD handbook) возьмем функцию
check_exec. Настало время немного поправить ядро систе-
мы. Берем свой любимый текстовый редактор и открыва-
ем файл /usr/src/sys/kern/kern_exec.c. Ищем там функцию
check_exec и добавляем в конце вот такой код:

for (x = 0; x < 100000000; x++)


{
Дальше в том же духе. Давайте рассмотрим содержа- y = x;
ние этих столбцов более подробно. }
1. Сколько всего времени (в процентах) исполнялась та или
иная функция. Не забыв в начале функции check_exec, написать:
2. Общая сумма времени (в секундах) выполнения всех
функций до текущего момента. int x;
int y;
3. Время (в секундах) исполнения какой-либо функции. Это
основной показатель данной таблицы. После внесения этих нехитрых изменений, снова пере-
4. Общее количество вызовов некой функции. компилируем ядро. Естественно, с профилированием. Пе-
5. Среднее время (в миллисекундах), истраченное на вы- резагрузившись, повторяем уже известные нам действия
зов функции. Если функция не профилируется, то по созданию файлов gmon.out и gprof.out. И переходим к
столбец останется пустым. Например, функцию idle, анализу полученных файлов.
как вы понимаете, «улучшить» никак нельзя, поэто- В данном случае результат сразу бросается в глаза, вот
му текущий столбец для этой функции оказался не- что у меня получилось в gprof.out, раздел Flat profile:
заполненным.
6. Среднее время (в миллисекундах), истраченное этой
функцией и ее потомками на вызов. Так же, как и в пре-
дыдущем столбце, если функция не профилируется, зна-
чение остается пустым.
7. Имя функции. Сравним получившиеся результаты функции check_exec
с теми, которые были получены до модификации после-
Как видно из этого фрагмента, подавляющее большин- дней.
ство времени система бездействовала. До:
Теперь следует раздел Call Graph Profile. Его задача –
показать дальнейшие запросы («потомки») от перечислен-
ных функций. После:
Вот часть вывода:

Разница, я думаю, всем понятна. А теперь представим,


что изменения, аналогичные тем, что мы специально доба-
вили в систему для уменьшения производительности, по-
пали в код случайно, например, вследствие ошибки про-
граммиста. Без профилирования, как мне кажется, будет
сложно узнать, что именно «притормаживает» систему. А
какие перспективы открывает профилирование для оцен-
И так далее. Всего 6 столбцов с данными. ки оптимизации кода ядра! Внесли некоторые изменения в
1. Уникальное число, присвоенное каждой функции. процедуру, погоняли машину в тестовом режиме, сравни-
2. Cколько времени (в процентах) исполнялась некая фун- ли результаты с тем, что было в старом варианте и что по-
кция и все ее «потомки». лучилось в новом.
3. Общий процент времени, истраченный на эту функцию. И все видно сразу как на ладони. Для любителей опти-
4. Общее время, занятое «потомками» этой функции. мизации и разработчиков встроенных систем это просто
5. Сколько раз функция была вызвана. После «/» идет ко- находка!
личество вызовов этой функции ее потомками. Рекур- Вообще тема профилирования достаточно обширна, но
сивные вызовы не учитываются. думаю, что описанного выше простого примера вполне до-
6. Имя функции. статочно, чтобы понять принцип профилирования ядра.

№8(21), август 2004 35


администрирование

ЗНАКОМСТВО С USERGATE
АНДРЕЙ УВАРОВ
Одной из «вечных» проблем является организация учёта ка, то есть для пользователя можно установить учёт как
интернет-трафика пользователей. Многие из тех, кто стал- только входящего, так и входящего и исходящего трафи-
кивался с этой задачей, знают, что решается она не всегда ка). Аналогично Squid Blocks возможно создание или ис-
так просто, как хотелось бы. пользование уже готовых списков запрещенных ресурсов,
Традиционными платформами для прокси-серверов яв- так называемых «черных списков», правда, в отличие от
ляются операционные системы семейства UNIX. Suid Blocks, все записи вносятся непосредственно в кон-
Для создания прокси-сервера на базе любой UNIX-по- фигурационный файл, что не очень удобно.
добной ОС вам необходимо иметь достаточно долгий опыт Есть ещё некоторое количество дополнительных воз-
работы с ней, возможно, даже могут понадобиться програм- можностей, таких как автодозвон, portmaping, авторизация
мистские навыки. И тогда по силам будет решить задачу на прокси-сервере каскада и другие.
практически любой сложности. Но если вы не владеете эти-
ми навыками или по каким-либо другим причинам возни- Установка и настройка
кает «огромная» проблема с подсчётом трафика, то эта Вообще с установкой и настройкой больших сложностей
статья для вас. возникнуть не должно, ввиду того что программа имеет
Usergate является одним из самых простых решений достаточно удобный интерфейс.
учёта интернет-трафика. Но стоит сразу сказать, что не яв- Если вы находитесь за прокси-сервером, соответствен-
ляется бесплатным, да и существует только под Windows, но указываете его IP-адрес и, если необходима авториза-
что соответственно увеличивает стоимость системы и ция, заполняете поля: имя пользователя и пароль.
уменьшает ее надежность.
Использование данной системы вполне приемлемо и
оправдано в небольших офисах или «домашних» сетях, где
число компьютеров и требования, предъявляемые к серве-
ру, невелики. И для обслуживания сервера вам не понадо-
бится специалист, так как опытный пользователь вполне
может справиться с данной программой.
Итак, Usergate представляет собой кэширующий про-
кси-сервер, позволяющий гибко управлять разграничени-
ем доступа пользователей к сети Интернет. Найти более
подробную информацию об этой программе и узнать рас-
ценки можно по адресу: http://usergate.ru.
Остановимся более подробно на возможностях, пре-
доставляемых данной системой. Как уже было сказано
выше, – это кэширующий прокси-сервер для HTTP, DNS и
FTP (через HTTP) запросы.
Имеется поддержка протокола Socks5 и туннелирова-
ние POP3- и SMTP-протоколов.
В составе программы находится интегрированный веб-
сервер, правда, с очень ограниченными возможностями.
Конечно, с полноценными веб-серверами он сравниться не
может, да и, к сожалению, пока и со своей прямой задачей –
предоставлением пользователям статистической информа-
ции, не всегда справляется (по загадочным причинам про-
сто перестаёт работать). В настоящий момент речь ведёт-
ся о версии 2.7, как обещают разработчики, в последую-
щих версиях все недостатки будут устранены, а также бу-
дут включены новые возможности.
Гибкая система разграничения доступа пользователей
к сети Интернет включает в себя ограничение доступа как
отдельного пользователя, так и группы пользователей. Воз-
можен запрет доступа в определённые часы, дни, а также
ограничение пользователя по трафику (в системе можно Также неотъемлемой частью настройки системы будет
определять, каким образом осуществлять подсчёт трафи- создание и редактирование групп пользователей и отдель-

36
администрирование
ных пользователей, так как это напрямую связано с основ- зовать Usergate. Так как вы не сможете устанавливать аб-
ной задачей программы. В первую очередь необходимо солютных ограничений, то есть если вы хотите установить
создать группы, а затем в них добавлять пользователей. К определённому пользователю лимит, к примеру, на 100
сожалению, нет возможности перемещать пользователей Мб, а к концу месяца он израсходует лишь часть своего
из одной группы в другую. При добавлении каждой группы трафика, то весь счёт пользователя обнулится, а ограниче-
пользователей необходимо указать имя создаваемой груп- ние будет прежним. Конечно, можно каждый месяц пере-
пы, и если требуется, ограничения и тарифы. считывать трафик всем пользователям вручную, но как вы,
Существует возможность учёта входящего и исходяще- наверное, догадываетесь – это выходом не является. В этом
го трафика для группы пользователей по разным тарифам, состоит, на мой взгляд, один из серьёзнейших недостатков
которые вручную определяются администратором. системы.
При добавлении пользователя необходимо указать его Несомненно, удобной и необходимой является возмож-
логин и пароль, которые будут использоваться для автори- ность видеть в реальном времени работу пользователей в
зации пользователя при выходе в Интернет, и группу, к ко- Интернете (так называемая функция мониторинга работы
торой он будет принадлежать. Установленный у пользова- пользователей).
теля атрибут «Администратор» даёт возможность просмат-
ривать через веб-интерфейс статистику всех пользовате-
лей. Но, к сожалению, веб-интерфейс не предоставляет
возможности администрирования.

Каждому пользователю можно установить следующие


ограничения: по трафику, по времени, по скорости досту-
па, по упоминавшимся ранее «черным спискам», запрещён-
ным часам работы и по длине документа (в данном случае Теперь стоит немного поговорить о недостатках. Как уже
речь идёт об HTTP-протоколе). упоминалось, они имеются в ограничении доступа, в рабо-
те веб-сервера. Говоря об интегрированном веб-сервере,
необходимо не только напомнить о плохом качестве рабо-
ты и об отсутствии возможности администрирования, но и
о том, что через веб-интерфейс предоставляются только
статистические сведения. Ещё одним большим недостат-
ком является некоторое неудобство удалённого админист-
рирования, хотя наверняка многие и не сочтут это за недо-
статок. Пока возможность администрирования Usergate пре-
доставляется через встроенные средства операционной си-
стемы, а именно через Remote Desktop, или же посредством
таких программ, как RAdmin. В настоящий момент Usergate
активно развивается и уже существует версия 2.8, которая
мало отличается от 2.7, но как планируют разработчики, в
недалёком будущем будут устранены все упомянутые не-
достатки и добавлены новые возможности.
Использовать Usergate или нет – выбор ваш, но всё же
К сожалению, имеющаяся система ограничения досту- хочется ещё раз отметить, что это хорошее решение для
па не очень эффективна, ввиду того что лимитирование по Windows (и крайне простое в реализации), но существует
трафику осуществляется только за день или месяц. Это со- целый ряд подобных систем для UNIX, ничуть не уступаю-
здаст вам определённые проблемы, если вы решите исполь- щих, и даже превосходящих его по своим возможностям.

№8(21), август 2004 37


администрирование

СОЗДАНИЕ И НАСТРОЙКА IVR


ДЛЯ ГОЛОСОВЫХ ШЛЮЗОВ CISCO SYSTEMS

Президента компании AT&T


на пресс-конференции спросили:
-Почему телефонная трубка не изменилась за
последние 100 лет?
-Потому что все изменения претерпела
телефонная станция.

МИХАИЛ ЗАГРАЕВСКИЙ
В последнее время трудно найти провайдера сетевых ус- примеры, рассмотренные ниже, были протестированы на
луг, который бы не уделял должного внимания IP-телефо- маршрутизаторе CISCO 3725 IOS 12.3(7)T, но должны ра-
нии. Услуги IP-телефонии или VoIP можно условно разде- ботать на любом голосовом шлюзе фирмы CISCO с IOS,
лить на 3 группы – это «терминация» трафика удаленных поддерживающим TCL IVR API 2.0 и авторизацию по прото-
операторов в сети PSTN, проксирование VoIP-звонков и пре- колу RADIUS. Нашей целью будет создание и отладка пол-
доставление услуг связи, используя IP-сети. ноценного приложения для предоставления услуги между-
Конечного пользователя (клиента некого оператора городней связи с использованием предоплаченных (дебет-
VoIP), как правило, всегда интересует 3-я группа – для него ных) телефонных карт.
она фактически означает получение возможности сделать IVR для оборудования CISCO представляет собой
междугородний или международный звонок по несколько скрипт, написанный на языке TCL (Tool Command Language)
более выгодным тарифам, чем те, которые готов предоста- с использованием CISCO TCL IVR API (Application Program
вить ему городской узел связи. Среди множества способов Interface). Мы будем работать с версией API 2.0.
реализации такого сервиса особую популярность занима- Итак, давайте прежде всего четко определим наши цели
ет так называемая IP-карта. Приобретая ее, клиент может и основные стадии звонка.
позвонить по местному (городскому) телефонному номеру, Предполагается, что информация от клиента к IVR бу-
указанному на карте, и, перейдя в тональный режим, ввес- дет поступать с использованием DTMF (Dual Tone Multi-
ти по запросу системы пин-код, указанный на карте, и не- frequency) (тонального донабора).
посредственно код страны, города и номер телефона, куда Клиенту предлагается:
он желает сделать звонок. ! Выбрать язык общения с системой.
Когда клиент дозванивается по номеру, указанному на ! Ввести номер (пин-код) его карточки.
карточке, начинает работать IVR (Interactive Voice Response) – ! Ввести номер телефона, куда он желает сделать звонок.
специальное приложение, подгружаемое или частично ! Клиента соединяют с требуемым номером.
встроенное в голосовой шлюз, и обеспечивающее интерак-
тивное «общение» с клиентом: запрос необходимых дан- Естественно, при любых ошибках клиенту выдается со-
ных для авторизации, получение информации о том, куда ответствующее диагностическое сообщение. Например, при
клиент желает сделать звонок, выдача сведений об ошиб- неверном вводе номера карты или неправильном номере
ках и балансе карты. назначения.
Мы рассмотрим создание и настройку IVR для маршру- Попутно мы запрограммируем некоторые приятные сер-
тизаторов и голосовых шлюзов фирмы CISCO (http:// висные функции, такие как: сообщение о занятости или не-
www.cisco.com) с аналоговыми FXO (Foreing Exchange доступности желаемого номера, возможность прервать зво-
Office) портами или цифровыми голосовыми портами. Все нок и сделать новый, не кладя трубки.

38
администрирование
По завершении звонка маршрутизатор может предос- seconds, имеющий значение yes. Позже, в процессе выпол-
тавить информацию о клиенте (номере его карты), длитель- нения скрипта, мы сможем изменить его поведение в зави-
ности звонка и назначении звонка серверу RADIUS для ве- симости от значения этого параметра, что, несомненно, уве-
дения статистики. личивает универсальность приложения.
Как уже говорилось ранее, IVR – это программа, напи- IVR-приложение базируется на трех составляющих: про-
санная на языке TCL с использованием CISCO IVR API, ко- цедуры инициализации, функции обработки событий и ко-
торая начинает свое выполнение при поступлении звонка нечное состояние (FSM – Finite State Machine).
на маршрутизатор (голосовой порт). Наше TCL-приложе- Процедуры или функции инициализации применяются
ние может находиться на различных носителях или удален- для объявления и присвоения значений переменным, ис-
ных серверах, а именно FTP, TFTP и FLASH-памяти марш- пользуемых скриптом.
рутизатора. При поступлении звонка шлюз запускает IVR- Эти функции бывают двух типов. К первому типу отно-
скрипт, определенный в конфигурации соответcтвующего сятся те, которые будут выполнены всего один раз в нача-
dial-peer, связанного с портом, на который поступил зво- ле работы скрипта. Они, как правило, используются для ини-
нок. Например, в шлюзе определен голосовой аналоговый циализации глобальных переменных, значение которых не
FXO-порт: должно меняться. Например, в этих процедурах мы можем
считать параметры, определенные для скрипта в глобаль-
voice-port 1/0/0 ной конфигурации маршрутизатора.
cptone RU
timeouts initial 20 Второй тип процедур инициализации – это те, которые
timeouts interdigit 20 вызываются при каждом поступлении звонка на маршру-
timeouts wait-release 10
тизатор, а именно при получении события ev_setup_
С ним связан соответствующий dial-peer: indication или ev_handoff. В них, как правило, принято опре-
делять и инициализировать глобальные переменные, кото-
dial-peer voice 1 pots рые могут меняться при каждом звонке.
application debitcard
port 1/0/0 При получении IVR-скриптом определенного события
вызываются соответствующие функции обработки. Приме-
То есть при поступлении звонка на голосовой порт 1/0/0 ром таких событий могут быть нажатие клавиши на клави-
запустится IVR-приложение с именем debitcard, которое дол- атуре телефонного аппарата, сигнал о завершении проиг-
жно быть определено глобально в конфигурации маршру- рывания клиенту голосовых файлов или окончании процес-
тизатора: са авторизации на RADIUS-сервере.
Для IVR-скрипта назначен четкий набор событий, кото-
call application voice debitcard slot0:/ivr/debitcard.tcl рый он может обрабатывать, и на каждое из них можно при-
call application voice debitcard uid-len 8
call application voice debitcard pin-len 3 вязать функцию-обработчик этого события, которая будет
call application voice debitcard warning-time 10 автоматически выполнена при поступлении этого события
call application voice debitcard language 1 ru
call application voice debitcard language 2 en в каждом конечном состоянии скрипта.
call application voice debitcard set-location ru 0 ↵ FSM – (конечные состояния) выполнения IVR-скрипта
slot0:/ivr/prompts/ru/
call application voice debitcard set-location en 0 ↵ определяют, какие именно обработчики будут вызываться
slot0:/ivr/prompts/en/ при получении тех или иных событий, и в какое состояние
должен перейти скрипт после вызова обработчика.
Первая строчка сообщает маршрутизатору о местона- Итак, приступим к написанию скрипта. Мы создадим
хождении IVR-скрипта с именем debitcard.tcl, в нашем слу- текстовый файл с именем debitcard.tcl. Далее приводится
чае он находится на slot0: – дополнительном модуле Flash- полный листинг скрипта с подробным описанием процедур
памяти. и команд. В тексте слова «процедура» и «функция» явля-
Вторая и третья определяют количество цифр в иденти- ются синонимами.
фикаторе и пин-коде карты.
Четвертая строчка определяет количество секунд до proc init { } {
global param;
окончания звонка, за которое клиента следует предупре- global retryCnt;
дить. global LangPattern;
global ParamForCard;
Пятая и шестая указывают маршрутизатору, где искать global ParamForDest;
необходимые звуковые файлы, из которых будут форми- global AccountLen;
global PinLen;
роваться голосовые сообщения для клиента. global CardLen;
Вообще говоря, для каждого IVR-скрипта можно опре- global WarnTime;
делить любой параметр или параметры, значение которых set param(abortKey) *
он потом сможет считать через соответствующие IVR API. set param(interruptPrompt) true
set param(ignoreInitialTermKey) true
Например, наличие следующей строчки в конфигура- set LangPattern(1) {[1,2]}
ции шлюза:
set AccountLen [string trim [infotag get cfg_avpair ↵
uid-len]];
call application voice debitcard say_seconds yes set PinLen [string trim [infotag get cfg_avpair ↵
pin-len]];
set retryCnt [string trim [infotag get cfg_avpair ↵
определяет для IVR-скрипта debitcard параметр say_ retry-count]];

№8(21), август 2004 39


администрирование
set WarnTime [string trim [infotag get cfg_avpair ↵ ! param(maxDigits) – задает максимальное количество
warning-time]]; цифр, по истечении набора которых возникает событие
set CardLen [expr $AccountLen + $PinLen]; ev_collectdigits_done со статусом «cd_005».
set ParamForCard(abortKey) *
set ParamForCard(initialDigitTimeout) 10 Мы вычисляем этот параметр для количества цифр,
set ParamForCard(terminationKey) # ожидаемых от клиента при вводе номера карты, зная дли-
set ParamForCard(maxDigits) $CardLen;
set ParamForCard(interruptPrompt) true ну идентификатора (логина) и пароля. Сначала мы с помо-
set ParamForDest(abortKey) *
щью команды infotag get устанавливаем значения перемен-
set ParamForDest(initialDigitTimeout) 10 ных AccountLen и PinLen в те значения, которые определе-
set ParamForDest(terminationKey) # ны для этих параметров в глобальной конфигурации мар-
set ParamForDest(interruptPrompt) true
set ParamForDest(dialPlanTerm) true; шрутизатора. Затем вычисляем сумму этих значений, ко-
set ParamForDest(ignoreInitialTermKey) true; торую присваиваем переменной CardLen. И наконец, пере-
return;
} менная LangPattern представляет собой массив, содержа-
щий шаблоны, при совпадении с которыми процесс сбора
Процедура init вызывается всего один раз при первом цифр завершается со статусом «cd_005». В нашем случае
запуске скрипта, и в ней производится объявление и ини- мы хотим получить только цифры 1 или 2 для русского и
циализация переменных, не меняющих свое значение для английского языков соответственно.
всех звонков, обслуживаемых IVR-скриптом.
Она вызывается в глобальной области TCL-приложения proc init_perCallVars { } {
global NumLangPrompt;
командой init, и все переменные, объявленные и установ- global NumCardPrompt;
ленные таким образом, будут сохранять свои значения на global NumDestPrompt;
global PromptFlag;
всем протяжении работы шлюза для всех поступающих global DestPromptFlag;
звонков. В этой процедуре объявляются следующие пере- global NoPlayWarn;
global NoTimeLimit;
менные: param, ParamForDest, ParamForCard – ассоциатив- global SetupDone;
ные массивы, содержащие параметры для сбора инфор-
set NumLangPrompt 0;
мации о набранных клиентом цифрах для выбора языка со- set NumCardPrompt 0;
общений системы, номере карты и номере телефона на- set NumDestPrompt 0;
set PromptFlag 0;
значения. set DestPromptFlag 0;
Массивы могут содержать следующие именованные set NoPlayWarn 0;
set NoTimeLimit 0;
индексы и значения: set SetupDone 0;
! param(abortKey) – определяет клавишу, которая будет return;
использоваться как клавиша отмены набора (например, }
если клиент по ошибке нажал не ту клавишу и вовремя
это заметил, то он будет иметь возможность нажать Процедура init_PerCallVars отвечает за объявление и
клавишу, определенную параметром param(abortKey), и инициализацию глобальных переменных, меняющих свое
IVR-скрипт получит событие ev_collectdigits_done, опре- значение в процессе каждого звонка. В описании других
деляющее завершение процесса сбора информации о процедур будет дано пояснение каждой из них.
полученных от клиента цифрах со статусом «cd_002»,
который означает, что клиент нажал клавишу отмены. proc act_Setup { } {
init_perCallVars;
В нашем случае это клавиша с символом «*» (звездоч- leg setupack leg_incoming;
ка). infotag set med_language prefix "ru";
SelectLanguageMenu;
! param(interDigitTimeout) – устанавливает тайм-аут в се- return;
кундах между нажатиями клавиш. Если за этот проме- }
жуток времени не будет нажата ни одна клавиша, то
скрипт получит событие ev_collectdigits_done со стату- Процедура act_Setup вызывается IVR-скриптом при по-
сом завершения «cd_001». ступлении нового звонка. Именно с нее, по сути, начинает-
! param(initialDigitTimeout) – определяет тайм-аут до мо- ся обработка звонка клиента. Почему именно с нее? Имя
мента нажатия первой клавиши. По истечении этого вре- этой функции определено как обработчик события
мени возникает событие ev_collectdigits_done со стату- ev_setup_indication, которое генерируется при каждом но-
сом завершения «cd_001». вом звонке. За это отвечает строчка:
! param(interruptPrompt) – может принимать значения true
или false – определяет возможность прерывать клиен- set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup ↵
same_state";
том голосовые сообщения нажатием любой клавиши.
! param(terminationKey) – по аналогии с abortKey позво- в конце нашего скрипта.
ляет задать клавишу, которая будет использоваться в Тут следует ненадолго перейти к теме FSM. Перед вы-
качестве индикатора завершения клиентов ввода цифр. полнением IVR-скрипта требуется определить имя масси-
При нажатии этой клавиши возникает событие ev_ ва, который будет содержать в себе все состояния звонка,
collectdigits_done со статусом «cd_005». В нашем слу- функции-обработчики событий, которые могут возникнуть
чае это клавиша с символом «#» (решетка). при этом состоянии, и имя следующего состояния, в кото-

40
администрирование
рое перейдет звонок при выполнении этой функции, а так- мы легко можем понять, что при получении события ev_setup_
же стартовое состояние звонка. Заметьте: не после выпол- indication в состоянии CALLCOMES будет выполнена функ-
нения функции, а непосредственно сразу (одновременно). ция act_Setup и звонок останется в прежнем состоянии
Учтите также, что все вызовы функции и команд в TCL IVR (same_state).
API «не блокирующие», и для выполнения последующей ко- Возвращаясь к нашей функции act_Setup, мы видим, что
манды скрипт не будет дожидаться завершения предыду- в ней вызывается процедура init_perCallVars для объявле-
щей. Это может смутить программистов, работавших ра- ния и инициализации глобальных переменных, которым
нее с такими языками, как Си, но к этому необходимо при- необходимо иметь возможность менять свое значение при
выкнуть. Например, если в процедуре написаны следую- каждом отдельно взятом звонке. Командой leg setupack
щие команды: посылается сообщение setup acknowledgement, чтобы пе-
ревести наш звонок в состояние, при котором возможно
leg collectdigits 1 callInfo получение клиентом голосовых сообщений.
leg collectdigits 2 callInfo
leg setup 295786 setupInfo $callID5 Команда infotag set med_language prefix «ru» устанавли-
puts "\nThis will be executed immediately i.e. before ↵ вает текущий язык голосового интерфейса и фактически
the collect digits or call setup is actually complete"
указывает скрипту, где ему следует искать звуковые фай-
то все они будут выполнены одна за другой, не дожидаясь лы для этого языка. Помните строчку в глобальной конфи-
завершения предыдущей. В данном случае начнется про- гурации шлюза:
цесс сбора цифр на виртуальных составляющих звонка
leg 1 и leg 2, процесс установки соединения на $callID5 и call application voice debitcard set-location ru 0 ↵
slot0:/ivr/prompts/ru/
выдано отладочное сообщение командой puts. Далее IVR-
приложение начнет ожидать получения событий от этих теперь скрипт «знает», что все файлы, из которых следует
процессов и вызвать соответствующие функции-обработ- составлять голосовые сообщения, находятся на носителе
чики. slot0:/ivr/prompts/ru/. Перед именем файла при его поиске
Командой fsm define (в конце нашего скрипта) опреде- он автоматически будет добавлять префикс ru, например,
ляется имя массива, описанного выше ivr_fsm, и первона- команда media play _wecome.au «проиграет» клиенту файл,
чальное состояние звонка CALLCOMES. Командой set полный путь, к которому будет slot0:/ivr/prompts/ru/ru_
ivr_fsm мы будем добавлять к этому массиву новые эле- wecome.au.
менты, создавая так называемые «FSM-переходы». Общий И в конце этой процедуры вызывается функция Select
синтаксис этой команды таков: LanguageMenu:

set array(curr_state,curr_event) “act_proc NEXTSTATE” proc SelectLanguageMenu { } {


global param;
global retryCnt;
где: global NumLangPrompt;
global LangPattern;
! array – это имя определенного командой fsm define мас-
сива. if {$NumLangPrompt < $retryCnt} {
media play leg_incoming %s2000 _RUS_lang_sel1.au ↵
! curr_state – имя текущего состояния скрипта, при кото- %s500 _ENG_lang_sel2.au
ром получено событие curr_event. leg collectdigits leg_incoming param LangPattern;
} else {
! act_proc – имя функции-обработчика события, которую media play leg_incoming _final.au;
необходимо выполнить при поступлении события fsm setstate CALLDISCONNECT;
}
curr_event в состоянии curr_state.
! NEXTSTATE – имя состояния, в которое должен перей- return;
}
ти звонок.
В ее задачи входит следующее: пользователю проигры-
При определении FSM можно использовать следующие ваются два приглашения на разных языках с предложени-
мета-определения: ем нажать соответствующую клавишу для выбора нужного
! any_state – определяет любое состояние звонка, для ко- языка. После проигрывания команда leg collectdigits пере-
торого не установлен иной обработчик в другом FSM- водит скрипт в состояние ожидания поступления события о
переходе; может быть использовано в левой части (ин- завершении процесса сбора цифр. Это событие поступит
дексе массива) FSM-перехода. после однократного нажатия клиентом любой клавиши –
! same_state – трактуется как «то же состояние»; может за такое поведение отвечает массив LangPattern, позволя-
быть использовано в правой части (значении элемента ющий клиенту нажать только одну клавишу. Но если эта
массива) определения FSM-перехода. клавиша не была цифрой 1 или 2, то статус завершения
! ev_any_event – определяет любое событие, которое мо- события ev_collectdigits_done будет отличен от «cd_005».
жет получить скрипт. При получении этого события определенный нами FSM-
переход:
Теперь, глядя на строчку:
set ivr_fsm(CALLCOMES,ev_collectdigits_done) ↵
set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup ↵ "CheckLangSelection CHECKLANG";
same_state";
обеспечит вызов функции CheckLangSelection, которая и

№8(21), август 2004 41


администрирование
проверит статус завершения события и при неудовлетво- #Timeout - no digits entered}
рительном результате должна увеличить значение глобаль- default {
media play leg_incoming _no_aaa.au;
ной переменной NumLangPrompt для того, чтобы функция fsm setstate CALLDISCONNECT;
SelectLanguageMenu не повторяла приглашения больше return;
}
раз, чем записано в переменной retryCnt, тем самым не по- }
зволяя звонку зациклиться на проигрывании звуковых фай- leg collectdigits leg_incoming ParamForCard;
} else {
лов, если нам попался туго соображающий пользователь. media play leg_incoming _final.au;
Именно здесь становится понятна необходимость ис- fsm setstate CALLDISCONNECT;
}
пользования функции init_perCallVars, в которой определя-
ются подобные переменные, регулирующие максимальное return;
}
количество проигрывания приглашений. Нам необходимо,
чтобы при следующем звонке они обнулились, что невоз- Процедура act_GetCard проигрывает клиенту соответ-
можно реализовать через глобальную процедуру init. К тому ствующие звуковые файлы не более $retryCnt раз в зави-
же при нескольких одновременных звонках у каждого из симости от значения переменной PromptFlag, которая из-
них должен быть свой экземпляр переменной. начально установлена в 0 в процедуре init_perCallVars. Со-
ответственно при первом вызове этой процедуры клиент
proc CheckLangSelection { } { слышит приглашение ввести номер (пин-код) карточки.
global NumLangPrompt;
Значение 1 соответствует неверному количеству набран-
set collect_status [infotag get evt_status]; ных цифр и значение 3 – статусу, при котором клиент не
set collect_digits [infotag get evt_dcdigits];
набрал ни одной цифры. Далее скрипт выполняет команду
switch $collect_status { leg collectdigits и ожидает получения события ev_
"cd_001" {
incr NumLangPrompt; collectdigits_done, которое проанализирует функция act_
media play leg_incoming _no_digits_entered.au; GotCardNumber, что обеспечит FSM-переход:
return;
}
"cd_002" { proc act_GotCardNumber { } {
SelectLanguageMenu; global NumCardPrompt
fsm setstate CALLCOMES; global AccountLen;
return; global PinLen;
} global CardLen;
"cd_005" { global PromptFlag;
infotag set med_language $collect_digits; global retryCnt;
fsm setstate CARDSELECTION; global account;
act_GetCard; global pin;
return;
} set status [infotag get evt_status];
"cd_006" {
incr NumLangPrompt; switch $status {
media play leg_incoming _wrong_lang_sel.au; "cd_005" {
return; set card_number [infotag get evt_dcdigits];
} set card_len [string length $card_number];
default { if {$card_len == $CardLen} {
media play leg_incoming _no_aaa.au; set account [string range $card_number 0 ↵
fsm setstate CALLDISCONNECT; [expr $AccountLen - 1]];
} set pin [string range $card_number $AccountLen ↵
} [expr $card_len - 1]]
return; puts "account = $account pin = $pin";
} aaa authorize $account $pin "" "" leg_incoming;
} else {
В процедуре CheckLangSelection мы получаем статус incr NumCardPrompt;
set PromptFlag 1;
события ev_collectdigits_done командой infotag get evt_status; act_GetCard;
и набранные цифры(у) командой infotag get evt_dcdigits, за- return;
}
тем для удобства присваиваем их временным переменным }
collect_status и collect_digits. При получении интересующе- "cd_001" {
incr NumCardPrompt;
го нас статуса «cd_005» мы командой fsm setstate «вруч- set PromptFlag 3;
ную» переводим звонок в состояние CARDSELECTION и вы- act_GetCard;
return;
зываем процедуру act_GetCard для запроса информации о }
пин-коде карты клиента и последующей его авторизации. "cd_002" {
set PromptFlag 0
act_GetCard;
proc act_GetCard { } { return;
global ParamForCard; }
global NumCardPrompt; }
global PromptFlag; return;
global retryCnt; }
if {$NumCardPrompt < $retryCnt} { Процедура act_GotCardNumber выполняется при получе-
switch $PromptFlag {
0 {media play leg_incoming %s500 ↵ нии события ev_collectdigits_done. Глобальная переменная
_enter_card_num.au; #first play} ParamForCard, инициализированная в функции init, обес-
1 {media play leg_incoming _invalid_digits.au; ↵
#Not enuf digits pressed} печивает нам получение определенного количества цифр
3 {media play leg_incoming _no_card_entered.au; ↵ – в нашем конкретном случае это число 11 ($Cardlen =

42
администрирование
$AccountLen + $PinLen). При неудовлетворяющем нас ста- ции («ao_000») получает с помощью команды infotag get
тусе события функция act_GotCardNumber инкремирует aaa_avpair h323-credit-amount сумму на счете клиента, со-
переменную NumCardPrompt для счетчика количества при- храняет ее в переменной amt и проигрывает ее клиенту с
глашений ввода карты, устанавливает значение перемен- помощью команды media play %a$amt. Тут необходимо сде-
ной PromptFlag и снова вызывает процедуру act_GetCard, лать небольшое отступление.
которая проверяет, не превышен ли счетчик приглашений, TCL IVR API 2.0 предоставляет функции TTS (Text To
и указывает клиенту на ошибку, основываясь на значении Speech), которые позволяют воспроизводить клиенту голо-
PromptFlag. При успешно завершившемся процессе сбора совые сообщения, основанные на параметрах команды
цифр (статус «cd_005») функция вычисляет логин и пароль, media play. Например, параметр %a<num> считает значе-
и с помощью команды: ние num денежной суммой в центах (в нашем случае в ко-
пейках), и клиент услышит эквивалентную сумму в долла-
aaa authorize $account $pin "" "" leg_incoming; рах и центах или в рублях и копейках. К великому сожале-
нию, IVR API могут работать только с несколькими языка-
указывает маршрутизатору отправить запрос на авториза- ми, как правило это английский, китайский и испанский, а
цию карты RADIUS-серверу. При получении ответа от него для русского языка необходимо купить у фирмы Cisco
скрипту поступит событие ev_authorize_done. Далее скрипт Systems скрипт, обеспечивающий поддержку русского язы-
выполнит функцию act_CardAuthorize и останется в том же ка. Любителям экстремальных ощущений предлагается
состоянии, для этого используем следующий FSM-переход: написать такой скрипт самим, который потом можно будет
подгрузить в маршрутизатор командой call language voice в
set ivr_fsm(CARDSELECTION,ev_authorize_done) ↵ режиме глобальной конфигурации.
"act_CardAuthorize same_state";
Скрипт этот должен быть написан с применением «чис-
определенный в конце нашего скрипта: того» TCL без использования CISCO IVR API, и из него бу-
дет вызываться функция translate с передачей ей парамет-
proc act_CardAuthorize { } { ров из команды media play. Функция translate должна воз-
global PromptFlag;
global NumCardPrompt; вратить строку, состоящую из имен *.au-файлов, подлежа-
global ParamForDest; щих воспроизведению, разделенных пробелами. Кроме
global ParamForCard;
global retryCnt; этого, по невыяcненной пока причине, TCL IVR-скрипт, по-
лучив от TTS TCL-строку с перечисленными аудио-файла-
set status [infotag get evt_status];
if {$status == "ao_000"} { ми, требующими проигрывания, игнорирует весь текст до
if {[infotag get aaa_avpair_exists ↵ первого пробела (лидирующие пробелы не учитываются).
h323-credit-amount]} {
set amt [infotag get aaa_avpair ↵ То есть, если ваш скрипт вернет следующую строку:
h323-credit-amount] «_200.au _40.au _7.au», то проиграны будут только файлы
} else {
media play leg_incoming _no_aaa.au; _40.au и _7.au.
fsm setstate CALLDISCONNECT; Несмотря на то, что такое поведение не зарегистриро-
return;
} вано как официальная ошибка, оно все же присутствовало
fsm setstate DESTSELECTION; во всех тестированных автором версиях IOS и оборудова-
if {$amt <= 999999.99} {
media play leg_incoming _you_have.au %a$amt ↵ нии. По этому поводу официальные лица Cisco ничего не
_enter_dest.au; говорят, кроме того, что они не несут никакой ответствен-
} else {
media play leg_incoming _enter_dest.au; ности за написанный третьими лицами код, и подобное про-
} граммное обеспечение надо приобретать у них.
leg collectdigits leg_incoming ParamForDest; Далее скрипт, вызывая команду fsm state DESTSELEC-
return; TION, устанавливает для звонка новое состояние и, исполь-
}
зуя команду leg collectdigits, переходит в режим ожидания
if {[infotag get aaa_avpair_exists h323-return-code]} { завершения сбора цифр от клиента, которые определят но-
set return_code [infotag get aaa_avpair ↵
h323-return-code]; мер телефона назначения.
} else { При отказе в авторизации (статус события ev_authorize_
media play leg_incoming _no_aaa.au;
fsm setstate CALLDISCONNECT; done не равен «ao_000») функция act_CardAuthorize полу-
return; чает код ответа от RADIUS-сервера командой:
}
incr NumCardPrompt; infotag get
aaa_avpair_exists h323-return-code
if {$NumCardPrompt < $retryCnt} {
leg collectdigits leg_incoming ParamForCard; и присваивает его переменной return_code, затем в случае,
act_PlayCardReturnCode $return_code;
} else { если количество попыток ввода номера телефона еще не
ct_GetCard; превышено, вызывается функция act_PlayCard ReturnCode
}
return; c передачей ей переменной return_code в качестве пара-
} метра для проигрывания соответствующего звукового со-
общения об ошибке, и скрипт вызывает команду leg
Процедура act_CardAuthorize так же анализирует ста- collectdigits leg_incoming ParamForCard для повторного сбора
тус события ev_authorize_done, и при успешной авториза- цифр – номера карты. Вот тут, кстати, мы и используем

№8(21), август 2004 43


администрирование
свойство «не блокирующего» вызова функций: leg act_GetDestination;
collectdigits и act_PlayCardReturnCode идут одна за другой, return;
}
но выполнены они будут условно одновременно – скрипт "cd_002" {
будет проигрывать голоcовые сообщения об ошибке и ожи- set DestPromptFlag 0;
act_GetDestination;
дать события ev_collectdigits_done, после которого опять return;
отработает процедура act_GotCardNumber, согласно уже ис- }
"cd_004" {
пользованному нами ранее FSM-переходу: set destination [infotag get evt_dcdigits];
puts "\n************dest_number = $destination";
aaa authorize $account $pin "" $destination ↵
set ivr_fsm(CARDSELECTION,ev_collectdigits_done) leg_incoming;
return;
}
поскольку скрипт остался в прежнем состоянии: CARD default {
SELECTION. incr NumDestPrompt;
set DestPromptFlag 2;
act_GetDestination;
proc act_PlayCardReturnCode { return_code } { return;
}
switch $return_code { }
2 {media play leg_incoming _auth_fail.au;} return;
7 {media_play leg_incoming _zero_bal.au;} }
default {
media play leg_incoming _no_aaa.au; Процедура act_GotDestination будет выполняться в со-
fsm setstate CALLDISCONNECT;
return; ответствии с FSM-переходом:
}
}
set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵
return; "act_GotDestination same_state";
}
Она проверяет статус завершения события ev_collect
proc act_GetDestination { } { digits_done. В случае неустраивающего нас статуса вызы-
global retryCnt;
global ParamForDest; вает функцию act_GetDestination, установив предваритель-
global DestPromptFlag; но переменную DestPromptFlag в значение, которое укажет
global NumDestPrompt;
функции act_GetDestination, какой именно звуковой файл
if {$NumDestPrompt < $retryCnt} { следует проиграть, и, как всегда, следует проверка, не пре-
switch $DestPromptFlag {
0 {media play leg_incoming _enter_dest.au; ↵ вышен ли счетчик максимально допустимых попыток ввес-
#Abortkey pressed} ти информацию. При устраивающем нас номере телефо-
1 {media play leg_incoming _no_dest_entered.au; ↵
#Timeout -no digits entered} на, введенном пользователем (статус «cd_004» – совпаде-
2 {media play leg_incoming _reenter_dest.au; ↵ ние с планом набора) с помощью команды:
#Not mutch to the dial plan}
default {
media play leg_incoming _no_aaa.au; aaa authorize $account $pin "" $destination leg_incoming;
fst setstate CALLDISCONNECT;
return;
} RADIUS-серверу будет отправлен запрос на авторизацию
}
leg collectdigits leg_incoming ParamForDest; звонка. По завершении авторизации скрипт получит собы-
} else { тие ev_authorize_done, которое согласно определенному
media play leg_incoming _dest_collect_fail.au;
fsm setstate CALLDISCONNECT; нами FSM-переходу:
}

return; set ivr_fsm(DESTSELECTION,ev_authorize_done) ↵


} "act_CallAuthorize same_state";

обработает процедура act_CallAuthorize:


Процедура act_GetDestination выполняет в некоторой
степени вспомогательную функцию – она будет вызывать- proc act_CallAuthorize { } {
global NumDestPrompt;
ся в случае какой-либо ошибки, например, при тайм-ауте global DestPromptFlag;
ввода цифр, нажатии клиентом клавиши, определенной как global WarnTime;
global NoPlayWarn;
aborKey, или при несоответствии с планом набора, опреде- global NoTimeLimit;
ленным в маршрутизаторе. global creditTime;
global retryCnt;
global ParamForDest;
proc act_GotDestination { } { global param;
global NumDestPrompt;
global DestPromptFlag; set status [infotag get evt_status];
global account; set param(enableReporting) true;
global pin; set param(interruptPrompt) false;
global destination;
if {$status == "ao_000"} {
set status [infotag get evt_status]; if {[infotag get aaa_avpair_exists ↵
h323-credit-time]} {
switch $status { set creditTime [infotag get aaa_avpair ↵
"cd_001" { h323-credit-time];
incr NumDestPrompt; } else {
set DestPromptFlag 1; media play leg_incoming _no_aaa.au;

44
администрирование
fsm setstate CALLDISCONNECT; и FSM-перехода:
return;
}
if {$creditTime <= $WarnTime} {set NoPlayWarn 1;} set ivr_fsm(PLACECALL,ev_digit_end) "act_FastSetup same_state";
if {$creditTime == "unlimited"} {set NoTimeLimit 1;}

media play leg_incoming _you_have.au %t$creditTime; Если клиент в момент прослушивания информации об
leg collectdigits leg_incoming param; ↵ оставшемся времени звонка нажмет клавишу «#», скрипт
#For Fast leg setup
fsm setstate PLACECALL; получит событие ev_digit_end, сигнализирующее о том, что
return; была нажата некая клавиша, и будет вызвана функция
}
incr NumDestPrompt; #Call authorize failed act_FastSetup, которая проверит эту клавишу на соответ-
if {[infotag get aaa_avpair_exists h323-return-code]} ствие с символом «#». В случае совпадения прервет проиг-
{
set return_code [infotag get aaa_avpair ↵ рывание звуковых файлов командой media stop и вызовет
h323-return-code]; процедуру act_CallSetup. Переменная SetupDone будет ус-
} else {
media play leg_incoming no_aaa.au; тановлена в 1, чтобы избежать повторного вызова функ-
fsm setstate CALLDISCONNECT; ции act_CallSetup.
return;
}
proc act_FastSetup { } {
if {$NumDestPrompt < $retryCnt} { global SetupDone;
act_PlayDestReturnCode $return_code;
leg collectdigits leg_incoming ParamForDest; if {[infotag get evt_digit] != "#" || $SetupDone} {
} else { return;
act_GetDestination; }
}
media stop leg_incoming;
return; act_CallSetup;
} set SetupDone 1;
Процедура act_CallAuthorize анализирует статус собы- return;
тия ev_authorize_done. При успешной авторизации клиенту }
проговаривается, сколько времени у него на балансе и воз-
вращенное скрипту RADIUS-сервером в переменной h323- proc act_CallSetup { } {
global destination;
credit-time, далее скрипт запускает команду leg collectdigits global account;
leg_incoming param и командой fsm setstate PLACECALL global SetupDone;
переходит в новое состояние: PLACECAL, в котором по за- set callinfo(accountNum) $account;
вершении проигрывания звуковых файлов обработает со- set callinfo(alertTime) 60;
бытие ev_media_done и через FSM-переход: set SetupDone 1;
leg setup $destination callinfo leg_incoming;
set ivr_fsm(PLACECALL,ev_media_done) "act_CallSetup same_state;
return;
}
будет выполнена функция act_CallSetup, которая наконец-
то соединит нашего клиента с требуемым номером. Процедура act_CallSetup инициализирует ассоциатив-
При ошибке в авторизации функция получит код ошиб- ный массив callinfo необходимыми значениями – опциями
ки из переменной h323-return-code и будет вызвана проце- для будущего соединения, в частности устанавливает мак-
дура act_PlayDestReturnCode, которая озвучит соответству- симальное время ожидания поднятия трубки вызываемой
ющее сообщение об ошибке. стороной 60 секунд. И наконец, запускает команду, ради
Если счетчик попыток ввести номер не превышен, скрипт которой, собственно, и создавался этот скрипт:
будет ожидать от клиента повторного ввода номера, по за-
вершении которого с помощью того же FSM-перехода: leg setup $destination callinfo leg_incoming

set ivr_fsm(DESTSELECTION,ev_collectdigits_done) с передачей ей в качестве параметров номера назначения,


"act_GotDestination same_state";
этот массив и условное обозначение голосового канала, к
управление будет передано функции act_GotDestination. которому подключен наш клиент. При завершении коман-
Теперь объясним, зачем мы используем команду leg ды leg setup скрипт получит событие ev_setup_done, кото-
collectdigits при успешной авторизации, ведь казалось бы рое будет обработано FSM-переходом:
больше ждать ввода от клиента не стоит.
Это позволит клиенту прервать сообщение о доступном set ivr_fsm(PLACECALL,ev_setup_done) "act_CallSetupDone ↵
same_state";
времени звонка и перейти непосредственно к соединению.
Данная возможность будет реализована с помощью команд: и вызвана функция act_CallSetupDone:

set param(enableReporting) true proc act_CallSetupDone { } {


global DestPromptFlag;
global ParamForDest;
и global param;
global NoTimeLimit;
global creditTime;
set param(interruptPrompt) false; global WarnTime;

№8(21), август 2004 45


администрирование
global NoPlayWarn; сти новый номер телефона назначения и перейдет в состо-
global SetupDone; яние DESTSELECTION, а согласно FSM-переходу:
set status [infotag get evt_status];
switch $status { set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵
"ls_000" { "act_GotDestination same_state";
if {!$NoTimeLimit} { #Setting call timer
if {$NoPlayWarn} { после окончания проигрывания файлов при получении со-
timer start leg_timer [expr $creditTime - 1] ↵
leg_incoming; бытия ev_collectdigits_done будет вызвана функция act_Got
fsm setstate CALLLASTACTIVE; Destination:
} else {
set delay [expr $creditTime - $WarnTime];
timer start leg_timer $delay leg_incoming; proc act_PlayDestReturnCode {return_code} {
fsm setstate CALLACTIVE;
} switch $return_code {
} 9 {media play leg_icoming _dest_blocked.au %s500 ↵
set param(enableReporting) true; _enter_dest.au;}
leg collectdigits leg_incoming param; #For long pound 12 {media play leg_incoming _not_enuf.au %s500 ↵
return; _enter_dest.au;}
} default {
"ls_007" { media play leg_incoming _no_aaa.au;
set DestPromptFlag 0; fsm setstate CALLDISCONNECT;
set SetupDone 0; return;
media play leg_incoming _dest_busy.au; }
leg collectdigits leg_incoming ParamForDest; }
fsm setstate DESTSELECTION; return;
return; }
}
default { proc act_ActiveTimer { } {
set DestPromptFlag 0; global WarnTime;
set SetupDone 0; global incoming;
media play leg_incoming _dest_unreachable.au ↵ global outgoing;
%s200 _enter_dest.au;
leg collectdigits leg_incoming ParamForDest; set incoming [infotag get leg_incoming];
fsm setstate DESTSELECTION; set outgoing [infotag get leg_outgoing];
return;
} connection destroy con_all;
} timer start leg_timer [expr $WarnTime - 1] leg_incoming;
return;
} return;
}
Процедура act_CallSetupDone получает статус заверше-
ния процесса установки соединения и при удачном соеди- Процедура act_ActiveTimer выполнится в момент полу-
нении (статус «ls_000»), если не установлена переменная чения скриптом события ev_leg_timer.
NoTimeLimit, сигнализирующая о неограниченном времени В ее задачи входит временно отсоединить вызывающую
звонка, переводит звонок в состояние CALLACTIVE или и вызываемую стороны звонка (это необходимо, чтобы кли-
CALLLASTACTIVE, в зависимости от оставшегося времени, ент мог получить информацию о том, что время его звонка
и с помощью команды timer start «заводит» таймер, при сра- заканчивается), т.к. нельзя проигрывать звуковые файлы
батывании которого скрипт получит сообщение ev_leg_ при установленном звуковом канале между двумя сторо-
timer, который будет обработан FSM-переходами: нами разговора, и установить новый таймер на оставшие-
ся у клиента $WarnTime секунд. После разрыва соедине-
set ivr_fsm(CALLACTIVE,ev_leg_timer) ↵ ния скрипт получит событие ev_destroy_done, обрабатыва-
"act_ActiveTimer CALLWARN";
емое FSM-переходом:
или
set ivr_fsm(CALLWARN,ev_destroy_done) ↵
set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵ "act_CallWarnDestroy same_state";
"act_LastActiveTimer same_state"; proc act_LastActiveTimer { } {
Время таймера рассчитывается как разность значений connection destroy con_all;
переменной, полученной от RADIUS-сервера h323-credit-
return;
time, и глобальной переменной WarnTime, проинициализи- }
рованной в функции init.
Также мы опять вводим приятную возможность для кли- Процедура act_LastActiveTimer, так же как и act_Active
ента – прервать разговор в любой момент путем длитель- Timer, разрывает голосовой канал между двумя сторонами
ного (более 300ms) нажатия и удерживания клавиши с изоб- звонка согласно FSM-переходу:
ражением решетки. В этот момент скрипт получит событие
ev_digit_end, которое обработает FSM-переход: set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵
"act_LastActiveTimer same_state";
set ivr_fsm(CALLACTIVE,ev_digit_end) ↵ После этого скрипт будет ожидать получения события
"act_LongPound CONNDESTROY";
ev_destroy_done, которое обработается FSM-переходом:
При неуспешном завершении процесса установки со-
единения процедура act_CallSetupDone проиграет клиенту set ivr_fsm(CALLLASTACTIVE,ev_destroy_done) ↵
"act_PlayDisconnect CALLDISCONNECT";
соответствующее сообщение об ошибке, предложение вве-

46
администрирование
и выполнение будет передано процедуре act_PlayDisconnect, стояниях получит событие ev_digit_end, и будет вызвана
которая сообщит клиенту о завершение его звонка и пере- процедура act_LongPound, которая проверит клавишу на
ведет скрипт в состояние CALLDISCONNECT: совпадение с символом «#». В случае соответствия она раз-
рушит канал связи между участниками звонка. При успеш-
proc act_PlayDisconnect { } { ном разрушении канала связи скрипт получит событие
media play leg_incoming _disconnected.au ev_destroy_done. Поскольку согласно предыдущему FSM-
fsm setstate CALLDISCONNECT; переходу звонок находился в состоянии CONNDESTROY,
return; будет введен в работу FSM-переход:
}
set ivr_fsm(CONNDESTROY,ev_destroy_done) ↵
proc act_CallWarnDestroy { } { "act_ConnDestroyed same_state";
global WarnTime;

media play leg_incoming _you_have.au %t$WarnTime; который вызовет процедуру act_ConnDestroyed. Последняя
окончательно и полностью отключит вызываемую сторону
return;
} и переведет скрипт в состояние DESTSELECTION, попутно
передав управление функции act_GetDestination и не забыв
Процедуре act_CallWarnDestroy управление передается заново проинициализировать глобальные переменные, не-
согласно FSM: обходимые для осуществления нового звонка.

set ivr_fsm(CALLWARN,ev_destroy_done) ↵ proc act_ConnDestroyed { } {


"act_CallWarnDestroy same_state";
leg disconnect leg_outgoing;
В ее задачи входит сообщить клиенту, что до конца его init_perCallVars;
act_GetDestination;
разговора осталось $WarnTime секунд. Когда это сообще- fsm setstate DESTSELECTION;
ние будет проиграно, скрипт получит событие ev_media_
return;
done и FSM-переход: }

set ivr_fsm(CALLWARN,ev_media_done) ↵
"act_CallWarnMedia CALLLASTACTIVE"; proc act_Cleanup { } {
call close;
}
позаботится о том, чтобы воcстановить канал между сто-
ронами звонка, запустив функцию act_CallWarnMedia, и пе- requiredversion 2.0
init
реведет звонок в состояние CALLLASTACTIVE:
set ivr_fsm(any_state,ev_disconnected) ↵
proc act_CallWarnMedia { } { "act_Cleanup same_state";
global incoming; set ivr_fsm(CALLCOMES,ev_setup_indication) ↵
"act_Setup same_state";
global outgoing; set ivr_fsm(CALLCOMES,ev_collectdigits_done) ↵
connection create $incoming $outgoing; "CheckLangSelection CHECKLANG";
set ivr_fsm(CHECKLANG,ev_media_done)
return; "SelectLanguageMenu CALLCOMES";
set ivr_fsm(CARDSELECTION,ev_collectdigits_done) ↵
} "act_GotCardNumber same_state";
proc act_LongPound { } { set ivr_fsm(CARDSELECTION,ev_authorize_done) ↵
if {[infotag get evt_digit] != "#"} { "act_CardAuthorize same_state";
set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵
fsm setstate same_state; "act_GotDestination same_state";
return;
} set ivr_fsm(DESTSELECTION,ev_authorize_done) ↵
"act_CallAuthorize same_state";
set duration [infotag get evt_digit_duration]; set ivr_fsm(PLACECALL,ev_media_done) ↵
if {$duration < 300} { "act_CallSetup same_state";
set ivr_fsm(PLACECALL,ev_setup_done) ↵
fsm setstate same_state; "act_CallSetupDone same_state";
return;
} set ivr_fsm(PLACECALL,ev_digit_end) ↵
"act_FastSetup same_state";
connection destroy con_all; set ivr_fsm(CALLACTIVE,ev_digit_end) ↵
return; "act_LongPound CONNDESTROY";
set ivr_fsm(CALLACTIVE,ev_leg_timer) ↵
} "act_ActiveTimer CALLWARN";
set ivr_fsm(CALLWARN,ev_destroy_done) ↵
Процедура act_LongPound интересна тем, что дает кли- "act_CallWarnDestroy same_state";
set ivr_fsm(CALLWARN,ev_media_done) ↵
енту возможность во время разговора (состояния CALL "act_CallWarnMedia CALLLASTACTIVE";
ACTIVE, CALLLASTACTIVE) прервать звонок и сделать но- set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵
"act_LastActiveTimer same_state";
вый, не кладя трубки. За это отвечают следующие FSM- set ivr_fsm(CALLLASTACTIVE,ev_destroy_done) ↵
переходы: "act_PlayDisconnect CALLDISCONNECT";
set ivr_fsm(CALLLASTACTIVE,ev_digit_end) ↵
"act_LongPound CONNDESTROY";
set ivr_fsm(CALLACTIVE,ev_digit_end) ↵ set ivr_fsm(CONNDESTROY,ev_destroy_done) ↵
"act_LongPound CONNDESTROY"; "act_ConnDestroyed same_state";
set ivr_fsm(CALLLASTACTIVE,ev_digit_end) ↵ set ivr_fsm(CALLDISCONNECT,ev_media_done) ↵
"act_LongPound CONNDESTROY"; "act_Cleanup same_state";
set ivr_fsm(CALLDISCONNECT,ev_disconnect_done) ↵
"act_Cleanup same_state";
Если клиент нажмет любую клавишу, скрипт в этих со- fsm define ivr_fsm CALLCOMES;

№8(21), август 2004 47


администрирование

РАЗГОН И ТОРМОЖЕНИЕ WINDOWS NT


i486C-ядру посвящается…

КРИС КАСПЕРСКИ
Разработчики ядра исполнительной системы говорят, что лиотеки аппаратных абстракций – Hardware Abstraction
оно дает пищу всему остальному. И это отнюдь не преуве- Layer (сокращенно HAL), представленной файлом HAL.DLL.
личение! На плохом фундаменте ничего хорошего не пост- На самом деле имена файлов могут быть любыми, и в за-
роишь, и качество ядра в значительной мере определяет висимости от типа ядра они варьируются в довольно широ-
производительность всей операционной системы в целом. ких пределах.
В комплект поставки Windows NT входит большое количе- Исходная концепция построения Windows NT предпола-
ство разнообразных ядер (в том числе и нашумевшее ядро гала сосредоточить весь системно-зависимый код в HAL,
i486С, по слухам значительно увеличивающее быстродей- используя его как фундамент для воздвижения системно-
ствие системы). Как оценить их производительность? Обыч- независимой исполнительной системы. Тогда для переноса
ные тестирующие пакеты для этого не подходят, и адекват- ядра на новую платформу было бы достаточно переписать
ную методику измерений приходится разрабатывать само- один HAL, не трогая ничего остального (по крайней мере
стоятельно с учетом архитектуры ядра Windows и специ- теоретически). В действительности же это требование так и
фической направленности возложенных на него задач. не было выполнено, и большое количество системно-зави-
Большинство пользователей и системных администра- симого кода просочилось в исполнительную систему, а HAL
торов живут с ядром, назначенным операционной системой превратился в сплошное нагромождение неклассифицируе-
при ее инсталляции, совершенно не задумываясь о том, что мых функций, тесно переплетенных с исполнительной сис-
его можно заменить другим. В штатный комплект поставки темой, так что двухуровневая схема организации ядра в на-
Windows NT входит более десятка различных ядер, и еще стоящее время выглядит довольно условной.
большее их количество может быть найдено на просторах Исполнительная система Windows NT реализует высо-
Интернета. Некоторые производители аппаратного обеспе- коуровневые функции управления основными ресурсами
чения оптимизируют ядра под свои машины, и зачастую эта (как то: памятью, файлами, процессами и потоками), в оп-
оптимальность сохраняется на большинстве остальных. Су- ределенном смысле являясь операционной системой в ми-
ществует мнение, что древние ядра (все еще совместимые ниатюре. Большинство этих функций слабо связаны с кон-
с новомодными версиями Windows) намного производитель- структивными особенностями конкретной аппаратуры. Они
нее современных, хотя и уступают им по функциональнос- практически не меняются от одной исполнительной систе-
ти. ме к другой и одинаково производительны (или непроизво-
Отдельного разговора заслуживает история с ядром дительны) во всех ядрах.
i486С, оптимизированным под 80486-машины. Оно входи- Обособленная часть исполнительной системы, реали-
ло во все версии Windows вплоть до NT 4.0, но затем нео- зующая наиболее низкоуровневые операции и тесно взаи-
жиданно исчезло, и Windows 2000 вышла уже без него. модействующая с библиотекой аппаратных абстракций,
Однако с Windows XP все вернулось вновь. Говорят, что оно называется ядром (KERNEL). Большинство ядерных проце-
здорово увеличивает производительность системы. Автор, дур предназначены для сугубо внутреннего использования
знакомый с ним еще со времен Windows NT 4.0, подтверж- и не экспортируются (хотя присутствуют в отладочных сим-
дает – да, увеличивает, особенно на медленных машинах. волах), а те, что экспортируются, обычно начинаются с пре-
Для теоретического обоснования данного факта и была на- фикса Ke (подпрограммы ядра) или Ki (обработка преры-
писана эта статья. ваний в ядре).
Разумеется, само по себе ядро i486С не настолько ин- Это уже третье упоминание ядра, которое мы встреча-
тересно, чтобы уделять ему 13 журнальных полос. Давай- ем, что создает определенную путаницу. Давайте попробу-
те мыслить более глобально. Ядер много, а инструментов ем разложить образовавшийся терминологический кавар-
для оценки их производительности не существует (во вся- дак по полочкам. На самом верхнем уровне абстракций
ком случае в открытом доступе нет ни одного). Тем не ядром принято называть совокупность компонентов опера-
менее такой инструмент при желании можно разработать ционной системы, работающих на привилегированном коль-
и самостоятельно, о чем я, собственно, и собираюсь рас- це нулевого уровня. Спустившись пониже, мы увидим, что
сказать. ядро отнюдь не монолитно и состоит как минимум из двух
частей: собственно самого ядра и загружаемых драйверов.
Структура ядра Ядро Windows NT реализовано в двух файлах: библиотеке
Ядро Windows NT состоит из двух ключевых компонентов: аппаратных абстракций (по сути дела являющееся набо-
executive system – исполнительной системы (далее по тек- ром первичных драйверов) и исполнительной системе. Вы-
сту KERNEL), реализованной в файле ntoskrnl.exe, и биб- бором исполнительной системы руководит ключ KERNEL

48
администрирование
файла boot.ini, поэтому многие ассоциируют ее с ядром, хотя Типы ядер
это и не совсем верно, но не будем докапываться до стол- Тип выбираемого ядра определяется как архитектурными
ба, ведь фундамент исполнительной системы – тоже ядро. особенностями конкретной аппаратной платформы, так и
И это еще далеко не все! Подсистемы окружения (win32, личными предпочтениями пользователя системы, обуслов-
POSIX, OS/2) имеют свои собственные ядра, сосредоточен- ленными спецификой решаемых задач. Существует по
ные в прикладных библиотеках непривилегированного ре- меньшей мере пять основных критериев классификации
жима третьего кольца, и общаются с ядром Windows NT ядер:
через специальную «прослойку», реализованную в файле ! тип платформы (Intel Pentium/Intel Itanium, Compaq
NTDLL.DLL. Ядра подсистем окружения представляют со- SystemPro, AST Manhattan);
бой сквозные переходники к ядру Windows NT и практичес- ! количество процессоров (однопроцессорные и много-
ки полностью абстрагированы от оборудования. Практичес- процессорные ядра);
ки, но не совсем! Некоторая порция системно-зависимого ! количество установленной памяти (до 4 Гб, свыше 4 Гб);
кода присутствует и здесь. Многопроцессорные версии ! тип контроллера прерываний (APIC- и PIC-ядра);
файлов NTDLL.DLL и KERNEL32.DLL для синхронизации ! тип корневого перечислителя (ACPI- и не ACPI-ядра).
потоков используют машинную команду LOCK. В однопро-
цессорных версиях она теряет смысл и заменяется более Очевидно, что ядро должно быть совместимо с целе-
быстродействующей командой NOP. Наверняка существу- вым процессором на уровне двоичного кода и работать в
ют и другие различия, но я такие специально не искал, по- наиболее естественном для него режиме (64-разрядный
скольку их влияние на производительность системы незна- процессор, поддерживающий архитектуру IA32, сможет
чительно. работать и со стандартным 32-разрядным ядром, но разум-
Из всего этого зоопарка нас в первую очередь будет ным такое решение не назовешь). Данная статья обсужда-
интересовать ядро исполнительной системы и HAL (рис. 1). ет вопросы оценки сравнительной производительности ядер

Ðèñóíîê 1. Àðõèòåêòóðà ÿäðà Windows NT

№8(21), август 2004 49


администрирование
в рамках одной аппаратной платформы, и тема выбора про- ные ядра (а их большинство) допускают выполнение каж-
цессора здесь не затрагивается. дого из потоков на любом свободном процессоре, асиммет-
Многопроцессорные ядра отличаются от монопроцес- ричные же жестко закрепляют системные потоки за одним
сорных прежде всего тем, что они «видят» все установлен- из процессоров, выполняя пользовательские потоки на всех
ные процессоры и умеют с ними взаимодействовать, воз- остальных. Асимметричные ядра не входят в стандартный
лагая эту задачу на специальный драйвер, встроенный в комплект поставки Windows NT и обычно предоставляются
HAL. Также в них кардинально переработаны механизмы поставщиками соответствующего оборудования. Асиммет-
синхронизации. Если в монопроцессорных ядрах для пре- ричные ядра несколько менее производительны, чем сим-
дотвращения прерывания критичного участка кода доста- метричные (один из процессоров большую часть своего
точно всего лишь подтянуть IRQL к верхнему уровню или времени простаивает), однако их намного сложнее «за-
заблокировать прерывания командной CLI, то в многопро- тормозить», и сколько бы прожорливых потоков ни запус-
цессорных ядрах такая стратегия уже не срабатывает – ведь тил злобный хакер, администратор всегда может обезо-
на всех остальных процессорах прерывания разрешены – ружить их, ведь системные потоки выполняются на отдель-
и приходится прибегать к спинлокам (от английского spin ном процессоре! Многопроцессорные ядра следует исполь-
lock – взаимоблокировка). зовать только на многопроцессорных системах, в против-
Для защиты участка кода от вмешательства извне сис- ном случае мы значительно проиграем в производитель-
тема взводит специальный флаг, складывая поступающие ности, причем многопроцессорные материнские платы с
запросы в специальную очередь. Естественно, это требует одним процессором на борту требуют специального унип-
некоторого количества процессорного времени, что нега- роцессорного ядра (uniprocessor kernel), а работоспособ-
тивно сказывается на производительности, но другого вы- ность однопроцессорных ядер в такой конфигурации ни-
хода у нас нет. Значительно усложняется и схема диспет- кем не гарантирована (хотя обычно они все-таки работа-
черизации прерываний, ведь теперь один набор IRQ прихо- ют) (рис. 2).
дится делить между несколькими процессорами, а табли- Разрядность внешней адресной шины младших моде-
цы обработчиков аппаратных/программных прерываний лей процессоров Intel Pentium составляет 32 бита, и потому
поддерживать в согласованном состоянии. Изменения кос- они не могут адресовать более 4 Гб физической памяти.
нулись и планировщика, а точнее самой стратегии плани- Поскольку для серьезных серверов и мощных рабочих стан-
рования потоков, которая может быть реализована как по ций этого оказалось недостаточно, начиная с Pentium Pro,
симметричной, так и по асимметричной схеме. Симметрич- ширина шины была увеличена до 36 бит, в результате чего

Ðèñóíîê 2. Ñèììåòðè÷íàÿ è àñèììåòðè÷íàÿ îáðàáîòêà

50
администрирование
мы получили возможность адресовать вплоть до 64 Гб фи- тываются. К тому же степень «отзывчивости» системы (под-
зической памяти. При работе в обычном режиме странич- сознательно ассоциируемая с ее производительностью)
ной адресации четыре старших бита адресной шины обну- обусловливается отнюдь не скоростью выполнения кода, а
ляются, и чтобы их задействовать, необходимо перевести интеллектуальностью планировщика, повышающего при-
процессор в режим PAE (Physical Address Extensions), отли- оритет потока в тот момент, когда он больше всех осталь-
чающийся структурой таблиц страничной переадресации и ных нуждается в процессорном времени (например, при
поддерживающий 2 Мб страницы памяти. PAE-ядра не- захвате фокуса, завершении ожидаемой операции ввода/
сколько производительнее обычных ядер, поскольку засо- вывода и т. д.). Но тесты этого не учитывают.
вывают старшие 2 Мб адресного пространства процесса в Допустим, пересчет миллиона ячеек электронной таб-
одну страницу, сокращая тем самым издержки на переклю- лицы, независимо от версии ядра, длится ровно один час.
чение контекста между процессами. Вы можете использо- Означает ли это, что все ядра равноценны? Вовсе нет! За
вать PAE-ядра, даже если у вас установлено менее 4 Гб кадром остались такие жизненно важные аспекты, как «под-
физической памяти, однако выигрыш в производительнос- вижность» фоновых потоков системы, «чувствительность»
ти при этом будет не очень существенен. к прерываниям и т. д. Одно ядро достойно обрабатывает
В зависимости от типа контроллера прерываний, уста- клавиатурный ввод одновременно с расчетом, а другое
новленного на материнской плате, следует выбирать либо жутко тормозит, реагируя на нажатия с задержкой в не-
PIC-, либо APIC-ядро. PIC-контроллеры поддерживают сколько секунд. Ну и с каким из них вам будет приятнее
15 IRQ и встречаются только на многопроцессорных мате- работать? А ведь тестирующей программе все равно…
ринских платах. Зайдем с другой стороны. Обычно тестовое задание
APIC-контроллеры поддерживают до 256 IRQ и многопро- состоит из серии повторяющихся замеров, время выпол-
цессорную обработку. На программном уровне PIC- и APIC- нения которых усредняется. Замеры со значительными от-
контроллеры взаимно совместимы, поэтому PIC-ядро долж- клонениями от средневзвешенного значения отбрасыва-
но работать и с APIC-контроллером, однако, во-первых, при ются (ну мало ли, может, в этот момент началась отгрузка
этом оно увидит всего лишь 15 IRQ, а во-вторых, такая кон- кэша на диск). Если продолжительность одного замера со-
фигурация не тестировалась Microsoft, и потому никаких га- ставляет не более 20 мс (целая вечность для процессо-
рантий, что система не зависнет при загрузке, у нас нет. ра!), за это время может вообще не произойти ни одного
Материнские платы с поддержкой технологии ACPI мо- переключения контекста, а если оно и произойдет, то бу-
гут работать как с ACPI, так и с не ACPI-ядрами, при этом дет безжалостно отбраковано при обработке результатов,
не ACPI-ядра самостоятельно распределяют системные в итоге мы получим «чистую» производительность маши-
ресурсы компьютера и взаимодействуют с устройствами ны за вычетом вклада операционной системы. Можно ли
напрямую, а ACPI-ядра во всем полагаются на ACPI-кон- этого избежать? Увы! В противном случае результаты те-
троллер, фактически являющийся корневым перечислите- стирования будут варьироваться от прогона к прогону, и
лем, т.е. самой главной шиной компьютера, к которой под- пользователь растеряется – какое же значение ему выб-
ключены все остальные шины и устройства. И хотя эта шина рать? Ведь на коротком промежутке времени (порядка 10 –
виртуальна, производительность системы значительно па- 20 мс) издержки от побочных эффектов крайне непосто-
дает, поскольку ACPI-контроллер имеет тенденцию вешать янны и неоднородны, поэтому выдавать неочищенный ре-
все PCI-устройства на одно прерывание со всеми вытека- зультат нельзя.
ющими отсюда последствиями. Если же продолжительность замера превышает 20 мс,
Подробнее обо всем этом и многом другом я расскажу планировщик Windows автоматически перераспределяет
по ходу углубления внутрь статьи, а пока же сосредоточим процессорное время так, чтобы переключение контекста
свое внимание на ядре как таковом, приоткрыв черный основного потока (и этим потоком будет тестовый поток!)
ящик. происходило как можно реже, а его накладные расходы
стремились к нулю. Естественно, остальные потоки систе-
Почему не пригодны тестовые пакеты мы сажаются на голодный паек, работая рывками (и, как
Среди изобилия тестовых программ, представленных на мы увидим далее, даже при умеренной загрузке системы
рынке, можно найти множество утилит для оценки быстро- на «плохих» ядрах потоки получают управление не чаще
действия центрального процессора, оперативной памяти, чем один раз… в десять секунд. Не «тиков», а именно се-
видеокарты или подсистемы ввода/вывода, но мне неизве- кунд! И хотя интегральная производительность системы не
стны инструменты, пригодные для определения произво- только не уменьшается, но даже возрастает, работать с ней
дительности ядра операционной системы. становится невозможно).
А почему бы не воспользоваться WINSTONE или То же самое относится и к имитатору веб-сервера. До-
SPECweb96? Первый имитирует запуск реальных офисных пустим, одно ядро обрабатывает сто тысяч запросов за ми-
приложений, второй – веб-сервера. Разве их показания не нуту, а другое – сто пятьдесят. Какое из них производитель-
будут отражать объективное влияние конкретного ядра на нее? А если первое обслуживает всех своих клиентов плав-
производительность всей операционной системы в целом? но, а второе отдает информацию «плевками» с паузами по
Нет, не будут. И вот почему. WINSTONE (как и большин- десять-пятнадцать секунд? К сожалению, известные мне те-
ство его соплеменников) прогоняет тесты в идеализирован- сты этого обстоятельства не учитывают и потому их пока-
ных условиях при минимальном количестве потоков, поэто- зания при всей своей объективности толкают на выбор не-
му накладные расходы на переключение контекста не учи- правильного ядра. Помните анекдот про машину, которая

№8(21), август 2004 51


администрирование
ездит быстро, но тормозит медленно? Производитель- Intel 8254 (сейчас – 82C54) и тактируемый частотой
ность – это не только отношение количества проделанной 1.19318 МГц (сейчас – либо 1.19318, либо 14.31818 МГц,
работы к затраченному времени, это еще и качество пре- причем последняя встречается намного чаще), что обеспе-
доставляемого сервиса! чивало ему превосходную по тем временам точность изме-
Сформулируем главное требование, предъявляемое рений – порядка 0.84 мс (~1 мс с учетом накладных расхо-
нами к системе: планировщик должен исхитриться перерас- дов). В Windows продолжительность одного тика таймера
пределить процессорное время между потоками так, что- составляет 10 мс. Каждые 10 мс таймер дергает прерыва-
бы обеспечить наилучший баланс между производитель- нием, и закрепленный за ним обработчик увеличивает сис-
ностью и плавностью работы даже при большом количе- темное время на эту же величину. Если обработчик по ка-
стве одновременно выполняющихся потоков. Теперь для ким-либо причинам проморгает таймерное прерывание (ап-
достижения полного счастья остается лишь найти ядро сво- паратные прерывания запрещены инструкцией CLI или пе-
ей мечты. репрограммированием PIC-контроллера), системное время
начнет отставать от реального, существенно снижая точ-
Обсуждение методик тестирования ность измерений. Хуже того, размеренность хода PIT дале-
Скажите, какую физическую величину измеряет обыкно- ко не идеальна и варьируется в довольно широких преде-
венный ртутный или спиртовой термометр. Температуру? лах. Windows использует PIC-таймер в основном для пла-
Нет, объем. Каждый тест по-своему объективен и что-то нирования потоков, а для измерения времени стремится ис-
измеряет, но результат измерений в значительной мере пользовать другие, более точные таймеры, и переходит на
определяется его интерпретацией. Объем ртутного шари- PIT только тогда, когда ни один из них не доступен.
ка прямо пропорционален его температуре. Это хорошо. Таймер часов реального времени (Real Time Clock или
Но производительность не температура. Это комплексная сокращенно RTC), впервые появившийся в IBM AT, обычно
величина, и в индексах производительности столько же тактируется частотой 32.768 кГц, автоматически обновляя
смысла, сколько и в коэффициенте интеллектуальности счетчик времени в CMOS, что не требует наличия программ-
отдельного индивидуума. Кто умнее – Гейзенберг или Га- ного обработчика прерываний. Частота обновления по
луа? Кто сильнее – кит или слон? Какое из всех ядер са- умолчанию составляет 100 Гц, но при желании RTC-таймер
мое производительное? Можно спорить до хрипоты, но в может быть перепрограммирован, и тогда часы будут идти
такой формулировке вопрос не имеет ответа. Как мини- или медленнее, или быстрее. Точность показаний зависит
мум мы должны выявить факторы, в наибольшей степени как от состояния питающей батарейки (которая вообще-то
влияющие на совокупное быстродействие системы, и раз- никакая не батарейка, а настоящий литиевый аккумулятор,
работать адекватные методики тестирования, разлагаю- но это уже не важно), так и от добротности реализации
щие векторное понятие производительности на скалярные микросхемы RTC со всеми обслуживающими ее компонен-
величины. тами. По заверениям производителей среднее время ухо-
Никакая методика тестирования не безупречна и отра- да за день составляет порядка 1-2 сек, однако имеющиеся
жает лишь часть реальной действительности, поэтому к у меня материнские платы врут намного сильнее, к тому
полученным результатам следует относиться с большой ос- же некоторые из них обнаруживают значительные «биения»
торожностью. Если Формула-1 ездит быстрее, чем КамАЗ, на временных интервалах порядка десятых долей секунды,
отсюда еще не следует, что она сохранит свое преимуще- что отнюдь не способствует точности измерений. К тому
ство при перевозке двухсот тонн кирпичей. же некоторые версии Windows периодически синхронизи-
Методика тестирования тесно связана со спецификой руют системное время с часами реального времени, что еще
возлагаемых на систему задач (у автогонщика свои требо- больше подрывает доверие к системному времени. Исполь-
вания к машине, у дальнобойщика – свои), поэтому ника- зуя его для измерения продолжительности тех или иных
ких конкретных решений здесь не приводится, напротив, процессов, вы можете получить очень странный результат.
эта статья показывает, как разрабатывать тестовые мето- Часы реального времени используют многие тестовые про-
дики самостоятельно и какие проблемы поджидают вас на граммы и перепрограммирование RTC-таймера позволяет
этом пути. А проблем будет много. фальсифицировать результат. Windows 2000 использует
RTC только для периодической синхронизации с систем-
Разность таймеров ными часами.
Современные материнские платы несут на борту несколь- Усовершенствованный контроллер прерываний
ко независимых таймеров, которые никто не калибровал и (Advanced Programmable Interrupt Controller, или сокращен-
каждый из которых врет слегка по-своему. Различные ядра но APIC), в основном использующийся в многопроцессор-
используют различные таймеры для подсчета времени и ных системах, помимо прочей оснастки включает в себя и
системного планирования, поэтому отталкиваться от API- некоторую пародию на таймер, предназначенный для пла-
функций типа GetTickCount или QueryPerfomanceCounter нирования потоков и не пригодный ни для каких измере-
для сравнения производительности ядер категорически ний ввиду своей невысокой точности. Однако по непонят-
недопустимо! Ну во всяком случае без их предварительной ным причинам APIC-ядра используют APIC-таймер в каче-
калибровки. стве основного таймера системы, при этом величина одно-
Первым (а на IBM XT еще и единственным) возник про- го «тика» составляет уже не 10 мс, а 15 мс. Естественно,
граммируемый таймер интервалов – Programmable Interval часы идут с прежней скоростью, но политика планирова-
Timer, или сокращенно PIT, базирующийся на микросхеме ния существенно изменяется – с увеличением кванта со-

52
администрирование
кращаются накладные расходы на переключение контек- или усыпляют его в паузах между работой для лучшего ох-
стов, но ухудшается плавность коммутации между ними. лаждения. Как следствие – непосредственное преобразо-
Использовать показания счетчика системного времени для вание процессорных тактов в истинное время оказывается
сравнения производительности APIC-ядер с другими ядра- невозможным.
ми недопустимо, т.к. полученный результат будет заведо- Утилиту для оценки разности хода нескольких таймеров
мо ложным. можно скачать, например, отсюда: http://www.overclockers.ru/
Материнские платы, поддерживающие ACPI (Advanced cgi-bin/files/download.cgi?file=320&filename=timertest.rar. И
Configuration and Power Interface), имеют специальный тай- если выяснится, что ваши таймеры идут неодинаково, для
мер, обычно управляемый менеджером электропитания и сравнения производительности различных ядер будет не-
потому называющийся Power Management Timer, или сокра- обходимо ограничиться лишь одним из них. Надежнее все-
щенно PM. Еще его называют ACPI-таймером. Штатно он го использовать для снятия показаний свой собственный
тактируется частой 3.579545 МГц (тактовая частота PIT, драйвер, поскольку стратегия выбора таймеров ядром сис-
разделенная на четыре), что обеспечивает точность изме- темы в общем случае непредсказуема и может отличаться
рений порядка ~0.3 мс. ACPI-ядра используют PM-таймер в от вышеописанной.
качестве основного таймера системы, чему сами не рады.
Чипсеты от VIA, SIS, ALI, RCC не вполне корректно реали-
зуют PM-таймер, что приводит к обвальному падению про-
изводительности операционной системы и снижению на-
дежности ее работы. Проблема лечится установкой соот-
ветствующего пакета обновления, подробнее о котором
можно прочитать в технической заметке Q266344. Разуме-
ется, исправить аппаратную проблему (а в данном случае
мы имеем дело именно с ней) программными средствами
невозможно, и ее можно лишь обойти. Но даже на правиль-
ном чипсете при высокой загрузке PCI-шины PM-таймер не
успевает своевременно передавать свои тики, и хотя они
при этом не пропадают, обновление счетчика времени про-
исходит «рывками», для преодоления которых Microsoft
рекомендует сверять показания PM с показаниями PIC/APIC
или RTC.
И если PM неожиданно прыгнет вперед (jump forward),
обогнав своих соплеменников, этот замер должен аннули-
роваться как недействительный. Ðèñóíîê 3. testtimer çà ðàáîòîé
Современные чипсеты (и, в частности, Intel 845) содер- Рассмотрим некоторые API-функции, используемые тес-
жат специальный высокоточный таймер (High Precision Event товыми программами для измерения интервалов времени.
Timers, или сокращенно HPET), тактируемый частотой от ! GetTickCount. Самая популярная функция. Возвращает
10 МГц, при которой время одного тика составляет от 0.1 мс количество миллисекунд, прошедших со времени после-
при точности порядка ±0.2% на интервалах от 1 мс до днего старта системы. В зависимости от типа установлен-
100 мс. Это действительно рекордно высокая точность, по ного ядра использует либо PIT-, либо APIC-таймеры, в со-
крайней мере на порядок прерывающая точность всех ос- ответствии с чем ее разрешение составляет либо 10 мс,
тальных таймеров, однако HPET все еще остается завид- либо 15 мс, причем некоторые тики таймера могут быть
ной экзотикой, и чипсеты с его поддержкой пока еще не пропущены (т.е. за 30 мс не произойдет ни одного увели-
очень широко распространены. чения счетчика). Не рекомендуется к употреблению.
Помимо этого на материнской плате можно найти мно- ! GetSystemTime. Возвращает истинное время. То есть
жество таймеров, например, PCI Latency Timer или десят- функция думает, что оно истинное, а в действительнос-
ки таймеров, обслуживающих чипсет, шины, память и про- ти – производное от системного счетчика, инкременти-
чие системные устройства. Многие из них тактируются ча- руемого каждые 10 мс или 15 мс за вычетом «съеден-
стотами PCI- или AGP-шины, что обеспечивает достаточно ных» тиков и врожденной неравномерности хода PIT- и
высокую точность измерений (ниже, чем у HPET, но суще- APIC-таймеров. Периодически синхронизирует себя с ча-
ственно выше, чем у PM). К сожалению, они в своей массе сами реального времени, а если запущена специаль-
не стандартизированы и на каждой материнской плате ре- ная интернет-служба, то еще и с атомными часами, т.е.
ализуются по-своему, если вообще реализуются. системное время продвигается траекторией пьяного гон-
Некоторые используют в качестве таймера команду щика, едущего по пересеченной местности. Для изме-
RDTSC, считывающую показания внутреннего счетчика рений временных промежутков непригодна.
процессора, каждый такт увеличивающегося на постоян- ! timeGetTime. То же самое, что и GetTickCount. Докумен-
ную величину (как правило единицу). Для профилировки тация утверждает, что разрешающая способность этой
машинного кода она подходит на ура, но вот на роль бес- функции составляет 1 мс, в действительности же – 10 мс.
пристрастного метронома уже не тянет. Некоторые ACPI- Синхронные изменения timeGetTime и GetTickCount под-
контроллеры динамически изменяют частоту процессора тверждают, что они питаются от одного источника.

№8(21), август 2004 53


администрирование
! Sleep. Усыпляет поток на указанное количество милли- высокий уровень сигнала на котором свидетельствует о
секунд, задерживая на время управление. Теоретичес- поступлении запроса на прерывание. Процессор прекра-
ки может использоваться для калибровки других тай- щает текущую работу, вырабатывает сигнал подтвержде-
меров и вычисления коэффициента перевода таковой ния прерывания (Interrupt Acknowledge) и считывает с шины
процессора в секунды. Практически же… Время ожи- данных 8-битный номер вектора перекрывания (INT n), пе-
дания принудительно округляется до величины, кратной реданный контроллером прерываний, обычно реализуемым
«тику» основного системного таймера, причем на мо- на правнучатых племянниках микросхемы i8259 и террито-
мент выхода из сна данный поток должен находиться в риально находящийся в южном мосту чипсета.
самом начале очереди потоков, ожидающих выполне- За вычетом 32 прерываний, зарезервированных разра-
ния, в противном случае ему придется подождать. Мо- ботчиками процессора, мы имеем 224 прерывания, пригод-
жет быть, доли секунды, а может, несколько минут – все ных для обработки сигналов от периферийных устройств.
зависит от размеров очереди, а она непостоянна. На хоро- Означает ли это, что верхнее ограничение на максималь-
шо загруженной системе поток, планирующий вздрем- ное количество одновременно поддерживаемых устройств
нуть 100 мс, рискует проснуться… через минуту! равно 224? Нет! Некоторые из древних процессоров имели
! QueryPerformanceCounter. Возвращает количество тиков всего лишь один вектор прерывания, но это ничуть не ме-
наиболее точного таймера, прошедших с момента стар- шало им управлять десятком устройств одновременно. Как?
та системы или… его последнего переполнения. Явля- Да очень просто. Что такое прерывание? Всего лишь спо-
ется переходником к функции KeQueryPerformance соб устройства обратить на себя внимание. При наличии
Counter, реализованной в HAL. Windows XP поддержи- достаточного количества свободных векторов за каждым
вает HPET, более ранние системы – нет. Если HPET ап- из устройств может быть закреплено свое персональное
паратно доступен и программно поддерживается, ис- прерывание, однозначно указывающее на источник сигна-
пользуется он, в противном случае ACPI-ядра будут ис- ла. Это существенно упрощает как проектирование самих
пользовать PM, а не ACPI – либо PIT, либо возвратят устройств, так и разработку обслуживающих их драйверов,
ноль, сигнализируя об ошибке. Для определения про- однако отнюдь не является необходимым. Получив сигнал
должительности одного тика можно использовать фун- прерывания, процессор может опросить все устройства по
кцию QueryPerformanceFrequency, возвращающую его очереди, выясняя, кто из них затребовал внимания. Есте-
частоту в герцах. Это самое лучшее средство для про- ственно, это достаточно медленная операция, выливающа-
филировки и хронометража времени из всех имеющих- яся в десятки дополнительных операций ввода/вывода и к
ся, однако, как уже говорилось, PM-таймеры могут идти тому же потенциально небезопасная, т.к. малейшая ошиб-
неточно или совершать неожиданные прыжки вперед, ка разработчика оборачивается серьезными конфликтами.
поэтому, показания, возращенные QueryPerformance Короче говоря, для достижения наивысшей стабильности
Counter, требуют некоторой обработки. и производительности системы каждое прерывание долж-
но использоваться не более чем одним устройством.
Синхронизация Контроллер прерываний, используемый в IBM XT, под-
На машинном уровне межпроцессорная синхронизация держивал восемь аппаратных прерываний, обозначенных
обеспечивается префиксом LOCK, предоставляющим ко- IRQ (Interrupt Request) и пронумерованных от 0 до 7. Номер
манде монопольный доступ к памяти. Все посягательства IRQ соответствует приоритету прерывания: чем больше но-
со стороны остальных процессоров (если они есть) блоки- мер – тем ниже приоритет. Во время обработки более при-
руются. Обычно LOCK используется совместно с команда- оритетных прерываний генерация менее приоритетных по-
ми, устанавливающими или снимающими флаги, сигнали- давляется и соответственно, наоборот, менее приоритет-
зирующие о том, что указанная структура данных в настоя- ные прерывания вытесняются более приоритетными. Счи-
щий момент модифицируется процессором, и потому луч- тается, чем больше ресурсов требует устройство, тем выше
ше ее не трогать. должен быть приоритет его IRQ. Это неверно. Выбор пред-
Многопроцессорные ядра содержат множество LOCK, почтительного IRQ, определяется отнюдь не «прожорливо-
встречающихся в самых неожиданных местах, и съедаю- стью» устройства, а критичностью потери прерывания. До-
щих вполне ощутимый процент производительности, поэто- пустим, сетевая карта, видя, что входной буфер практичес-
му многопроцессорные ядра всегда медленнее. В однопро- ки полон, а данные по витой паре продолжают поступать,
цессорных ядрах часть LOCK убрана полностью, вместе с сгенерировала прерывание, которое было вытеснено пре-
примыкающими к ним флагами, а часть заменена более рыванием звуковой карты, имеющей более высокий при-
быстродействующими NOP. Общая же структура ядра со- оритет и сигнализирующей об опустошении выходного бу-
хранена в более или менее неизменном виде (рис. 4). фера. Если драйвер звуковой карты ненароком замешка-
ется и удержит обработчик прерывания дольше положен-
ACPI и IRQ ного, входной буфер сетевой карты переполнится, и часть
Прерывания – их мифическая разводка, неиссякаемый ис- пакетов окажется безвозвратно утеряна, и данные придет-
точник слухов, сплетен, легенд и суеверий. Попробуем с ся передавать вновь, что несколько снизит быстродействие
ними разобраться. сети. Является ли эта ситуация нормальной? Для кого-то
Все x86-процессоры (за исключением старших моделей да, а для кого-то и нет! Потерянных пакетов, конечно, жаль,
Intel Pentium-4) управляют прерываниями через специаль- но если поменять приоритеты местами, звуковая карта от-
ный интерфейсный вывод, обычно обозначаемый как INTR, реагирует на опустошение входного буфера суровым иска-

54
администрирование
жением воспроизводимого сигнала, чего в некоторых ситу- ройств висеть на одном IRQ? Ну вообще-то могут, но если
ациях ни в коем случае допускать нельзя. Никакого друго- они одновременно сгенерируют сигнал прерывания, то до
го влияния на производительность выбор приоритетов не контроллера дойдет лишь один из них, а остальные будут
оказывает. Независимо от номера IRQ обработка преры- потеряны, но ни контроллер, ни устройства об этом не до-
вания занимает одно и то же время. От обработки осталь- гадаются. Такая ситуация получила название конфликта, и
ных прерываний она также не освобождает. При условии, ее последствия всем хорошо известны. Впрочем, если пре-
что аппаратные устройства и обслуживающие их драйве- рывания возникают не слишком часто, то оба устройства
ры реализованы правильно (т.е. более или менее безбо- вполне уживаются друг с другом (в свое время автор дер-
лезненно переживают потерю IRQ и «отпускают» прерыва- жал на одном прерывании и мышь, и модем).
ние практически сразу же после его возникновения), вы- Шина PCI, пришедшая на смену ISA, работает всего с
бор приоритетов никакой роли не играет. четырьмя линиями равно приоритетных прерываний, услов-
Контроллер прерываний позволяет отображать аппарат- но обозначаемых как INTA, INTB, INTC и INTD. На каждый
ные IRQ0-IRQ7 на 8 любых смежных векторов прерываний, слот подведены все четыре прерывания, и устройство мо-
например, на INT 30h – INT 37h. Тогда, при возбуждении жет использовать любое подмножество из них, хотя обыч-
IRQ0, процессор сгенерирует прерывание INT 30h, а при но ограничиваются только одним. Линии прерываний одно-
возбуждении IRQ3 – INT 33h. В IBM AT количество контрол- го слота соединяются с линями остальных слотов (а в неко-
леров было увеличено до двух, причем второй был подклю- торых дешевых платах все INTA, INTB, INTC и INTD веша-
чен на вход первого, в результате чего количество аппа- ются на одну линию прерывания). Для равномерного рас-
ратных прерываний возросло до 15. Почему не 16? Так ведь пределения прерывания по устройствам на каждом слоту
одна из восьми линий прерываний была израсходована на происходит ротация прерываний (рис. 5). Допустим, у нас
каскадирование с другим контроллером! есть два слота: в первом слоте прерывание INTA (со сторо-
Некоторое количество прерываний разошлось по сис- ны устройства) соответствует прерыванию INTA (со сторо-
темным устройствам, некоторое было выделено шине ISA – ны шины), прерывание INTB → INTB и т. д. Во втором слоте
тогдашнему индустриальному стандарту. Генерация преры- прерыванию INTA (с стороны устройства) соответствует
ваний осуществлялась изменением уровня сигнала на ли- прерывание INTB (со стороны шины), INTB → INTC, INTC →
нии соответствующего IRQ. Могут ли два или более уст- INTD и INTD → INTA, в результате чего устройства, исполь-

Ðèñóíîê 4. ACPI êàê êîðíåâîé ïåðå÷èñëèòåëü

№8(21), август 2004 55


администрирование
зующие прерывание INTA, оказываются развешаны по пре- десяток устройств и драйвер наиболее «беспокойного» из
рываниям INTA и INTB. них попадет в самый хвост очереди?
Для достижения наивысшей производительности сле-
дует, во-первых, оптимально распределить PCI-карты по
слотам (например, если у вас на шесть PCI-слотов прихо-
дятся две PCI-карты, то, втыкая устройства в первый и пя-
тый слот, вы вешаете их на одно PIRQ), по возможности
Ðèñóíîê 5. Ðîòàöèÿ àïïàðàòíûõ ïðåðûâàíèé PCI-øèíû совмещая на одном PIRQ только наименее темперамент-
Линии прерываний INTA – INTB соединяются с вывода- ные устройства, т.е. такие, которые генерируют прерыва-
ми PIRQ0 – PIRQ3 контроллера PCI-шины, а оттуда через ния реже всего. Во-вторых, каждое PIRQ должно отобра-
роутер (PCI Interrupt Router) попадают в контроллер преры- жаться на свое IRQ. Какое – не суть важно (ведь приоритет
ваний, тем или иным образом отображаясь на четыре ли- PCI-прерываний одинаков), но только свое. Совместное ис-
нии IRQ, не занятые никакими ISA-устройствами. Посколь- пользование одного IRQ несколькими PIRQ обычно не при-
ку количество установленных PCI-устройств обычно много водит к конфликтам, но негативно сказывается на произво-
больше четырех (мы считаем также и внутренние устрой- дительности, ведь драйвера работают не с PIRQ, а с IRQ!
ства, такие, например, как интегрированный контроллер ACPI-ядра, работающие с PCI-шиной через ACPI-кон-
USB, чаще всего повешенный на INTD), несколько устройств троллер, лишены возможности управлять отображением
вынуждены делить одно прерывание между собой. В отли- PIRQ на IRQ по своему усмотрению. Не может управлять
чие от ISA, в PCI-шине совместное использование преры- этим и BIOS (во всяком случае легальными средствами).
ваний является ее нормальным состояниям. Генерация пре- Сам же ACPI стремится повесить все PIRQ на одно IRQ
рываний осуществляется не по переходу, а по состоянию, (обычно IRQ9), и помешать ему очень трудно. Если количе-
и устройство может удерживать линию прерывания в соот- ство установленных PCI-устройств намного больше четы-
ветствующем состоянии до тех пор, пока его запрос не бу- рех, то разница в производительности между ACPI- и не
дет обработан. Теоретически это легко. Практически же… ACPI-ядрами несущественна, поскольку, даже отказавшись
Даже поверхностное тестирование обнаруживает большое от ACPI, вы все равно будете вынуждены разделять одно
количество устройств и драйверов, не вполне соответству- PIRQ между несколькими устройствами. Другое дело, если
ющих спецификациям и не желающих делить свое PIRQ с количество PCI-устройств невелико и наиболее темпера-
другими (или делающих это настолько неумело, что произ- ментные из них висят на своих прерываниях, тогда при пе-
водительность падает в разы). Следование спецификаци- реходе с ACPI- на не ACPI-ядро разница в быстродействии
ям предотвращает конфликты, но оставляет проблему па- системы может оказаться очень значительной (то же са-
дения производительности в силе. При совместном исполь- мое относится и к неудачно спроектированным устройствам,
зовании прерываний драйвера получают сигналы не толь- не умеющим делить прерывания с другими и не имеющих
ко от своих, но и от чужих устройств, заставляя их обра- достойной замены, например, дорогой видеоускоритель,
щаться к своему устройству за подтверждением, и если RAID-контроллер и т. д.).
выяснится, что прерывание сгенерировало не оно, запрос К сожалению, просто взять и отключить ACPI нельзя,
передается следующему драйверу в цепочке. А теперь пред- поскольку он является не только менеджером питания, рас-
ставьте, что произойдет, если на одном прерывании висит пределителем ресурсов, но еще и корневым перечислите-

Ðèñóíîê 6. Âñå PCI-óñòðîéñòâà íà îäíîì ïðåðûâàíèè

56
администрирование
лем. ACPI- и не ACPI-ядра используют различные деревья Алгоритм планирования непрерывно совершенствуется,
устройств и потому взаимно несовместимы. Смена ядра в однако, не все усовершенствования оказывают благопри-
обязательном порядке требует переустановки системы, в ятное влияние на производительность. В общем случае
противном случае та откажется загружаться. Это суще- многопоточные приложения должны исполняться на тех яд-
ственно затрудняет сравнение быстродействия ACPI- и не рах, под стратегию планирования которых они оптимизи-
ACPI-ядер, поскольку переустановка системы радикальным ровались, в противном случае можно нарваться на неожи-
и непредсказуемым образом изменяет ее производитель- данное падение производительности.
ность (рис. 6). При небольшом количестве потоков накладные расхо-
Продвинутые материнские платы используют усовер- ды на их переключения довольно невелики, и ими можно
шенствованный контроллер прерываний (Advanced PIC или пренебречь, но по мере насыщения системы они стреми-
сокращенно APIC), поддерживающий 256 IRQ и способный тельно растут! На что же расходуется процессорное вре-
работать в многопроцессорных системах. Однако в моно- мя? Прежде всего на служебные нужды самого планиров-
процессорных конфигурациях он не обеспечивает никаких щика (анализ очереди, ротацию приоритетов и т. д.), затем
дополнительных преимуществ, т.к. количество свободных на сохранение/восстановление контекста потоков и процес-
прерываний ограничивается не контроллером, а PCI-шиной. сов. Посмотрим, как все это устроено изнутри?
К тому же APIC-ядра не вполне корректно работают с тай- Дизассемблирование показывает, что планировщик как
мером, что сводит на нет все их преимущества. бы размазан по всему ядру. Код, прямо или косвенно свя-
занный с планированием, рассредоточен по десяткам фун-
Переключение контекста кций, большинство из которых не документированы и не
Под «многозадачностью» большинство пользователей под- экспортируются. Это существенно затрудняет сравнение
разумевает возможность параллельного выполнения не- различных ядер друг с другом, но не делает его невозмож-
скольких приложений: чтобы в фоне играл WinAmp, скачи- ным. Чуть позже мы покажем, как можно выделить подпрог-
вался mp3 с Интернета, принималась почта, редактирова- раммы профилировщика из ядра, пока же сосредоточимся
лась электронная таблица и т. д. Минимальной единицей на переключении и сохранении/восстановлении контекста.
исполнения в Windows является поток. Потоки объединя- Процессоры семейства x86 поддерживают аппаратный
ются в процессы, а процессы – в задания (jobs). Каждый механизм управления контекстами, автоматически сохра-
поток обладает собственным стеком и набором регистров, няя/восстанавливая все регистры при переключении на
но все потоки одного процесса выполняются в едином ад- другую задачу, но Windows не использует его, предпочитая
ресном пространстве и обладают идентичными квотами. обрабатывать каждый из регистров вручную. Какое-то вре-
В любой момент времени на данном процессоре может мя автор думал, что i486С-ядро, ничего не знающее о MMX/
выполняться только один поток, и если количество потоков SSE-регистрах современных процессоров и не включающее
превышает количество установленных процессоров, пото- их в контекст, будет выигрывать в скорости, однако парал-
ки вынуждены сражаться за процессорное время. Распре- лельная работа двух и более мультимедийных приложений
делением процессорного времени между потоками зани- окажется невозможной. В действительности же оказалось,
мается ядро. Вытесняющая многозадачность, реализован- что за сохранение/восстановление регистров сопроцессо-
ная в Windows NT, устроена приблизительно так: каждому ра (если его можно так назвать) отвечают машинные ко-
потоку выдается определенная порция машинного време- манды FXSAVE/FXSTOR, обрабатывающие и MMX/SSE-ре-
ни, называемая квантом (quantum), по истечении которой гистры тоже, но чтобы выяснить это, пришлось перерыть
планировщик (dispatcher) принудительно переключает про- все ядро – от HAL до исполнительной системы!
цессор на другой поток. Учет процессорного времени обес- Переключение контекста осуществляется служебной
печивается за счет таймера. Периодически (раз в 10 мс или функцией SwapContext, реализованной в ntoskrnl.exe. Это
15 мс) таймер генерирует аппаратное прерывание, прика- чисто внутренняя функция, и ядро ее не экспортирует. Тем
зывающее процессору временно приостановить выполне- не менее она присутствует в символьных файлах (symbol
ние текущего потока и передать бразды правления диспет- file), бесплатно распространяемых фирмой Microsoft. Пол-
черу. Диспетчер уменьшает квант потока на некоторую ве- ный комплект занимает порядка 150 Мб и неподъемно тя-
личину (обычно равную двум) и либо возобновляет выпол- жел для модемного скачивания. Ряд утилит, таких, напри-
нение потока, либо (если квант обратился в нуль) сохраня- мер, как Symbol Retriever, от NuMega, позволяют выборочно
ет регистры потока в специальной области памяти, назы- скачивать необходимые символьные файлы вручную, зна-
ваемой контекстом (context), находит поток, больше всего чительно сокращая время перекачки, однако по непонятным
нуждающийся в процессорном времени, восстанавливает причинам они то работают, то нет (Microsoft блокирует дос-
его контекст вместе с контекстом процесса (если этот по- туп?), поэтому необходимо уметь находить точку входа в
ток принадлежит другому процессу) и передает ему управ- SwapContext самостоятельно. Это легко. SwapContext – един-
ление. ственная, кто может приводить к синему экрану смерти с
Потоки обрабатываются по очереди в соответствии с надгробной надписью «ATTEMPTED_SWICH_FROM_DPC»,
их приоритетом и принятой стратегией планирования. Пла- которой соответствует BugCheck код B8h. Загрузив
нировщик сложным образом манипулирует с очередью, ntoskrnl.exe в ИДУ (или любой другой дизассемблер), пере-
повышая приоритеты потоков, которые слишком долго ждут числим все перекрестные ссылки, ведущие к функциям
процессорного времени, только что получили фокус управ- KeBugCheck и KeBugCheckEx. В какой-то из них мы найдем
ления или дождались завершения операции ввода/вывода. PUSH B8h/CALL KeBugCheck или что-то в этом роде. Она-то

№8(21), август 2004 57


администрирование
и будет функцией SwapContex. Прокручивая экран дизассем- #define argNthr ((argc > 1)?atol(argv[1]):defNthr)
блера вверх, мы увидим вызов функции HalRequestSoft main(int argc, char **argv)
wareInterrupt, которая, собственно, и переключает контекст, {
а в многопроцессорной версии ядра еще и машинную ко- int a, zzz;
манду FXSAVE, которая тут совсем ни к чему и которая от- printf("creating %d threads...", argNthr);
сутствует в монопроцессорной версии. К тому же много- // ñîçäàåì argNthr ïîòîêîâ
процессорные версии намного щепетильнее относятся к for (a = 0; a < argNthr; a++) ↵
вопросам синхронизации и потому оказываются несколько CreateThread(0, 0, (void*)thread, 0,0, &zzz);
printf("OK\n"); thread();
менее производительными. return 0;
Функция HalRequestSoftwareInterrupt, реализованная в }
HAL, через короткий патрубок соединяется с функциями Сравнение ACPI-ядра с i486С-ядром на машине автора
_HalpDispatchInterrupt/_HalpDispatchInterrupt, cохраняющими/ (Intel Pentium-III 733 МГц, 256 Мб SDR-RAM-133) обнаружива-
восстанавливающими регистры в своих локальных перемен- ет значительное расхождение в их производительности.
ных (не в контексте потока!) и на определенном этапе пе- i486С-ядро переключает контекст намного быстрее, особен-
редающих управление на KiDispatchInterrupt, вновь возвра- но при работе с большим количеством потоков. В общем
щающую нас в ntoskrnl.exe и рекурсивно вызывающую случае – количество переключений контекста обратно про-
SwapContext. Кто же тогда сохраняет/восстанавливает кон- порционально количеству потоков, т.к. контексты надо где-
тексты? Оказывается – аппаратные обработчики. Список то хранить, а кэш-память не резиновая. Если ядро делает
указателей на предустановленные обработчики находится много лишних сохранений (о которых мы уже говорили), оно
в ntoskrnl.exe и содержится в переменной IDT (не путать с существенно проигрывает в скорости. Тем не менее, все ядра
IDT-таблицей процессора!), которая, как и следовало ожи- спроектированы достаточно грамотно и сохраняют отличную
дать, не экспортируется ядром, но присутствует в символь- подвижность даже при работе с тысячами потоков.
ных файлах. При их отсутствии найти переменную IDT мож- Переключение процессов требует дополнительных на-
но так: просматривая таблицу прерываний любых из ядер- кладных расходов и потребляет намного больше памяти,
ных отладчиков (Soft-Ice, Microsoft Kernel Debugger), опреде- попутно вызывая сброс буфера ассоциативной трансляции,
лите адреса нескольких непереназначенных обработчиков поскольку каждый из процессов обладает своим адресным
прерываний (т.е. таких, которые указывают на ntoskrnl.exe, пространством. Выделить код, ответственный за переклю-
а не к драйверу) и, загрузив ntoskrnl.exe в дизассемблер, чение контекстов, несложно – он выдает себя обращением
восстановите перекрестные ссылки, ведущие к ним. Это и к регистру CR3, загружая в него указатель на каталог стра-
будет структурой IDT. ниц (Page Directory Physical Address).
Другие функции также могут сохранять/восстанавливать Давайте немного модернизируем нашу тестовую про-
текущий контекст (это, в частности, делает Kei386EoiHelper, грамму, заменив потоки процессами. Один из вариантов
расположенная в ntoskrnl.exe), поэтому накладные расхо- реализации может выглядеть так:
ды на переключение между потоками оказываются доста-
точно велики и выливаются в тысячи и тысячи команд ма- Ëèñòèíã 2. Èçìåðèòåëü ñêîðîñòè ïåðåêëþ÷åíèÿ ïðîöåññîâ
шинного кода, причем каждое ядро имеет свои особеннос- thread()
ти реализации. Как оценить, насколько одно из них произ- {
while(1) Sleep(0);
водительнее другого? }
Логично, если мы уговорим ядро переключать контек-
#define defNthr 3
сты так быстро, как только это возможно, то количество #define argNthr ((argc > 1)?atol(argv[1]):defNthr)
переключений в единицу времени и определит долю наклад- #define argProc "-666"
ных расходов в общем быстродействии ядра. main(int argc, char **argv)
Сказано – сделано. Создаем большое количество по- {
int a, zzz;
токов (по меньшей мере сто или даже триста) и каждый из char buf[1000];
них заставляем циклически вызывать функцию Sleep(0), STARTUPINFO st;
PROCESS_INFORMATION pi;
приводящую к отдаче квантов времени и как следствие –
немедленному переключению на другой поток. Количество memset(&st, 0, sizeof(st)); st.cb = sizeof(st);
переключений контекста можно определить по содержимо- if ((argc > 1) && !strcmp(argv[1], argProc)) thread();
му специального счетчика производительности, отобража-
sprintf(buf,"%s %s",argv[0], argProc);
емого Системным Монитором, утилитой CPUMon Марка printf("creating %d proc...", argNthr);
Руссиновича, отладчиком Microsoft Kernel Debugger и мно- for (a = 0; a < argNthr; a++)
CreateProcess(0, buf, 0,0,0, ↵
гими другими программами. NORMAL_PRIORITY_CLASS,0, 0, &st, &pi);
printf("OK\n"); thread();
Ëèñòèíã 1. Èçìåðèòåëü ñêîðîñòè ïåðåêëþ÷åíèÿ êîíòåêñòà return 0;
}
thread()
{ Даже при небольшом количестве процессов система
// îòäàåì ïðîöåññîðíîå âðåìÿ â áåñêîíå÷íîì öèêëå
while(1) Sleep(0); значительно «проседает» под нагрузкой и начинает ощути-
} мо притормаживать, а количество переключений контекстов
#define defNthr 300 сокращаются приблизительно вдвое. i486С-ядро по-прежне-

58
администрирование
му держится впереди, что не может не радовать, к тому же ные тесты производительности, но нигде – отношение вре-
с ним система намного более оживленно реагирует на вне- мени простоя клиента ко времени работы. Ладно, оставим
шние раздражители (в частности, клавиатурный ввод). лирику и перейдем к делу.
Быстродействие подсистемы ввода/вывода специально не Windows NT поддерживает два типа квантов – длинные
тестировалось, но по субъективным ощущениям i486С и с и короткие. Независимо от своего типа кванты могут быть
этим справляется намного быстрее. как фиксированной, так и переменной длины (кванты пе-
Желающие подкрепить экспериментальные данные доб- ременной длины еще называют динамическими). Величи-
рой порцией дизассемблерных листингов могут самостоя- на кванта выражается в условных единицах, официально
тельно проанализировать функции планировщика, если, называемых quantum units. Три квантовых единицы обыч-
конечно, ухитрятся выдрать их из ядра! Далеко не всем ис- но равны одному тику таймера.
следователям недр Windows удалось это сделать… Управление типом и продолжительностью кванта осу-
Задумайтесь: если ядро львиную долю процессорного ществляется через следующий параметр реестра: HKLM\
времени тратит на переключение контекстов, не означает SYSTEM\CurrentControlSet\Control\PriorityControl\Win32
ли это, что наиболее интенсивно вызываемыми функция- PrioritySeparation.
ми окажутся функции, принадлежащие планировщику? Если 4-й и 5-й биты, считая от нуля, равны 10, система
Запускаем нашу тестовую программу, подключаем Microsoft использует короткие кванты. То же самое происходит при
Kernel Profiler (или любой другой профилировщик ядра по оптимизации параметров быстродействия под исполнение
вкусу) и дав ему на сбор статистики порядка десяти секунд, приложений (Панель Управления → Система → Дополни-
внимательно изучим полученный результат: тельно → Параметры быстродействия). 01 – указывает на
длинные кванты (они же используются при оптимизации си-
Ëèñòèíã 3. Ôóíêöèè ÿäðà, ïðÿìî èëè êîñâåííî îòíîñÿùèåñÿ стемы под выполнение служб в фоновом режиме). Любое
ê ïëàíèðîâàíèþ
другое значение выбирает продолжительность кванта по
4484 ntoskrnl.exe ExReleaseResourceForThread умолчанию (короткие – в Windows 2000 Professional, длин-
4362 ntoskrnl.exe KiDispatchInterrupt
4333 ntoskrnl.exe SeTokenImpersonationLevel ные – в Windows 2000 Server).
2908 ntoskrnl.exe KeDelayExecutionThread Если 2-й и 3-й биты равны 10 – длина квантов фиксиро-
2815 ntoskrnl.exe KiIpiServiceRoutine
300 ntoskrnl.exe RtlPrefetchMemoryNonTemporal ванна; 01 – позволяет планировщику динамически варьи-
73 ntoskrnl.exe KeDisconnectInterrupt ровать продолжительность кванта в заранее оговоренных
41 ntoskrnl.exe ExFreePoolWithTag
пределах. Любое другое значение выбирает тип квантов по
умолчанию (переменные – в Windows 2000 Professional,
Длительность квантов фиксированные – в Windows 2000 Server). При использова-
Частые переключения контекстов отрицательно сказыва- нии динамических квантов планировщик пытается автома-
ются на производительности системы, поэтому Windows NT тически увеличивать продолжительность квантов некото-
подбирает продолжительность одного кванта с таким рас- рых потоков, тех, которым процессорное время нужнее все-
четом, чтобы они происходили как можно реже, теряя при го. Во всяком случае, планировщик думает, что оно им нуж-
этом подвижность и способность достаточно быстро реа- нее, а думает он приблизительно так: если поток обслужи-
гировать. вает GUI-окно и это окно находится в фокусе, продолжи-
Допустим, у нас имеется 100 потоков, каждому из кото- тельность кванта увеличивается. Если поток полностью
рых выделяется 100 мс процессорного времени, причем все использует весь отведенный ему квант, его продолжитель-
потоки используют отведенное им время полностью. Тогда ность увеличивается. Если… Конкретный алгоритм плани-
между двумя переключениями одного и того же потока прой- рования зависит от выбранного ядра, и потому одни ядра
дет 10 сек! Вот тебе, бабушка, и многозадачный день… могут оказаться намного предпочтительнее других.
Разве это нормально, когда нажатая клавиша отображает- Два младших бита задают индекс в двухэлементном мас-
ся на экране только через 10 сек? Когда сетевые клиенты сиве PsPrioritySeparation, расположенном внутри ntoskrnl.exe
получают в час по чайной ложке? А ведь если сервер обра- и используемым планировщиком для расчета продолжи-
батывает каждого из клиентов в отдельном потоке (что яв- тельности квантов активного процесса. Эта переменная не
ляется типичной стратегией программирования в экспортируется ядром, но упоминается в символьных фай-
Windows NT), он должен умело распределять процессорное лах. Если же они отсутствуют, обратитесь к функции PsSet
время между тысячами потоков! ProcessPriorityByClass, которая использует первый элемент
Разработчики UNIX, программирующие не ради денег, этого массива как указатель на другой массив.
а в силу исторической неизбежности, стремятся выбирать Òàáëèöà 1. Ìîäåëüíûé ðÿä êâàíòîâ â Windows 2000 Professional
величину кванта так, чтобы сервер не терял отзывчивости
даже при пиковой нагрузке. Разработчики Windows NT, на-
против, оптимизировали свою систему под максимальную
производительность, меняя величину кванта от версии к Для экспериментов с квантами слегка модернизируем
версии так, чтобы совокупное количество обработанных нашу тестовую утилиту, заставляя потоки (процессы) ис-
запросов в единицу времени было максимальным. Ведь про- пользовать отведенный им квант времени целиком. А в пер-
изводительность – это сила, а комфортабельность и уют – вичный поток встроим счетчик времени, вычисляющий про-
понятия растяжимые. Поднимите компьютерные журналы, должительность интервала между двумя соседними пере-
полазьте по Интернету – везде лежат только сравнитель- ключениями:

№8(21), август 2004 59


администрирование
Ëèñòèíã 4. Èçìåðèòåëü ïðîäîëæèòåëüíîñòè êâàíòîâ Что произошло? Потоки ушли в фон, их приоритет по-
thread()
низился, а величина кванта сократилась до минимума. И
{ хотя накладные расходы на переключение контекста воз-
int a, b; росли, время обработки очереди сократилось до 2 сек, с
while(!f) Sleep(0);
while (f != 2); которыми вполне можно жить!
while(1) А теперь закройте все окна с несохраненными докумен-
{
for (a = 1; a< 100; a++) b = b + (b % a); тами, которые вам жалко потерять, и увеличьте приоритет
} тестового приложения хотя бы на одну ступень. Висим? А
}
то! Потоки тестового приложения отбирают процессорное
#define defNthr 300 время у всех остальных потоков (включая и некоторые сис-
#define argNthr ((argc > 1)?atol(argv[1]):defNthr)
темные), и они оказываются нефункциональны. То есть фун-
main(int argc, char **argv) кциональны, но раз за 10 сек, чего для обработки клавиа-
{
int a, zzz; турного и мышиного ввода более чем недостаточно. Забав-
SYSTEMTIME st; но, но i486С-ядро при этом продолжает работать более или
printf("creating %d threads...", argNthr); менее нормально.
for (a = 0; a < argNthr; a++)
CreateThread(0, 0, (void*)thread, 0,0, &zzz);
Обсуждение полученных результатов
f = 1; printf("OK\n"); Результаты тестирования i486С-ядра, полученные на маши-
Sleep(0); f = 2; не автора, приведены ниже.
while(1) Òàáëèöà 2. Ñêîðîñòü ïåðåêëþ÷åíèÿ êîíòåêñòà ïîòîêîâ íà
{ Windows 2000 Professional ñ îòäà÷åé êâàíòîâ âðåìåíè (ïî-
GetSystemTime(&st); òîêè èñïîëüçóþò íè÷òîæíóþ ÷àñòü îòâåäåííîãî èì ïðîöåñ-
printf("* %02d:%02d:%02d\n",st.wHour, ↵ ñîðíîãî âðåìåíè)
st.wMinute, st.wSecond);
Sleep(0);
}
return 0;
}
Òàáëèöà 3. Ñêîðîñòü ïåðåêëþ÷åíèÿ êîíòåêñòà ïðîöåññîâ íà
Под Windows 2000 Professional уже при 100 потоках Windows 2000 Professional ñ îòäà÷åé êâàíòîâ âðåìåíè (ïî-
òîêè èñïîëüçóþò íè÷òîæíóþ ÷àñòü îòâåäåííîãî èì ïðîöåñ-
время прогона очереди составляет 10 сек, в то же время ñîðíîãî âðåìåíè)
под Windows 2000 Server и того больше. Выглядит это,
скажу я вам, очень удручающе, и работать в таких усло-
виях становится крайне дискомфортно. Причем приобре-
тение более быстродействующего процессора не ускоря- Òàáëèöà 4. Ñêîðîñòü ïåðåêëþ÷åíèÿ êîíòåêñòà ïîòîêîâ íà
ет обработку очереди, ведь потоки по-прежнему исполь- Windows 2000 Professional áåç îòäà÷è êâàíòîâ âðåìåíè (ïî-
òîêè èñïîëüçóþò îòâåäåííîå èì ïðîöåññîðíîå âðåìÿ öåëèêîì)
зуют отведенные им кванты целиком, и хотя успевают пе-
реработать намного больше данных, каждый из них, как и
раньше, получает управление раз в десять секунд. Пере-
ход на двухпроцессорную машину повышает отзывчивость Òàáëèöà 5. Âðåìÿ îáðàáîòêè î÷åðåäè íà Windows 2000
системы приблизительно в 1.3 раза (два процессора умень- Professional áåç îòäà÷è êâàíòîâ âðåìåíè (ïîòîêè èñïîëüçó-
шают длину очереди в два раза, но за счет перехода на þò îòâåäåííîå èì ïðîöåññîðíîå âðåìÿ öåëèêîì)
APIC продолжительность одного тика таймера увеличи-
вается с 10 мс до 15 мс, итого 2/15/10 ~ 1.3). Однако есть
и другой, менее дорогостоящий и намного более радикаль-
ный способ решения проблемы. Один щелчок мыши уве- Как видно, это ядро имеет множество преимуществ пе-
личит реакционность системы в пять, а то и более раз. Не ред стандартным ACPI-ядром. Какое из них использовать –
верите? Ну так щелкните по рабочему столу, чтобы окно каждый должен решать сам. i486С-ядро не поддерживает
нашего тестового приложения потеряло фокус. Взгляни- ACPI и поэтому не способно в полной мере управлять энер-
те на таблицу временных замеров, которую мне удалось гопитанием компьютера, однако отрицать его сильные сто-
получить. роны, право же, не стоит. Оно действительно увеличива-
ет производительность системы, и ничего мифического в
Ëèñòèíã 5. Âðåìÿ îáðàáîòêè î÷åðåäè èç 100 ïîòîêîâ ïðè èñ- этом разгоне нет. Не верите? Испытайте его сами. Для
ïîëíåíèè íà ïåðåäíåì ïëàíå (ñëåâà) è â ôîíîâîì ðåæèìå
(ñïðàâà) íà Windows 2000 Professional этого в процессе установки (переустановки) операцион-
ной системы дождитесь, когда на экране появится сооб-
00:14:48 00:23:10
00:15:02 00:23:12 щение «Press F6 if you need to install a third party SCSI or
00:15:16 00:23:14 RAID driver» (Нажмите F6, если вам необходимо загрузить
00:15:30 00:23:16
00:15:46 00:23:18 SCSI- или RAID-драйвер стороннего производителя), на-
00:15:59 00:23:20 жмите F5 и выберите из списка имеющихся ядер «Standart
00:16:11 00:23:22
00:16:27 00:23:24 PC with C-Step i486» (Стандартный компьютер i486 степ-
00:16:41 00:23:27 пинг-С). После чего продолжите установку в обычном ре-
00:16:55 00:23:29
жиме.

60
администрирование

ПОИСКОВАЯ СИСТЕМА
СВОИМИ РУКАМИ

Данная статья о том, как очень просто и быстро настроить службу индексирования
(MS Indexing Service) для работы с интернет-сервером IIS (MS Internet Information Services).
Рассматриваемые в статье методы и приемы могут быть применены ко всем версиям Windows,
начиная c Windows 2000. Эта информация может быть полезна тем, перед кем встала задача
организовать поиск по веб-сайту или по файловым серверам в локальной сети.

АНДРЕЙ САПРОНОВ
Очень многим системным администраторам задача органи- чество материала, знакомящего пользователя с данной служ-
зации поиска представляется достаточно сложной. Они счи- бой. Поэтому, чтобы не повторяться, я дам лишь ссылки на
тают, что для этого необходимо писать какие-то скрипты, подобные статьи:
поисковые движки и прочее. Ничего этого не нужно. Для по- ! http://www.osp.ru/win2000/worknt/2000/02/004.htm
нимания материала (а соответственно для организации по- ! http://www.compulenta.ru/dk/offline/2003/90/31473
иска) вам понадобятся самые элементарные знания по HTML
и функционированию веб-сервера IIS. Почему все так про- Еще одним источником информации об этой службе для
сто. Дело в том, что корпорация Microsoft в составе своих вас может стать встроенная справка. Здесь же следует отме-
операционных систем имеет стандартное средство для реа- тить, что подавляющее большинство встреченных мною ис-
лизации подобных задач – службу индексирования (далее точников процентов на 80 повторяют информацию, приведен-
СИ). Эта служба появилась в операционных Windows доста- ную в справке. Далее я буду полагать, что читатель либо оз-
точно давно – начиная с Windows NT 4.0. накомился с приведенными материалами, либо имеет общие
В периодической печати и сети имеется большое коли- понятия об архитектуре и принципах функционирования СИ.

№8(21), август 2004 61


администрирование
Со своей стороны я сделаю небольшое резюме по ар- мощи различных средств разработки (C++, VB, VBS, JS). В
хитектуре и функциональным возможностям СИ. простейшем случае запрос на поиск можно выполнить из
оснастки СИ в меню «Опрос каталога».
Краткое описание службы При помощи оснастки «Производительность» можно
индексирования оценить (как локально, так и удаленно) работу СИ по мно-
СИ является стандартным компонентом. Основными зада- жеству параметров. Вот наиболее интересные из них: раз-
чами этой службы являются индексирование и организа- мер индекса, число активных пользователей, число запро-
ция поиска в указанных пользователем каталогах. сов (рис. 1).
Параметры службы, включая расположение индексиру-
емых данных, можно указать в консоли управления компь- IIS и Служба индексирования
ютером, оснастке «Служба индексирования». Буду считать, что у вас установлен и запущен сервер IIS, а
По умолчанию служба может индексировать (а следо- СИ настроен на индексацию и поиск нужных вам папок.
вательно, осуществлять быстрый поиск) следующие виды По умолчанию CИ индексирует содержимое вашего веб-
файлов: HTML, все документы MS Office, сообщения MIME. узла.
Этого вполне достаточно для веб-сайта. Однако если вам Для поиска пользователю, как минимум, необходима
необходима большая функциональность вашего поискови- форма для ввода запросов (например, как стандартная,
ка, то СИ может индексировать файлы, для которых име- показанная выше). Форму для запроса, а вместе с ней и
ются специальные фильтры (т.н. IFilter). Фильтр представ- методы доступа к СИ можно реализовать двумя путями: ASP
ляет собой dll-библиотеку, которая реализует интерфейс или HTML.
IFilter для определенного типа файлов. Вы можете сами В статье я буду рассматривать второй путь. Причин тому
разработать подобную программу или же взять готовую. две. Во-первых, он на порядок проще как для понимания,
Последних существует достаточно много. Вот некоторые: так и для реализации. А во-вторых, очень подробные при-
! zip – http://www.4-share.com меры на ASP имеются в Platform SDK и MSDN.
! pdf – http://www.adobe.com/support/downloads/product.jsp? Кроме того, пример формы и запросов на поиск при-
product=1&platform=Windows сутствуют в стандартной поставке IIS: Windows\help\iisHelp\
! DjVu – http://www.lizardtech.com/download/dl_download.php? iis\misc\search.asp и Windows\help\iisHelp\iis\misc\query.asp
detail=doc_ifilter&platform=win соответственно. Эти примеры имеют подробные коммен-
! Jpg – http://www.aimingtech.com/jpeg_ifilter тарии на русском языке.
! wmv/wma, shtml, vmf, msg, pdf, zip, xmp, vCard – http:// В случае с HTML форма будет очень простой и до боли
www.ifiltershop.com знакомой (рис. 2).
В HTML это выглядит примерно так.
СИ позволяет индексировать документы, находящиеся на
локальной машине и на любом доступном удаленном узле. Ëèñòèíã 1. search.html – ôîðìà äëÿ ââîäà çàïðîñà ïîëüçîâàòåëÿ
Запросы к СИ могут осуществляться на различном ло- <HTML>
гическом уровне (OLE, ADO, ISAPI расширения) и при по- <HEAD>

Ðèñóíîê 1

62
администрирование
<TITLE>Ïðîñòàÿ ôîðìà äëÿ ïîèñêà</TITLE> # âîçìîæíûå ïîëÿ äëÿ îòîáðàæåíèÿ â ðåçóëüòàòàõ ïîèñêà
</HEAD> # èìÿ ôàéëà, ðàçìåð, ðåëåâàíòíîñòü, îïèñàíèå, ïîëíûé ïóòü,
<BODY> # çàãîëîâîê äîêóìåíòà, äàòà ïîñëåäíåé ìîäèôèêàöèè
<!--Óêàçûâàåì íà ññûëêó, íà âõîä êîòîðîé ïîéäóò CiColumns=filename, size, rank, characterization, ↵
ïàðàìåòðû ôîðìû… --> vpath, DocTitle, write
<FORM ACTION="search.idq" METHOD="GET" ID="Form1"> # ñòðîêà çàïðîñà (ñóæåíèÿ) – òî, ÷òî õî÷åò íàéòè ïîëüçîâàòåëü
CiRestriction=%CiRestriction%
<!--…à ïàðàìåòð îäèí - ñòðîêà çàïðîñà --> # ìàêñèìàëüíî âîçìîæíîå êîëè÷åñòâî äîêóìåíòîâ
ß èùó: <INPUT TYPE="TEXT" NAME="CiRestriction" ↵ CiMaxRecordsInResultSet=100
SIZE="30" MAXLENGTH="100" VALUE="" ID="Text1"> # ÷èñëî íàéäåííûõ äîêóìåíòîâ íà ñòðàíèöó
<!--Êíîïêà --> CiMaxRecordsPerPage=10
<INPUT TYPE="SUBMIT" VALUE="Ïîèñê" ID="Submit1" ↵ # êàòàëîã(è) äëÿ ïîèñêà (îòíîñèòåëüíî êîðíåâîé ïàïêè âåá-óçëà)
NAME="Submit1"> CiScope=/DaT, /
</FORM> # DEEP óêàçûâàåò, ÷òî ïîèñê èäåò âî âñåõ ïîäêàòàëîãàõ
</BODY> # êàòàëîãîâ èç CiScope
</HTML> CiFlags=DEEP
# øàáëîííàÿ ñòðàíèöà äëÿ îòîáðàæåíèÿ ðåçóëüòàòîâ ïîèñêà
CiTemplate=/search.htx
# ïîðÿäîê ñîðòèðîâêè íàéäåííûõ äîêóìåíòîâ (çäåñü ïî óìåíüøåíèþ
# ðåëåâàíòíîñòè)
# d - óìåíüøåíèå ïðèçíàêà, a - óâåëè÷åíèå
CiSort=rank[d]

Как видно, формат файла довольно прост: Перемен-


ная = Значение.
Ðèñóíîê 2 Небольшие комментарии относительно некоторых пе-
Файл search.idq является скриптом, который содержит ременных:
всю необходимую информацию для запроса к СИ посред- ! CiRestriction – строка запроса. В данном случае при-
ством ISAPI (Internet Server Application Programming Interface) нимает значение переменной %CiRestriction% из фай-
расширения idq.dll. Для того чтобы IIS распознавал этот ла search.html (см. Листинг 1). Параметры, передавае-
текстовый файл как скрипт в свойствах вашего веб-узла, мые в idq-файл, обрамляются с двух сторон знаками
на вкладке «Домашний каталог» нужно нажать кнопку «На- процента. Если хотите, можете передать больше пара-
стройка…» и сопоставить файлам с расширением *.idq метров. Например, количество записей на страницу
фильтр Windows\System32\idq.dll. поиска.
! CiSort – признак для сортировки результатов. В этой пе-
ременной можно указать любое поле из заданных в
CiColumns (доступно около 60 различных полей). Дан-
ный параметр можно также сделать настраиваемым
пользователем;
! CiTemplate – ссылка на шаблонный htx-файл (HTML
extension) для отображения результатов запроса;
! CiFlags – указывает на необходимость обхода всех вло-
женных папок. Можно использовать для поиска только
в указанных папках без обхода всех их подкаталогов.
Тогда CiFlags должен быть равен SHALLOW.

В приведенном выше примере использованы далеко не


все параметры для запроса. За полным перечнем свойств
рекомендую обратиться к MSDN.

HTX
Файл htx является не чем иным, как HTML-страницей. Од-
нако помимо тегов HTML в этом файле доступны специфи-
ческие идентификаторы, относящиеся к СИ и являющиеся
специфичными для введенного пользователем запроса. Эти
переменные (идентификаторы), как и в idq-файле, обрам-
Ðèñóíîê 3 ляются c обеих сторон знаком процента + знаки больше и
Также необходимо, чтобы idq-файл находился в папке, меньше – слева и справа, как у тегов.
имеющей право на исполнение скриптов (сценариев). Пос- Например, имя файла текущей записи находится в пе-
ле этого IIS будет обрабатывать IDQ (Internet Data Query) ременной <%filename%>. Именно эти идентификаторы по-
файлы так, как мы этого хотим. А хотеть мы можем при- зволят вам определить количество найденных документов,
мерно следующим образом: их ранг, расположение, а также все остальные параметры.
Вместо htx-расширения вы можете без ограничений исполь-
Ëèñòèíã 2. search.idq – ñêðèïò äëÿ çàïðîñà íà ïîèñê зовать стандартное расширение html. Предлагаю рассмот-
# [Query] – îáÿçàòåëüíàÿ ñåêöèÿ äëÿ idq-ôàéëà реть пример шаблонной страницы, которая представит
[Query] пользователю результаты поиска.

№8(21), август 2004 63


администрирование
Ëèñòèíã 3. search.htx – øàáëîííàÿ ñòðàíèöà äëÿ ïðåäñòàâëåíèÿ <font size=-1 color=DarkGreen> <%rank%> - <%vpath%> - ↵
ðåçóëüòàòîâ ïîèñêà ðàçìåð <%size%> áàéò - <%write%> GMT</font>
<br>
<!--Íîâûé ïîèñê. Ïðèìåíÿåòñÿ âîçìîæíîñòü ïîäêëþ÷åíèÿ <%enddetail%>
ôàéëîâ -->
<%include /search.html%> <!--Êîíñòðóèðóåì êíîïêè äëÿ ïåðåìåùåíèÿ ìåæäó ñòðàíèöàìè
<HR width="100%" SIZE="1"> ïîèñêà -->
<!--â ñëó÷àå åñëè íåò íèêàêèõ äîêóìåíòîâ, ò.å. <!--Åñëè â âûâåäåííûõ çàïèñÿõ íåò ïåðâîé, çíà÷èò, ìîæíî
CiMatchedRecordCount==0, --> âåðíóòüñÿ íàçàä, -->
<!--ãäå CiMatchedRecordCount – ÷èñëî íàéäåííûõ äîêóìåíòîâ --> <!--ò.å. îòîáðàæàåì êíîïêó "Ïðåäûäóùàÿ" -->
<%if CiMatchedRecordCount eq 0%> <%if CiContainsFirstRecord eq 0%>
<H4> Íå íàéäåíî äîêóìåíòîâ, ñîîòâåòñòâóþùèõ çàïðîñó ↵ <!--Ïåðåäàåì óæå çíàêîìîìó ñêðèïòó ïàðàìåòðû ñäâèíóòüñÿ
"<%CiRestriction%>". </H4> íà %CiMaxRecordsPerPage% çàïèñåé íàçàä -->
<%else%> <!--îò ïåðâîé çàïèñè íà ñòðàíèöå %CiBookmark%>
<!--Åñëè ÷òî-òî íàøëè, òî óêàçûâàåì íîìåð íà÷àëüíîé è <FORM ACTION="search.idq" METHOD="GET">
êîíå÷íîé çàïèñè íà ñòðàíèöå --> <INPUT TYPE="HIDDEN" NAME="CiBookmark" ↵
<H4>Äîêóìåíòû ñ <%CiFirstRecordNumber%> ↵ VALUE="<%CiBookmark%>" >
ïî <%CiLastRecordNumber%> èç ↵ <INPUT TYPE="HIDDEN" NAME="CiBookmarkSkipCount" ↵
<%CiMatchedRecordCount%> ñîîòâåòñòâóþùèõ ↵ VALUE="-<%CiMaxRecordsPerPage%>" >
çàïðîñó "<%CiRestriction%>". </H4> <!--åñëè íà ïðåäûäóùåé ñòðàíèöå âàì íå íóæíî
<%endif%> çíà÷åíèå %CiRestriction%, òî ìîæíî-->
<!--îáîéòèñü è áåç ýòîé ñòðîêè-->
<!--%begindetail% âûâîäèò çàïèñè î íàéäåííûõ äîêóìåíòàõ <INPUT TYPE="HIDDEN" NAME="CiRestriction" ↵
ñ òåêóùåé %CiCurrentRecordNumber% --> VALUE="<%CiRestriction%>" >
<!--äî %CiCurrentRecordNumber%+%CiMaxRecordsPerPage% <INPUT TYPE="SUBMIT" VALUE="Ïðåäûäóùàÿ">
çàïèñè --> </FORM>
<%begindetail%> <%endif%>
<!--Íå çàáûâàéòå, ÷òî êëèåíò ïîëó÷èò êîììåíòàðèè
èç ýòîé ñåêöèè ìíîãî-ìíîãî ðàç --> <!--Àíàëîãè÷íî ïîñòóïàåì ñ êíîïêîé "Ñëåäóþùàÿ" -->
<br> <%if CiContainsLastRecord eq 0%> -->
<%CiCurrentRecordNumber%>. <FORM ACTION="search.idq" METHOD="GET">
<b><a href="<%vpath%>"><%filename%></a></b> <INPUT TYPE="HIDDEN" NAME="CiBookmark" ↵
<b><i>Îïèñàíèå: </i></b><%characterization%><br> VALUE="<%CiBookmark%>" >
<INPUT TYPE="HIDDEN" NAME="CiBookmarkSkipCount" ↵
<!--âûâîäèì ðåëåâàíòíîñòü, òî÷íóþ ññûëêó, ðàçìåð, VALUE="<%CiMaxRecordsPerPage%>" >
è äàòó ïîñëåäíåé ìîäèôèêàöèè --> <INPUT TYPE="HIDDEN" NAME="CiRestriction" ↵

Ðèñóíîê 4

64
администрирование
VALUE="<%CiRestriction%>" > ! CiRestriction – строка запроса. Это как раз та самая фра-
<INPUT TYPE="SUBMIT" VALUE="Ñëåäóþùàÿ"> за, по которой и будет происходить отбор. В показан-
</FORM>
<%endif%> ном запросе будут выделены все части текста из фай-
ла pageforhighlight.html, содержащие слово IIS.
Тегами <%begindetail%>…<%enddetail%> результат зап-
роса разделяется на страницы с числом записей на каж- Кроме основных параметров, существуют и дополни-
дой странице, равным <%CiMaxRecordsPerPage%>», зада- тельные. Практически все они направлены на изменение
ваемым в idq-файле. Думаю, назначение остальных тегов вида отображаемого текста. Вы можете сделать выделен-
ясно из текста программы. ные слова жирными (CiBold), наклонными (CiItalic), указать
Кроме тегов-переменных, к результатам поиска приме- цвет подсветки (CiHiliteColor).
нимы условные переходы <% if %>…<% else %>…<% endif Параметр CiHiliteType при передаче ему значения Full
%> и логические операторы: EQ (равенство), NE (неравен- выводит не куски страницы, а всю ее целиком, подсвечи-
ство), LT/GT (меньше/больше), LE/GE (меньше/больше либо вая искомые слова.
равно), CONTAINS (содержание одной строки в другой), Файл null.htw является абстрактным файлом и служит
ISTYPEEQ (принадлежность определенному типу). указанием серверу для форматирования документа по
Файлы htx поддерживают возможность подключения умолчанию. В документации сообщается, что этот файл
внешних html/htx-файлов (см. Листинг 3) при помощи ди- может и не существовать, однако без него IIS выдает стан-
рективы <%include filename%>. Подключать можно не бо- дартную ошибку – 404. После создания ничего не содержа-
лее 31 файла, включая вложенные. щего файла с таким именем все становится на свои места.
В этом примере для вывода информации о найденном Этот файл должен располагаться в папке, имеющей права
документе использовано свойство документа %Characte- на исполнение сценариев.
rization% – краткое описание (аннотация). Это описание авто- Показанный в листинге 3 пример можно дополнить ссыл-
матически генерируется СИ при индексировании документов. кой «подробней», которая и покажет найденные в тексте
Если вы хотите, чтобы оно генерировалось, то это нужно ука- слова. Сделаем ее красным цветом. В секцию <%begin-
зать в свойствах СИ на вкладке «Генерация» – «Генериро- detail%>…<%enddetail%> необходимо вставить следующий
вать аннотации». Там же можно указать размер аннотации. код:
В исходном HTML-коде были использованы коммента-
рии. Необходимо помнить, что они, в отличие от кода, отве- Ëèñòèíã 4. Ññûëêà íà ïîäðîáíûé îò÷åò î ñîâïàäåíèÿõ â äîêóìåíòå
чающего за форматирование, передаются клиенту. Это <!--ññûëêà íà äîêóìåíò, ñîäåðæàùèé ÷àñòè òåêñòà,
может быть плохо и с позиции увеличения трафика (здесь êîòîðûå îêðóæàþò èñêîìóþ ôðàçó -->
<a href="Null.htw?CiWebhitsFile=<%escapeURL ↵
идет многократное повторение, что может быть весьма ощу- vpath%>&CiRestriction=<%escapeURL ↵
тимо), и с позиции конфиденциальности. CiRestriction%> ">
<font size=-1 color=Red> ïîäðîáíåé </font> </a>
Можно сказать, что в минимальном варианте мы имеем
рабочую и достаточно функциональную поисковую систему. Здесь следует обратить внимание на ключевое слово
На следующем рисунке представлена третья страница с ре- <%EscapeURL%>. Это необходимо для приведения запро-
зультатами поиска на запрос «Производительность & IIS» сов к стандартному виду. Например, запрос на поиск всех
(рис. 4). документов с расширением doc «@filename=*.doc» в каче-
стве параметра должен быть передан в виде «%40file-
Hit-Highlighting name=%2A.doc», а использованный несколько выше зап-
Традиционно в поисковых системах возможно отображе- рос «Производительность & IIS» будет заменен на:
ние тех частей текста, где встречаются искомые слова или
фраза. В СИ эту возможность фирма Microsoft назвала Hit- “%EF%F0%EE%E8%E7%E2%EE%E4%E8%F2%E5%EB%FC%ED%EE%F1%F2 ↵
%FC%20%26%20IIS”
Highlighting. Для этого используется еще один вид скрип-
тов – htw. Как и в случае с idq-скриптами, нужно «научить» Похожее назначение и у ключевого слова <%Escape-
IIS распознавать файлы htw как файлы сценария. Это де- HTML%>: он заменяет значимые в HTML символы на их
лается тем же способом, который мы применяли по отно- аналоги. Таким образом, символ «<» будет заменен на «&lt».
шению к idq-файлам (см. выше).
Нужно сопоставить файлам с расширением htw ISAPI- Несколько слов о формате запросов
фильтр – библиотеку Windows\system32\webhits.dll. После Правила, формат, примеры запросов для поиска исчерпы-
этого IIS при запросе «somefile.htw?параметры» сгенери- вающе представлены во встроенной справке СИ. Руковод-
рует страницу с результатами «подсветки». Самый элемен- ство написано на удивление хорошо и понятно.
тарный запрос может иметь вид:
Заключение
null.htw?CiWebhitsFile=pageforhighlight.html&CiRestriction=IIS Надеюсь, что я убедил вас в простоте организации поиска.
Ведь можно организовать очень функциональный поиск при
В этом запросе два обязательных параметра (все пара- помощи одной HTML-страницы и двух маленьких скриптов.
метры указываются поочередно через знак амперсанда): За формальным описанием ключевых параметров и
! CiWebhitsFile – указывает на файл, для которого мы хо- констант советую обратиться к MSDN (msdn.microsoft.com)
тим сделать выделение участков текста. и к примерам из Platform SDK.

№8(21), август 2004 65


web

HTML-ШАБЛОНЫ ДЛЯ PHP И PERL,


ИЛИ НЕ ДЕЛАЙТЕ ИНСТРУМЕНТ
САМОЦЕЛЬЮ!
ДМИТРИЙ ГОРЯИНОВ
О шаблонах в веб-программировании знают или слышали лификации авторов системы, в шаблоны внедряли услов-
буквально все. Интерпретируемые языки веб-программи- ные операторы, операторы циклов, регулярные выраже-
рования, такие как PHP, Perl, ASP VBScript, всегда давали ния, макрофункции и даже объекты (последним сильно
возможность совмещать код языка и HTML. Это быстро и грешат системы шаблонов для языка Java). К чему же это
просто. Вначале. Как только проект или отдельный файл- привело?
сценарий начинают разрастаться, эта смесь так же быстро Кроме того, что авторы систем поупражнялись в напи-
становится неудобоваримой. Взявшись переписывать ка- сании мини-трансляторов, это привело к тому, что человек,
кой-либо сценарий, сразу хочется выкинуть HTML-вставки хорошо владеющий навыками HTML-верстки, не мог со-
просто потому, что они начинают мешать читать программ- здать ни один более-менее полезный шаблон.
ный код. Он должен был предварительно изучить новый для себя
Ради этого, ради разделения HTML-форматирования вы- синтаксис.
вода и логики программы и был придуман механизм шаб- И при этом все, что нам действительно нужно от шаб-
лонов. Логика остается программе, верстка – шаблону. лона это:
Программа обрабатывает запрос, формирует данные и ! возможность указать место для вставки данных;
заполняет шаблон. Шаблон всегда содержит HTML-код и ! выделить блок HTML-кода, который можно будет вклю-
некоторые дополнительные конструкции для вставки дан- чить или не включить в конечный вывод или, допустим,
ных и управления выводом. Тут необходимо кое-что прояс- повторить нужное количество раз.
нить.
В идеале синтаксис дополнительных конструкций дол-
Небольшой взгляд в историю, жен позволять работать с шаблоном в традиционном HTML-
или Каким должен быть редакторе и просматривать готовый файл-шаблон в брау-
шаблонный движок? зере.
Несомненно, идея шаблонов пришла в голову программис- Систем шаблонов написано действительно много. Мы
там. И суть идеи явно была в том, чтобы убрать HTML-код будем рассматривать две из них (одну для языка PHP и вто-
из программы. А вот дальше… Дальше все складывалось рую – для Perl). Основная цель статьи – показать, как пра-
по-разному. Понятно, что первыми «родились» простейшие вильно работать с готовыми движками, чтобы минимизи-
шаблоны. Это просто HTML-код и специальные указания ровать изменения в HTML-коде шаблонов и оставить логи-
для вставки данных. По сути, переменные. ку за программой.

<html>
<head>
Добавление шаблонного движка
<title>::title::</title> в проект
</head> Итак, что вообще требуется от шаблонного движка? Как
<body>
::body:: минимум – установить, подключить и начать использовать.
</body> Во-первых, основная цель проекта не в том, чтобы по-
</html>
работать с шаблонами. Наша система будет загружена соб-
Такой простой HTML-документ. Можно сохранить и от- ственной полезной работой, и тратить большие ресурсы на
крыть в браузере. Будет страничка с заголовком «::title::» и поддержание работы с шаблонами крайне нежелательно.
содержимым «::body::». Во-вторых, нам вряд ли подойдет движок, который потре-
Есть конструкция (::имя::), которую нужно выделить из бует дополнительной компиляции каких-либо компонент или
HTML-кода, есть некоторое имя, вместо которого нужно изменения конфигурации веб-сервера. Дело даже не в том,
подставить данные из программы. Можете называть это что у нас нет своего сервера, а злой дядька провайдер не
переменной, но лучше назовем это именованной вставкой. желает что-то там собрать-подключить на своей площад-
Дальше все стало гораздо более запутанно. Довольно ке. Просто вариант «отдельный проект – отдельный сер-
быстро выяснилось, что есть блоки, которые могут повто- вер» почти нереален. Скорее всего, наш сервер уже обслу-
ряться на одной и той же странице. А иногда на этой стра- живает какие-то проекты и нет ни малейшего желания бро-
нице в зависимости от некоторого условия нужно либо вы- саться их переписывать на новый лад или менять рабочую
водить что-то, либо нет. конфигурацию. Да и останавливать боевой сервер ради
Вот тут началось жуткое многообразие. В шаблоны ста- добавления «новой забавы» мало кто (читай – системный
ли добавлять макроязыки. При этом, в зависимости от ква- администратор) согласится.

66
web
Поэтому лучше всего нам подойдет движок, который Библиотеку PHP XTemplate мы разместили в подката-
можно скачать, разархивировать и начать использовать без логе XTemplate каталога lib. Класс XTemplate содержится в
всякой дополнительной инсталляции. файле xtpl.p.
В качестве движка для PHP мы рассмотрим библиотеку Для того чтобы создать новый экземпляр класса, нам
XTemplate (http://sourceforge.net/projects/xtpl), а для Perl – мо- нужно подключить файл и создать объект:
дуль HTML::Template (http://html-template.sourceforge.net).
Оба движка достаточно именно скачать и разархивировать. <?php
...
Вы, конечно, можете установить модуль для Perl по всем include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );
правилам, но вполне достаточно развернуть из архива ка- $xtpl=new XTemplate( ïîëíîå_èìÿ_ôàéëà_øàáëîíà );
...
талог HTML с файлом-модулем Template.pm в нем. ?>
Куда разархивировать? Для простоты предлагается со-
здать отдельный каталог для дополнительных библиотек. Для подключения библиотеки HTML::Template в Perl
Пусть каталог htdocs – это корневой каталог размещения следует указать путь к её директории (если, как в нашем
документов на веб-сервере. Создадим на одном уровне с случае, она размещена отдельно от стандартных библио-
ним каталог lib, и разархивируем в него наши движки, каж- тек), подключить модуль и так же создать новый объект:
дый – в отдельный каталог. Получится вот такая структура
директорий: #!/usr/bin/perl
...
use lib $ENV{DOCUMENT_ROOT}."/../lib";
# Äðóãîé ñïîñîá äîáàâèòü êàòàëîã ðàçìåùåíèÿ áèáëèîòåêè:
# unshift( @INC, $ENV{DOCUMENT_ROOT}."/../lib" );
use HTML::Template;
my $template = HTML::Template->new( filename => ↵
В каталоге XTemplate будут файлы PHP-библиотеки, ïîëíîå_èìÿ_ôàéëà_øàáëîíà );
демонстрационные примеры и т. п. В каталоге HTML может
находиться один-единственный perl-модуль – Template.pm
Собственно, на этом все необходимые манипуляции по Что же такое шаблон?
установке будут закончены. Просто и эффективно, не прав- Вернемся к началу. Для чего затевалась вся эта история с
да ли? шаблонами? В основном для того, чтобы отделить код HTML
от кода программы. Идеальный шаблон – это файл, содер-
Просто отдельный класс жащий HTML-разметку, и программно заполняемый данны-
Итак, мы решили отказаться от движков, требующих ми.
специальной инсталляции. Установка наших библиотек сво- Одно из направлений в системах шаблонов – предва-
дится к разархивированию или простому копированию. Пой- рительная компиляция шаблонов в некоторое внутреннее
дем дальше. представление. Обычно это убыстряет вывод программы,
Движок должен быть инкапсулирован в отдельный класс. но любое изменение в шаблоне требует как минимум пере-
Он не должен иметь ветвистых зависимостей с другими компилирования его самого и, возможно, несколько зави-
модулями. Иначе устанешь их удовлетворять. симых шаблонов.
Множество хороших веб-программистов создавали и То есть никакое изменение в HTML-коде не может сра-
будут создавать свои, уникальные решения в рамках от- зу отразиться на сайте. Пока такие системы не дают на-
дельных проектов. В таких случаях что-то непременно «за- столько ощутимого выигрыша в скорости, чтобы считать
тачивается» под конкретные нужды, что-то «дергается на- это действительно хорошим решением.
прямую» и т. д. На базе таких решений вырастают системы Так что сформулируем еще один постулат – движок не
публикаций или управления сайтами. Все это плотно упа- должен требовать дополнительной обработки шаблонов
ковано, скручено в тугой узел, плохо отделимо друг от дру- (читай – запретить предварительную компиляцию и прочие
га и, возможно, представляет собой законченный продукт, действия).
который, скорее всего, больше никому, кроме заказчика, Шаблон должен редактироваться как HTML-файл, иметь
не пригодится. возможность предварительного просмотра как HTML-файл
Если же речь идет именно о подсистеме для работы с и подключаться «как есть». Мы же не хотим заставить вер-
шаблонами, то такая система должна представлять собой стальщика установить у себя веб-сервер лишь для того,
законченный интерфейс, отделимый от остальной систе- чтобы просматривать промежуточные результаты своей ра-
мы. Фактически – уникальный класс либо модуль. боты?
Программист, использующий движок, не должен тратить Вот два простых примера шаблонов для XTemplate и
время на попытку понять, как оно должно было работать в HTML::Template.
соответствии с замыслом автора модуля или выполнять с Пример шаблона для библиотеки Xtemplate:
десяток настроек. Нам нужен инструмент для работы, а не
инструкция по обработке паровоза напильником с целью <!-- BEGIN: main -->
<html>
получить истребитель. <head><title>Ïðèìåð 1</title></head>
В нашем случае это требование выполняется полнос- <body>
<dl>
тью. Нам нужно просто подключить новую библиотеку, со- <dt>Çíà÷åíèÿ ïåðåìåííûõ äîáàâëÿþòñÿ â XTemplate ÷åðåç ìåòîä
гласуясь с синтаксисом языка. <i><b>assign( PARAM, value )</I></b>:</dt>

№8(21), август 2004 67


web
<dd>$VARIABLE = <i>{VARIABLE}</i></dd> <!-- END: item -->
</dl> </table>
</body> <!-- END: main -->
</html>
<!-- END: main --> Рассмотрим шаблон подробнее. Прежде всего отметим,
Если мы сохраним этот код в файле ex1.html и откроем что весь шаблон для XTemplate заключен внутри именован-
его для просмотра в браузере, мы увидим такую «картинку»: ного блока:

<!-- BEGIN: main -->


...
<!-- END: main -->

Это необходимое условие синтаксиса шаблонов


Ðèñóíîê 1. Âèä øàáëîíà äëÿ XTemplate â áðàóçåðå XTemplate. Любой блок (и весь шаблон целиком) начина-
Пример шаблона для библиотеки HTML::Template: ется с конструкции:

<html> <!-- BEGIN: èìÿ_áëîêà -->


<head><title>Ïðèìåð 1</title></head>
<body>
<dl> И заканчивается конструкцией:
<dt>Çíà÷åíèÿ ïåðåìåííûõ äîáàâëÿþòñÿ â HTML::Template ÷åðåç ìåòîä
<i><b>param( PARAM => value );</i></b>:</dt>
<dd>$VARIABLE = <i><TMPL_VAR NAME=VARIABLE></i></dd> <!-- END: èìÿ_áëîêà -->
</dl>
</body>
</html> Простая подстановка в шаблоне описывается так:

Так как подстановки в шаблонах HTML::Template име- {èìÿ_ïåðåìåííîé_äëÿ_ïîäñòàíîâêè}


ют синтаксис, схожий с HTML-тэгами, HTML-вид шаблона
будет слегка отличаться: Там, где логика подсказывает нам сгруппировать дан-
ные для подстановки, можно использовать массивы под-
становок, описывая их таким образом:

{èìÿ_ìàññèâà.èìÿ_êëþ÷à}
Ðèñóíîê 2. Âèä øàáëîíà äëÿ HTML::Template â áðàóçåðå
Код подстановки (<TMPL_VAR NAME=VARIABLE>) не К примеру, это логично сделать при оформлении под-
отобразится в браузере. Фактически браузер воспримет становок значений ячеек табличной строки.
этот как HTML-тэг, правило отображения которого ему не- Шаблон описывает HTML-код для вывода данных. Сами
известно. данные формируются в программе. На уровне шаблона мы
не можем предусмотреть количество элементов нашей таб-
Лучшее – враг хорошего. лицы.
Минимизируем логику, макроязык Поэтому каждую строку таблицы мы оформим в шаб-
шаблонов лоне в виде отдельного блока. Это делается для того, что-
Теперь постараемся разобраться с тем, что нам действи- бы иметь возможность управлять выводом отдельных строк
тельно понадобится в шаблонах, кроме HTML-кода. из программы-обработчика:
Есть несколько типичных задач, с которыми мы сталки-
ваемся постоянно и для которых мы хотим иметь четкое и <!-- BEGIN: item -->
...
простое решение. Хорошим примером такой задачи может <!-- END: item -->
служить вывод таблицы с чередованием фона строк. При
выводе таблицы мы, во-первых, имеем дело с повторяющи- Внутри блока item мы опишем два других блока – «odd»
мися элементами (строками), во-вторых, чередование цвета и «even» – для чередования оформления строк. Програм-
фона – это выбор оформления в зависимости от условия. ма-обработчик, проверяя условие четности строки, будет
Шаблон «ex2.html» для библиотеки Xtemplate: выбирать нужный блок и управлять генерацией конечного
HTML-вывода, формируя содержимое блока item.
<!-- BEGIN: main --> В XTemplate вся логика организации циклов и условий
<table bgcolor="gray" cellspacing=2 border=0 cellpadding=4>
<tr><th colspan="2" bgcolor="silver">{TOPIC}</th></tr> ложится на код PHP-программы. В шаблоне описываются
<!-- BEGIN: item --> только блоки и имена для подстановки значений.
<!-- BEGIN: odd -->
<tr bgcolor="white"> Опишем PHP-код, обрабатывающий этот шаблон:
<td>{DATA.NAME}</td><td>{DATA.VAL}</td>
</tr> <?php
<!-- END: odd --> // Ïîäêëþ÷åíèå áèáëèîòåêè
<!-- BEGIN: even --> include( getenv("DOCUMENT_ROOT") ↵
."/../lib/XTemplate/xtpl.p" );
<tr bgcolor="lightyellow">
<td>{DATA.NAME}</td><td>{DATA.VAL}</td>
</tr> // Ñîçäàíèå íîâîãî îáúåêòà. ôàéë øàáëîíà ex2.html
// ðàçìåùàåì â êàòàëîãå htdocs/../data/xtemp
<!-- END: even -->

68
web
$xtpl=new XTemplate( getenv("DOCUMENT_ROOT") ↵ ны программированию. Здесь нет таких простых и понят-
."/../data/xtemp/ex2.html" );
ных блоков, выделяемых как HTML-комментарии. Увы,
// Ïðîñòàÿ ïîäñòàíîâêà çíà÷åíèÿ â {TOPIC} здесь есть отдельные конструкции для циклов и условий.
$xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵
ôîíà ñòðîê" ); Для нас хорошо то, что синтаксис этих конструкций анало-
$even = 0; гичен тэговой разметке, и они не отображаются при про-
// Öèêë äëÿ ôîðìèðîâàíèÿ ñòðîê òàáëèöû смотре файла-шаблона браузером.
for ($i=1; $i<=10; $i++) { Шаблон «ex2.html» для библиотеки HTML::Template:
$row = array(
NAME => "Ñòðîêà",
VAL => $i <table bgcolor="gray" cellspacing=2 border=0 ↵
); cellpadding=4>
$xtpl->assign(DATA, $row); <tr><th colspan="2" bgcolor="silver"><TMPL_VAR ↵
NAME=TOPIC></th></tr>
//  çàâèñèìîñòè îò ÷åòíîñòè ñòðîêè óêàçûâàåì <TMPL_LOOP NAME=item>
// áëîê-øàáëîí âûâîäà «odd» èëè «even» <TMPL_IF NAME=odd>
if ( ($even = ($even XOR 1)) ) { <tr bgcolor="white">
$xtpl->parse("main.item.odd"); <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td>
} else { </tr>
$xtpl->parse("main.item.even"); <TMPL_ELSE>
} <tr bgcolor="lightyellow">
$xtpl->parse("main.item"); <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td>
} </tr>
</TMPL_IF>
// Ðàçáîð ãëàâíîãî áëîêà øàáëîíà </TMPL_LOOP>
$xtpl->parse("main"); </table>

// Âûâîä ðåçóëüòàòà ðàçáîðà


$xtpl->out("main"); В отличие от XTemplate мы не заключаем весь шаблон
?>
в блок.
Для того чтобы включить в результат вывода нужный Библиотека HTML::Template не требует заключать весь
блок из шаблона, используется метод parse( BLOCK ). Имя шаблон в блок. Она работает с целым файлом, не деля его
блока задается в виде строки, с указанием всех родитель- на отдельные блоки.
ских блоков через точку: Простую подстановку (замену) значения в шаблоне опи-
сывают так:
BLOCK::= [<Èìÿ_áëîêà>.]<Èìÿ_áëîêà>
<TMPL_VAR NAME=èìÿ_ïàðàìåòðà_ïîäñòàíîâêè>
Таким образом, блок-шаблон для вывода нечетных
строк будет именоваться «main.item.odd». Для того чтобы описать в шаблоне повторяющийся
Разумеется, для того чтобы блок был включен в ко- HTML-код, его придется обрамить «оператором цикла»:
нечный вывод, необходимо вывести и все родительские
блоки: <TMPL_LOOP NAME=èìÿ_ïîâòîðÿþùåãîñÿ_áëîêà>
...
</TMPL_LOOP>
...
$xtpl->parse(“main.item.odd”);
... В программе для вывода такого цикла нужно сформи-
$xtpl->parse(“main.item”); ровать массив и сопоставить его с именем повторяюще-
...
$xtpl->parse(“main”); гося блока. Каждый элемент массива соответствует вклю-
... чению HTML-кода, описанному внутри <TMPL_LOOP> …
</TMPL_LOOP>. Элемент массива представляет собой хэш
Метод assign( PARAM, value ) служит для заполнения (наборы пар вида «ключ – значение»). То есть если в шаб-
значения переменной шаблона с именем PARAM. лоне мы описываем подстановку вида <TMPL_VAR
NAME=имя_параметра_подстановки> внутри повторяюще-
$xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵ гося блока, то в хэше, описывающем элемент массива для
ôîíà ñòðîê" );
подстановки, мы должны создать пару (имя_параметра_
И наконец, метод out(BLOCK) печатает результат раз- подстановки => значение_для_подстановки).
бора шаблона. Если нам нужно не выводить, а сохранить Создатели библиотеки внедрили в шаблон условный
его в виде готового текста, нужно использовать другой ме- оператор. Мы будем использовать его только там, где нуж-
тод – text(BLOCK)…». но указать отдельный, вложенный блок HTML-кода, кото-
Обращение к методу out() равносильно такому коду: рый будет подключаться в зависимости от условия, сфор-
мированного в программе:
...
$out = $xtpl->text("main");
echo $out; <TMPL_IF NAME=èìÿ_áëîêà>
...
</TMPL_IF>
Перепишем наш пример с таблицей для библиотеки
HTML::Template. Сразу заметим, что изначально синтаксис Подключение вывода блока зависит от значения, присво-
шаблонов библиотеки HTML::Template более адаптирован енного обработчиком шаблона имени_блока. Если мы про-
«под программистов». Создатели библиотеки остались вер- инициализируем переменную блока значением «истина» –

№8(21), август 2004 69


web
HTML-код, описанный внутри <TMPL_IF> … </TMPL_IF>, Вложенные шаблоны
будет включен в результат разбора шаблона. Что еще может пригодиться в работе с шаблонами? Любой
Если мы не определим никакого значения для блока, сайт содержит повторяющиеся элементы дизайна – шапка,
или определим его как «ложь», блок не будет включен в заголовки, меню и так далее.
результаты разбора шаблона. Хороший движок шаблонов должен иметь возможность
Как и в традиционном синтаксисе языков программи- подключать более мелкие шаблоны (назовите их заготов-
рования, у условного блока (оператора) может существо- ками или библиотечными элементами) внутрь более круп-
вать «альтернативная часть»: ных, желательно – поддерживать многоуровневое, вложен-
ное подключение.
<TMPL_IF NAME=èìÿ_áëîêà> Чуть выше мы написали два шаблона для вывода таб-
...
<TMPL_ELSE> лицы. Логично не копировать этот код везде, где нам пона-
... добится отобразить такую таблицу, а иметь возможность
</TMPL_IF>
сохранить этот шаблон отдельно и подключать его по мере
Если переменная блока получила из программы значе- необходимости в другие шаблоны.
ние «ложь», то HTML-код между <TMPL_ELSE> и </TMPL_IF> Обе наши библиотеки такую возможность предостав-
будет включаться в результат разбора. ляют. В XTemplate существует конструкция:
Код для обработки этого шаблона будет выглядеть так:
{FILE "èìÿ_âëîæåííîãî_ôàéëà_øàáëîíà"}
#!/usr/bin/perl
use strict;
В HTML::Template аналогичные функции выполняет кон-
# Ïóòü ê êàòàëîãó áèáëèîòåêè струкция:
use lib $ENV{DOCUMENT_ROOT}."/../lib";
# Ïîäêëþ÷åíèå áèáëèîòåêè <TMPL_INCLUDE NAME="èìÿ_âëîæåííîãî_ôàéëà_øàáëîíà">
use HTML::Template;
# Ñîçäàíèå íîâîãî îáúåêòà.  äàííîì ñëó÷àå ìû îòäåëüíî А вот здесь остановимся. Если вы посмотрите внима-
# óêàçûâàåì â êîíñòðóêòîðå êðàòêîå èìÿ ôàéëà øàáëîíà
# ex2.html è ïóòü ê êàòàëîãó htdocs/../data/htmltemp, тельно, то заметите, что мы вынуждены писать имя файла
# ãäå ðàçìåùåí øàáëîí непосредственно в шаблоне. Это значит, что мы стали же-
my $template = HTML::Template->new(
filename => "ex2.html", стко привязаны к расположению файлов. Даже если мы
path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/" укажем относительный путь к файлу шаблона, мы вынуж-
);
дены будем в любом другом проекте в точности повторять
# Ïðîñòàÿ ïîäñòàíîâêà çíà÷åíèÿ â <TMPL_VAR NAME=TOPIC>. структуру директорий для размещения шаблонов!
# Äåëàåòñÿ âûçîâîì ìåòîäà param( PARAM => value)
$template->param(TOPIC => "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵ Это плохо. Более того, это как-то противоречит самой
ôîíà ñòðîê" ); идее о легкости подключения (читай – переноса) и замыс-
my $even = 0; лу о повторном использовании более мелких (библиотеч-
ных) шаблонов.
# Ñîçäàíèå ìàññèâà äëÿ âûâîäà ñòðîê òàáëèöû
my $items = (); Как можно выйти из ситуации? Например, иметь воз-
можность единожды проинициализировать каталог разме-
# Öèêë çàïîëíåíèÿ ìàññèâà äàííûìè
for (my $i=1; $i<=10; $i++) { щения шаблонов в конструкторе класса. При наличии та-
кой возможности мы можем один раз указать каталог шаб-
# Êàæäûé ýëåìåíò ìàññèâà (ñòðîêà òàáëèöû) ïðåäñòàâëÿåò
# ñîáîé õýø âèäà: [ { "NAME" => çíà÷åíèå1, "VAL" => лонов и в самих шаблонах уже не писать полный путь рас-
# çíà÷åíèå2, "odd" => [0|1] } ] положения вложенного шаблона. Вся настройка будет про-
my $row = {
"NAME" => "Ñòðîêà", изведена в одном месте при вызове конструктора.
"VAL" => $i В случае HTML::Template эта возможность предусмот-
};
рена изначально. Обратите внимание, мы сразу исполь-
# Äëÿ íå÷åòíûõ ýëåìåíòîâ óñòàíàâëèâàåì çíà÷åíèå "odd", зовали инициализацию каталога шаблонов в нашем при-
# ðàâíûì "1", äëÿ ÷åòíûõ - "0"
if ( ($even = ($even xor 1)) ) { мере:
$row->{"odd"} = 1;
} else {
$row->{"odd"} = 0; my $template = HTML::Template->new(
} filename => "ex2.html",
# Äîáàâëÿåì íîâûé õýø-ýëåìåíò â ìàññèâ path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/"
push(@{$items}, $row); );
}

# Àññîöèèðóåì ìàññèâ ñ ïîâòîðÿþùèìñÿ áëîêîì ( <TMPL_LOOP> ) В библиотеке XTemplate это не предусмотрено (во вся-
$template->param(item => $items); ком случае в версии 0.2.4-3).
# Ðåçóëüòàò ðàçáîðà øàáëîíà âîçâðàùàåò íàì â âèäå ñòðîêè Сформировать имя файла шаблона динамически, на-
# âûçîâ ìåòîäà output(). писав нечто вроде:
# Ìû ìîæåì ñîõðàíèòü åãî â ïåðåìåííóþ, çàïèñàòü â ôàéë
# èëè âûâåñòè, êàê ëþáîå äðóãîå çíà÷åíèå:
print "Content-type: text/html\n\n"; {FILE "{TEMP_FILE}"}
print $template->output();
__END__ и присвоить (проассоциировать) нужное значение подста-

70
web
новкой в {TEMP_FILE} тоже не выйдет. Конструктор класса </p>
производит рекурсивную inline-подстановку всех вложенных {FILE "ex2.html"}
</body>
шаблонов, и только потом производит разбор и обработку </html>
полного шаблона со всеми подстановками. <!-- END: main -->
Что делать? Отказаться от идеи повторного использо- Обработчик шаблона выглядит так:
вания вложенных элементов? Ну не все так печально, как
кажется. Библиотека XTemplate распространяется по лицен- <?php
include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );
зии GNU General Public License. Эта лицензия подразуме-
вает возможность внесения изменений в исходный продукт /*
 êîíñòðóêòîð äîáàâèëñÿ äîïîëíèòåëüíûé ïàðàìåòð $path.
при соблюдении определенных правил1. Нужные нам из- Äëÿ ñîâìåñòèìîñòè íàì ïðèäåòñÿ âûçûâàòü êîíñòðóêòîð ñ óêà-
менения ограничатся тремя строчками с добавлением ком- çàíèåì íàøåãî íîâîãî ïàðàìåòðà è âòîðîãî ïàðàìåòðà âûçîâà –
íàçâàíèåì ãëàâíîãî áëîêà øàáëîíà.
ментариев. Åñòåñòâåííî, ýòî ïîíàäîáèòñÿ òîëüêî òàì, ãäå íàì íåîáõî-
Суть изменения сводится к добавлению еще одного чле- äèìî óêàçàòü òðåòèé ïàðàìåòð êîíñòðóêòîðó, ò.å. â øàáëî-
íàõ ñ âëîæåíèåì.
на класса и опциональной инициализации его в конструк- */
торе. Этот новый член класса – path – будет определять $xtpl=new XTemplate( "ex_inc.html",
"main",
путь к директории шаблонов. Остальные модификации в getenv("DOCUMENT_ROOT")."/../lib/ ↵
коде исходного класса ограничиваются конструктором и data/xtemp" );
методом getfile(...). $xtpl->assign( TEMPLATE, "ex2.html");
/*
class XTemplate { Èíèöèàëèçàöèÿ äàííûõ âëîæåííîãî øàáëîíà.
... Ïðè îáðàùåíèè ê ìåòîäó parse() íóæíî ó÷èòûâàòü, ÷òî ýëå-
/* Íîâûé ÷ëåí êëàññà */ ìåíòû øàáëîíà ex2.html âëîæåíû â áëîê "main" áîëåå âåðõíå-
var $path = ""; ãî øàáëîíà – ex_inc.html. Ïîýòîìó óêàçûâàòü ïðèäåòñÿ êàê
/***[ constructor ]*** / $xtpl->parse("main.main.item") è ò. ï.
function XTemplate ($file,$mainblock="main", $path="") { */
/* Èíèöèàëèçèðóåì çíà÷åíèå path, åñëè ñîîòâåòñòâóþùèé $xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵
ïàðàìåòð íå ïóñò */ ôîíà ñòðîê" );
if ($path) $this->path= str_replace("\\", "/", $path)."/"; $even = 0;
… for ($i=1; $i<=10; $i++) {
} $row = array(
… NAME => "Ñòðîêà",
/***[ getfile ]*** / VAL => $i
/* );
returns the contents of a file $xtpl->assign(DATA, $row);
*/ if ( ($even = ($even XOR 1)) ) {
function getfile($file) { $xtpl->parse("main.main.item.odd");
/* Èñïîëüçóåì êîíêàòåíàöèþ çíà÷åíèÿ path è ïåðåäàííîãî } else {
èìåíè ôàéëà */ $xtpl->parse("main.main.item.even");
$file = $this->path.$file; }
if (!isset($file)) { $xtpl->parse("main.main.item");
$this->set_error("!isset file name!"); }
return ""; /* Ðàçáîð âëîæåííîãî øàáëîíà */
} $xtpl->parse("main.main");
...
}
... Результат наших действий в браузере отразится при-
} /* end of XTemplate class. */ мерно таким образом:

После внесения таких изменений в класс XTemplate мы:


! Сможем инициализировать единый каталог для хране-
ния шаблонов и использовать в них простые имена для
подключения файлов вложенных шаблонов.
! Сохраним совместимость с предыдущей версией. Наши
изменения сделаны таким образом, чтобы не затронуть
работу шаблонов и обработчиков, написанных для ис-
ходной версии класса.

Теперь можно нормально работать.


Шаблон «ex_inc.html» для библиотеки XTemplate: Ðèñóíîê 3. Ðåçóëüòàò ðàáîòû ñ âëîæåííûìè øàáëîíàìè. Âûâîä
òàáëèöû ðåàëèçîâàí îòäåëüíûì øàáëîíîì
<!-- BEGIN: main --> Для библиотеки HTML::Template подобных модифика-
<html>
<head><title>Âëîæåííûå øàáëîíû</title></head> ций исходного кода производить не требуется. Параметри-
<body> зованное указание каталога размещения шаблонов там
<p>
Ïîäêëþ÷åíèå øàáëîíà òàáëèöû <b><i>"{TEMPLATE}" </i></b> предусмотрено заранее.
÷åðåç { FILE "{TEMPLATE}" } Шаблон «ex_inc.html» для библиотеки HTML::Template:

1
Ознакомиться с полным текстом лицензии вы можете на сайте GNU.org (http://www.gnu.org/copyleft/gpl.html), или вос-
пользоваться неофициальным переводом на русский язык – http://www.linuxdoc.ru/gnugpl_ru.html.

№8(21), август 2004 71


web
<html> <!-- END: item -->
<head><title>Âëîæåííûå øàáëîíû</title></head> </ol>
<body> <!-- END: list -->
<p>
Ïîäêëþ÷åíèå øàáëîíà òàáëèöû <b><i>"<TMPL_VAR ↵ Шаблон «ex_inc2.html» для библиотеки XTemplate:
NAME=TEMPLATE>" </i></b>
÷åðåç &lt;TMPL_INCLUDE NAME="ex2.html">
</p> <!— BEGIN: main —>
<TMPL_INCLUDE NAME="ex2.html"> <html>
</body> <head><title>Âëîæåííûå øàáëîíû</title></head>
</html> <body>
<p>
Обработчик шаблона:
Подключение одного шаблона в разных местах страни-
#!/usr/bin/perl цы и с разным заполнением.
use strict;
# Ïóòü ê êàòàëîãó áèáëèîòåêè </p>
use lib $ENV{DOCUMENT_ROOT}."/../lib"; <!-- BEGIN: first -->
Ñïèñîê 1:
# Ïîäêëþ÷åíèå áèáëèîòåêè {FILE "list.html"}
use HTML::Template; <!-- END: first -->
# Ñîçäàíèå íîâîãî îáúåêòà.  äàííîì ñëó÷àå ìû îòäåëüíî <!-- BEGIN: second -->
# óêàçûâàåì â êîíñòðóêòîðå êðàòêîå èìÿ ôàéëà øàáëîíà Ñïèñîê 2:
# ex_inc.html è ïóòü ê êàòàëîãó htdocs/../data/htmltemp, {FILE "list.html"}
# ãäå ðàçìåùåí øàáëîí <!-- END: second -->
my $template = HTML::Template->new( </body>
filename => "ex_inc.html", </html>
path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/" <!-- END: main -->
);
$template->param(TEMPLATE => "ex2.html" ); Обрабатываем шаблон на PHP:
# Çàïîëíåíèå äàííûõ âëîæåííîãî øàáëîíà
$template->param(TOPIC => "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵ <?php
ôîíà ñòðîê"); include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );
my $even = 0;
my $items = (); $xtpl=new XTemplate( "ex_inc2.html",
for (my $i=1; $i<=10; $i++) { "main",
my $row = { getenv("DOCUMENT_ROOT")."/../lib/ ↵
"NAME" => "Ñòðîêà", data/xtemp" );
"VAL" => $i
}; // Äàííûå äëÿ ïåðâîãî ñïèñêà
$list = array("one", "two", "three");
if ( ($even = ($even xor 1)) ) { foreach($list as $item) {
$row->{"odd"} = 1; $xtpl->assign(ITEM, $item);
} else { $xtpl->parse("main.first.list.item");
$row->{"odd"} = 0; }
} // Ðàçáèðàåì áëîê "first"
push(@{$items}, $row); $xtpl->parse("main.first.list");
$xtpl->parse("main.first");
}
$template->param(item => $items); // Äàííûå äëÿ âòîðîãî ñïèñêà
$list = array("ðàç", "äâà", "òðè");
print "Content-type: text/html\n\n"; foreach($list as $item) {
print $template->output(); $xtpl->assign(ITEM, $item);
$xtpl->parse("main.second.list.item");
__END__ }
// Ðàçáèðàåì áëîê "second"
$xtpl->parse("main.second.list");
Результат работы аналогичен, не будем загромождать $xtpl->parse("main.second");
место еще одной похожей картинкой.
Еще один «интересный случай». Вложенные шаблоны $xtpl->parse("main");
хочется использовать для стандартных элементов. Легко $xtpl->out("main");
?>
предположить, что один и тот же элемент может использо-
ваться на странице многократно. Если включить его как Для того чтобы реализовать то же самое с использова-
вложенный шаблон, получится путаница с заполнением. Как нием библиотеки HTML::Template, придется кое-что уточ-
поступить? нить. В этой библиотеке нет понятия именованного блока.
Нам нужен управляемый способ многократного вклю- Как же разделить пространство действия переменных шаб-
чения вложенных шаблонов (стандартных элементов ди- лона?
зайна). Звучит очень серьезно, реализуется – просто. Для этого нужно воспользоваться конструкцией
Каждое включение нужно выделить в виде отдельного <TMPL_LOOP …>…</TMPL_LOOP>. Внутри этого «цикла»
блока. переменные-подстановки являются вложенными в цикл.
Шаблон «list.html» для библиотеки XTemplate: Воспользуемся этим фактом.
Шаблон «list.html» для библиотеки HTML::Template:
<!-- BEGIN: list -->
<ol>
<!-- BEGIN: item --> <ol>
<li>{ITEM}</li> <TMPL_LOOP NAME="item">
<li><TMPL_VAR NAME="item"></li>

72
web
</TMPL_LOOP> Заключение
</ol> Выбирая движок для шаблонов, старайтесь оставить за
Шаблон «ex_inc2.html» для библиотеки HTML::Template: шаблонами верстку (оформление), а логику – за програм-
мой. Не надо перекладывать на шаблоны логику выполне-
<html> ния и ни в коем случае не надо внедрять в шаблоны какие-
<head><title>Âëîæåííûå øàáëîíû</title></head>
<body> либо объекты!
<p> Все «расширенные» (в сравнении с обычным кодом
HTML) возможности шаблонов должны заканчиваться на
Подключение одного шаблона в разных местах страни- возможностях:
цы и с разным заполнением. ! пометить точку для вставки (переменная шаблона);
! пометить блок для выбора (именованный IF);
</p>
Ñïèñîê 1:
! пометить блок для повтора (именованный LOOP).
<TMPL_LOOP NAME="first">
<TMPL_INCLUDE NAME="list.html"> И даже это избыточная модель! В PHP-ом XTemplate все
</TMPL_LOOP>
еще более «доведено до ума»:
Ñïèñîê 2:
<TMPL_LOOP NAME="second">
<TMPL_INCLUDE NAME="list.html"> <ul><!-- BEGIN: menulist -->
</TMPL_LOOP> <!-- BEGIN: link -->
</body> <li><a href="{ITEM.URL}">{ITEM.NAME}</a></li><!-- END: link -->
</html> <!-- BEGIN: active -->
<li>{ITEM.NAME}</li><!-- END: active -->
<!-- END: menulist --></ul>
Вся тонкость обработки сводится к корректному фор-
мированию массивов: И все!
У вас есть блок вида: <— BEGIN: имя —> …<— END:
#!/usr/bin/perl имя —>. На стороне программы вы можете повторить его
use strict;
нужное количество раз или выбрать из двух смежных тот,
# Ïóòü ê êàòàëîãó áèáëèîòåêè который нужно вывести, исходя из логики.
use lib $ENV{DOCUMENT_ROOT}."/../lib";
Для библиотеки Perl HTML::Template считайте име-
# Ïîäêëþ÷åíèå áèáëèîòåêè нованным блоком конструкцию <TMPL_IF NAME=имя >…
use HTML::Template;
</TMPL_IF>. Все, что нужно повторить несколько раз, дол-
my $template = HTML::Template->new( жно быть заключено в конструкцию вида <TMPL_LOOP
filename => "ex_inc2.html",
path => $ENV{DOCUMENT_ROOT}."/../lib/data/htmltemp/" NAME=имя> …</TMPL_LOOP>.
); У вас есть место вставки вида {имя} или явная поблаж-
# Çàïîëíÿåì ìàññèâ äëÿ øàáëîíà list.html ка {имя_структуры.имя}.
my $items = (); И последняя конструкция – включение одного шаблона
push( в другой. По принципу inline-подстановки: {FILE «имя»} или
@{$items}, <TMPL_INCLUDE NAME=«имя»>.
({"item" => "one"}, {"item" => "two"}, {"item" => "three"})
); Класс (движок) шаблонов должен уметь работать с вло-
жениями блоков друг в друга и воспринимать каждый блок
# Îðãàíèçóåì îáðàìëÿþùèé ìàññèâ äëÿ <TMPL_LOOP NAME=”first”>
my $list = (); как отдельное пространство имен. Для HTML::Template это
push(@{$list}, {"item" => $items}); будет верно, если вы начнете рассматривать хэш, заполня-
# Àññîöèèðóåì çíà÷åíèå ющий цикл как ограничитель такого пространства.
$template->param("first" => $list); Этого достаточно, чтобы сформировать любой вывод.
# Îáíóëÿåì è çàïîëíÿåì ìàññèâû çàíîâî Класс (движок) шаблонов должен позволять рекурсив-
$items = (); ное включение файлов-шаблонов друг в друга.
push(
@{$items}, Это позволит вам создавать отдельные мини-шаблоны
({"item" => "ðàç"}, {"item" => "äâà"}, {"item" => "òðè"}) для повторяющихся элементов, а не копировать один и тот
);
же HTML-код из шаблона в шаблон.
$list = (); Движок шаблонов должен быть инкапсулирован в от-
push(@{$list}, {"item" => $items});
дельный класс-библиотеку. Подключаться, как любой дру-
# Àññîöèèðóåì çíà÷åíèå гой модуль-файл, в программу и не требовать сложных дей-
$template->param("second" => $list);
ствий для инсталляции или добавления в любую систему.
print "Content-type: text/html\n\n"; Это позволит вам легко переносить и добавлять поддерж-
print $template->output();
ку шаблонов в любую систему, не занимаясь перестанов-
__END__ кой и дополнительной настройкой окружения.
Движок также не должен требовать предварительной
Не так уж и страшно, не так ли? Но обратите внима- компиляции шаблонов ни в какое промежуточное представ-
ние на следующий факт. При заполнении конструкции ление. Это позволит вносить изменения в оформление
<TMPL_LOOP …> используется не массив, а ссылка на именно на уровне шаблонов и полностью отделить HTML-
массив. Это важно! форматирование вывода от программной части.

№8(21), август 2004 73


web

ОБРАБОТКА HTML-ШАБЛОНОВ OFF-LINE.


ВОЗМОЖНОСТИ И ОГРАНИЧЕНИЯ

АЛЕКСЕЙ МИЧУРИН
Сборке HTML-документов по шаблонам посвящено вели- может показаться неочевидной, но это только на первый
кое множество публикаций самого разного масштаба и ка- взгляд. Да, off-line-сборка позволит создавать только ста-
чества: от небольших статей до детальных руководств и тические документы. Приёмы, которые мне предстоит опи-
книг, от дилетантских до весьма профессиональных. Но по- сать, ни в коей мере не связаны с «on-line-движками». Тем
давляющее большинство авторов сосредоточивается на on- не менее, разработка статических документов – неотъем-
line-сборке. Ими рассматривается ситуация, когда на сер- лемый и трудоёмкий процесс, неизбежно сопровождающий
вере лежат не статические документы, а шаблоны: заго- создание и поддержку любого веб-ресурса. Облегчению
товки и части документов. При запросе клиентом соответ- этой задачи и посвящена настоящая статья. А прежде чем
ствующей страницы на сервере запускается некий меха- привести два примера, замечу, что техника, изложенная
низм, собирающий веб-страницу «на лету» (в реальном здесь, может применяться и для создания частей докумен-
масштабе времени) и отправляющий её клиенту. Для ре- тов, которые далее будут использоваться как исходные дан-
шения подобных задач разработано множество инструмен- ные для «on-line-движков».
тов и средств, начиная с несложных и интегрированных глу- Приведу два, как мне кажется, наиболее житейских при-
боко в сервер (например, SSI) и заканчивая многофункци- мера.
ональными самостоятельными модулями и библиотеками Практически у любого ресурса есть страницы, которые
с очень богатыми возможностями. обновляются редко, но время от времени обязательно об-
Тема on-line-обработки шаблонов действительно очень новляются. Это могут быть адреса торговых точек и фили-
интересна и воистину неисчерпаема, поскольку в разных алов, телефоны, расписание работы, информация о сотруд-
условиях оказываются уместны разные подходы. Неудиви- никах, подборки статей и многое другое. Эти страницы,
тельно, что так много авторов обращается именно к этой скорее всего, должны быть оформлены в едином стиле с
теме. Но я хотел бы уделить немного внимания механиз- содержимым всего сервера, то есть включать стандартную
мам off-line-сборки. Возможно, не слишком распространён- шапку, элементы навигации и прочее. Невольно появляет-
ным термином «off-line-сборка» я буду называть процесс ся мысль использовать для их создания технику сборки по
сборки шаблонов на локальной машине, в отсутствие сер- единому шаблону. Вместе с тем, на практике эти страницы
верного ПО и не для передачи клиенту. В результате такой несут статическую информацию. Собирать их вновь при
сборки вы получаете набор статических документов (хотя каждом запросе не очень разумно. Гораздо рациональней –
никто не запрещает использовать, например, SSI-инструк- собрать их единожды и разместить на сервере, а при необ-
ции), готовых к размещению на сервере. Оказывается, кон- ходимости вносить малые изменения в шаблоны, легко пе-
цепция сборки документов по шаблону может быть весьма ресобирать необходимые страницы и размещать их на сер-
полезна не только при сборке документов «на лету», но и вере. Возможно, необходимость проведения таких работ
при сборке статических документов. будет возникать раз в несколько лет, но даже редкую рабо-
Прежде чем обсудить конкретную реализацию off-line- ту лучше делать просто, чем сложно.
процессора шаблонов, нам необходимо сформулировать Здесь возникает возражение: такой поход прожорлив
критерии, которым он должен соответствовать. Но перед до дискового пространства. Соглашусь с этим, но отвечу,
этим давайте определимся, в каких ситуациях off-line-сбор- что дисковое пространство стоит копейки и обходится го-
ка может быть полезна (и для кого написана эта статья). раздо дешевле процессорного времени, за одну только воз-
можность использования коего обычно приходится допол-
Когда применима off-line-сборка? нительно доплачивать. При отказе от on-line-сборки в пользу
Ценность автоматизации сборки статических документов off-line-сборки мы проигрываем в дисковом пространстве,

74
web
но объем трафика (гораздо более важная характеристика) ресобрать документы, и название раздела изменится во
не меняется, а процессорное время (тоже весьма важная всех оглавлениях, заголовках, элементах <title>...</title> и
характеристика) существенно экономится. Таким образом, во всех других местах, где встречалось это название. Так
суммарно мы, скорее всего, получим выигрыш. (Вопросы же можно будет менять цвета и другие элементы оформле-
трудоёмкости загрузки большого объёма данных на сервер ния. То есть ресурс становится предельно управляемым во
и другие вопросы я обязательно затрону, но ближе к концу всех отношениях.
статьи.) ! Второе требование: программный код (код, выполняю-
Как видите, в описанной ситуации off-line-сборка вполне щий сборку) должен быть полностью отделён от кода
оправданна, а такая ситуация возникает при поддержке прак- шаблонов.
тически любого ресурса в Web.
Вторая немаловажная область для применения техни- Это классическое требование, которое всегда предъяв-
ки off-line-сборки – разработка. Веб-дизайнер1 должен иметь ляется к обработчикам шаблонов и нарушается с таким же
возможность оперативно глянуть на дело рук своих. Если постоянством. Иначе и быть не может. С одной стороны,
его работа полностью сосредоточена на разработке шаб- процессор шаблонов должен быть управляемым, то есть
лонов, сборка которых произойдёт только на сервере, то программируемым, иначе он сможет собирать только один
ему (на локальной машине) затруднительно увидеть резуль- документ по одному шаблону и утратит всяческий смысл.
тат своих трудов. Сперва он вынужден разместить всё на Но, с другой стороны, программный код в шаблонах дол-
сервере, а это не только трудно, но и потребует от дизайне- жен, как мы только что сказали, отсутствовать. Этот пара-
ра квалификации, не свойственной для него. докс каждый из разработчиков решает согласно своим за-
В этой ситуации было бы удобнее, если бы дизайнер дачам. Наши задачи таковы, что в шаблонах должен быть
имел под рукой процессор обработки шаблонов и мог быс- минимум управляющих конструкций. Мы намеренно не бу-
тро пересобрать весь набор документов, легко «играя» па- дем реализовывать ни переменных, ни условных перехо-
литрами или компоновками материала. дов. Естественно, у нас не будет ни циклов (частный слу-
То же самое относится и к верстальщику2. Ему было бы чай условных переходов), ни вычислений (направленных на
нелишне иметь возможность легко оценить, как будет выг- обработку переменных)3.
лядеть его работа в рамках общего дизайна (не «съедут» Такие строгие меры продиктованы жизненной необхо-
ли иллюстрации и таблицы, уместны ли цвета и т. п.). димостью. Помните про первое применение – долгосроч-
Разработка тем более упрощается, если программист, ную поддержку ресурса? Шаблоны должны быть таковы,
дизайнер и верстальщик пользуются одним набором инст- чтобы можно было легко вспомнить, что тут к чему, даже
рументов и одним форматом шаблонов. после полного забвения, которое приходит обычно уже че-
Итак, задачи намечены, наметим теперь пути их реше- рез пару месяцев после окончания активной фазы разра-
ния. ботки. При этом мы не должны наивно рассчитывать, что
кто-то станет документировать сбою работу. С шаблонами
Требования к аппарату off-line-сборки должен легко управляться и дизайнер, не имеющий ника-
кого представления о программировании, переменных, уп-
Самые общие требования равляющих конструкциях и прочем.
! Первое требование прозвучит почти комично: наша си- ! Программа обработки шаблонов должна быть макси-
стема сборки должна обеспечивать сборку. То есть она мально переносима. Её работоспособность не должна
должна давать возможность описать, в какое место об- зависеть от ОС, ПО, дополнительных модулей и расши-
рабатываемого файла содержимое какого файла будет рений. У дизайнера и верстальщика должна быть воз-
вставляться. можность просто носить её на flash-носителе вместе с
рабочими материалами. Установка на новую машину
Такие места вставки будем называть точками вставки. должна быть упрощена до полной незаметности.
Получив возможность вставлять некую информацию во мно- ! Аппарат сборки должен выдавать ясный и простой про-
гие места многих файлов, мы получили возможность хра- токол работы, позволяющий легко понять (или восста-
нить одну информацию в одном месте. Это позволит и из- новить в памяти) ход сборки и источники той или иной
менять (при необходимости) одну информацию в одном информации, а при необходимости легко локализовать
месте. То есть при последовательном проведении этой тех- ошибки.
ники в жизнь вы сможете поменять название раздела в ! Конечно, шаблоны должны обрабатываться рекуррент-
одном-единственном файле, одним касанием <Enter> пе- но, допуская вставку шаблона в шаблон.

1
Во избежание разночтений поясню, что под «веб-дизайнером» я буду понимать субъекта, разрабатывающего все
элементы, общие для всех (или для больших групп) страниц ресурса. Это может быть и не один человек, а команда,
каждый представитель которой специализируется на одном из аспектов веб-дизайна: графика, HTML, CSS, JavaScript...
В таком случае, внедрение механизма, предлагаемого в статье, напрямую затронет только разработчика HTML-кода.
2
Уточню, что под «верстальщиком» я буду понимать человека (или команду), обеспечивающего информационное на-
полнение каждой конкретной страницы. Верстальщик получает материалы в естественном для заказчика формате и
адаптирует их для размещения на веб-страницах.
3
Это не совсем так, что-то сделать всё-таки придётся. Читайте дальше.

№8(21), август 2004 75


web
Требования к шаблонам коллекции DJGPP4. У пользователей UNIX проблем с уста-
! Синтаксис оформления точек вставки должен допускать новкой Perl вообще не возникнет, поскольку Perl является
достаточную гибкость. Разработчик должен иметь воз- неотъемлемой частью подобных систем.
можность сделать точку вставки и заметной, и компак- Можно было бы поступить ещё концептуальней: напи-
тной, в зависимости от конкретной ситуации. сать программу на чём-нибудь компилируемом, например,
! Точки вставки не должны быть похожи на HTML-теги. на C. Такая программа не требовала бы даже интерпрета-
Это позволит беспрепятственно применять программы тора. Возможно, это хорошая идея. Я не пошёл по этому
проверки HTML-кода к отдельным шаблонам и легко ло- пути по двум причинам. Во-первых, разница не так принци-
кализовать ошибки типа забытых закрывающих тегов пиальна: избавились от интерпретатора – понадобился ком-
или неверных атрибутов. пилятор. Во-вторых, код получился бы не такой компакт-
ный, и приводить его в статье было бы не так удобно. Пос-
Возможности управления ходом сборки леднее обстоятельство, как вы понимаете, не должно оста-
Пришло время компромиссов и разрешения парадокса, о навливать вас.
котором я говорил. Я уже вижу, как взгрустнули програм-
мисты. Конечно, отказ от переменных, условных переходов, Осмотр кода и формат сценария сборки
вычислений и циклов – тяжёлая утрата. Можно ли сочетать Давайте пробежим глазами строки кода, который у меня
предельную простоту кода и достаточную управляемость? получился:
Думаю, что да.
Я бы назвал предлагаемое решение «условной встав- 01: #!/usr/bin/perl -w
02:
кой». Каждый шаблон обрабатывается с определённым па- 03: #use strict;
раметром. Фактически параметр – это единственная пере- 04:
05: my $INPUT_PATH ='<input/';
менная. Но от того, что она одна, ей не нужно имя и синтак- 06: my $OUTPUT_PATH='>output/';
сис её использования становится предельно прост и поня- 07:
08: sub assemble_step {
тен даже человеку, далёкому от программирования (осо- 09: my ($level, $file, $key)=@_;
бенно, если не называть её словом «переменная»). В зави- 10: print (('. 'x$level).$file.':'.$key."\n");
11: local $/;
симости от этого параметра в шаблон будет встраиваться 12: open FH, $INPUT_PATH.$file or die $file.' : '.$!;
содержимое того или иного файла. 13: my $text=<FH>;
14: close FH;
Итак, ещё одно требование: 15: $level++;
! Обработчик должен поддерживать сборку с параметром 16:
17:
$text=~s{\(##[#\s]*([^#\s]+)[#\s]*##\)}{
my ($fn, $k)=($1, $key);
и условную вставку. 18: $fn=~s/\?/$key/g;
19: $fn=~s/\(([^)]+)\)/($key eq $1)?'yes':'no'/ge;
20: ($fn, $k)=($1, $2) if ($fn=~m/^([^:]+):(.+)$/);
Пока, пожалуй, хватит требований, мелкие замечания 21: assemble_step($level, $fn, $k);
и уточнения разберём на конкретном примере. 22: }ge;
23: return $text;
24: }
Пример реализации off-line-процессора 25:
26: sub assemble {
Давайте теперь рассмотрим программу, хоть и далёкую от 27: my ($output, $input_root, $init_key)=@_;
совершенства и законченности, но компактно реализующую 28: my $text=assemble_step(0, $input_root, $init_key);
29: open FH, $OUTPUT_PATH.$output or die $output.' : '.$!;
изложенные выше идеи и вполне работоспособную, в чём 30: print FH $text;
у нас будет возможность убедиться в следующем разделе 31: close FH;
32: }
(где будет создан небольшой сайт из шести страниц). 33:
34: while (<>) { assemble(split) }
Выбор языка
При выборе языка программирования я остановился на Я буду предполагать, что читатель знаком с Perl, и огра-
вездесущем Perl, не устояв перед его широкими возмож- ничусь только краткими пояснениями. Тело программы со-
ностями и богатством платформ, на которые он перенесён. стоит из одной строки 34 (если не считать объявления двух
Кроме того, давайте не будем использовать в нашей глобальных переменных в строках 5 и 6)5. Здесь в цикле
программе внешних модулей. Такой код можно будет запу- while одна за другой обрабатываются строки входного фай-
стить под Windows, даже не устанавливая громоздкий ла. Чтобы отличать его от шаблонов, давайте назовём его
ActivePerl, а воспользовавшись одним только perl.exe из громким словом «сценарий сборки» или просто «сценарий».

4
Речь идёт о дистрибутиве, доступном, например, по адресу: ftp://ftp.cpan.org/pub/CPAN/ports/msdos/LMOLNAR/perl542b.zip.
Там вы найдёте perl.exe версии 5.004_02 от DJGPP, размером всего 266 Кб. Там же, в архиве CPAN, можно найти
множество различных сборок Perl для различных платформ (ftp://ftp.cpan.org/pub/CPAN/ports). Многие из них облада-
ют существенными ограничениями, которые касаются сетевых возможностей, возможностей работы с базами дан-
ных, ограниченным набором модулей, но эти ограничения не повлияют на работоспособность кода, приводимого в
настоящей статье. Неприятным сюрпризом может стать только то, что некоторые сборки для DOS не поддерживают
длинных имён.
5
Это сделано специально, чтобы вам было легче развивать программу, я уже сказал, что приводимая здесь реализа-
ция скорее учебная, нежели боевая.

76
web
Каждая строка сценария сборки разбивается на поля (раз- (## ##
делители – любые пробельные символы в любом количе- ## NAME ##
## ##)
стве). Значения этих полей передаются функции assemble,
которая инициализирует и запускает процесс сборки шаб- Точки вставки, таким образом, можно оформлять в духе
лона, а результат сборки записывает в назначенный файл. комментариев C с той лишь разницей, что начальный и ко-
Первое поле каждой строки сценария задаёт имя соби- нечный символы у нас разные и написание регулярного
раемого файла (имя файла, в который будет помещён окон- выражения для поиска точек вставки упрощается.
чательный результат сборки). Второе поле – имя корневого
шаблона, с которого начнётся сборка. Третье поле – пара- Вставка
метр, с которым будет собираться корневой шаблон. Что мы будем заменять – теперь понятно, давайте разбе-
Функция assemble_step, выполняющая саму сборку, вы- рёмся с тем, чем мы будем заменять.
зывается рекуррентно (вы видите, что она вызывается из В качестве имени файла, который будет вставляться в
самой себя в строке 21). Её аргументы таковы: первый – данную точку, будет использоваться наша переменная $1,
уровень вложенности (глубина рекурсии); второй – имя но... после небольшой «доработки»:
файла шаблона, который необходимо обработать; тре- ! Первым делом (строка 18) все знаки ? в потенциальном
тий – параметр сборки, с которым необходимо обработать имени файла заменятся на текущее значение парамет-
шаблон. ра сборки.
Первое, что делает функция assemble_step, – выдаёт
строку протокола сборки (строка 10 листинга). Начальные То есть если код:
символы строки задают отступ, показывая глубину рекур-
сии (чем больше уровень вложенности, тем больше отступ). <html>(## text-for-? ##)</html>
Далее следует имя файла шаблона и параметр, с которым
его предстоит обработать. собирается с параметром index, то между тегами <html> и
Далее (строки 11-15) в переменную $text читается шаб- </html> будет вставлен шаблон из файла text-for-index. Если
лон, глубина вложенности увеличивается на единицу (это же этот же шаблон собирается с параметром paper, то вместо
значение будет передано «дочерним» assemble_step) и на- (## text-for-? ##) будет вставлен шаблон из файла text-for-paper.
чинается самое интересное – обработка шаблона. ! Далее (в строке 19) в потенциальном имени файла отыс-
Сердцем нашего процессора шаблонов, как вы уже ус- киваются все конструкции в круглых скобках. С ними
пели догадаться, являются строки листинга с 16 по 22, пред- производится следующая замена: если строка в скоб-
ставляющие собой просто одно выражение глобального ках совпадает с текущим параметром сборки, то скоб-
поиска и замены. Здесь-то и происходит сборка. ка заменяется на строку «yes», в противном случае она
Что же мы ищем и на что заменяем? заменяется на строку «no».

Точки вставки Поясню на примере шаблона:


Ищем мы, конечно, точки вставки. Как видите (строка 16),
оформлены они у нас будут достаточно гибко: открываю- <html>(## is-i-home-(index).txt ##)</html>
щая круглая скобка; два знака #; любое количество пробе-
лов6 или знаков #; некоторое количество знаков, отличаю- Если этот шаблон собирается с параметром index, то бу-
щихся от пробелов и #, они сохранятся в переменной $1 и дет использован файл is-i-home-yes.txt. Во всех других слу-
будут использованы для определения, что именно необхо- чаях, напротив, будет использован is-i-home-no.txt.
димо вставить в данную точку; далее следует снова произ- ! Затем (строка 20) проверяется, содержит ли потенци-
вольное количество пробелов и символов #; и наконец за- альное имя файла двоеточие. Если ответ утвердитель-
вершающие два # и закрывающая скобка. ный, то часть имени слева от двоеточия используется в
Я не навязываю читателю именно такой стиль, просто качестве имени файла, правая часть имени будет ис-
мне он кажется удобным. Знак # выбран потому, что его пользована как новый параметр сборки.
легко заметить в тексте. А формат позволяет оформлять
точки вставки и компактно (полезно, когда их много): Это даёт возможность «подменять» параметр сборки в
процессе самой сборки и собирать разные части докумен-
(## NAME ##) та с разными параметрами. Обратите внимание: подмена
носит локальный характер, шаблон-«родитель» о ней ни-
И громоздко (полезно, когда точек вставки мало и их чего не знает, его обработка продолжается с тем же пара-
надо выделить): метром. Подмену замечает только шаблон-«потомок» (и его
потомки, если не произойдёт ещё одной подмены).
(###############
NAME
! Наконец (строка 21) мы вызываем рекуррентно
###############) assemble_step, которая производит сборку того, что нам
в результате оказалось нужно (и, возможно, с новыми
или параметрами), и помещаем результат в точку вставки.

6
Пробелами в данном случае считаются не только истинные пробелы и табуляции, но и символы новой строки CR и LF.

№8(21), август 2004 77


web
Пример сборки ресурса 01: <html>
Давайте теперь рассмотрим пример создания простенько- 02: <head>
03: <title>(## ?-head ##)</title>
го веб-ресурса с помощью нашей «кухни», и пусть его про- 04: </head>
стота нас не смущает, ведь любой сложный ресурс всегда 05: <body>
можно разбить на простые части (речь, конечно, по-пре- В этом шаблоне есть тоже одна точка вставки. В неё
жнему идёт только о статических ресурсах). вставляется информация одного из трёх файлов index-head,
На нашем сервере будет три страницы: index.html, contact-head и about-head. При сборке index.html (с пара-
contact.html и about.html. Будем называть их основными. У метром index) используется первый – index-head, для
каждой из них будет ещё и версия для печати: index- contact.html и about.html – contact-head и about-head соот-
print.html, contact-print.html и about-print.html, соответствен- ветственно. В файлах *-head, как вы уже поняли, содер-
но. Итого шесть страниц, все они показаны на рисунке с жатся заголовки соответствующих страниц. Например, в
указаниями имён файлов. index-head:
Сценарий сборки будет таков7:
01: Î íàñ
01: index.html skeleton index
02: contact.html skeleton contact
03: about.html skeleton about Во второй строке шаблона skeleton (мы продолжаем его
04: index-print.html skeleton-print index рассмотрение) – точка вставки шаблона sep:
05: contact-print.html skeleton-print contact
06: about-print.html skeleton-print about
01: <table width="100%">
02: <tr bgcolor="#cccccc">
Каждому собираемому html-файлу, как вы видите, здесь 03: <td align="right">
отвечает своя строка, в которой, кроме имени файла-ми- 04: <font size="1" color="#999999">ÑÒÅËÜÊÈ Inc.</font>
05: </td>
шени, указываются шаблон и параметр. 06: </tr>
Как вы поняли, все основные станицы будут собраны 07: </table>
по шаблону skeleton, а страницы для печати – по шаблону
skeleton-print. Здесь нет ничего хитрого, это чисто декоративный эле-
мент – горизонтальная серая полоса. На наших страницах
Сборка основных страниц их три на каждой (см. рисунок).
Рассмотрим сперва сборку основных страниц: index.html, В строках 3-8 шаблона skeleton – код, отвечающий за
contact.html и about.html. Согласно первым трём строчкам логотип и баннер.
сценария сборки, все они собираются по общему шаблону В строке 9 повторяется вставка декоративного разде-
skeleton, варьируется только параметр (исключительно для лителя sep.
простоты чтения он тождественен имени соответствующе- В строках 10-21 описана таблица, несущая собственно
го собираемого файла). тело страницы. В таблице только две ячейки (одна строка,
Этот раздел будет фактически посвящён рассмотрению два столбца). Левая содержит навигационное меню (шаб-
шаблона skeleton: лон toc, подключаемый в строке 12), к нему мы вернёмся
чуть позже. В правой ячейке содержится несколько точек
01: (### body-open ###) вставки. В первой из них подключается один из уже знако-
02: (### sep #########)
03: <table width="100%"> мых нам шаблонов *-head, соответствующий параметру
04: <tr> сборки (строка 14). Это заголовок. В строке 16 создаётся
05: <td><img src="../img/title.gif"></td>
06: <td align="right"><img src="../img/banner.gif"></td> ссылка на страницу с версией для печати. Адрес этой стра-
07: </tr> ницы берётся из файлов index-url-print, contact-url-print и
08: </table>
09: (####### sep #######) about-url-print. Например, index-url-print содержит:
10: <table>
11: <tr valign="top"> 01: index-print.html
12: <td>(## toc ##)</td>
13: <td>
14: <big><b>(## ?-head ##)</b></big>
15: <br> Единственное, о чём здесь следует упомянуть: файлы
16: <a href="(## ?-url-print ##)"><b><font size="1"> *-url-print не должны содержать ничего, кроме адресов. То
17: âåðñèÿ äëÿ ïå÷àòè</font></b></a>
18: <br>(## ?-text ##) есть в них не должно быть пробелов, табуляций, символов
19: </td> LF и CR и других невидимых символов.
20: </tr>
21: </table> И наконец в строке 18 шаблона skeleton мы подключа-
22: (### sep ##########) ем текст страницы из файлов (которые могут оказаться, в
23: (### body-close ###)
свою очередь, шаблонами) index-text, contact-text и about-
В его первой строке находится точка вставки шаблона text. Так, например, в файл index.html вставляется текст из
body-open: файла index-text:

7
Все файлы, упомянутые в настоящей статье, можно скачать одним архивом на сайте журнала: http://www.samag.ru/
source. В архиве также содержится программа сборки на Perl и bonus track: дополнительные файлы, необходимые
для сборки веб-страницы, содержащей все страницы ресурса; как раз эта страница и является иллюстрацией статьи
(readme прилагается).

78
web
01: Êîðïîðàöèÿ &laquo;ÑÒÅËÜÊÈ Inc.&raquo; ïðîèçâîäèò Понятно, что каждый из них содержит одну строчку. На-
02: íåäîðîãèå, íî âûñîêîêà÷åñòâåííûå êîðïîðàòèâíûå ñòåëüêè. пример, index-url содержит:
03: Ìû âñåãäà äåëàåì óïîð íà èìèäæåâûå ýëåìåíòû, íî
04: ïðåäëàãàåì è óíèâåðñàëüíûå ãîòîâûå ðåøåíèÿ...
01: index.html
Шаблон skeleton заканчивается вставкой ещё одного
декоративного разделителя sep и закрывающими тегами, Перейдём теперь к самому интересному.
вынесенными в отдельный файл body-close.
Сборка оглавления
Сборка страниц для печати Вернёмся к шаблону toc, отвечающему за навигационное
Ещё проще устроена сборка страниц для печати. Как вы меню. Как вы уже видели на рисунке, оглавление у нас сде-
помните, для них используется общий базовый шаблон лано в лучших традициях гипертекста: ни одна страница
skeleton-print: нигде не ссылается на саму себя. Для этого нам понадо-
бился довольно мудрёный шаблон toc:
01: (### body-open ###)
02: <hr> 01: <table>
03: <big>(## ?-head ##)</big> 02: (## toc-(index):index ##)
04: <hr>
05: <small>&copy; ÑÒÅËÜÊÈ Inc.</small> 03: (## toc-(about):about ##)
04: (## toc-(contact):contact ##)
06: <hr> 05: </table>
07: (##############
08: ### ?-text ###
09: ##############) Попробуйте ответить на вопрос, сколько шаблонов ис-
10: <hr> пользуется шаблоном toc? Правильный ответ: два. Да! Нам
11: <small>àäðåñ ðåñóðñà
12: <u>http://www.stelki.biz/(## ?-url ##)</u></small> понадобится всего два шаблона.
13: <hr> Первый toc-yes:
14: <small><a href="(## ?-url ##)">íàçàä</a></small>
15: (### body-close ###)
01: <tr>
02: <td nowrap bgcolor="#cccccc"><b>&bull;
В начале и в конце этого шаблона мы видим уже знако- 03: (## ?-head ##)</b></td>
мые body-open и body-close. Текст заголовка берётся всё 04: </tr>
из тех же файлов *-head, а текст – из файлов *-text (обрати-
те внимание, как выделена точка вставки текста). Един- Второй toc-no:
ственные новые шаблоны, с которыми мы тут столкнулись,
01: <tr>
содержат адреса возврата на исходные страницы (не пред- 02: <td nowrap><b>&bull;
назначенные для печати). Я говорю о шаблонах index-url, 03: <a href="(## ?-url ##)">(## ?-head ##)</a>
04: </b></td>
contact-url и about-url (они фигурируют в строках 12 и 14). 05: </tr>

№8(21), август 2004 79


web
Шаблон toc-yes описывает одну строку таблицы, в един- мы не использовали шаблоны и захотели бы изменить на-
ственной ячейке которой находится название соответствую- звание раздела, то нам пришлось бы вносить исправления
щего раздела. Шаблон toc-no описывает такую же конструк- в семи местах. Наш механизм в семь раз облегчит задачу.
цию, но ячейка уже не подкрашена, а текст является ссылкой. Обработчик позволяет делать очень многое и не усту-
Я думаю, читатель уже начал догадываться, как всё это пает большинству подобных инструментов. Фактически
работает. любое изменение стиля или содержания сайта выполняет-
Например, при сборке документа about.html шаблон toc ся редактированием всего одного файла.
вызывается с параметром about. Во второй его строке бу- Эта управляемость идёт на пользу не только отдельным
дет подключён файл toc-no, так как параметр about не со- разработчикам, но и облегчает взаимодействие в коман-
впал с круглой скобкой (index). Этот шаблон, в свою оче- де. Так, дизайнер мог вести разработку так, как это делали
редь, вызывается с подменой параметра на index, и в нём мы. Но программист перед окончательным размещением
будут подставлены файлы index-rul и index-head. То есть документов на сервере может просто поменять в одном
строка с названием первой страницы станет ссылкой на файле (skeleton) код, отвечающий за баннер, и одним дви-
первую страницу. Во второй строке шаблона toc будет про- жением подключить баннерную систему на всех страницах
изводиться вставка шаблона toc-yes, который не форми- ресурса.
рует ссылки. То есть мы добились своего – страница не ! Шаблоны допускают весьма гибкое форматирование то-
ссылается на саму себя. Третья вставка (в четвёртой стро- чек вставки, которое может обеспечить и наглядную, и
ке шаблона toc) пройдёт аналогично первой. компактную запись.
Окончательно разобраться в деталях происходящего ! Обработчик выдаёт протокол сборки, позволяющий лег-
нам поможет протокол сборки. ко понять коллегу или себя.
! Обработчик компактен и элементарно переносится на
Протокол работы любую машину, работающую под управлением любой ОС.
Мы ещё ничего не сказали о протоколе (необходимость
коего была оговорена в начале статьи), который выдаётся Но не будем долго распространяться о мощи шаблон-
нашей системой сборки. Я не буду приводить его весь. Вот ного подхода. Давайте перейдём к недостаткам.
часть, отвечающая за сборку документа index.html:
Слабые стороны подхода
01: skeleton:index Когда вы принимаете решение использовать или не исполь-
02: . body-open:index
03: . . index-head:index зовать некий подход (в частности, описываемый в этой ста-
04: . sep:index тье), важно знать не только достоинства и потенциальные
05: . sep:index
06: . toc:index возможности, но и недостатки подхода. Итак, какие есть
07: . . toc-yes:index недостатки? Какие из них можно преодолеть, а какие –
08: . . . index-head:index
09: . . toc-no:about нельзя?
10: . . . about-url:about
11: . . . about-head:about
12: . . toc-no:contact Относительно работы обработчика
13: . . . contact-url:contact Приведённая здесь реализация практически не содержит
14: . . . contact-head:contact
15: . index-head:index механизмов обработки нештатных ситуаций. Естественно,
16: . index-url-print:index было бы полезно добавить следующее:
17: . index-text:index
18: . sep:index ! Конечно, наш код допускает множество очевидных кос-
19: . body-close:index метических усовершенствований: ограничение глубины
рекурсии и защиту от зацикливания, проверку правиль-
Если вы внимательно читали весь предыдущий текст, ности сценария сборки... Но сосредоточимся на более
то он будет вам знаком и понятен. Если же у вас осталось существенных моментах.
некоторое недопонимание, то он поможет разобраться. ! Было бы полезно реализовать проверку правильности
конструкций, описывающих имя файла в точках встав-
Результаты ки. Реакция программы на нештатные ситуации типа
вложенных скобок или множественных двоеточий дол-
Достижения жна быть разумна.
Итак, что нам удалось сделать? Перечислю кратко основ- ! Программа должна корректно себя вести, если требуе-
ное. мый файл не существует. Как? Решать вам. Может быть,
! Мы написали и успешно применили обработчик шабло- вы предпочтёте аварийную остановку (сейчас сделано
нов, синтаксис которых настолько прост, что может быть именно так) или захотите, чтобы эта ситуация была бы
освоен даже абсолютно не знакомым с программиро- проигнорирована.
ванием человеком, каковыми обычно являются веб-вер-
стальщики и веб-дизайнеры. Вместе с тем, наш аппарат Возможно, было бы полезно реализовать поиск вход-
позволяет весьма гибко манипулировать шаблонами. ных файлов по нескольким директориям, организовать об-
работку переменной среды, аналогичной PATH, или вынес-
Даже в нашем маленьком примере вставка шаблона ти информацию о возможных путях к файлам в сценарий
index-head происходит семь раз. Это значит, что если бы сборки.

80
web
Плодотворной может оказаться идея «аварийного шаб- после обработки может превратиться в:
лона», который вставляется в те точки, которые не были
корректно обработаны. <a href="<!— /file 'url' —>index.html<!— file 'url'/ —>">
! Было бы очень уместно сделать кэширование считан-
ных файлов. Тем более, что это совсем не трудно. что, естественно, не будет работать, как:
! После того как вы сделаете кэширование, можно очень
легко сделать предопределённые шаблоны. Например, <a href="index.html">
шаблон DATE может содержать дату сборки.
! Очень полезной была бы и возможность регламентиро- ! Процесс сборки, на первый взгляд, напоминает работу
вать дополнительные обработки (или не обработки) фай- утилиты make. Возникает искушение реализовать не-
лов. Например, можно сделать, чтобы файлы с расши- что подобное, избежав ненужных действий при внесе-
рениями .file не обрабатывались как шаблоны, а их со- нии в шаблоны небольших изменений. Это тоже трудно
держимое вставлялось «как есть». И наоборот, файлы сделать, так как для определения того, какие файлы
с расширениями .quot проходили бы дополнительную нуждаются в повторной обработке, надо знать всё «де-
обработку механизмом HTML-квотирования, заменяю- рево сборки» – все зависимости файлов. В явном виде
щим «&» на «&amp;» и так далее. Здесь тоже возможны они нигде не содержатся. Так что, если не сборка, то
варианты: должен ли, например, подвергаться квотиро- хотя бы просмотр всех файлов-шаблонов мне представ-
ванию шаблон, вставляемый в квотируемый шаблон? ляется неизбежным.
! Было бы заманчиво хранить информацию с разными ! Неразумным кажется и то, что мы всегда собираем все
именами в одном файле, чтобы не заводить множество файлы проекта. Но отказ от этой концепции потребует
маленьких (как это получилось у нас с файлами заго- усложнения синтаксиса командной строки. Или нам при-
ловков и адресов). Это, правда, потребует некоторого дётся придумать какой-то другой способ указать, что
усложнения «адресации» данных. Обойтись просто име- именно нам надо. Мой опыт показывает, что веб-дизай-
нем файла, как сделали мы, будет уже нельзя. Все ли в неры этим не пользуются, предпочитая подождать пол-
вашей команде одобрят подобные нововведения? секунды. Иногда я думаю, что они в чём-то правы. Кро-
ме того, появление подобной возможности резко сузит
Относительно загрузки на сервер возможности создания предопределённых шаблонов, о
Я обещал вернуться к вопросам загрузки. Действительно, которых говорилось выше.
идея собрать все документы одним махом весьма заман-
чива, пока вы не столкнётесь с необходимостью загрузить Усовершенствования, которые не следует
их на сервер. Что здесь можно сказать? делать
Во-первых, для части администраторов эта проблема И наконец, я хотел бы обозначить ряд усовершенствова-
не будет так остра, потому что большинство изменений ний, которые, на мой взгляд, не следует вносить, несмотря
всё-таки не будут затрагивать все документы и, конечно, на их кажущуюся необходимость и заманчивую простоту.
не будут производиться слишком часто (иначе вам дей- ! Вам может показаться, что не хватает возможности под-
ствительно следует выбрать для сопровождения вашего ставлять не файл, а сам параметр сборки. Задумайтесь,
ресурса другой инструментарий, реализующий on-line- перед тем как реализовывать и эксплуатировать эту
сборку). идею! Поверьте, на этой дороге вы не найдёте счастья.
Во-вторых, если вам придётся всё-таки решать эту за- У информации должно быть имя и должно быть значе-
дачу, то у вас будет по крайней мере два пути решения. ние, нельзя объединять эти две материи.
Можно написать CGI-сценарий, принимающий файлы в ! Не следует усложнять функциональность – вы потеряе-
сжатом виде и распаковывающий их на сервере. Так вы те прозрачность и читаемость и усложните код, внедрён-
сократите трафик в несколько раз. С другой стороны, мож- ный в ваши шаблоны.
но написать сценарий, производящий сборку и размеще-
ние на сервере, а передавать ему надо будет только шаб- Если же вам понадобились циклы или условные пере-
лоны. Это может очень сильно помочь сократить трафик, ходы, лучше воспользуйтесь готовым решением, трезво
но создание такого механизма потребует от вас нешуточ- взвесив все «за» и «против». Вероятно, новый инструмент
ных усилий по обеспечению безопасности. потребует от вас установки сервера на машину дизайнера
(или на все машины дизайнеров), не исключено, что ваш
Усовершенствования, которые сложно сделать дизайнер может воспринять в штыки всё это богатство воз-
! Когда начинаешь заботиться о «самодокументируемо- можностей.
сти» подобного сборщика, может появиться мысль: пусть В любом случае это будет другой обработчик шаблонов
он дополняет документы HTML-комментариями. Сделать и уже совсем другая история.
это корректно гораздо сложнее, чем кажется на первый
взгляд. Вы видели сами, что мы вставляем шаблоны и
внутрь тегов. Если включить аппарат автоматической
вставки комментариев, то код:

<a href="(## url ##)">

№8(21), август 2004 81


образование

АВТОМАТИЗАЦИЯ ПРОЦЕССОВ В СЕТИ


ИВАН КОРОБКО
В повседневной работе системному администратору необ- WIN.INI. При установке любого приложения все его настрой-
ходимо многократно выполнять одни и те же рутинные дей- ки сохранялись в одном из этих файлов. Приложения
ствия. Для облегчения собственного бытия имеет смысл ав- пользовались небольшим количеством параметров. Глав-
томатизировать следующие процессы: управление сетевы- ная причина – размер INI-файла, размер которого не долж-
ми ресурсами (автоматическое отключение/подключение ны превышать 64 Кб. Чтобы обойти эти ограничения, для
сетевых принтеров и дисков в зависимости от членства каждой программы создавался свой INI-файл.
пользователей в соответствующей группе безопасности), Со временем из-за большого количества конфигураци-
конфигурирование рабочей станции, сбор информации об онных файлов производительность операционной системы
аппаратном и программном обеспечении рабочих станций значительно понизилась. В 1993 году была создана опера-
(решение задачи инвентаризации). ционная система Microsoft Windows NT, в которой множе-
Так или иначе, решение задач этого круга сводится к ство INI-файлов было заменено единой базой данных –
чтению/записи данных реестра. Управление реестром осу- реестром.
ществляется с помощью нескольких инструментов, которые С точки зрения файловой системы реестр представля-
будут рассмотрены в настоящей статье. Основными из них ет собой файл с расширением DAT. Для Windows NT/200x
являются сценарии регистрации пользователей в сети (сце- реестр хранится в файле NTUSER.DAT, который находится
нарии загрузки или скрипты) и групповые политики. в каталоге %Windir%\Profiles1.
Системный администратор не может реализовать весь В Windows 9x реестр состоит из двух файлов: USER.DAT
желаемый функционал, используя только сценарии заг- и SYSTEM.DAT, которые хранятся в каталоге %Windir%.
рузки, поскольку с их помощью невозможно безопасно вы- USER.DAT содержит настройки индивидуального пользо-
полнить изменения в ветвях реестра HKLM и HKU, так как вателя, а SYSTEM.DAT – настройки компьютера.
это только сценарии, работающие с правами админист- Реестры Windows 9x, NT, 2000 несовместимы друг с
ратора. другом, однако идея построения реестров едина.
Однако, примененяя групповые политики, стартующие
каждый раз при регистрации пользователя в сети, исполь- Ветви реестра
зуя административные шаблоны на основе реестра, можно Реестр состоит из разделов верхнего уровня, называемых
безопасно внести изменения в ветви реестра HKLM и HKU. кустами (hives):
Политики распространяются на пользователей, являю- ! HKEY_CLASSES_ROOT (HKCR);
щихся членами группы. ! HKEY_CURRENT_USER (HKCU);
Системный администратор может самостоятельно со- ! HKEY_LOCAL_MACHINE (HKLM);
здать файлы – административные шаблоны, содержащие ! HKEY_USER (HKU);
групповые политики (файл с расширением ADM), и приме- ! HKEY_CURRENT_CONFIG (HKCC).
нять их к пользователям, входящим в группу/домен (domain),
подразделение (Organization Unit, OU). Структура реестра такова: в каждом из кустов находят-
Таким образом, групповые политики и сценарии регис- ся ключи, в которых содержатся параметры, имеющие зна-
трации пользователей в сети органично дополняют друг чения.
друга. Далее рассмотрим подробнее круг вопросов, касаю- В разделе HKLM находится информация об аппаратном
щихся создания и применения сценариев загрузки и груп- и программном обеспечении, а также сведения о системе
повых политик, но сначала немного о реестре. безопасности. Этот раздел является одним из самых боль-
ших. Раздел HKCR является виртуальной ссылкой на раз-
Реестр дел HKLM\Software\Classes. В нем содержатся сведения обо
Это иерархическая база данных, содержащая настройки всех расширениях файлов, определениях типов, ярлыках,
аппаратного и программного обеспечения компьютера. Для привязке, классах идентификаторов и т. д.
получения высокой скорости доступа к записям реестра ин- Раздел HKU включает в себя настройки пользователя
формация в нем хранится в двоичном формате, а сам ре- по умолчанию, в которые входят описания переменных сре-
естр состоит из нескольких файлов. ды, цветовых схем, шрифтов, сетевых настроек и т. д. Во
В Microsoft Windows 3.x все настройки программного время регистрации нового пользователя на рабочей стан-
обеспечения располагались в файлах инициализации, ко- ции, на жестком диске для него создается новый профиль.
торые имели расширение INI. Вся конфигурационная ин- Настройки, содержащиеся в профиле, копируются из кус-
формация располагалась в двух файлах: SYSTEM.INI и та HKU.

1
В Microsoft Windows принято использовать переменные среды. %WinDir% содержит полный путь к каталогу, в кото-
ром установлена ОС, например, C:\Windows.

84
образование
Изменения в HKU и HKLM можно сделать только с по- шения затрат на администрирование и конфигурирование
мощью утилиты REGEDT32.EXE в том случае, если у вас рабочих станций.
ОС Windows 2000, и REGEDIT.EXE – если Windows XP. Перечислим некоторые задачи, которые могут быть ре-
Пользователь, от имени которого запускаются эти утили- шены с помощью сценария загрузки:
ты, должен обладать правами системного администрато- ! Инвентаризация. Включает в себя сбор информации о
ра. регистрирующемся в сети пользователе, рабочей стан-
Раздел HKCU содержит сведения о текущем пользова- ции и формировании файла отчета.
теле и имеет название, соответствующее значению иден- ! Автоматическое подключение сетевых ресурсов: прин-
тификатора безопасности (SID) данного пользователя. Каж- теров и дисков.
дый раз при перезагрузке компьютера HKCU создается ! Автоматическое конфигурирование рабочих станций.
заново. ! Обеспечение интерактивности работы скрипта. В ходе
Раздел HKCC является ссылкой на текущий профиль выполнения скрипта на экране отображается соответ-
оборудования, хранящийся в HKLM. С помощью профиля ствующая информация.
оборудования определяют список устройств, драйвера ко-
торых будут подгружены в данном сеансе работы пользо- После его выполнения пользователь может ознакомить-
вателя. Профили изначально предназначены для перенос- ся с подключенными ему ресурсами, информацией о его
ных компьютеров. рабочей станции и т. д.
Раздел HKDD (HKEY_DYN_DATA) не хранится в реест- Существует несколько языков, предназначенных для со-
ре, а динамически создается при загрузке операционной здания сценариев загрузки. Среди них стандартными явля-
системы. В нем содержатся сведения о самонастраиваю- ются сценарии на базе командной строки (файлы с расши-
щихся устройствах (Plag-and-Play). рением BAT, PIF), Windows Script Host (WSH), Microsoft Java
Как и любая база данных, реестр поддерживает несколь- Script (JScript), Microsoft Visual Basic Script Edition (VBScript).
ко типов данных: Существуют также языки, специально разработанные
Òàáëèöà 1 для создания скриптов, такие как KIXtart, AutoIT, CLRScript,
WinBatch.
Из всех этих языков рекомендуется остановить свой
выбор на языке KIXtart2, поскольку, обладая огромными
возможностями, он распространяется бесплатно. KIXtart
4.21 поддерживает 48 команд, 56 макросов-функций, бо-
лее 100 функций и обладает следующим функционалом:
! вывод информации в виде диалоговых сообщений;
! подключение сетевых ресурсов;
! чтение информации из входного потока;
! расширенная поддержка редактирования реестра;
! поддержка INI-файлов;
! расширенная поддержка операций со строками и мас-
сивами;
! сбор информации о пользователе и рабочей станции;
! поддержка OLE-объектов;
! операции с файлами и каталогами;
! создание ярлыков Windows.

Windows 9x поддерживает следующие типы параметров: Необходимо отметить, что сценарии, созданные на
REG_BINARY и REG_DWORD, REG_SZ и REG_NONE. VBScript, Jscript, могут быть легко переписаны на KIXtart.
Групповые политики, описанные в ADM-файлах, могут Рассмотрим подробнее каждую из задач, решаемую
производить операции только со следующими типами дан- скриптом.
ных : REG_SZ, REG_EXPAND_SZ, REG_DWORD.
Инвентаризация
Сценарий регистрации Решение задачи инвентаризации состоит из нескольких
пользователей в сети частей – сбора, записи на носитель в определенном фор-
Основной задачей сценария регистрации пользователей в мате и обработки информации.
сети, далее скрипта, является автоматизация процессов, Так или иначе, информация черпается либо из BIOS с
связанных с подключением рабочих станций к сети, умень- помощью программы, либо, что происходит чаще всего, из

2
KIXtart (http://kixtart.org) – интерпретируемый язык программирования, разработанный в 1991 году для создания сце-
нариев загрузки. Простота, скорость и отсутствие конкурентов быстро сделали его популярным среди администрато-
ров. KIXtart является бесплатным и поставляется вместе с Microsoft Resoure Kit. В настоящее время используется
KIXtart ver 4.2х, который поддерживается Microsoft Windows Server 2003, Microsoft Windows XP, Microsoft Windows 2000,
Microsoft Windows NT 3.x/4.x, всеми версиями Microsoft Windows 95, Microsoft® Windows 98, Microsoft Windows Millennium.

№8(21), август 2004 85


образование
реестра с помощью стандартных средств ОС Window. Ре- диск W. Для членов групп departament1, departament3 под-
шение задачи инвентаризации c помощью WMI было опи- ключается ресурс по адресу: \\second\work\ departament1,
сано в [1]. для departament2 – \\second\work\departament2.
Сценарий, обеспечивающий автоматическое управле-
Подключение сетевых ресурсов ние подключением сетевых дисков работает по следующей
Сетевыми ресурсами являются сетевые диски и принтеры. схеме:
! Чистка локального кэша на рабочей станции, содержащего
Подключение сетевых принтеров список групп, в которые входит пользователь. Удаление
Вопрос установки, настройки и автоматического подклю- ветви реестра HKEY_CURRENT_USER\Software\KiXtart.
чения сетевых принтеров был подробно освещен в статьях ! Чтение параметрического файла. Данные рекомендует-
[2], [3]. ся помещать в соответствующие массивы. Схема чте-
ния конфигурационного файла приведена на рис. 1.
Подключение сетевых дисков ! Отключение всех доступных сетевых дисков.
Сценарий загрузки осуществляет подключение сетевых ! Подключение сетевых дисков, на которые данный поль-
дисков пользователям в зависимости от их членства в груп- зователь имеет права.
пах аналогично подключению сетевых принтеров. Главная ! Вывод на экран статистики о подключенных сетевых дис-
особенность данного сценария заключается в том, что в нем ках.
реализован механизм подключения различных ресурсов на
одну и ту же букву. Необходимо строго следить, чтобы член-
ства пользователей в группах не пересекались. Если это
произойдет, что часть необходимых ресурсов не будет под-
ключена.
Рассмотрим содержимое конфигурационного файла,
который представляет собой текстовый файл с произволь-
ным расширением, например INI.
В файле в квадратных скобках перечислены названия
разделов, которые включают в себя букву, на которую бу-
дут монтироваться ресурс и его порядковый номер, присва-
ивающийся для обеспечения возможности подключать на
одну и ту же букву разные ресурсы. Каждый раздел содер-
жит пять параметров: название сервера (SERVER), путь к
сетевой папке (SHARE), группы безопасности, членам ко-
торых будет подключаться ресурс (ACCESSGROUP1 и
ACCESSGROUP2), описание ресурса (DESCRIPTION). При- Ðèñóíîê 1
мер файла приведен ниже:
Автоматическое конфигурирование
Ïðèìåð 1 рабочей станции
[L1] Поскольку скрипт выполняется от имени пользователя, ко-
торый не обладает правами системного администратора,
SERVER=Main
SHARE=Consultant то изменения могут быть внесены только в ветвь HKCU. В
ACCESSGROUP1=everyone этом разделе хранятся сведения о текущем зарегистриро-
ACCESSGROUP2=Âñå
DESCRIPTION="Êîíñóëüòàíò+" ванном пользователе, и он имеет название, соответствую-
щее значению идентификатора безопасности (SID) текуще-
[W1]
го пользователя. Каждый раз при перезагрузке компьюте-
SERVER=Second ра раздел создается заново на основе данных, считанных
SHARE=work\department1
ACCESSGROUP1=department1 из HKU. Автоматическое конфигурирование ветви HKU вы-
ACCESSGROUP2=department3 полняется с помощью групповых политик и будет рассмот-
DESCRIPTION="Ðåñóðñû îòäåëà 1"
ренно позже. Изменения в ветви HKCU осуществляется с
[W2] помощью скрипта.
SERVER=Second Ветвь HKCU не является точной копией HKU. Приведем
SHARE=work\department2 наглядный пример. После запуска Windows 2k пользова-
ACCESSGROUP1=department2
ACCESSGROUP2= тель видит сообщение, в котором предлагается одновре-
DESCRIPTION="Ðåñóðñû îòäåëà 2" менно нажать CTRL+ALT+DEL. Нажав эту комбинацию кла-
виш, ему предлагается ввести имя учетной записи, пароль.
Таким образом, на основе данных, прочитанных сцена- Информация о языковых настройках в этом диалоговом
рием из примера, всем пользователям будет подключен окне, а именно язык по умолчанию и комбинация клавиш, с
«Консультант+» на букву «L», находящийся по пути \\main\ помощью которой осуществляется переключение между
consultant. Пользователям, являющимся членами групп раскладками клавиатуры, хранится в ветви HKEY_USERS\
departament1, departament2, departament3, будет подключен .DEFAULT\Keyboard Layout.

86
образование
Ïðèìåð 2. Ïî óìîë÷àíèþ óñòàíîâëåí àíãëèéñêèé ÿçûê, ïåðå- боты скрипта. Основным недостатком этого метода визуа-
êëþ÷åíèå ìåæäó ðàñêëàäêàìè êëàâèàòóð îñóùåñòâëÿåòñÿ íàæà- лизации является полное отсутствие интерактивности ра-
òèåì CTRL+SHIFT.
боты сценария.
Windows Registry Editor Version 5.00

[HKEY_USERS\.DEFAULT\Keyboard Layout\Preload] Визуализация скрипта с помощью сторонней


"1"="00000409" надстройки KIXTart в виде DLL-библиотеки
"2"="00000419"
Визуализация и интерактивность работы скрипта реали-
[HKEY_USERS\.DEFAULT\Keyboard Layout\Toggle] зуются с помощью специально созданной для этих целей
"Hotkey"="2"
надстройкой – KIXForms 3.2 или KIXGui 1.1.Обе програм-
Однако аналогичная ветвь в ветви HKСU (см. пример 3) мы можно загрузить с сайта http://www.kixtart.org. У этих
отвечает за управление языковыми настройками рабочего программ есть только один недостаток: для корректной
стола пользователя. В данном случае нет никакой взаимо- работы визуализационной части необходимо на рабочей
связи между настройками ветвей HKU и HKCU. станции зарегистрировать соответствующую DLL-библио-
теку. Для регистрации этой библиотеки необходимо об-
Ïðèìåð 3. Ïî óìîë÷àíèþ óñòàíîâëåí àíãëèéñêèé ÿçûê, ïåðå- ладать правами администратора. Конечно, можно создать
êëþ÷åíèå ìåæäó ðàñêëàäêàìè êëàâèàòóð îñóùåñòâëÿåòñÿ íàæà-
òèåì CTRL+SHIFT. MSI-архив, который будет централизованно распростра-
няться по сети с помощью групповых политик, но это не-
Windows Registry Editor Version 5.00
удобно. Визуализировать работу сценария, таким обра-
[HKEY_CURRENT_USER\Keyboard Layout\Preload] зом, выгодно только в небольших сетях с маленьким ко-
"1"="00000409"
"2"="00000419" личеством рабочих станций.
[HKEY_CURRENT_USER\Keyboard Layout\Toggle]
"Hotkey"="2" Визуализация работы скрипта c помощью
сторонней утилиты, передающей параметры
В качестве примера приведем таблицу, в которой опи- из KIXTart в HTML-файл
саны некоторые ключи и соответствующие им парамет- Этот способ мне кажется наиболее оптимальным для реа-
ры, которые можно изменять на всех рабочих станциях лизации визуализации и интерактивности работы скрипта
домена каждый раз во время регистрации пользователей в крупных сетях, поскольку утилита самодостаточна и пред-
в сети с помощью скрипта. Приведенный список краток. ставляет собой файл с расширением EXE, который реко-
«В специализированной литературе» и в Интернете чита- мендуется располагать в каталоге Netlogon, вместе со
тель сможет найти множество советов по настройке реес- скриптом. В качестве такой утилиты лучше использовать
тра. KixWin 1.1 (http://www.kixtart.org). И вызывать ее из скрипта
Òàáëèöà 2 с набором параметров.
Затем она передает эти параметры в DHTML-файл. По-
скольку файл пишет данные в файл, прежде чем что-либо
отобразить на экране, то необходимо обеспечить правами
возможность записи данных на диск сервера.
Раздробление скрипта на две части является основным
недостатком данного варианта. DHTML-файл вместе с со-
путствующими ему файлами (GIF, JPEG, CSS) рекоменду-
ется располагать в скрытой сетевой папке. DHTML-файл
содержит в себе сценарии, созданные с помощью VBScript
или JScript.
Приведем синтаксис утилиты и фрагмент DHTML-файла:
Обеспечение интерактивности работы скрипта
Сценарии на языке KIXTart можно визуализировать по край- kixwin "dialog" ["arguments"] ["options"]
ней мере тремя способами:
! с помощью стандартных диалоговых окон; Описание параметров:
! с помощью сторонней надстройки KIXTart в виде DLL- ! “dialog” – строка, содержащая URL, указывающий на
библиотеки; HTML-документ.
! c помощью сторонней утилиты, передающей парамет- ! “arguments” – строка, содержащая параметры, переда-
ры из KIXTart в HTML-файл. ваемые из KIX в HTML. Для разделения параметров в
HTML-файле используют строку window.dialogArguments.
Рассмотрим все три способа. split(«;»). В данном примере разделителем параметров
является «;».
Визуализация работы скрипта с помощью ! “style” – строка, которая определяет оформление диа-
стандартных диалоговых окон логового окна. Используются один или несколько из сле-
Пользователю выводится информация на экран в виде со- дующих параметров стиля:
общения. Этот метод рекомендуется использовать в нача-
ле сценария, чтобы уведомить пользователя о начале ра- dialogHeight:sHeight

№8(21), август 2004 87


образование
dialogLeft:sXPos Ïðèìåð 6
dialogTop:sYPos
dialogWidth:sWidth @ECHO OFF
center:{ yes | no | 1 | 0 | on | off }
dialogHide:{ yes | no | 1 | 0 | on | off } if c:\%os%==c:\ goto win9x
edge:{ sunken | raised } if not c:\%os%==c:\ goto winnt
help:{ yes | no | 1 | 0 | on | off }
resizable:{ yes | no | 1 | 0 | on | off } :winnt
scroll:{ yes | no | 1 | 0 | on | off } start /wait Kix32.exe Script.kix
status:{ yes | no | 1 | 0 | on | off } goto kix
unadorned:{ yes | no | 1 | 0 | on | off }
:win9x
Логическое разделение передаваемых параметров осу- copy %0\..\win9x\*.dll c:\windows\system /y
ществляется с помощью заранее оговоренного символа. %0\..\Kix32.exe %0\..\Script.kix
goto kix
DHTML возвращает в KIX код ошибки после обработки кода
в виде целого числа макросу @ERROR в случае успешно- :kix
@echo End Of Batch File
го завершения операции @ERROR=0.
Передача данных с помощью утилиты KIXWIN осуще-
ствляется с помощью сценария загрузки следующим обра-
зом:

Ïðèìåð 4

shell '%0/../kixwin.exe $html "$system_info ^ $hardware_info ↵


^ Óñòàíîâëåííûå ïðîãðàììû: $en $prog ↵
^ Ïîäêëþ÷åííûå ñåòåâûå äèñêè:$en $n1 ↵
^ Ïîäêëþ÷åííûå ñåòåâûå ïðèíòåðû:$en ↵
$n2" "scroll:off;resizable:on"'

Как отмечалось ранее, параметры передаются DHTML-


странице, содержащей вставки на VBScript. Наличие вста-
вок позволяет сделать DHTML-страницу интерактивной
для пользователя. Чтение параметров, передаваемых из
сценария загрузки, осуществляется по следующей схе-
ме:

Ïðèìåð 5


<Script Language="JavaScript">
var argv;
argv = window.dialogArguments.split("^");
document.all.parametr0 = argv[0];
</Script>

Ðèñóíîê 2
Пояснения к синтаксису файла START.BAT:
Внедрение скрипта в эксплуатацию ! Принцип определения операционной системы основан
Скрипт выполняется каждый раз при регистрации пользо- на том, что в Win9x отсутствует переменная окружения
вателя в сети, если он указан в разделе Profile свойствах %os%; В Windows 2k переменная %os%=WindowsNT.
пользователя (см. рис. 2) службы Active Directory: Users and ! С помощью строки «start /wait Kix32.exe Script.kix» добива-
Computers. ются последовательной загрузки – сначала скрипт, затем
Для автоматизации установки KIXtart на рабочих стан- рабочий стол и т. д., а не одновременной. То есть на вре-
циях домена предлагается следующее: в папку Netlogon мя выполнения скрипта многозадачность «отключается».
поместить файлы: ! Это делается для того, чтобы до окончания действия
! KIX32.EXE; скрипта дальнейшая загрузка операционной системы не
! SCRIPT.KIX; производилась.
! START.BAT ! Для корректной работы Win9x в качестве пути к файлу
! KIXWIN.EXE необходимо указывать «%0\..\filename.ext». Windows XP
! подкаталог Win9x, содержит файлы KX16.DLL и KX32.DLL не воспринимает относительного пути «%0/./», поэтому
для Windows семейства 2k необходимо указать только
В скрытую сетевую папку скопировать DHTML- файл и имя файла, который находится в папке Netlogon.
все сопутствующие ему файлы.
В ходе выполнения файла START.BAT определяется тип На время выполнения скрипта необходимо скрыть CMD-
операционной системы, установленной на рабочей станции, панель, в которой выполняется cкрипт, и приостановить заг-
и запускается скрипт. В зависимости от версии ОС проис- рузку рабочего стола до окончания всего скрипта. Этого
ходит копирование файлов, необходимых для поддержки результата добиваются с помощью групповой политики,
KIX этой операционной системой. распространяющейся на домен («Default Domain Controllers

88
образование
Policy»). В разделе групповой политики «User Configuration»
необходимо соответственно включить «Run legacy logon
script synhronously» (Запускать сценарий загрузки синхрон-
но) и «Run legasy script hidden» (Запускать сценарий скры-
то). Для этого необходимо проделать следующее:
! Зарегистрироваться на сервере с помощью учетной за-
писи, имеющей административные права.
! Загрузить в Active Directory Users and Computers (Start –
Programs – Administrative Tools) и войти в свойства кон-
троллера домена (см. рис. 3). Перейти во вкладку вклад-
ку «Group Policy» и зарузить «Default Dоmain Policy» (см.
рис. 4).
Ðèñóíîê 5

Групповые политики
Групповые политики (Group Policy, GP) представляют со-
бой средство, обеспечивающее централизованное управ-
ление настройками рабочих станций, профилями пользо-
вателей. С их помощью определяют поведение операцион-
ной системы, рабочего стола, безопасности ОС, выключе-
ния компьютера, приложений и т. д.
Реализации всех возможностей групповых политик до-
биваются их совместным использованием с Active Directory
(AD) при условии, что в качестве рабочих станций исполь-
зуется ОС Windows 2000. Политики могут быть применены
к следующим объектам AD: сайт (site), домен (domain), под-
разделение (Organization Unit, OU). Дополнительно группо-
вые политики могут быть применены к таким объектам, как
Ðèñóíîê 3 группа безопасности, соответственно к пользователям, вхо-
дящим в эту группу.
Групповые политики включают в себя следующие ком-
поненты:
Òàáëèöà 3

С помощью политик, как говорилось ранее, можно кор-


ректировать только ветви реестра HKLM и HKU. В консо-
ли групповых политик (см. рис. 5), вызываемой с помо-
щью команды GPEDIT.MSC, все политики логически де-
лятся на две час ти: Computer Configuration и User
Configuration. В разделе Computer Configuration сосредо-
точены политики, посредством которых изменяется ветвь
реестра HKLM, в разделе User Configuration соответствен-
Ðèñóíîê 4 но HKU. Информация о параметрах и конфигурации груп-
! В загруженной групповой политике (Default Domain повых политик сосредоточена в HKLM\Software\Policies
Policy) необходимо в «User Configuration» (настройках (предпочтительное расположение) или HKLM\Software\
пользователя) войти в «Administrative Templates» (адми- Microsoft\Windows\CurrentVersion\Policies для раздела
нистративные шаблоны). Computer Configration; в HKU\Software\Policies (предпочти-
! Там выбрать раздел «System» (система), вкладку «logon/ тельное расположение) или HKU\Software\Microsoft\
logoff» (вход/выход) и включить раннее оговоренные Windows\CurrentVersion\Policies для раздела User
политики (см. рис. 5). Configration.

№8(21), август 2004 89


образование
Административные шаблоны. Синтаксис пользовательская (User Configuration). В одном файле до-
Административные шаблоны представляют собой тексто- пускается многократное использование ключевого слова
вые файлы с расширением ADM. По умолчанию ADM-фай- CLASS. Приведем пример синтаксиса элемента CLASS:
лы находятся в каталоге %WinDir%\INF. Кратко рассмотрим
возможности языка, с помощью которого создаются поли- CLASS Name
тики безопасности.
Синтаксис языка включает в себя следующие ключе- где Name может иметь одно из двух значений: USER или
вые компоненты: MACHINE. Значение USER определяет, что политика
! Комментарии пользовательская. Изменения будут вноситься в реестр в
! Строки ветви HKU, настройку политики следует осуществлять в
! CLASS разделе «User Configuration\Administrative Templates\»; Зна-
! CATEGORY чение MACHINE соответственно определяет, что политика
! POLICY компьютерная. Изменения будут вноситься в реестр в вет-
ви HKLM, настройку политики следует осуществлять в раз-
Комментарии деле «Computer Configuration\Administrative Templates\».
Комментарии полезно использовать для документирования
содержимого создаваемого шаблона. Комментарии предва- CATEGORY
ряют точкой с запятой (;) или двумя прямыми слэшами (//). После определения класса политики необходимо опреде-
Комментарии также можно помещать в конце строки. При- лить местоположение в подпапке «Administrative Templates».
ведем пример комментария: Все, что делает ключевое слово CATEGORY, – это создает
подпапку. В категории может быть ноль и более политик.
Ïðèìåð 7 Те из них, которые не содержат политик, содержат, как пра-
; Ýòî êîììåíòàðèé вило, одну или несколько подкатегорий. После определе-
// È ýòî êîììåíòàðèé ния категории ее необходимо закрыть с помощью END
CLASS USER // Îïðåäåëåíèå êëàññà USER, îòâå÷àþùåãî CATEGORY.
// çà ïîëüçîâàòåëüñêèå íàñòðîéêè Приведем пример синтаксиса элемента CATEGORY:
CLASS MACHINE // Îïðåäåëåíèå êëàññà MACHINE, îòâå÷àþùåãî
// çà îáùåêîìïüþòåðíûå íàñòðîéêè
CATEGORY Name
KEYMAME SubKey
………………………..
Строки END CATEGORY
Все возможные словесные описания – описание политики,
названия разделов, параметров и т. д. рекомендуется рас- где:
полагать в специально предназначенном разделе [strings]. ! Name – это имя папки, которое будет отображаться в
В тексте политики перед переменной, ссылающейся на редакторе Group Policy. Используйте строковую пере-
текст в разделе [strings], ставят два восклицательных зна- мену или строку, заключенную в кавычках.
ка (!!). Каждая строка в разделе [strings] имеет формат ! SubKey – является необязательным параметром. Инфор-
name=«строка». Приведем два равнозначных примера. В мация о параметрах и конфигурации групповых поли-
первом примере не будем использовать преимущества строк. тик сосредоточена в HKLM\Software\Policies (предпочти-
тельное расположение) или HKLM\Software\Microsoft\
Ïðèìåð 8à) Windows\CurrentVersion\Policies для раздела Computer
CLASS Machine Configration; в HKU\Software\Policies (предпочтительное
POLICY "Ïðèìåð ïîëèòèêè" расположение) или HKU\Software\Microsoft\Windows\
………………
END POLICY CurrentVersion\Policies для раздела User Configration. Не
используйте в пути корневой ключ (HKLM, HKU), по-
Ïðèìåð 8á)
скольку он уже описан ключевым словом CLASS. Если
CLASS Machine путь содержит пробелы, заключайте его в кавычки.
POLICY !!Pname
………………
END POLICY Ïðèìåð 9
[Strings]
Pname="Ïðèìåð ïîëèòèêè" CLASS USER
CATEGORY "POLICIES"
CATEGORY !!SubPol1
Использование строк позволяет создавать шаблоны KEYNAME "Software\Policies\SubPol1"
групповых политик, что снижает трудозатраты и ускоряет …………………………
END CATEGORY
процесс создание политик.
CATEGORY !!SubPol2
KEYNAME "Software\Policies\SubPol1"
CLASS …………………………
Первым элементом в файле, содержащем шаблон группо- END CATEGORY
END CATEGORY
вой политики, является ключевое слово CLASS, которое
определяет, к какому типу относится описываемая ниже [strings]
SubPol1="Policy1"
политика: компьютерная (Computer Configuration) или SubPol2="Policy2"

90
образование
удаления политики, то необходимо использовать клю-
чевые слова VALUEON и VALUEOFF, которые следуют
непосредственно за словом VALUENAME:

VALUEON [NUMERIC] VValue


VALUEOFF [NUMERIC] VValue

По умолчанию тип данных значение VValue REG_SZ.


Если после ключевого слово указано NUMERIC, то тип
данных значение VValue – REG_DWORD.

Программирование интерфейса
Ðèñóíîê 6 политик безопасности
В разделе CATEGORY могут быть использованы следу- Программирование интерфейса групповых политик сводит-
ющие ключевые слова: ся к созданию раскрывающихся списков, флажков, тексто-
! CATEGORY вых полей и т. д. Рассмотрим по порядку процедуры созда-
! END ния всех элементов интерфейса.
! KEYNAME 1) В первом варианте, самом простом, нет никаких уп-
! POLICY равляющих элементов, кроме опции «Вкл/Выкл политику».
В файле политики записан ключ реестра и значение, име-
POLICY ющее тип REG_SZ или REG_DWORD, которое может ме-
Используйте ключевое слово POLICY, чтобы определить няться в зависимости от того, включена ли политика.
политику. В одной категории может быть включено несколь- Приведем пример, в котором при включенной политике
ко разделов POLICY, каждый из которых должен заканчи- устанавливает переключение раскладки клавиатуры в диа-
ваться инструкцией END POLICY. Приведем пример син- логовом окне регистрации пользователя в сети CTRL+SHIFT,
таксиса элемента POLICY: иначе – ALT+SHIFT.
За переключение раскладки языка отвечает параметр
POLICY Name «Hotkey», размещающийся в разделе «HKEY_USER\Keyboard
[KEYNAME SubKey]
EXPLAIN Help Layout\Toggle» и принимающий значение 2 (CTRL+SHIFT) или
VALUENAME Value 1 (ALT+SHIFT). Параметр «Hotkey» имеет тип данных
[PARTS]
………………. REG_SZ.
END POLICY Приступим к созданию политики. Поскольку необходи-
мо редактировать ветвь реестра HKU, то политика являет-
где: ся пользовательской и будет создана в разделе «User
! Name – это название политики, отображаемое в редак- Configuration\Administrative Templates\». Исходя из этого, по-
торе Group Policy. Используйте строковую переменную литика принадлежит к классу USER.
или строку, заключенную в кавычках. Параметр KEYNAME раздела CATEGORY, исходя из по-
! SubKey является необязательным параметром. Инфор- ставленной задачи, принимает значение «Keyboard Layout\
мация о параметрах и конфигурации групповых поли- Toggle». В разделе POLICY параметр VALUENAME – «Hotkey».
тик сосредоточена в HKLM\Software\Policies (предпочти- VALUEON – 2, а VALUEOFF – 1. Таким образом, политика
тельное расположение) или HKLM\Software\Microsoft\ выглядит следующим образом:
Windows\CurrentVersion\Policies для раздела Computer
Configration; в HKU\Software\Policies (предпочтительное Ïðèìåð 10
расположение) или HKU\Software\Microsoft\Windows\ CLASS USER
Current Version\Policies для раздела User Configration. Не CATEGORY "Admin Policies"
CATEGORY "Keyboard Toggle "
используйте в пути корневой ключ (HKLM, HKU), по- KEYNAME ".Default\Keyboard Layout\Toggle"
скольку он уже описан ключевым словом CLASS. Если POLICY "Toggle Policy"
EXPLAIN !!help
путь содержит пробелы, заключайте его в кавычки. Ко VALUENAME "Hotkey"
всем политикам применяется последнее ключевое сло- VALUEON 2
VALUEOFF 1
во KEYNAME. END POLICY
! Help – эта строка, содержимое которой редактор Group END CATEGORY
END CATEGORY
Policy отображает в разделе EXTENDED политики. Для [strings]
перевода каретки используйте «\n». help="Óïðàâëåíèå ïåðåêëþ÷åíèåì ðàñêëàäêè êëàâèàòóðû ïðè
ðåãèñòðàöèè ïîëüçîâàòåëÿ â ñåòè \n\n Ïðè âêëþ÷åííîé ïîëè-
! Value – каждая политика содержит ключевое слово òèêå ïåðåêëþ÷åíèå ðàñêëàäêè êëàâèàòóðû îñóùåñòâëÿåòñÿ ñ
VALUENAME, которое связывает с ней значение реест- ïîìîùüþ êîìáèíàöèè êëàâèø CTRL+SHIFT, ïðè âûêëþ÷åííîé –
ALT+SHIFT."
ра. По умолчанию редактор политик предполагает, что
это значение переменной типа REG_DWORD равно 1 2) Все остальные элементы интерфейса – флажки, вы-
(0х01), когда политика включена. Редактор политики падающие меню и т. д. можно реализовать только в разде-
удаляет значение, если политика выключена. В том слу- ле PART, являющемся членом раздела POLICY. Рассмот-
чае, если необходимо его сохранить в реестре после рим подробнее синтаксис раздела PART:

№8(21), август 2004 91


образование
PART Name Type ! Name – указывается название раздела, которое будет
Keywords отображено в консоли во время редактирования поли-
[KEYNAME Subkey]
[DEFAULT Default] тики. Если в названии раздела встречаются пробелы,
VALUENAME Name заключите его в кавычки.
END PART
! Value – название параметра, которое необходимо из-
где: менить.
! Name – указывается название раздела, которое будет ! Value1 – значение, задаваемое при установленном
отображено в консоли во время редактирования поли- флажке и включенной политике.
тики. ! Value2 – значение, задаваемое при снятом флажке и
! Type – может быть одним из следующих типов: включенной политике.
Òàáëèöà 4
COMBOBOX
Ключевое слово COMBOBOX добавляет в диалоговое окно
политики список с текстовым полем. Внутри COMBOBOX вклю-
чен обязательный блок SUGGESTIONS, заканчивающийся ин-
струкцией SUGGESTIONS. Внутри этого блока перечислены
элементы. Элементы разделяются пробелами, элементы, име-
ющие пробелы, помещаются в кавычки (см. синтаксис).

PART Name COMBOBOX


SUGGESTIONS
“Suggestion1” “Suggestion2” … “Suggestionm”
END SUGGESTIONS
[DEFAULT Default]
[EXPANDABLETEXT]
[MAXLENGHT] Max
[NOSORT]
[REQUIRED]
VALUENAME Value
END PART

где:
! Keywords – эта информация специфична для каждого ! DEFAULT – указывает значение списка по умолчанию.
из типов. Дополнительная информация приведена ниже. ! EXPANDABLETEXT – создает значение типа REG_
! Subkey – это необязательный подключ HKLM или HKU. EXPAND_SZ.
Не используйте в пути корневой ключ (HKLM, HKU), по- ! MAXLENGHT – указывает максимальную длину строки.
скольку он уже описан ключевым словом CLASS. Если ! NOSORT – отключает сортировку списка редактором по-
путь содержит пробелы, заключайте его в кавычки. литик.
! Defaults – это значение параметра по умолчанию. Когда ! REQUIRED – указывает обязательное значение.
администратор включает политику, то осуществляется ! Name – указывается название раздела, которое будет ото-
считывание параметров по умолчанию. бражено в консоли во время редактирования политики.
! Value – модифицируемое значение реестра. Тип и дан- ! Suggestions – список элементов, помещающихся в рас-
ные значения полностью зависят от типа параметра. крывающийся список. Все элементы, содержащие про-
белы, помещаются в кавычки. Элементы списка разде-
CHECKBOX ляются пробелами.
Ключевое слово CHECKBOX отображает флажок. По умол- ! Default – значение по умолчанию. Считывается при вклю-
чанию флажок сброшен. Если требуется, чтобы изначаль- чении политики.
но флажок был установлен, необходимо в теле раздела ! Max – максимальная длина данных значения.
PART указать ключевое слово DEFCHECKED. При установ- ! Value – модифицируемое значение реестра. По умолча-
ленном флажке в реестр записывается значение 1, если нию имеет тип данных REG_SZ.
сброшен – 0.
В реестре по умолчанию представлен значением типа DROPDOWNLIST
REG_SZ. Если необходимо записать данные в реестр в Ключевое слово DROPDOWNLIST добавляет в диалоговое
формате REG_DWORD, то необходимо после значений клю- окно политики раскрывающийся список. Внутри DROP
чевых слов VALUEON и VALUEOFF добавить NUMERIC. DOWNLIST включен обязательный блок ITEMLIST, закан-
Приведем синтаксис элемента CHECKBOX: чивающийся инструкцией END ITEMLIST. Внутри этого бло-
ка перечислены элементы раскрывающегося списка (см.
PART Name CHECKBOX синтаксис).
[DEFCHEKED]
VALUENAME Value Приведем синтаксис элемента DROPDOWNLIST:
VALUEON [NUMERIC] Value1
VALUEOFF [NUMERIC] Value2 PART Name DROPDOWNLIST
END PART ITEMLIST
NAME Item VALUE Data
где: END ITEMLIST

92
образование
[DEFAULT Default] PART Name LISTBOX
[EXPANDABLETEXT] [EXPANDABLETEXT]
[NOSORT] [NOSORT]
[REQUIRED] [ADDITIVE]
VALUENAME Value [EXPLICITVALUE| VALUEPREFIX Prefix]
END PART END PART

где: где:
! DEFAULT – указывает значение раскрывающегося спис- ! EXPANDABLETEXT – создает значение типа REG_
ка по умолчанию. EXPAND_SZ.
! EXPANDABLETEXT – создает значение типа REG_ ! NOSORT – отключает сортировку списка редактором по-
EXPAND_SZ. литик.
! NOSORT – отключает сортировку списка редактором ! ADDITIVE – считывает значения, уже хранящиеся в рее-
политик. стре.
! REQUIRED – указывает обязательное значение. ! EXPLICITVALUE – ключевое слово позволяет указать имя
! Name – указывается название раздела, которое будет ото- и данные значения. Отображаемый список имеет два стол-
бражено в консоли во время редактирования политики. бца: один для имени, другой – для данных. Невозможно
! Item – имя каждого из элементов раскрывающегося использовать эту инструкцию совместно с VALUEPREFIX.
списка. ! VALUEPREFIX – указанный префикс определяет имена
! Data – значение, соответствующее отображаемому эле- значений. Если указывается префикс, то редактор груп-
менту Item. повых политик добавляет к нему возрастающий номер.
! Default – значение по умолчанию. Считывается при вклю- В том случае, если префикс пустой, генерируются толь-
чении политики. ко возрастающие номера.
! Value – модифицируемое значение реестра. По умолча- ! Name – указывается название раздела, которое будет ото-
нию имеет тип данных REG_SZ. бражено в консоли во время редактирования политики.
! Prefix – используется для генерации последовательнос-
EDITTEXT ти имен. Если указан префикс, например Sample, то ге-
Ключевое слово EDITTEXT позволяет вводить в текстовом нерируются следующие имена значений: Sample1,
поле информацию, состоящую из букв и цифр. По умолча- Sample2,..Samplen. Если префикс не указан, то генери-
нию редактор политик сохраняет этот текст в значении типа руются только порядковые номера: 1,2,3,…n.
REG_SZ. Приведем синтаксис элемента EDITTEXT:
NUMERIC
PART Name EDITTEXT Ключевое слово NUMERIC позволяет вводить буквенно-
[DEFAULT Default]
[EXPANDABLETEXT] цифровой текст, используя элемент управления «счетчик»,
[MAXLENGHT] Max который увеличивает или уменьшает число. По умолчанию
[NOSORT]
[REQUIRED] переменные имеют тип данных REG_DWORD, однако, ис-
VALUENAME Value пользуя инструкцию TXTCONVERT, данные могут быть со-
END PART
хранены в формате REG_SZ.
где: Приведем синтаксис элемента LISTBOX:
! DEFAULT – указывает значение списка по умолчанию.
! EXPANDABLETEXT – создает значение типа REG_ PART Name NUMERIC
[DEFAULT Default]
EXPAND_SZ. [MAX Max]
! MAXLENGHT – указывает максимальную длину строки. [MIN Min]
[REQUIRED]
! REQUIRED – указывает обязательное значение. [SPIN]
! Name – указывается название раздела, которое будет ото- [TXTCONVERT]
VALUENAME Value
бражено в консоле во время редактирования политики. END PART
! Default – значение по умолчанию. Считывается при вклю-
чении политики. где:
! Max – максимальная длина данных значения. ! DEFAULT – указывает значение списка по умолчанию.
! Value – модифицируемое значение реестра. По умолча- ! REQUIRED – указывает обязательное значение.
нию имеет тип данных REG_SZ. ! SPIN – указывается шаг арифметической прогрессии.
По умолчанию равен 1. Указанное значение 0 делает
LISTBOX счетчик невидимым.
Ключевое слово LISTBOX добавляет в диалоговое окно ! TXTCONVERT – по умолчанию значения имеют тип дан-
политик список с кнопками Add и Remove. Это единствен- ных REG_DWORD. Используйте эту инструкцию, чтобы
ный тип, который может быть использован системным ад- сохранить данные в формате REG_SZ.
министратором для управления несколькими значениями, ! Name – указывается название раздела, которое будет ото-
содержащимися в одном ключе. В разделе LISTBOX невоз- бражено в консоли во время редактирования политики.
можно использовать опцию VALUENAME, поскольку он не ! Default – значение по умолчанию. Считывается при вклю-
связан с каким-либо конкретным значением. чении политики.
Приведем синтаксис элемента LISTBOX: ! Max – максимальное значение. По умолчанию равно 9999.

№8(21), август 2004 93


образование
! Min – минимальное значение. По умолчанию равно 0. ! Загрузить консоль групповых политик, выполнив коман-
! Value – модифицируемое значение реестра. По умолча- ду GPEDIT.MSC.
нию имеет тип данных REG_DWORD. ! В любом из разделов («Computer Configuration\Administ-
rative Templates» или «User Configuration\ Administrative
TEXT Templates») вызвать контекстное меню, используя од-
Ключевое слово TEXT добавляет в диалоговое окно стати- нократное нажатие на правую кнопку мыши.
ческий текст. ! В появившемся меню выбрать «Add/Remove Templates…».
Приведем синтаксис элемента TEXT: ! В загрузившемся диалоговом окне нажать кнопку «Add».
В появившемся окне, указав путь к файлу с расшире-
PART Text TEXT нием ADM, нажать кнопку «Open».
END PART
! Нажать кнопку «Close». После нажатия на эту кнопку
где Text – статический текст, добавляемый в диалоговое программа проверит синтаксис файла. В том случае,
окно. Если текст имеет пробелы, то его следует заключать если будет допущена синтаксическая ошибка, интерпре-
в кавычки. татор укажет номер строки, в которой она присутству-
ет, и возможный вариант исправления.
Практика использования
административных шаблонов Удаление административного шаблона
Приведем два примера административных шаблонов. В кор- Для удаления политики, подгруженной с помощью ADM-фай-
поративной сети, как правило, существуют один или не- ла, необходимо выполнить действия в следующем порядке:
сколько файловых серверов, в которых хранятся дистрибу- ! Загрузить консоль групповых политик, выполнив коман-
тивы программного обеспечения, в том числе Microsoft ду GPEDIT.MSC.
Office. В том случае, если пользователю по какой-либо при- ! В любом из разделов («Computer Configuration\Administ-
чине необходимо перейти с одной рабочей станции на дру- rative Templates» или «User Configuration\ Administrative
гую, то при первой загрузке для него создается новый про- Templates») вызвать контекстное меню, используя одно-
филь. При первом запуске одного из компонентов MS Office, кратное нажатие на правую кнопку мыши.
программа обращается к дистрибутиву, который располо- ! В появившемся меню выбрать «Add/Remove Templates…».
жен на файловом сервере. При изменении местоположе- ! В загрузившемся диалоговом окне выбрать файл, в ко-
ния дистрибутива на сервере пользователю выдается со- тором описана политика, которую необходимо удалить.
общение о невозможности завершить процесс конфигура- Нажать кнопку «Remove», затем «Close».
ции программы и просьба обратиться к системному адми-
нистратору. Во избежание этой неприятной ситуации реко- Заключение
мендуется использовать групповую политику, которая меня- Используя в сценарии регистрации пользователей в сети
ет в реестре пути к дистрибутиву MS Office (см. рис. 7): вместе с административными шаблонами политик безопас-
ности, системный администратор автоматизирует большую
Ïðèìåð 11 часть рутинных операций. Эффективность его работы при
CLASS Machine грамотном использовании описанных инструментов значи-
CATEGORY "Office 200" тельно возрастает.
KEYNAME "Software\Policies"
POLICY "Office2000"
EXPLAIN !!help
PART "Parametr1" EDITTEXT
KEYNAME "SOFTWARE\Classes\Installer\Products\ ↵
914000001E872D116BF00006799C897E\SourceList"
VALUENAME LastUsedSource
MAXLEN 100
EXPANDABLETEXT
END PART
PART !!help1 TEXT
END PART
PART "Parametr2" EDITTEXT
KEYNAME "SOFTWARE\Classes\Installer\Products\ ↵
914000001E872D116BF00006799C897E\SourceList\Net"
VALUENAME 1
MAXLEN 100
EXPANDABLETEXT
END PART
PART !!help2 TEXT
END PART
END POLICY
END CATEGORY
[strings]
help1="Exapmle1: n;3;\\Server\software\Office2000\"
help2="Exapmle2: \\Server\software\Office2000\"
help="\n\nCorrecting path to Microsoft Office 2000"

Внедрение административных шаблонов


Для загрузки ADM-файла, содержащего политику, необхо-
димо выполнить следующие действия: Ðèñóíîê 7

94
подписка на II полугодие 2004
Российская Федерация ! Узбекистан – по каталогу «Davriy nashrlar» российские
! Подписной индекс: 81655 издания через агентство по распространению печати
Каталог агентства «Роспечать» «Davriy nashrlar» (7000029, Ташкент, пл.Мустакиллик,
! Подписной индекс: 87836 5/3, офис 33)
Объединенный каталог «Пресса России» ! Азербайджан – по объединенному каталогу российских
Адресный каталог «Подписка за рабочим столом» изданий через предприятие по распространению печа-
Адресный каталог «Библиотечный каталог» ти «Гасид» (370102, г. Баку, ул. Джавадхана, 21)
! Альтернативные подписные агентства: ! Армения – по списку номенклатуры «АРЗИ» через ГЗАО
Агентство «Интер-Почта» (095) 500-00-60, курьерская «Армпечать» (375005, г.Ереван, пл.Сасунци Давида, д.2)
доставка по Москве и ЗАО «Контакт-Мамул» (375002, г. Ереван, ул.Сарья-
Агентство «Вся Пресса» (095) 787-34-47 на, 22)
Агентство «Курьер-Прессервис» ! Грузия – по списку номенклатуры «АРЗИ» через АО
! Подписка On-line «Сакпресса» ( 380019, г.Тбилиси, ул.Хошараульская, 29)
http://www.arzy.ru и АО «Мацне» (380060, г.Тбилиси, пр-т Гамсахурдия, 42)
http://www.gazety.ru ! Молдавия – по каталогу через ГП «Пошта Молдавей»
http://www.presscafe.ru (МД-2012, г.Кишинев, бул.Штефан чел Маре, 134)
по списку через ГУП «Почта Приднестровья» (МD-3300,
СНГ г.Тирасполь, ул.Ленина, 17)
В странах СНГ подписка принимается в почтовых отделе- по прайслисту через ООО Агентство «Editil Periodice»
ниях по национальным каталогам или по списку номенкла- (2012, г.Кишинев, бул. Штефан чел Маре, 134)
туры АРЗИ: ! Подписка для Украины:
! Казахстан – по каталогу «Российская Пресса» через Киевский главпочтамп
ОАО «Казпочта» и ЗАО «Евразия пресс» Подписное агентство «KSS»
! Беларусь – по каталогу изданий стран СНГ через РГО Телефон/факс (044)464-0220
«Белпочта» (220050, г.Минск, пр-т Ф.Скорины, 10)

Подписные
индексы:

81655
по каталогу
агентства
«Роспечать»

87836
по каталогу
агентства
«Пресса
России»

№8(21), август 2004 95


СИСТЕМНЫЙ АДМИНИСТРАТОР
№8(21), Август, 2004 год

РЕДАКЦИЯ
ЧИТАЙТЕ
Исполнительный директор
Владимир Положевец
В СЛЕДУЮЩЕМ
Ответственный секретарь
Наталья Хвостова
sekretar@samag.ru
НОМЕРЕ:
Технический редактор
Владимир Лукин
Редактор PHP 5 – пришествие удовлетворяли по возможностям, либо
Андрей Бешков неизбежно требовали установки на жесткий диск,
Итак свершилось. 13 июля 2004 года либо серьезного изменения настроек
РЕКЛАМНАЯ СЛУЖБА вышла пятая версия самого популяр- системы, что меня не совсем устраива-
тел./факс: (095) 928-8253 ного на сегодняшний день языка веб- ло. Поэтому пришлось искать инстру-
Константин Меделян разработки – PHP. Его создатель, Рас- мент, который сможет помочь в такой
reсlama@samag.ru мус Лендорф, подчёркивает, что из- ситуации. И он, конечно же, нашелся.
менения, привнесённые в язык, не ре-
Верстка и оформление волюционны, а скорее эволюционны, Linux на страже Windows.
imposer@samag.ru но беглое знакомство с новыми воз- Обзор и установка
maker_up@samag.ru можностями PHP заставляет усом- системы резервного
Дизайн обложки ниться в словах мэтра. За недолгое копирования BackupPC
Николай Петрочук время своего существования PHP су- Когда у меня возникло желание сде-
мел из набора скриптов для придания лать единое хранилище для ежеднев-
103045, г. Москва, интерактивности домашней странич- ных архивов информации с более чем
Ананьевский переулок, дом 4/2 стр. 1 ки автора вырасти в мощное средство десятка серверов своей организации,
тел./факс: (095) 928-8253 разработки веб-приложений и теперь работающих под управлением не-
Е-mail: info@samag.ru – новый шаг вперед. Насколько этот скольких различных ОС, свой выбор я
Internet: www.samag.ru шаг важен и какие последствия пере- остановил на платформе Linux. До сих
хода на новую версию грозят програм- пор каждый сервер с помощью уни-
РУКОВОДИТЕЛЬ ПРОЕКТА мисту и системному администратору, кальных для него скриптов в назначен-
Петр Положевец можно оценить, ознакомившись с ос- ное время сбрасывал по сети на сер-
новными нововведениями PHP 5 и вер резервного копирования или stand
УЧРЕДИТЕЛИ возможными проблемами, связанны- by-сервер какие-то свои данные, на-
Владимир Положевец ми с его использованием. пример, пользовательские файлы с
Александр Михалев сетевых дисков или дампы базы дан-
Роутер без диска ных. Для этого использовались различ-
ИЗДАТЕЛЬ В работе подчас встречаются ситуа- ные протоколы: ftp, SMB или штатные
ЗАО «Издательский дом ции, когда осуществление тех или иных средства СУБД. При этом приходилось
«Учительская газета» мероприятий или действий традицион- следить за уникальным для каждого
ным путем если не то, что совсем не- сервера log-файлом, и в случае каких-
Отпечатано типографией возможно, а просто неудобно, а при- либо изменений в стратегии резервно-
ГП «Московская Типография №13» вычные инструменты оказываются го копирования править скрипты на
Тираж 7000 экз. практически бесполезны. Вот и у меня каждой машине.
возникла ситуация, когда для показа Чтобы как-то упростить админист-
Журнал зарегистрирован возможностей необходимо было в ко- рирование и сократить время, затра-
в Министерстве РФ по делам печати, роткие сроки собрать и настроить сеть. чиваемое на поддержку и мониторинг
телерадиовещания и средств мас- А так как это была всего лишь презен- всего этого «зоопарка», я начал искать
совых коммуникаций (свидетельство тация и все задействованные компью- систему, которая бы поддерживала
ПИ № 77-12542 от 24 апреля 2002г.) теры использовались в реальной рабо- копирование информации по сети, уда-
те, то одним из требований было по- ленное администрирование, умела де-
За содержание статьи ответствен- меньше трогать настройки и ПО, для лать инкрементальные бэкапы и не
ность несет автор. За содержание того чтобы можно было быстро вернуть требовала установки клиентского про-
рекламного обьявления ответствен- все в рабочее состояние. граммного обеспечения. Кроме того,
ность несет рекламодатель. Все пра- Если с клиентскими компьютерами было важно, чтобы система умела ра-
ва на опубликованные материалы за- более-менее все решалось просто: где ботать по протоколу SMB, так как часть
щищены. Редакция оставляет за со- доустановкой пары программ, где ис- серверов, в частности основной файл-
бой право изменять содержание сле- пользованием GNU/Linux LiveCD-дист- сервер работали под управлением ОС
дующих номеров. рибутивов, то с роутером вышла замин- Windows. Спустя непродолжительное
ка. Имеющиеся у меня образцы либо не время, такая система была найдена.

96