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

021 Системный Администратор 08 2004

Embed Size (px)

DESCRIPTION

Особенности установки антивируса ClamAV во FreeBSD Обзор ОС Darwin 7.0 на платформе x86 Обзор ОС Darwin 7.0 на платформе x86 Практика работы с NetBSD: профилирование ядра Практика работы с NetBSD: профилирование ядра Разгон и торможение Windows NT Разгон и торможение Windows NT Обработка HTML-шаблонов off-line

Citation preview

№8(21) август 2004подписной индекс 81655

www.samag.ru

Кроссплатформеннаячастная сеть: OpenVPN

Linux и NTFS

Особенности установкиантивируса ClamAV во FreeBSD

Обзор ОС Darwin 7.0на платформе x86

Практика работы с NetBSD:профилирование ядра

Создание и настройка IVRдля голосовых шлюзов Cisco Systems

Разгон и торможение Windows NT

Поисковая система своими руками

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

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

Кроссплатформеннаячастная сеть: OpenVPN

Linux и NTFS

Особенности установкиантивируса ClamAV во FreeBSD

Обзор ОС Darwin 7.0на платформе x86

Практика работы с NetBSD:профилирование ядра

Создание и настройка IVRдля голосовых шлюзов Cisco Systems

Разгон и торможение Windows NT

Поисковая система своими руками

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

Обработка HTML-шаблонов off-line№8(

21)

авг

уст

2004

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

оглавление

Запуск в VMWare гостевой оси,установленной на физическом диске

Николай Почабытов[email protected] 13

Linux и NTFS

Сергей Яремчук[email protected] 18

Еще раз о ClamAV:особенности установки во FreeBSD

Сергей Супрунов[email protected] 24

CrossOver и лицензионный вопрос

Андрей Бешков[email protected] 26

В яблочко!Краткий обзор ОС Darwin 7.0на платформе x86 (Mac OS X 10.3 Jaguar)

Антон Борисов[email protected] 28

Практика работы с NetBSD:профилирование ядра

Александр Байрак[email protected] 34

Поисковая система своими руками

Андрей Сапронов[email protected] 61

HTML-шаблоны для PHP и Perl,или Не делайте инструмент самоцелью

Дмитрий Горяинов[email protected] 66

Разгон и торможение Windows NT

Крис Касперски[email protected] 48

OpenVPN, или Кроссплатформеннаячастная сеть

Андрей Бешков[email protected] 4

Знакомство с Usergate

Андрей Уваров[email protected] 36

Создание и настройка IVRдля голосовых шлюзов Cisco Systems

Михаил Заграевский[email protected] 38

АДМИНИСТРИРОВАНИЕ

ОБРАЗОВАНИЕ

Обработка HTML-шаблонов off-line.Возможности и ограничения

Алексей Мичурин[email protected] 74

Автоматизация процессов в сети

Иван Коробко[email protected] 84

WEB

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

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

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

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

4

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

Рано или поздно большинство системных администрато-ров сталкивается с необходимостью соединить свои тер-риториально удаленные сети с помощью надежного, дос-таточно быстрого и в то же время защищенного от прослу-шивания и вмешательства злоумышленников канала. Та-ким образом, получается, что нам нужно создать частнуювиртуальную сеть (Virtual Private Network), или VPN. В этотмомент перед нами открывается развилка из несколькихпутей для претворения задуманного в жизнь:1. Использовать свободную реализацию IPSec (Internet

Protocol Security). В качестве таковой могут выступатьFreeSWAN или FreeBSD IPSec.

2. Купить и внедрить коммерческое решение. Например,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 не обязательно смо-жет мирно сосуществовать с вашим межсетевым экраном,особенно если тот выполняет над пакетами изуверские опе-рации, называемые в народе NAT. Опять же экраны с конт-ролем состояния соединения (statefull firewall), находящие-ся между двумя точками виртуального тоннеля и управляе-мые провайдером, могут не пропускать те или иные IPSec-пакеты. О кроссплатформенности разных реализаций IPSecпока что остается только мечтать в связи с тем, что для

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

OpenVPN,ИЛИ КРОССПЛАТФОРМЕННАЯ

ЧАСТНАЯ СЕТЬ

реализации функций IPSec приходится вносить в ядро опе-рационной системы и IP-стек довольно много изменений.Как гласит старинная пословица: «Надежность заканчива-ется там, где начинается сложная механика», это значит,что одна-единственная ошибка в коде, реализующем IPSec,приведет к огромной дыре в безопасности. Вдобавок, на-сколько я знаю, на данный момент не существует легконастраиваемого свободного клиента IPSec для Windows.Значит, либо придется разбираться в настройках тех кли-ентов, что распространяются под эгидой opensource, либопокупать коммерческий. Также стоит обратить внимание нанесколько большую сложность установки и настройки та-кого комплекса по сравнению с остальными типами VPN.Еще одним из минусов является отсутствие техническойподдержки. Если что-то пойдет не так, как планировалось,то на квалифицированную помощь от производителя рас-считывать трудно. Это ведь свободный продукт, поэтомупридется просить совета либо у своих более удачливых кол-лег, либо в форумах и списках, посвященных данному про-граммному обеспечению. Впрочем, не каждый производи-тель проприетарного софта может похвастаться квалифи-цированной службой поддержки. К тому же обычно у мно-гих свободных разработок существует внушительный ла-герь сторонников, которые будут рады обратить вас в своюверу и заодно помочь в настройке программы.

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

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

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

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

Решив разработать свой собственный пакет, реализу-ющий функции VPN, он в отличие от других авторов нестал заниматься созданием своего собственного уникаль-ного криптоалгоритма с нуля. Вместо этого был исполь-зован проверенный временем и неоднократно доказавшийсвою надежность стандарт SSL, для работы с которымможно было опереться на доступную для многих систембиблиотеку OpenSSL. Стоит отметить, что такая же идеяиспользуется и в программном обеспечении, называемомvtun.

Итак, давайте посмотрим поближе на возможности, пре-доставляемые OpenVPN.! Официально OpenVPN успешно работает под управле-

нием следующих операционных систем: Linux, Solaris,OpenBSD, FreeBSD, NetBSD, Mac OS X, Windows 2000/XP. Это позволяет создавать сложные кроссплатфор-менные туннели. Впрочем, не составит никакого трудапортировать OpenVPN в любую другую систему, для ко-торой существует драйвер tun/tap-устройств. К тому жеданная разработка независима от размера и старшин-ства байтов в машинном слове, что облегчает переносна новые операционные системы.

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

! Поддерживаются два типа туннелей: IP и Ethernet, соот-ветственно называемые routed и bridged. Таким обра-зом, появляется возможность туннелировать как IP-под-сети, так и виртуальные Ethernet-адаптеры.

! Отлично работает в сетях, где адреса распределяютсяс помощью DHCP. Помогает подключаться к VPN-кли-ентам, попадающим в Интернет через dial-up.

! Позволяет создать туннели поверх NAT, несмотря на точто NAT изменяет содержимое заголовков передавае-мых пакетов.

! Дает возможность работать с любыми механизмамишифрования, встроенными в OpenSSL для защиты пе-редаваемого трафика. А это, в свою очередь, позволя-ет каждому клиенту выбрать тип, режим работы (CBC,CFB, OFB) и размер ключа шифра в соответствии с ин-дивидуальными предпочтениями.

! Для аутентификации датаграмм, пересылаемых по тун-нелю, можно использовать HMAC-дайджест.

! В случае если в передаваемых данных есть повторяю-щиеся последовательности, для их сокрытия будет ис-пользован алгоритм explicit IV.

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

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

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

Вариант решения, основанный на PPT, большей частьюиспользуется приверженцами Microsoft. Данный стандартреализует довольно стойкое шифрование и аутентифика-цию соединений. Созданный в недрах большой Редмондс-кой корпорации, он не получил особого распространения вмире Unix. Хотя свободное программное обеспечение дляработы с ним все же существует и пользуется некоторымспросом, все же решения на основе IPSec практикуют го-раздо чаще. Происходит это, видимо, потому, что IPSecимеет более надежные процедуры шифрования. Но поли-тические мотивы сопротивления со стороны юниксистов но-винкам, внедряемым Microsoft, тоже не стоит сбрасыватьсо счетов.

Ну а большинству читателей, не воспользовавшихся ва-риантами, предлагаемыми выше, видимо, придется обра-титься к программе, которой собственно и посвящена дан-ная статья. Между делом неплохо было бы посетить сайтпроекта http://openvpn.sourceforge.net. Ну а я продолжу своеповествование.

История разработки OpenVPN выглядит довольно забав-но и в то же время является хрестоматийной иллюстраци-ей принципов, типичных для движения открытых исходни-ков. В конце 2001 года Джеймс Йонан (James Yonan) ус-пешно выполнил работу над большим проектом для компа-нии, сотрудником которой он является и по сей день. В ка-честве благодарности работодатель разрешил ему не хо-дить каждый день на работу и выполнять свои обязанностиудаленно. С этого момента жизнь Джеймса кардинальноизменилась. Словно в калейдоскопе мелькали страны и кон-тиненты: Египет, Кыргызстан, Монголия, Китай. Как обыч-но, без русских тут не обошлось. Уж очень автора OpenVPNбеспокоил тот парадоксальный факт, что в России есть до-статочно большое количество безработных, но очень та-лантливых хакеров. А судя по его наблюдениям, некотораячасть трафика шла именно через российские сети. Объез-див всю центральную Азию, Джеймс попутно продолжал ис-кать решение, которое позволит ему работать с централь-ным офисом удобно, безопасно, надежно и в то же времяне будет стоить бешеных денег. Испробовав на себе всевышеперечисленные способы создания VPN, он понял, чтона данный момент идеал, к сожалению, недостижим.

6

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

! В качестве дополнительной меры безопасности можетбыть использован протокол TLS, позволяющий аутен-тифицировать сессию с помощью динамического обме-на сертификатами. Довольно большой оптимизации бы-стродействия при динамическом обмене SSL/TLS-клю-чами позволяет добиться использование мультипоточ-ной библиотеки pthread. Таким образом, даже частыйобмен между сервером и клиентом ключами размеромболее чем 2048 байт практически не влияет на скоростьпередачи туннелируемых данных.

! Для увеличения безопасности OpenVPN позволяет пе-реместить себя в chroot-окружение и снижает свои при-вилегии после старта так, чтобы отлично работать отимени самого бесправного пользователя системы.

! Еще одним полезным с точки зрения безопасности свой-ством является наличие ключа --mlock. Он позволяет зап-ретить OpenVPN записывать в процессе работы на жест-кий диск какую-либо информацию, связанную с секрет-ными ключами и данными, передаваемыми по туннелю.

! В связи с тем, что данная программа является всего лишьобычным пользовательским приложением, а не частьюядра, она может вполне мирно сосуществовать с други-ми приложениями, использующими tun/tap-туннели.

! OpenVPN создавался для тесной интеграции с пользова-тельскими скриптами и другими высокоуровневыми при-ложениями. Что в свою очередь дает возможность по пер-вому требованию легко создавать и уничтожать туннели.

! Позволяет удобно работать через межсетевые экраныс контролем состояния соединения. В случае если потуннелю не передаются данные, OpenVPN позволяет че-рез определенные промежутки времени посылать ping,для того чтобы не дать межсетевым экранам разорватьсоединение из-за неактивности.

В данной статье мы будем работать только с маршрути-зируемыми (routed) туннелями, а разговор о туннелях типамост (bridge) отложим до следующего раза. Как я уже упо-мянул ранее, маршрутизируемый туннель умеет работатьтолько с TCP/IP-трафиком. Соответственно широковеща-тельный трафик через него не пройдет. А это значит, чтонекоторые протоколы вроде Microsoft Netbios работать по-верх нашего туннеля не смогут.

Задачей на сегодня будет соединение через VPN трехудаленных филиалов одной организации. Будем считать,что они называются магазин, склад и офис. В каждом изфилиалов стоит маршрутизатор, имеющий на борту по двареальных и два виртуальных сетевых интерфейса. Первыйиз реальных интерфейсов направлен во внутреннюю ло-кальную сеть с серым адресным пространством, а второй –в Интернет. Адреса для внешних интерфейсов выданы нампровайдером и принадлежат к сети 80.80.20.0/24. Для вир-туальных tun-интерфейсов выбрана подсеть 10.3.0.0/24.

Более наглядно все вышеуказанное отображено на при-веденной схеме.

Итак, начнем установку пакетов на ALT Linux. В систем-ном репозитарии есть все, что нам может понадобиться.

После удачного завершения установки создаем пользо-вателя openvpn, входящего в группу openvpn. Процессыopenvpn, стартуя от имени пользователя root, будут сни-жать свои права, чтобы с наименьшими привилегиямиработать от имени вышеуказанного пользователя. Согла-ситесь, что дополнительная безопасность никому не по-мешает.

Теперь можно приступить к тестам общей работоспо-собности openvpn. Первым делом нужно посмотреть, какиеиз алгоритмов шифрования нам доступны:

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

Для аутентификации каждой получаемой UDP-датаграм-мы применяется так называемый механизм дайджеста.Смотрим доступные дайджесты:

Òàáëèöà 1

# apt-get update# apt-get install iptables openssl liblzo liblzo-devel openvpn

# useradd openvpn

# openvpn --show-ciphers

# openvpn --show-digests

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

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

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

Внутри файла static.key должно появиться что-то похо-жее на следующие данные:

После этого можно провести тестирование криптосис-темы с использованием только что созданного ключа.

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

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

Файл linux-windows.conf:

Начинаем разбираться по порядку, что означает каж-дая из этих опций.! proto – протокол, используемый для передачи данных.

Может принимать значения udp, tcp-client, tcp-server. На-стоятельно рекомендуется использовать udp, потому чтотуннелирование поверх TCP создает слишком большиенакладные расходы. Впрочем, если нет другого выхода,то и TCP сойдет. В таком случае нужно на одном концетуннеля установить настройки этой переменной как tcp-server, а на другом – как tcp-client. Соответственно от-вечать за инициацию соединения будет машина со зна-чением tcp-client. По умолчанию эта переменная имеетзначение udp, поэтому если вы с этим согласны, то ееопределение можно в дальнейшем не использовать.

! port – порт, на котором нужно ждать соединений. Дол-жен быть одинаковым на обоих концах туннеля. Номе-ра портов для разных туннелей не могут совпадать.

! dev – тип виртуального устройства туннеля. Может при-нимать значения tun, tap, null. В этом случае OpenVPNбудет пытаться динамически выбрать и задействоватьпервое свободное устройство данного типа. Если же емуэто не удается, то нужно будет четко указать имя устрой-ства с помощью опции dev-node. Например, dev-node tun1.

! comp-lzo – включение упаковки потока данных. В слу-чае если поток все же не стоило упаковывать, размерпередаваемых данных увеличится всего на один байт.Впрочем, за все время эксплуатации данного программ-ного комплекса я не встречал случая, когда системаадаптивной компрессии ошиблась и стала пытаться при-менить сжатие там, где делать этого не стоило.

#openvpn --genkey --secret /etc/openvpn/static.key

# openvpn --test-crypto --secret /etc/openvpn/static.key

proto udpdev tunport 5000comp-lzoping 15verb 3user openvpngroup openvpnremote 80.80.20.129ifconfig 10.3.0.9 10.3.0.10route 10.10.120.0 255.255.255.0 10.3.0.10secret /etc/openvpn/static.keyauth MD5cipher DES-CBCtun-mtu 1500

proto udpdev tunport 5002comp-lzoping 15verb 3user openvpngroup openvpnremote 80.80.20.128ifconfig 10.3.0.6 10.3.0.5route 10.10.130.0 255.255.255.0 10.3.0.5secret /etc/openvpn/static.keytun-mtu 1500auth MD5cipher DES-CBC

8

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

! ping – в случае если по VPN-каналу не передается ни-каких данных, приказывается отправлять ping каждыеn секунд, чтобы не позволить соединению разорватьсяиз-за простоя. Полезно в случае, если между конечны-ми точками vpn находятся межсетевые экраны с конт-ролем состояния.

! verb – уровень подробности выводимых сообщений.Чем больше число, тем многословнее и педантичнеепрограмма будет рассказывать о том, что происходиту нее внутри. Верхним пределом для этой переменнойявляется число 11. Данная опция чаще всего исполь-зуется при отладке и первоначальной настройке тун-нелей.

! user, group – имя пользователя и группы, от имени ко-торой будет работать программа. Первоначальноopenvpn стартует от имени root и, прочитав все инте-ресующие файлы, снижает привилегии до указанногоуровня.

! remote – IP-адрес хоста, представляющего из себя даль-нюю сторону туннеля. Если его не указать, то openvpnбудет пассивно принимать все входящие соединения,не пытаясь самостоятельно соединяться с удаленноймашиной. Затем все полученные соединения должныбудут пройти авторизацию. Данный режим удобен дляработы с dial-up системами, у которых постоянно меня-ется IP-адрес.

! ifconfig – назначает виртуальному tun/tap интерфейсу IP-адрес. Заодно указывает адрес удаленного виртуаль-ного интерфейса. Это необходимо, потому что туннельработает как стандартное соединение «точка-точка».

! route – описывает маршрут, который должны пройтипакеты, чтобы попасть в удаленную сеть. Можно обо-значить маршрут двумя способами. Либо с помощьюэтой настройки, либо переменной up, значением кото-рой необходимо указать имя командного скрипта, отве-чающего за выполнение правильной настройки марш-рутизации. В случае если вы пользовались первым спо-собом настройки маршрутизации, то openvpn самосто-ятельно удалит из таблицы маршрутов нужную запись,когда пользователь попросит его завершить работу. Нуа если прибегали к услугам опции up, то необходимотакже описать опцию down, которая будет указывать наскрипт, выполняющий самостоятельные действия поудалению маршрута.

! secret – указывает имя файла, в котором хранится ста-тический ключ, используемый для шифрования потока.

! tun-mtu – максимальный размер пакета, передаваемо-го по виртуальному интерфейсу. Пакеты большего раз-мера будут разбиваться на несколько кусков и переда-ваться последовательно отдельными датаграммами.Желательно, чтобы на обоих концах туннеля значениеэтой переменной было одинаково, иначе можно прове-сти несколько интересных часов в забавном поиске при-чины, почему авторизация и передача первых пакетовпроходит нормально, а затем все начинает работатьвесьма медленно и нестабильно.

! auth – наименование алгоритма, используемого дляаутентификации приходящих пакетов. В моем случае этоMD5. Хотя никто не мешает взять любой другой.

! cipher – алгоритм, используемый для шифрования паке-тов. Blowfish выбран потому, что является весьма стойкими в то же время достаточно быстрым. В случае если хочет-ся повысить надежность шифрования, нужно использоватьпеременную keysize и указывать большой размер ключа.По умолчанию Blowfish использует ключ длиной 128 бит,хотя максимально возможный размер – 448 бит.

На этом установку и первоначальную настройку пакетана Linux-машине можно считать завершенной. Переходимна FreeBSD. Как человек рассудительный и здравомысля-щий, предпочитаю устанавливать сложное программноеобеспечение из портов.

К сожалению, несмотря на то что перед началом инстал-ляции я обновил список портов, система упрямо не хотеластавить openvpn-1.6.0, а вместо него подсовывала openvpn-1.4.0.

Дабы не наступать на грабли и впоследствии не ломатьголову над совмещением разных версий, пришлось деин-сталлировать ранее установленную версию 1.4.0. Хотя вдокументации описано, как «подружить» старые и новыеверсии, я решил рисковать.

Отправляемся по адресу: http://www.freebsd.org/cgi/ports.cgi?query=openvpn&stype=all и с помощью поиска вы-ясняем, что исходные тексты самой свежей версии портаopenvpn-1.6.0 не желают скачиваться. Ну на нет и суда нет.Видимо, такова судьба, и нам придется снова пойти в об-ход. Берем отсюда бинарный пакет ftp://ftp.freebsd.org/pub/FreeBSD/ports/i386/packages-4-stable/All/openvpn-1.6.0.tgz иустанавливаем его с помощью утилиты pkg_add.

Не забываем добавить в систему пользователя и груп-пу openvpn.

Затем создаем директорию /etc/openvpn, где у нас бу-дут храниться файлы с настройками и ключом. С помощьюкакого-либо безопасного транспорта переносим с Linux-машины файл static.key. В качестве средства, пригодногодля этой цели, можно использовать scp, sftp, архив с паро-лем или просто дискету, переданную доверенным лицом.Теперь давайте посмотрим на содержимое конфигураци-онных файлов.

Файл freebsd-linux.conf:

# cd /usr/ports/archivers/lzo# make install clean# cd ../../security/openssl# make install clean# cd ../openvpn# make install clean

# make deinstall

# pkg_add openvpn-1.6.0.tgz

# adduser openvpn

dev tun

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

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

Файл freebsd-windows.conf:

Я думаю, что, пользуясь предыдущими объяснениями,будет довольно легко понять все используемые настройки.Права на директорию /etc/openvpn и файлы в ней должныпозволять чтение только пользователю root и группе wheel.

Если очень хочется, то можно выполнить проверку дос-тупных шифров так же, как мы делали это для Linux.

Наконец-то мы готовы к запуску первого из наших тун-нелей. На Linux-машине выполняем следующую команду:

И соответственно на FreeBSD делаем так:

На терминале Linux должны появляться следующие над-писи:

Ну а FreeBSD-машина должна говорить что-то вроде это-го:

Как только увидите надпись «Peer Connection Initiatedwith», считайте, что дело сделано. Теперь с помощью ко-манды ping можно проверить, видны ли оконечные адресаиз соединяемых частных сетей. В нашем примере это10.10.120.1 и 10.10.140.1. Не забываем настроить компью-теры, находящиеся в наших локальных сетях так, чтобы онисчитали машины Linux и FreeBSD шлюзами по умолчанию.

Кстати, стоит отметить, что openvpn может вполне ус-пешно работать и без всяких конфигурационных файлов.Дело в том, что все параметры можно передать в програм-му и из командной строки. К примеру, для поднятия того жесамого туннеля можно было скомандовать вот так:

Хотя, с моей точки зрения, данный способ выглядит не-сколько громоздко.

Стоит отметить один интересный факт: дистрибутивopenvpn для Linux устанавливает в систему скрипт /etc/rc.d/init.d/openvpn, который помогает удобно управлять нашимитуннелями с помощью команды service openvpn. Главнойособенностью скрипта является способность поднять тунне-ли для всех *.conf-файлов, находящихся в /etc/openvpn. Та-ким образом, нам не надо придумывать, как автоматическизапустить требуемое количество экземпляров openvpn с нуж-ными настройками после перезагрузки системы.

Во FreeBSD тоже есть скрипт подобного предназначе-ния. Сразу после инсталляции он обычно находится в /usr/local/etc/rc.d/openvpn.sh.sample. Посмотрев внутрь него,понимаем, что он совершенно бесполезен, так как практи-чески ничего не умеет делать. Поэтому нам придется смас-терить свой собственный вариант такого скрипта. Для это-го нужно создать файл openvpn.sh и внести в него следую-щие данные:

dev tunremote 80.80.20.128port 5001ifconfig 10.3.0.1 10.3.0.2route 10.10.130.0 255.255.255.0 10.3.0.2secret /etc/openvpn/static.keyping 10verb 3tun-mtu 1500user openvpngroup openvpnauth MD5cipher DES-CBCcomp-lzo

port 5000comp-lzoping 15verb 3user openvpngroup openvpnremote 80.80.20.131ifconfig 10.3.0.10 10.3.0.9route 10.10.140.0 255.255.255.0 10.3.0.9secret /etc/openvpn/static.keyauth MD5cipher DES-CBCtun-mtu 1500comp-lzo

# openvpn --config /etc/openvpn/linux-freebsd.conf

# openvpn --config /etc/openvpn/freebsd-linux.conf

# openvpn --remote 80.80.20.128 --dev tun --port 5001 ↵↵↵↵↵--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 ↵↵↵↵↵--ping 10 --verb 3 --tun-mtu 1500 --user openvpn ↵↵↵↵↵--group openvpn --auth MD5 --cipher DES-CBC

#! /bin/shcase x$1 in xstart) /usr/local/sbin/openvpn --config ↵↵↵↵↵

/etc/openvpn/freebsd-linux.conf & /usr/local/sbin/openvpn --config ↵↵↵↵↵

/etc/openvpn/freebsd-windows.conf & ;; xstop) killall -SIGTERM openvpn route delete 10.10.130.0 route delete 10.10.140.0 ;; *) echo >&2 "Usage: $0 {start|stop}"esac

10

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

Обязательно кладем файл в /usr/local/etc/rc.d/ и даем емуправо на выполнение. Стоит обратить внимание на нали-чие знака «&» после запуска каждого экземпляра openvpn.Сделано это потому, что данная программа жестко привя-зывается к терминалу, с которого была запущена. Соот-ветственно при отсутствии этого знака второй процессopenvpn не запустится до тех пор, пока терминал не осво-бодится, а этого при правильном развитии событий не дол-жно произойти никогда. Вторым важным моментом явля-ется наличие команд route, удаляющих записи о маршру-тах в той ветке скрипта, которая отвечает за выполнениедействия stop. Удалять маршруты приходится вручную, по-тому что к моменту завершения работы openvpn выполня-ется от лица одноименного пользователя и группы, а изме-нять таблицу маршрутизации имеет право только root. Ко-нечно, можно было бы портировать под FreeBSD версиюскрипта, поставляющуюся в комплекте с Linux-версией про-граммы, но сейчас заниматься этим как-то недосуг. Воз-можно, я сделаю это в следующей статье.

Настало время перейти к настройке Windows-системы. Напервый взгляд здесь все довольно просто. Берем дистрибу-тив для этой платформы на родном сайте программы http://prdownloads.sourceforge.net/openvpn/openvpn-1.6.0-install.exe.Затем запускаем только что скачанный инсталлятор и ме-тодично жмем на кнопки «Далее» и «ОК». После установ-ки в систему будет добавлен новый сетевой интерфейс состранным именем «Подключение по локальной сети 3».

Если присмотреться внимательно к свойствам данногоподключения, то можно заметить что оно представляет со-бой не что иное, как интерфейс tap.

Тут стоит сделать одну важную оговорку: под Windowsне существует различия между tun- и tap-устройствами. Аеще точнее было бы сказать, что такая реализация tap-драй-вера позволяет устройству работать в любом из двух ре-жимов.

Для нас столь длинное название устройства неудобно,поэтому переименовываем его во что-нибудь более крат-кое и информативное. Например, в Linux. Затем с помо-щью меню Пуск →→→→→ Программы →→→→→ Open →→→→→ VPNAdd a newTAP-win32 virtual ethernet adapter создаем еще один вирту-альный интерфейс и переименовываем его во FreeBSD. Ду-маю, название каждого из этих интерфейсов достаточнокрасноречиво говорит об их предназначении. Надеюсь, чтовсе еще помнят, зачем мы создавали файл static.key. Еслиэто действительно так, то копируем его в C:\ProgramFiles\OpenVPN\config\ под именем staic.txt. По непонятнойпричине файлы с расширением .key openvpn, работающиепод Windows, не воспринимаются как секретные ключи. За-тем создаем конфигурационные файлы наших туннелей.Стоит обратить внимание на тот факт, что для успешнойработы у этих файлов должно быть расширение .ovpn.

Файл windows-freebsd.ovpn:

Файл windows-linux.ovpn:

В сущности, все тесты, которые мы использовали доэтого, отлично работают и под управлением Windows. По-этому, если есть желание, можете выполнить openvpn спараметрами --show-digests и --show-ciphers. Единственное,на что стоит обратить внимание, – это обязательное нали-чие в конфигурационном файле записи dev-node с именемиспользуемого устройства. К сожалению, Windows умеетавтоматически находить и использовать нужное виртуаль-ное устройство туннеля только в том случае, если оно всистеме представлено в единственном числе. Кстати, сто-ит отметить тот факт, что в случае работы устройства tunпод Windows использовать для туннеля какие попало адре-са из подсети 10.3.0.0 не получится, придется выбирать изтаблицы, выводимой командой openvpn --show-valid-subnets.

Как и под UNIX-системами, можно поднять туннель вы-зовом openvpn с указанием имени используемого конфи-гурационного файла, а можно и без него, просто перечис-

remote 80.80.20.129dev-node FreeBSDdev tunport 5001ifconfig 10.3.0.2 10.3.0.1secret static.txtping 10verb 3route 10.10.120.0 255.255.255.0 10.3.0.1auth MD5cipher DES-CBCcomp-lzo

remote 80.80.20.131dev tundev-node Linuxport 5002ifconfig 10.3.0.5 10.3.0.6secret static.txtping 10verb 3route 10.10.140.0 255.255.255.0 10.3.0.6tun-mtu 1500comp-lzo

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

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

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

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

То же самое проделываем и под управлением Windows.После этого все вышеописанные туннели должны пра-

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

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

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

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

Адаптер Ethernet Linux:

Адаптер Ethernet FreeBSD:

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

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

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

12

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

Для проверки того, как работает туннель, запускаем сразных сторон ping, направленный в удаленные сети, и смот-рим на результаты.

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

# tcpdump �i tun0 -lenx

А проходя через интерфейс lnc1, в Интернет они попа-дают уже зашифрованными:

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

# tcpdump �i lnc1 -lenx

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

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

У системного администратора или программиста очень ча-сто бывают ситуации, когда необходимо проверить рабо-ту программы или что-то сделать в другой ОС. Для этогона компьютер ставятся несколько систем. Но для того что-бы проверить работоспособность новой программы, надоперегружать компьютер, а это не всегда хорошо. Поэто-му был выпущен такой продукт, как VMWare (http://www.wmware.com). Его особенности можно не расписывать,он позволяет многое, главное – с его помощью можно за-пустить в качестве гостевой операционной системы уже ус-тановленную на жестком диске. Вся суть заключается в том,

НИКОЛАЙ ПОЧАБЫТОВ

ЗАПУСК В VMWare ГОСТЕВОЙ ОСИ,УСТАНОВЛЕННОЙ НА ФИЗИЧЕСКОМ ДИСКЕ

что на виртуальном компьютере мы получаем возможностьработать сразу, ничего не переустанавливая.

В данной статье в качестве базовой ОС рассматрива-ется Linux Fedore Core 2 test1, а в качестве гостевой –Windows 2000 Professional.

Из-за того, что FC2 работает на ядре серии 2.6.*, нужноустанавливать VMWare 4.5.1, в которой учтена возможностьработать с этой серией ядер.

Переходим к главному – запуску установленной системы.Сформулируем требования:

! Должны быть установлены Windows и Linux.

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

14

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

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

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

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

! Хороший boot-менеджер.

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

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

В появившемся окне вводим название профиля:

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

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

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

В первую очередь удаляем видеоадаптер:

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

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

Теперь систему можно перегружать в Linux, первый этапзакончен.

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

И начинаем создавать новую виртуальную машину:

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

Здесь необходимо отметить один из трех вариантов:! Создание нового виртуального диска.! Использование существующего виртуального диска.! Использование физического диска.

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

16

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

При нажатии кнопки «Next» появится предупреждение,соглашаемся с ним и видим следующий экран, на которомпредстоит выбрать устройство и использование какой-точасти или всего диска. Лучше остановиться на тех разде-лах, на которых у вас установлена система Windows, и мо-жет быть, какие-то дополнительные диски (если у вас су-ществуют дополнительные разделы для Windows. Если накомпьютере существует два и более жестких диска, и сис-темы установлены на разных физических дисках, то необ-ходимо для нормальной работы подключать все жесткиедиски, иначе система не будет загружаться):

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

Выбираем редактирование свойств виртуальной маши-ны. Надо поправить всего лишь один параметр – подклю-чение CD/DVD-ROM должно быть SCSI:

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

И вот появляется долгожданное меню выбора профилязагрузки:

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

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

Естественно, что надо выбирать профиль, созданныйдля виртуальной машины. При загрузке Windows будет ис-кать новые устройства и драйвера для них, поэтому послетого, как будут введены логин и пароль, нужно проинстал-лировать VMWare Tools (VM →→→→→ Install VMWare Tools).

После этого перезагружаемся, если возникнет необхо-димость. Теперь можно полноценно работать с системой:

Здесь необходимо сразу отметить несколько моментовпо работе:! USB Flash-накопители работают очень хорошо и без до-

полнительных настроек.! Может возникнуть серьезная проблема у тех, кто пользу-

ется USB-шнуром для связи мобильного телефона и ком-пьютера. Драйвера и программное обеспечение уста-навливаются без проблем и также нормально работа-ют. Но при перезагрузке можно увидеть «синий экрансмерти». Возможно, он появляется из-за отсутствия под-ключенного кабеля или телефона. Вариантов решенияданной проблемы я пока не нашел, поэтому нужно заг-рузиться в Windows и там удалить драйвера и ПО отэтого кабеля.

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

Также в VMWare можно загрузить установленные на же-стком диске и другие системы, в частности Linux. Здесь си-туация попроще и система спокойно может стартовать припомощи стандартных линуксовых загрузчиков lilo или GRUB.Для запуска Linux в качестве гостевой системы не требуетсяникаких дополнительных заморочек и профилей. Достаточ-но создать в VMWare новую гостевую систему и запуститьее. В качестве гостевой системы бралась Red Hat Linux AS3.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 г.

18

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

До недавнего времени острой необходимости в доступе кразделам с файловой системой NTFS, в общем-то, и не было.Ругать разработчиков ядра Linux не за что, необходимыеработы ведутся уже давно и отнюдь не безуспешно. Уже в1995 году для ядер серии 2.0 был доступен патч для работыс этой файловой системой, а с версии 2.2 (если быть точнее2.1.74) подержка NTFS в ядро была включена стандартно.Но все равно разработки велись несколько вяло. Причин томунесколько. Семейство пользовательских операционных си-стем Windows до Me включительно поддерживают исключи-тельно FAT, а NTFS использовалась на ОС корпоративно-серверного уровня, где вряд ли кто-то додумается поставитьодновременно две операционные системы на одном компь-ютере. Появление Windows 2000 практически ничего не из-менило, на домашних компьютерах ее устанавливали доволь-но неохотно, и если посмотреть на форумах тех времен, томожно отметить относительное спокойствие. А вот WindowsXP смог привлечь пользователя, этот момент и стоит счи-тать началом действительного интереса разработчиков к про-блеме. Многие (да почти все) производители включили под-держку NTFS в ядрах своих дистрибутивов. Проблем с дос-тупом к разделам NTFS не обнаружат пользователиMandrake, SUSE, ALTLinux, ASPLinux, Slackware, Debian ипрочих популярных дистрибутивов. Только компания RedHat,очевидно, руководствуясь лицензионной чистотой своего ди-стрибутива, не включила поддержку данной ФС. Поэтомупользователи RedHat и Fedora увидят при попытке монтиро-вания раздела сообщение, вынесенное в эпиграф.

На данный момент имеются два свободных проекта, по-своему решающие вопрос работы с разделами NTFS. Пер-вый, проект Linux-NTFS, предлагает традиционный подход,т.е. написание драйвера, который позволит нормально ра-ботать с этой файловой системой. Второй, проект CaptiveNTFS, пробует решить все эмуляцией системы NT.

Проект Linux-NTFSСейчас для GNU/Linux фактически существуют два драй-вера NTFS. Первый используется в ядрах серии 2.4.х и в2.5.0-2.5.10, этот драйвер имеет ограниченные возможно-сти по записи в раздел NTFS, и поэтому такой режим по-мечен как опасный, т.е. может привести к потере данных.Этот драйвер долго практически не развивался. Втораяверсия драйвера была фактически переписана зановоодним из разработчиков Антоном Алтапармаковым (AntonAltaparmakov), причем записи в раздел NTFS уделялосьповышенное внимание, драйвер стал более легким, про-стым и быстрым, поддерживаются NTFS версий 1.2, 3.0 и3.1, Unicod, сжатые файлы. Хотя в настоящее время име-

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

Теперь немного практики. В ядрах, начиная с 2.5.11 и,конечно же, в серии 2.6, используется новый драйвер, по-этому если у вас с поддержкой не сложилось, то простоперекомпилируйте ядро, включив нужные пункты. Так какв народе еще популярны ядра серии 2.4, то для их владель-цев доступен патч.

Текущую версию драйвера узнать очень просто:

Или такой вариант:

Как видите, ядро Slaksware 9.1, которое использовалосьво время написания статьи, поддерживает первую версиюдрайверов, но это довольно легко исправить. Если выводэтих команд ничего не дал, то дополнительно проверитьподдерживаемые ядром файловые системы можно, введя:

За драйверами, патчами, утилитами для работы с NTFSи документацией идем на сайт http://linux-ntfs.sourceforge.net.

Владельцам RedHat и Fedora можно идти сразу на стра-ницу http://linux-ntfs.sourceforge.net/rpm/index.html, где дос-тупны прекомпилированные rpm-пакеты с необходимымимодулями. Выбираем нужный и устанавливаем.

СЕРГЕЙ ЯРЕМЧУК

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

# dmesg | grep -i ntfs

# grep -i ntfs /var/log/messages

# cat /proc/filesystems

#rpm -ihv --noscripts kernel-ntfs-2.6.5-1.358.i586.rpm#/sbin/depmod -a

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

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

Например, мы хотим пересобрать ядро 2.4.25 с новымдрайвером.

Скачиваем по ссылке патч к используемому ядру, в моемслучае это linux-2.4.25-ntfs-2.1.6a.patch.bz2, и распаковыва-ем его.

Теперь распаковываем ядро, взятое с www.kernel.org,можно использовать и имеющееся в вашем дистрибутиве,только тогда возьмите и патч с соответствующим номером.

Накладываем патч:

В последнем случае не должно быть сообщений обошибках, иначе проверьте, правильно ли заданы пути. Итеперь приступаем к конфигурации ядра. Набираем makenenuconfig, в «File systems» необходимо включить пунктыдрайвера NTFS (рис. 1). Сохраняем конфигурацию и выхо-дим. После чего идет стандарная компиляция ядра и на-стройка загрузчика.

Выполнив все, перезагружаемся с новым ядром и про-веряем:

Как видите, версия драйвера обновилась и соответству-ет установленной; пробуем примонтировать раздел, для тогочтобы узнать, какой именно, вводим:

Монтируем раздел.

Обратите внимание, что ключ -t, указывающий на фай-ловую систему, в данном случае обязательный, иначе сис-

тема не сможет сама определить ее и выдаст примернотакое сообщение:

Для того чтобы просмотреть опции по умолчанию, с ко-торыми монтируется раздел, вводим:

Как видите, раздел смонтирован в режиме «чтение-за-пись», это можно изменить на «только чтение», явно задавв командной строке опцию -r (или -o ro). Владельцем явля-ется root (uid=0, gid=o), опция errors указывает, как будетсебя вести система при возникновении ошибок, поддержи-вается два варианта: continue (продолжает работу) и recover(пытается восстановить), в настоящее время поддержива-ется только замена boot-сектора резервным. Опции fmaskи dmask задают параметры доступа к файлам и каталогамсоответственно, возможно использование общей опцииumask, задающей доступ к файлам и каталогам одновре-менно. NTFS хранит имена в Unicode, но драйвер перево-дит их в ASCII; чтобы указать на используемый язык, при-меняются две конструкции: -o iocharset= или в новом вари-анте -o nls=. Для отображения русских имен используютсяkoi8-r, возможно задание utf8 (если ядро не поддерживаетUnicod, то дополнительно используйте utf8=true). В моемслучае nls был выбран автоматически, потому что при кон-фигурировании ядра эта кодировка была прописана поумолчанию (смотрите в File Systems – Native LanguageSupport – Default NLS Option). Параметр mft_zone_multiplierуказывает на размер зарезервированной в master file tableчасти, которая содержит информацию о файле. Примеча-тельно, что маленькие файлы полностью помещаются вMFT, что позволяет быстро его находить и избежать потерьдискового пространства присущих FAT (в FAT файл разме-ром 1 байт займет на диске минимум 4 Кб). Первоначальнозадается при форматировании, но в процессе эксплуата-ции может изменяться на лету. Цифра 1 является значени-ем по умолчанию и соответствует 12.5% зарезервирован-ного объема, 2 – 25%, 3 – 37.5% и 4 – 50.0%. Полная опциямонтирования может быть указана в таком виде:

часть после второй -o, вообще говоря, не нужна и дана дляпримера.

После этого с чтением данных проблем быть не долж-но, все имена будут отображаться нормально.

Дополнительно проект Linux-NTFS предоставляет и рядутилит для работы с NTFS из-под Linux, библиотеку libntfs,обеспечивающую доступ к функциям NTFS программ, в томчисле и других разработчиков, а также libntfs-gnomevfs –модуль для Gnome VFS (virtual filesystem), обеспечивающийуниверсальный доступ ко всем файловым системам. Боль-шая часть утилит ориентирована скорее на разработчиков,но годом раньше вообще ничего подобного не было, про-гресс налицо. Все они доступны в пакете ntfsprogs, который

# bunzip2 linux-2.4.25-ntfs-2.1.6a.patch.bz2

#cd /usr/src

#cd linux#patch -p1 < ../linux-2.4.25-ntfs-2.1.6a.patch

Ðèñóíîê 1

#grep -i ntfs /var/log/messages

# fdisk -l | grep -i ntfs

# mount /dev/hda7 /mnt/temp/ -t ntfs

# cat /proc/mounts | grep -i ntfs

#mount /dev/hda7 /mnt/temp/ -t ntfs -r -o nls=koi8-r -o ↵↵↵↵↵uid=500,gid=winuser,umask=0222

20

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

распространяется как в исходных кодах, так и имеются пре-компилированные пакеты. Установка из исходников проблемне вызывает, все те же стандартные ./configure; make; makeinstall. В результате в системе появится еще 10 утилит:! ntfsfix – для установки измененных драйвером разде-

лов NTFS, нечто вроде scandisk, который должен исполь-зоваться после каждой записи (особенно со старымдрайвером) во избежание возможной потери данных,для того чтобы привести файловую систему в непроти-воречивое состояние.

! mkntfs – для создания NTFS 1.2 (поддерживается всемиWindows NT/2000/XP).

! ntfscat – является аналогом стандартной UNIX-утилитыcat, предназначен для чтения файлов в разделах NTFS.

! ntfsclone – предназначена для клонирования (копирова-ния, сохранения, создания резервного образа и восста-новления) раздела с файлововй системой NTFS. Рабо-тает на уровне секторов диска и сохраняет только ис-пользуемые данные, неиспользуемые заполняются ну-лями, что позволяет эффективно сжимать полученныеобразы. Полезна для создания точных копий раздела ивосстановления системы. Примеры:! Создание копии раздела:

! Восстановили раздел:

! Так можно заглянуть внутрь созданного образа:

! ntfscluster – идентификация файлов в указанном разде-ле или области NTFS. Работает в трех режимах:! info (режим по умолчанию) – покажет общую инфор-

мацию об области NTFS;! sector – покажет список файлов в заданном диапа-

зоне секторов;! cluster – то же, что и предыдущий, только выводит

список файлов в группе.

! ntfsinfo – выводит атрибуты по номеру inode или именифайла.

! ntfslabel – выведет или установит метку файловой сис-темы NTFS (метка до 128 Unicode-знаков).

! ntfsls – аналог UNIX-утилиты ls (Windows – dir) – выво-дит список файлов в разделе NTFS (монтировать нео-бязательно).

! ntfsresize – а вот это действительно полезная утилита,предназначена для изменения размера файловой сис-темы NTFS без потерь данных. Примечание: утилита неманипулирует размерами разделов, для этого необхо-димо воспользоваться утилитой fdisk. Как работать сней, поговорим дальше.

! ntfsundelete – восстановление удаленных файлов на раз-деле NTFS. Имеет три режима работы:! scan (режим по умолчанию) – просмотр файловой

системы на предмет наличия удаленных файлов, принахождении выводит список таких файлов;

! undelete – пытается, насколько это возможно, восста-новить утраченные данные (кроме сжатых и зашиф-рованных файлов), файл сохраняется в другой раздел;

! copy – полезен большей частью при отладке, сохра-няет данные MFT в файл.

Сообщение «Volume is dirty» может возникнуть после за-писи в раздел, изменения размера раздела и других воз-можных операций, связанных с изменением данных, послекаждой такой операции во избежание несоответствия ре-комендуется проверка раздела средствами Windows.

#ntfsclone --output system.img /dev/hda1

#ntfsclone --overwrite /dev/hda1 system.img

#mount -t ntfs -o loop system.img /mnt/ntfsclone

#./ntfscluster /dev/hda7

# ./ntfslabel -v /dev/hda7

# ./ntfsls -v -d /dev/hda7

#./ntfsundelete /dev/hda7

#./ntfsundelete /dev/hda7 --force

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

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

Пробуем восстановить один из найденных файлов:

В настоящее время начата разработка еще несколькихутилит, некоторые из них находятся в стадии альфы. Этоntfswipe – позволит зачистить нулями свободные части дис-ка, ntfsdefrag – дефрагментатор файлов, каталогов и MFT,для проверки диска будет использоваться ntfsck, nttoolsпозволит просмотреть/создать/изменить/копировать/найтитребуемые значения (эквивалентны командам ntfscp,ntfsgrep, ntfstouch, ntfsrm, ntfsrmdir, ntfsmkdir). И пока в пла-нах еще одна утилита ntfsdiskedit, которая позволит рабо-тать с дисковыми структурами NTFS.

Начиная с Windows 2000, файловая система получилановое понятие – динамический диск, который пришел насмену стилю разделов, принятых еще в MS-DOS (BasicDisks), и позволил снять все ограничения файловой систе-мы, в том числе и создавать многодисковые тома. Инфор-мация хранится в журналируемой базе данных Logical DiskManager (LDM) в последнем 1 Мб диска. Начиная с версииядра 2.5.29, в него включаются драйвера для работы с LDM,для версий 2.4.19 и 2.4.20-pre1-ac1 доступен патч. Отдель-но в пакете linux-ldm доступны две утилиты для работы сLDM. Первая ldminfo выводит детальную информацию оLDM-базе, ldmutil – предназначена для восстановления, ре-зервирования и изменения LDM-баз. Планируется в бли-жайшее время начать работу над библиотекой ldmlib, в ко-торую будет вынесена часть кода, и еще над несколькимиутилитами, которые позволят работать с отдельными час-тями раздела LDM и записывать/читать информацию сбазы. Как видите, хотя работа над новыми драйверами иутилитами идет полным ходом, но, увы, на данный моментони не могут обеспечить пользователя требуемой (полной)функциональностью, и, как говорится, нормальные героивсегда идут в обход, что и сделали разработчики в проек-те, о котором пойдет речь дальше.

Проект Captive NTFSТема эмуляции работы операционных систем, и в частно-сти Windows, довольно популярна в среде Linux/UNIX, до-статочно вспомнить проекты вроде Wine или CrossoverOffice, поэтому не удивительно, что нашлись разработчи-ки, попытавшиеся заставить работать 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 нормаль-

но работает не со всеми версиями драйверов Windows (пол-ный список проверенных в работе доступен на сайте), вовсяком случае со взятыми с русифицированной версииWindows XP с первым сервис-паком работа у меня не по-шла. В этом случае придется тащить файлы с сайта Microsoft,где они находятся по адресу: http://www.microsoft.com/WindowsXP/pro/downloads/servicepacks/sp1/checkedbuild.asp,с которыми captive работает отменно.

Правда, ситуация с их использованием двоякая, с од-ной стороны, они лежат свободно, с другой – предназначе-ны для пользователей Windows, во всяком случае во всехдистрибутивах, имеющих captive, их приходится добыватьсамостоятельно. Для работы их следует положить в ката-лог /var/lib/captive. Так как драйверы Windows требуют осо-бых привилегий вроде прямого доступа к железу, то пол-ную 100% эмуляцию реализовать не получится, т.к. UNIX-системы, естественно, будут защищаться от процессов, ко-торые лезут не в свое дело, поэтому эмулируемая средаотделена от остальной части UNIX и любой код, выполняю-щийся в этой среде, не должен привести к краху системы.Для защиты от возможного краха используются несколькотехнологий. Так, для работы потребуется модуль ядра LUFS(Linux Userland File System), который, если будете собиратьиз исходников, нужно взять с http://lufs.sourceforge.net/lufs.Во время установки создается новый пользователь и груп-па captive, от имени которых и будут работать процессы, асам процесс по умолчанию запускается в изолированойCORBA sandbox среде и chroot-окружении. К сожалению,эти ограничения сказались на невозможности работы од-новременно сразу с несколькими разделами. Драйверы жена сайте проекта captive доступны как в исходных кодах,так и в прекомпилированном виде со статической линков-кой, работают они одинаково, проблема может быть толь-ко с модулем ядра lufs.o, который должен быть собран подопределенную версию ядра. После установки для первона-чального конфигурирования необходимо запустить утили-ту captive-install-acquire (рис. 2), которая проверит наличиенеобходимых библиотек и при необходимости закачает всенужное.

#./ntfsundelete /dev/hda7 -s -m home.jpg --force

Ðèñóíîê 2

22

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

Если все есть, то можно монтировать раздел:

В случае ошибок вся информация доступна /var/log/messages.

Если требуется автоматическое монтирование при заг-рузке системы, используйте скрипт captive-install-fstab с па-раметром -add, который автоматически добавит в файл /etc/fstab используемый раздел.

Еще пару слов хочу сказать о коммерческом драйвереот Paragon Software Group, обеспечивающем прозрачныйдоступ к разделам с файловой системой NTFS. Доступендрайвер в двух версиях: персональной и профессиональ-ной. Как и у предыдущих проектов, поддерживаются всеверсии файловой системы NTFS, сжатые файлы и катало-ги, размеры дисков до 127 Гб, в персональной версии ра-бота возможна только в режиме чтения, а в професиональ-ной возможна запись (что, в общем, и не должно вызыватьудивления; учитывая опыт работы этой компании на подоб-ном поприще). Демо-версию драйвера, которую произво-дитель разрешает использовать без регистрации в тече-ние 30 дней и поддерживающую только чтение, можно ска-чать с http://www.ntfs-linux.com.

Для установки потребуются исходники ядра. Установкапроста до безобразия: после распаковки архива запуска-ем скрипт install.sh (возможен запуск в интерактивном ре-жиме ./install.sh –interactive и при помощи –iocharset=koi8-rвозможно установить кодировку по умолчанию для разде-ла NTFS).

После этого, если не выбрано автоматическое монти-рование при загрузке, можно примонтировать раздел вруч-ную:

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

источником, т.к. в некоторых дистрибутивах вам позволятизменить раздел только с командной строки (например, заб-равшись во вторую консоль [Ctrl]-[Alt]-[F2]). Для работыntfsresize драйвер поддержки NTFS в ядре не нужен, ути-лита обращается напрямую к диску. Также если не требу-ются все утилиты или хотите использовать ее в спасатель-ной дискете, то можно взять статически слинкованную вер-сию ntfsresize по адресу: http://mlf.linux.rulez.org/mlf/ezaz/ntfsresize-static-1.9.1.tgz.

Теперь посмотрим, что у нас есть:

Команда выдала все о разделе NTFS и сообщила, чтоможем уменьшить раздел вплоть до 5 Мб. Но перед реаль-ным изменением желательно прогнать тест.

Если в результате получим сообщение «The read-onlytest run ended successfully.», то можно смело приступать кизменению размера. Если же команда выдала «ERROR:»,то лучше для начала исправить ошибки, спешка в данномслучае ни к чему хорошему не приведет.

Если все данные сохранены, то соглашаемся:

Проверить работу можно, введя:

# mount -t captive-ntfs /dev/hda7 /mnt/utils/

#cat /var/log/messages | grep captive-lufs

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

# ./ntfsresize -i /dev/hda7

# ./ntfsresize --no-action --size 500M /dev/hda7

# ./ntfsresize -s 500M /dev/hda7

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

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

Как видите, утилита свою работу проделала, но изме-нила только размер самой файловой системы NTFS, раз-мер же дискового раздела остался неизменным (что мож-но легко проверить, введя: fdisk -l | grep -i ntfs), и далее,следуя инструкции, необходимо проделать еще несколькошагов.

Неплохо бы для начала на всякий случай сохранить MBR.

Запускаем fdisk, конечно, было бы нагляднее восполь-зоваться cfdisk, но если указать ему новое значение разде-ла в мегабайтах, то он округлит до ближайшего значения,которое, вполне возможно, будет меньше требуемой вели-чины, что приведет к невозможности работать с разделом,fdisk же корректно обрабатывает ситуацию.

Удаляем раздел, на котором размещается NTFS, в моемслучае это 7.

Создаем на его месте раздел с новым размером 500 Мб,при этом начальный цилиндр обязательно должен совпа-дать, т.е. судя по выводу выше – 3522.

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

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

Если все выше проделывалось с LiveCD или дискетногодистрибутива, то сразу же можно просмотреть информа-цию о разделе.

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

Но для нормальной работы обязательно нужно прове-рить файловую систему средствами Windows.

Уже появились и графические утилиты, в том числе иинсталляторы, позволяющие изменить раздел в наглядноми понятном любому пользователю виде, которые использу-ют код ntfsresize. К таким приложениям относятся DiskDrakeот MandrakeSoft, который может встретиться и в другихпроизводных от Mandrake дистрибутивах, YaST от SUSEдоступный и после установки. Некоторые дистрибутивыиспользуют коммерческие утилиты для разбиения разде-лов ASPLinux – PartitionExpert, Xandros использует PQDisk.Но самой известной, распространенной и, главное, свобод-ной графической утилитой, предназначенной для разбие-ния диска, является QTParted (http://qtparted.sourceforge.net,рис. 3). Те, кто пользовался PartitionMagic, трудностей с ос-воением не встретят. Выбираем нужный раздел, далее пра-вый клик – Resize (рис. 4 ), полозком или цифрами выстав-ляем новый размер и подтверждаем изменения File →→→→→Commit.

К сожалению, можно сделать вывод, что работа GNU/Linux с файловой системой NTFS еще далека от идеала, аучитывая трудности написания вслепую драйвера и пробле-мы с полной эмуляцией, когда будет окончательное реше-ние, сказать пока трудно. Но как видите, по сравнению спредыдущими годами, сдвиги есть, и работа ведется уси-ленными темпами. Так, сайт Linux-NTFS уже давно не об-новлялся, потому что разработчики сосредоточились наконечном результате. Будем надеяться, что он не заставитсебя долго ждать.

#./ntfsresize --info --force /dev/hda7

# dd if=/dev/hda of=hda.mbr bs=512 count=1

# fdisk /dev/hda

# ./ntfsresize -i -f /dev/hda7

Ðèñóíîê 4

Ðèñóíîê 3

24

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

Статья Сергея Яремчука «Свободный антивирус», опубли-кованная в мартовском номере журнала «Системный ад-министратор» 2004 года, вдохновила меня на установку это-го пакета на вверенном мне сервере. Однако выяснилось,что под FreeBSD этот процесс происходит несколько ина-че, чем под Linux, что и стало поводом для написания дан-ной статьи. Впрочем, ничего сложного нет, поэтому мате-риал ориентирован скорее на новичков.

Антивирус ClamAV имеется в коллекции портов FreeBSD,а потому вполне разумным мне показалось выполнить ус-тановку именно оттуда. Ряд проблем, с которыми пришлосьстолкнуться, показал, что ClamAV портирован на эту ОС несовсем корректно. Но обо всем по порядку…

Первым делом я отправился в /usr/ports/security/clamav.Чтение Makefile позволило узнать, что предлагаемая вер-сия – 0.73, что выглядит довольно хорошо (последняя натот момент была 0.74, но, как известно, «последнее» – незначит «лучшее»). Там же узнаем опции, с которыми пакетбудет сконфигурирован:

Опцию --disable-clamav, отменяющую работу от именипользователя clamav, я решил убрать – зачем пренебре-гать дополнительной безопасностью? Ну и поскольку сис-темные администраторы – люди исключительно бескорыс-тные, и все их заботы и труды – о пользователях и радипользователей, то была добавлена поддержка milter для пос-ледующей интеграции нашего антивируса с sendmail:

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

Попытка собрать систему вышеприведенной опцией ибез нее не обнаружила никаких различий. Более того, вbsd.ports.mk упоминания параметра OPTIONS не нашлось.Возможно, во FreeBSD он не используется.

Полагая, что подготовительные работы закончены и всеостальное инсталлятор сделает сам, была подана командаmake. И тут случилась первая аномалия: утилита, как и по-ложено, попыталась скачать дистрибутив пакета, но толь-ко с одного сайта – с ftp.freebsd.org, и заявила об отсут-ствии там нужного ей файла clamav-0.73.tar.gz. Раньше ятакого не видел – на сервере операционной системы все-гда в папке distfiles обнаруживался любой нужный портамархив. Пришлось забирать требуемый файл с сайта проек-та clamav.sourceforge.net.

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

Сборка завершилась успешно, инсталляция тоже особыхпроблем не породила, не считая того, что с первого разавывалилась ошибка, а повторный make install, запущенныйдля детального изучения проблемы, завершился без про-блем. Видимо, с первой попытки не удалось закачать ка-кой-то вспомогательный пакет. На стадии инсталляции тожечастенько докачиваются и устанавливаются вспомогатель-ные пакеты, которые для сборки не нужны, но используют-ся в дальнейшей работе. В частности, для clamav именнона этом этапе происходит установка портов unarj, unrar и т. д.,если их нет в системе, а проверка архивов включена.

На первый взгляд все выглядело весьма респектабель-но: конфигурационные файлы clamav.conf и freshclam.confчинно лежали в /usr/local/etc. В первом я включил заком-ментированные по умолчанию опции LogTime, выводящуюв лог время события (лог при этом растет вдвое быстрее,зато понятно, что и когда происходит), и ScanRAR, разре-шающую проверку rar-архивов (проверка остальных поумолчанию включена). Директория /usr/local/etc/rc.d обза-велась еще двумя сценариями, призванными автоматичес-ки запускать при перезагрузке компьютера демон clamd иутилиту обновления баз freshclam.

Запуск clamscan продемонстрировал его работоспособ-ность и вселил надежды на светлое будущее:

Однако сильно смущало время обработки – свыше 10секунд для 20 Кб проверенных файлов (конфигурация сер-вера – Celeron-466/64Мб, load average ~ 0.1). Может, дело вкаких-то «подготовительных работах» вроде подгрузкибазы данных, и на больших объемах дело пойдет лучше?Действительно, проверка на папке /usr/ports/distfiles пока-зала некоторое увеличение скорости:

ЕЩЕ РАЗ О ClamAV:ОСОБЕННОСТИ УСТАНОВКИ ВО FreeBSD

СЕРГЕЙ СУПРУНОВ

CONFIGURE_ARGS= --with-dbdir=${DATADIR} ↵↵↵↵↵--disable-clamuko --disable-clamav ↵↵↵↵↵--enable-bigstack --disable-dependency-tracking

CONFIGURE_ARGS+= --enable-milter

OPTIONS= MILTER “Compile the milter interface” off

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

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

сокетов соответствуют друг другу в строке запуска clamav-milter и в sendmail.mc.) С сокетом clamd.sock таких манипу-ляций не понадобилось, поскольку демон успешно убиваетстарый сокет сам, лишь сообщив в лог-файл об этом. Каквидно из файла, помимо демона clamd запускается иclamav-milter. А вот в вопросе обновления баз я решил надемонизацию freshclam не полагаться (хотя такая возмож-ность предусмотрена) – зачем запускать еще один демон,когда с этим отлично справится cron:

Настройка sendmail была выполнена в полном соответ-ствии с документацией и проблем не вызвала (см. статьюСергея Яремчука). Нужно только не забыть перезапуститьсервер (make restart в /etc/mail).

Итак, затратив немного усилий, удалось заставитьclamav работать так, как это принято во FreeBSD. Конечно,если бы порт был разработан более корректно, то и этиусилия не понадобились бы.

В первые часы работы было «зарезано» около 50 зара-женных писем (напомню, что лечить файлы ClamAV покане умеет), извещения о данном прискорбном факте былидобросовестно разосланы отправителям, получателям и ад-министратору. Огорчало одно – язык сообщений был анг-лийским, а зная своих пользователей, нетрудно было спрог-нозировать раскаленный телефон службы технической под-держки.

Но и эта проблема оказалась более чем решаемой: всесообщения, отсылаемые пользователям, были сосредото-чены в исходном файле clamav-milter.c и насчитывали неболее пяти строк. Таким образом, «русификация» свеласьк простому вбиванию новых слов взамен старых. Ну и взаголовок отсылаемого письма была добавлена (в том жеclamav-milter.c) строчка «Content-Type: text/plain; charset=”koi8-r”», поскольку без нее мой Outlook упорно пыталсяподсунуть мне письма под видом win-1251. После правкиисходника – повторная сборка и установка. Результат пред-ставлен на рисунке.

В заключение следует заметить, что проблемы, описан-ные в статье, вполне вероятны при попытке запустить наFreeBSD и иные приложения, первоначально разработан-ные для другой ОС. Методы решения большинства из нихбудут аналогичными. Главное – хорошо представлять себе,что должно получиться в итоге.

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

Оказалось, что стартовые сценарии разработаны в сти-ле Linux и для запуска из /usr/local/etc/rc.d не годятся. Пере-носить их в /etc/rc.d с добавлением соответствующих оп-ций (приведенных в начале стартовых скриптов) в файл/etc/rc.conf я посчитал неправильным. Как бы то ни было,но место всех программ, устанавливаемых пользователем, –в /usr/local.

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

Удалять clmilter.sock я решил вручную, поскольку натол-кнулся на то, что иногда он удаляется с некоторой паузой(или не удаляется вообще) в результате уничтожения де-мона clamav-milter, и при повторном запуске milter возника-ет ошибка. (Кстати, следует убедиться, что имена и пути

/usr/local/etc/rc.d/clamav-clamd.sh start

#!/bin/sh# Manager for ClamAV: clamd & clamav-miltercase $1 in

start)if [ -e /var/run/clamav/clamd.pid ]; then

echo 'Ingored - clamd already started.'exit 0

fiif [ -e /var/run/clamav/clmilter.sock ]; then

rm /var/run/clamav/clmilter.sockfi/usr/local/sbin/clamd/usr/local/sbin/clamav-milter -blo ↵↵↵↵↵

/var/run/clamav/clmilter.sockecho 'clamd & clamav-milter started';;

stop)killall clamav-milterkill -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;;

esacexit 0

0 0,12 * * * /usr/local/bin/freshclam

26

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

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

CROSSOVERИ

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

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

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

В ответ на публикацию трилогии о CrossOver Office в мойпочтовый ящик посыпались читательские письма с раз-нообразными вопросами. Сегодня мы займемся самым ча-сто встречающимся из них: «Объясните мне смысл поку-пать этот продукт, если есть wine».

Wine – это, конечно, хорошо, но в некоторых областяхCrossOver на данный момент впереди планеты всей. На-пример, продвинутая поддержка InstallShield, удобство ин-терфейса, простота установки и самое главное – оченьдружелюбная служба технической поддержки. Впрочем,это уже индивидуальные вкусовые предпочтения. Боль-шинство читателей в силу особенностей менталитета бо-лее всего интересует вариант, чтобы можно было и рыбкусъесть, и косточки сдать. То есть получить все бесплатно.Моралисты всех мастей сейчас начнут качать головой иговорить: «Ой, как нехорошо». Ну а мы посмотрим, какдобиться решения поставленной задачи. Предположим,что у нас установлена версия CrossOver Office c ограни-чением на 30 дней. Проявив немного любознательности,можно заметить, что после инсталляции у нас на жесткомдиске появился файл /var/run/cxoffice/.evaluation. Хотя в не-которых дистрибутивах Linux этот файл может находить-ся, к примеру, в домашней директории пользователя. Длятого чтобы пробный период никогда не закончился, нужновсего лишь удалять его раз в несколько дней. Данный трюкработает на основе того, что начало тестового периодавсегда отсчитывается от даты создания вышеупомянуто-го файла. А если его на диске нет – значит программа счи-тает, что запущена в первый раз и пользователю полага-ется законные тридцать пробных дней. Насколько я пони-маю, с юридической точки зрения тут нет никаких проблем.Владелец компьютера имеет полное право удалять любыефайлы, хранящиеся на жестком диске личного компьюте-ра. Ну а тот факт, что защита программы от этого будетнеправильно работать, однозначно относится к личнымпроблемам разработчика.

В то же время можно пойти совсем другим путем и статьофициальным пользователем коммерческой версии CrossOver Office совершенно бесплатно. И самое главное, чтомы не только соблюдаем все законы, что само по себедостойно всяческих похвал, но еще и помогаем развитиюпроекта. Для этого нужно, вооружившись именем пользо-вателя и паролем, которые нам выдали при первичной ре-гистрации, войти на сайт http://www.codeweavers.com/login.Затем ознакомиться с документацией по центру совмес-тимости приложений http://www.codeweavers.com/site/compatibility. Центр создавался с одной-единственной це-лью облегчить взаимодействие между разработчиками ипользователями продукта. Соответственно посещать егов любом случае полезно, но мы сейчас не об этом. В со-ставе центра находится список приложений, которые такили иначе работают под CrossOver Office. Посмотреть наего наполнение можно здесь: http://www.codeweavers.com/site/compatibility/browse/name. С выходом новых версийCrossOver Office из-за изменений, вносимых в код, одниприложения начинают работать лучше, а другие наобо-рот – хуже, соответственно, разработчики проекта заин-тересованы в большом количестве бета-тестеров. Такимобразом, можно будет очень быстро заметить, насколько

ухудшилось самочувствие той или иной программы и погорячим следам провести корректировку кода системы.Тут мы как раз и подходим к самому интересному. Бета-тестеров проекта называют адвокатами приложений потой причине, что каждый из них следит за самочувстви-ем дорогих ему сердцу программ. Подробнее почитать опроцессе адвокатства и получаемых от него выгодахможно тут – http://www.codeweavers.com/site/compatibility/advocate_overview. Если говорить коротко, то адвокатыполучают в свое распоряжение новейшие версии коммер-ческих разработок CrossOver Office. Все отчеты от адво-катов по поводу тех или иных проблем, возникающих впроцессе эксплуатации программного продукта, попада-ют на рассмотрение технической службы вне очереди.Плюс ко всему возможность напрямую общаться с нуж-ными вам людьми из команды СodeWeavers. Также при-ветствуется голосование за интересные для вас приложе-ния, таким образом можно стимулировать разработчиковуделять больше внимания именно этой программе.

В обмен на такие услуги адвокат должен заниматьсятестированием своего приложения на новых версияхCrossOver Office, помогать другим пользователям, у кото-рых возникли проблемы установки или использования дан-ного приложения, активно поддерживать форум приложе-ния, участвовать в дискуссиях по поводу тех или иных про-блем системы и, наконец, отправлять отчеты о найденныхошибках. Думаю, это небольшая плата за возможностьпользоваться коммерческой версией CrossOver Office, ктому же все это происходит на добровольных началах, по-этому не стоит ожидать, что кто-то будет стоять над вамис палкой и требовать отработать n-ое количество челове-ко-часов на благо проекта.

Все, кто захотел стать адвокатом и тем самым помочьпроекту, могут сделать это прямо сейчас. В списке про-грамм нужно выбрать то приложение, которое нравитсявам: http://www.codeweavers.com/site/compatibility/browse/name.

В качестве примера давайте используем 1C Предпри-ятие, у которого пока что нет адвокатов ht tp:/ /www.codeweave rs . com/s i t e / compa t i b i l i t y / b rowse /name?app_id=298. На страничке приложения нажать нассылки «Want to help?» затем «help CodeWeavers» и, на-конец, «Make Me an Advocate». В течение нескольких бли-жайших дней ваша заявка будет рассмотрена и, скореевсего, удовлетворена. О назначении вас адвокатом мож-но узнать, периодически заходя на страницу приложения.В случае если интересующих вас приложений в базе най-ти не удалось, есть возможность добавить их самому:http://www.codeweavers.com/site/compatibility/submit/ и ужезатем стать адвокатом. Кстати, стоит отметить, что ко-личество адвокатов, работающих над одним приложени-ем, теоретически не ограничено, но на практике большепяти не бывает.

Став адвокатом, можно будет получить доступ к внут-ренним форумам и разделу с новейшим программным обес-печением: http://www.codeweavers.com/site/compatibility/advocate_center/. Также стоит подписаться на списки рас-сылки: http://www.codeweavers.com/site/support, довольночасто там обсуждают интересные проблемы.

28

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

Что же из себя представляет Darwin? Если коротко – этонекое ядро операционной системы MacOSX, оно состоитиз 5 главных компонентов: микроядро Mach, BSD-подсис-тема, файловая система, сетевая подсистема и системаввода-вывода (I/O Kit).! Микроядро Mach занимается распределением вычисли-

тельных ресурсов, защитой памяти, обменом сообще-ний между процессами.

! Вокруг микроядра существует «обертка» из POSIX API,абстрактная файловая система и сетевая подсистема.Многое в микроядре заимствовано из 4.4 BSD-Lite2, со-ответственно модель процессов, система безопаснос-ти, потоковая поддержка покажутся знакомыми всем,кто работал с BSD-системами.

! Файловая система поддерживает как UNIX (UFS), так и«родную» маковскую файловую систему (HFS).

! TCP/IP-стек основан на проверенном временем BSD-коде.

! Объектно-ориентированная система ввода/вывода пред-ставляет развитую инфраструктуру для управлениядрайверами устройств.

К сожалению, графическая подсистема MacOSX (QuickTime, OpenGL, Quartz), пользовательский интерфейс (Aqua)не присутствуют в системе. Хотя и без них Darwin являетсяуже полноценной ОС. Более подробно об истории возник-новения Darwin см. [2].

В ЯБЛОЧКО!КРАТКИЙ ОБЗОР ОС DARWIN 7.0НА ПЛАТФОРМЕ X86 (MAC OS X 10.3 JAGUAR)

АНТОН БОРИСОВ

Полгода прошло с момента выхода в светпоследнего релиза упомянутого в заголовкепродукта. Президент Apple Стив Джобсподтвердил, что MacOSX действительноможно запустить на x86-платформе [1].Давайте разберемся, что же может привлечьпотенциального пользователя/администратора,помимо легендарного названия фирмы AppleComputer? В первую очередь, ориентированиекомпании на постепенное расширение своейпродукции. Как известно, на протяжении своейистории операционные системы фирмы Appleработали только на оборудовании самой Apple.Отчасти это правильно. Приходится меньшеволноваться о вопросах совместимостиаппаратного и программного обеспечения.В то же время это и откровенный тупикв развитии.

Дискутировать относительно открытости продуктов са-мой фирмы Apple можно долго, однако нас в первую оче-редь должно волновать несколько моментов:! списки совместимости с оборудованием (так называе-

мые hardware compatibility list [3]);! знание структуры *BSD-систем;! потребность в использовании данной ОС в обучающем

процессе на производстве.

Насчет первого пункта сильно огорчаться не следует,т.к. система стартует и работает на более-менее современ-ной x86 машине. В общем-то, Intel Celeron 366, 128 Мб па-мяти должно хватить для ознакомления. Также следует об-завестись одной из следующих сетевых карт: 3Com 3c90x,Intel 8255x, Broadcom 570x, Realtek 8139, Dec 21x4 (она жеTulip). Встроенные сетевые карты, к сожалению, не поддер-живаются. Видеокарта должна работать в VESA-режиме.

Для начала возьмем дистрибутив с сайта OpenDarwin[4]. Он из себя представляет упакованный gzip-образ, кото-рый потом надо записать на компакт-диск. Размер упако-ванного образа составляет 430 Мб.

Для дальнейшей работы потребуется пустой жесткийдиск размером не более 2 Гб или такого же объема раздел.

wget -ct0 http://opendarwin.org/downloads/darwin-701.iso.gzgzip -d darwin-701.iso.gzcdrecord -v darwin-701.iso

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

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

И запустив /sbin/lilo, перезаписываю MBR. Теперь пристарте можно будет выбрать загрузку с раздела hdd2, гдеобитает OpenDarwin.

Всё. Выставим флаг активности раздела (команда«active ID», где ID – опять-таки нужный нам номер разде-ла). И завершим разметку подав команду «exit».

После этого осталось дождаться, чтобы установилисьпакеты, входящие в состав компакт-диска. Как говорится,возьмите чай, кофе – сахар по вкусу.

В ходе установки внимание обращает тот факт, что всепакеты находятся в упакованном виде. Сжаты они с помо-щью bzip2. После установки истинно «яблочных» пакетов,таких как AppleUSBAudio, следом идут пакеты с до болизнакомыми названиями. Согласитесь, что такие строчки,как apache-670tar.bz2, bind9-7tar.bz2, gcc-1495tar.bz2, вамо чем-то говорят. В этом нет ничего удивительного, т.к. си-стема, как вы, наверное, слышали, базируется на BSD.

Большинство утилит, присутствующих в системе, хоро-шо знакомы людям, постоянно работающим в Open/Net/FreeBSD, да и тем, кто когда-либо занимался сборкой про-грамм под UNIX.

Вернемся к нашему яблоку. Перегружаемся, не забы-ваем вытащить из привода компакт-диск. Далее загрузкабудет происходить с нашего жесткого диска.

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

Итак, строка externalClock = 0x85 означает, что шинаработает на частоте 133 МГц (переведите цифры в деся-тичное представление). Строка currentClock = 0x5ba разъяс-няет, что текущая тактовая частота равна 1466 МГц, а мак-симальная частота для этого типа процессора равна 2250МГц (maximumClock = 0x8ca).

Стоит разъяснить, откуда появляется надпись «hfs_mount: invalid HFS+ sig 0x0000». Вы помните, когда мы ста-вили идентификатор раздела, то поставили 0xA8 (DarwinUFS). В связи с чем делаем вывод, что корневой раздел унас отформатировался в UFS-формате. Ничего страшно-го, кроме досадного факта, что файловая система у насполучилась нежурналируемая. Для того чтобы корневаяфайловая система была в HFS+ формате, при начальнойразметке диска следует ставить идентификатор 0xAF (вме-

Установленная система занимает примерно 1 Гб. Ос-тавшееся место будем использовать для сборки необходи-мых пакетов.

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

Пункт 1 предназначен для установки на пустой диск. Вслучае, когда не надо иметь на диске несколько систем,выбирайте его. Пункт 3 предназначен для случая, когдаместо заранее выделено и размечено под операционнуюсистему Darwin (ее идентификатор 0xA8, см. далее по тек-сту).

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

Выбираем пункт 2. Для начала следует ознакомиться скомандами fdisk данной системы.

Правильно, нажимаем слово «help» и читаем, что естьв меню. Команд немного, поэтому можно набирать не пол-ную версию, а, например, сокращать до 2 начальных букв.То есть вместо «help» писать «he».

Команды прочитаны, пора выделить место под нашесегодняшнее яблочко. Распечатаем содержимое MBR дис-ка, на который будем ставить ОС. Для этого подаем коман-ду «print». Видим теперь, где находятся разделы и свобод-ное место. Для редактирования свободного места подадимкоманду «edit ID», где ID – это номер раздела, предполо-жим под номером 2 (то есть на текущем диске первичныйраздел номер 2). Процедура выбора необходимого разме-ра для нового раздела не составит, я надеюсь, особого тру-да. Отмечу, что размер считается в секторах, а не в кило-байтах. Идентификатор файловой системы для Darwin име-ет код 0xA8 (Darwin UFS).

Затем следует обновить информацию в MBR (MasterBoot Record). Если вы ставите на чистый диск, то в MBRникакого загрузчика нет. Поэтому следует записать в негоштатный, идущий в комплекте с OpenDarwin. В случае ког-да вы устанавливаете на отдельный раздел, все равно при-дется перезаписать MBR. В этом нет ничего страшного, т.к.дальше в статье разъясняется на примере LILO, как сде-лать загрузку нескольких операционных систем.

Для этих целей предусмотрена команда «update». Пос-ле этого запишем в MBR непосредственно сведения о раз-деле, в котором будет жить наша «яблочная» операцион-ная система. Сделаем это с помощью команды «write».Следует помнить, что в MBR записывается новый загруз-чик, поэтому если у вас стоял LILO, GRUB или что-то ана-логичное, то он будет просто-напросто переписан загруз-чиком от Darwin. У меня используется LILO, поэтому я про-сто добавил в /etc/lilo.conf следующую строчку:

# DarwinOS beginsother = /dev/hdd2label = DarwinOS# DarwinOS ends

30

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

сто 0xA8). Перевести систему из UFS в HFS+ пока не пред-ставляется возможным.

Процесс загрузки ОС кардинально не отличается отпроцесса загрузки системы на FreeBSD. Знакомые конфи-гурационные файлы в /etc (rc, rc.boot, rc.common, fstab,syslog.conf и т. д.).

Итак, перед нами приглашение на вход в систему. За-ходим под пользователем root (пароль изначально не уста-новлен). Что необходимо сделать на этом этапе? Попробу-ем поднять сеть? Давайте сделаем!

Даем команду:

Ага, вот и наша сетевая карта (интерфейс en0). Естьдва пути – правильный и не очень правильный. Чтобы про-писать в Darwin IP-адрес для интерфейса, в /etc/ надо со-здать файл iftab. Структура его следующая:

где 10.0.0.10 – IP-адрес, 255.255.255.0 – маска сети, «up» –интерфейс при загрузке поднять.

Стоит отметить, что файла /etc/rc.conf в Darwin простонет. Поэтому вписать информацию о сетевых адресах поаналогии с FreeBSD не получится.

Так вот, правильный путь не работает. Поэтому либопропишем в /etc/rc.common наш сетевой адрес (для этогонайдите функцию CheckForNetwork() – в ее теле и надо про-писать), либо идем в системный каталог автозагрузки. Егополный путь – /System/Library/StartupItems. Каталог, отве-чающий за сеть, именуется Network.

Файл, который необходимо отредактировать, совпада-ет с названием каталога (его имя тоже Network). Обратитевнимание, что на нем установлены биты на исполнение.

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

Небольшое уточнение, выясненное в ходе настройки.При переходе на новую систему не надо забывать, что струк-турно Darwin не отличается от FreeBSD или, скажем, Linuxв плане загрузки модулей. Что я хочу этим сказать. Если увас сетевая карта опознана, то Darwin подгрузит для неемодуль. Как только модуль подгружен, то можно выстав-лять IP-адрес и маску. В общем, если выставляете IP-ад-рес в один из rc-файлов, будьте внимательны.

Вставляйте строку после загрузки модулей ядра (онизагружаются демоном kextd).

На этом этапе сеть у нас готова. Правда, развертка мо-нитора может раздражать. Мы ведь работаем в VESA-ре-жиме. Вертикальная развертка 60 Hz.

Давайте зададим текстовый режим консоли. Для этогоредактируем файл /Library/Preferences/SystemConfiguration/com.apple.Boot.plist.

В оригинале он выглядит так.

Да, забыл сказать. Почти все конфигурационные фай-лы в Darwin хранятся в xml-формате. Что мы сейчас и ви-дим.

Для текстового режима необходимо поменять параметрBoot Graphics с Yes на No. Со следующей загрузки будетиспользоваться текстовый режим.

Добавим теперь сервисы ftp, telnet. Редактируем файл/etc/inetd.conf, удаляем ненужные символы, ставим коммен-тарии перед нужными нам службами.

Посылаем сигнал, чтобы демон inetd перечитал своинастройки.

# ifconfig

en0 inet 10.0.0.10 netmask 255.255.255.0 up

# cat Network#!/bin/sh### Configure network interfaces and host name##. /etc/rc.commonStartService (){

ConsoleMessage "Initializing network"ipconfig waitall > /dev/null 2>&1if [ "${IPV6:=-YES-}" = "-NO-" ]then

sysctl -w net.inet6.ip6.auto_on=0 > /dev/nullip6 -x

fiif [ "${IPFORWARDING:=-NO-}" = "-YES-" ]then

sysctl -w net.inet.ip.forwarding=1 > /dev/null

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN""http://www.apple.com/DTDs/PropertyList-1.0.dtd"><plist version="1.0"><dict>

<key>Kernel</key><string>mach_kernel</string><key>Kernel Flags</key><string> -v</string><key>Boot Graphics</key><string>Yes</string><key>APM</key><string>Yes</string>

</dict></plist>

killall -1 inetd

elsesysctl -w net.inet.ip.forwarding=0 > /dev/null

fiifconfig en0 10.0.0.10 netmask 255.255.255.0

}StopService (){

return 0}RestartService (){

return 0}RunService "$1"

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

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

Отлично. Службы появились.Теперь задумаемся, а может, нам еще нужен и веб-сер-

вер? А DNS-сервер? Что ж, пробовать, так все и сразу.Чтобы запускать указанные и некоторые другие служ-

бы, при старте Darwin существует файл /etc/hostconfig:

Если параметр установлен в -YES-, тогда при стартеуказанные службы будут запущены.

Запуском apache (строка WEBSERVER=-YES-) занима-ется файл /System/Library/StartupItems/Apache/Apache.

Для DNS-сервера выделен файл /System/Library/StartupItems/BIND/BIND. В целом прослеживается аналогия соструктурой системных файлов, например, как в LinuxSlackware. В последней скрипты, запускающие определен-ный сервис, называются /etc/rc.d/rc.ServiceName.

Строка MAILSERVER предназначена для почтовой служ-бы postfix, TIMESYNC – для синхронизации времени, CUPS –для сервиса, отвечающего за печать, SMBSERVER – samba-сервис, добавим самостоятельно чуть позднее.

Картина вырисовывается следующая. Сначала отраба-тываются файлы /etc/rc*, а затем – соответствующие фай-лы в /System/Library/StartupItems.

Чтобы запустить свой сервис, необходимо создать в /Sys-tem/Library/StartupItems директорию с названием сервиса.В ней должны находиться: исполняемый файл, совпадаю-щий с названием только что созданной директории и ин-формационный файл StartupParameters.plist.

Давайте посмотрим, как создать скрипт сервиса, отве-чающего за старт samba-сервера.

Файл StartupParameters.plist:

Вставляем в файл /etc/hostconfig строчку SMBSER-VER=-YES- и в дальнейшем надо будет выделить samba-ресурсы (файл /etc/smb.conf).

Так, со службами все понятно. Установим локальноевремя в системе.

Для необходимого нам hostname следует подправитьфайл /etc/rc.boot. Вместо строки по умолчанию «hostnamelocalhost» вставим «hostname DarwinOS».

Теперь мы рассмотрим концепцию NetInfo, реализован-ную в Darwin. Система может искать информацию как вфайлах службы NetInfo, так и в «плоских» («plain text») фай-лах (/etc/fstab, /etc/passwd и др.).

Для добавления пользователя, существует утилита niutil(NetInfo Util). С помощью этой же утилиты настраиваетсябольшая часть системы.

Комментарии, надеюсь, излишни? Сначала мы создалиобъект Bob в иерархии /users, а затем добавили этомуобъекту свойства. Добавить добавили, но надо и создатьему домашний каталог.

Теперь у Bob домашняя директория, да и сам он полу-чился по умолчанию в группе wheel. Можно переделать,добавив через niutil пользователю uid и gid.

Для получения списка объектов в иерархии NetInfo вы-полним команду:

Получим список корневых иерархий. Для просмотраобъекта resolver (аналог /etc/resolver):

Создадим запись в объекте resolver:

Всю базу NetInfo можно просмотреть:

# cat /etc/hostconfig# /etc/hostconfig### This file is maintained by the system control panels### Network configurationHOSTNAME=DarwinOS# ServicesAUTOMOUNT=-NO-CUPS=-NO-IPFORWARDING=-NO-IPV6=-YES-NETINFOSERVER=-NO-NISDOMAIN=-NO-RPCSERVER=-YES-QTSSERVER=-YES-WEBSERVER=-YES-DNSSERVER=-NO-COREDUMPS=-NO-VPNSERVER=-NO-

#!/bin/sh# Include system wide configuration options. /etc/rc.common# Start SMB servicesif [ "${SMBSERVER:=-NO-}" = "-YES-" ]; then

ConsoleMessage "Starting SMB services"/usr/sbin/smbd -D/usr/sbin/nmbd -D

fi

{Description = "smb file server";Provides = ("Samba");Requires = ("Resolver");OrderPreference = "None";Messages ={

start = "Starting Samba";stop = "Stopping Samba";

};}

cd /etcln -s /usr/share/zoneinfo/Europe/Moscow localtime

niutil -create / /users/Bobniutil -createprop / /users/Bob shell /bin/tcshniutil -createprop / /users/Bob realname UncleBobniutil -createprop / /users/Bob home /Users/Bobniutil -createprop / /users/Bob _shadow_passwd

cd /Usersmkdir Bobchown -R Bob:wheel Bob

niutil -list . /

niutil -read . /locations/resolver

niutil -createprop . /locations/resolver ↵↵↵↵↵nameserver 10.0.0.100

nidump -r / / > nidump.txt

32

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

Приступаем к заключительной части нашей статьи. Го-товимся к прыжку в окна X-сервера.

В Darwin он называется XDarwin (это портированный вMac OS проект XFree86). В системе обнаруживается толь-ко один оконный менеджер. Это twm.

Для запуска twm создадим в домашней директориифайл .xinitrc:

Не забываем, что он должен иметь установленным ис-полняемый бит.

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

Как-то непривычно, не правда ли?Давайте поменяем twm на другой менеджер. Например,

blackbox. Сказано – сделано.

Меняем .xinitrc.

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

ников. Кому нравится GNOME, нет проблем. Дело вкуса.Для любителей клубнички даже существует проект darwine,запуск windows-приложений под Darwin (небезызвестныйродоначальник – проект wine).

Пару слов о модулях, упомянутых в начале статьи. Про-смотр информации о загруженных модулях:

В списке я оставил упоминания модулей, названия ко-торых отчасти говорят о платформе и используемом обо-рудовании.! kextload kextName – загрузка модуля под именем

kextName (они находятся в /System/Library/Extensions).! kextunload kextName – соответственно выгрузка из па-

мяти модуля kextName.! kextxcache – подготовка базы модулей, хранящихся в

/System/Library/Extensions.

Apache-сервер, которым комплектуется система, иден-тифицируется следующим образом:

# cat ~/.xinitrc/usr/X11R6/bin/xterm &/usr/X11R6/bin/twm

# chmod +x .xinitrc

# startx

wget -ct0 http://prdownloads.sf.net/blackboxwm/ ↵↵↵↵↵blackbox-0.65.0.tar.gz

mkdir ~/sourcesmv blackbox-0.65.0.tar.gz sourcestar xzvf blackbox-0.65.0.tar.gzcd blackbox-0.65.0

# kextstat

./configure --prefix=/usr/local/blackboxmake && make install

echo �/usr/local/blackbox/bin/blackbox� > ~/.xinitrc

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

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

Настройка ничем оригинальным не отличается.Интересно, как же идентифицируется система со сто-

роны. Сейчас поглядим. Заходим на удаленную машину. Иоттуда запускаем наш сканер портов nmap.

Со стороны не отличишь, действительно ли это MacOSXна платформе PPC или x86. Жаль, что ни Aqua, ни Quartzне предусмотрены.

Посмотрим на samba-ресурсы удаленной машины.

Посмотрим на геометрию диска из-под ОС Darwin.

Наглядно и просто. Файловых систем не так уж и много.

Файловая система NTFS доступна только на чтение. Длямонтирования файловой системы из-под Linux:

На момент написания статьи поддержка из-под Linuxтолько на чтение.

Напоследок пара строк о средстве фильтрации трафи-ка. Конечно, это ipfw. Полностью похож на своего *BSD-собрата. Впрочем, это не собрат, а скомпилированный подDarwin оригинальный ipfw. Правила задаются и убирают-ся абсолютно так же, как и в FreeBSD. Более подробносм. в статьях: «Ipfw и управление трафиком в FreeBSD»(№6 журнала «Системный администратор» за 2003 год),«Сам себе антихакер. Защита от хакерских атак с помо-щью ipfw» (№1 журнала «Системный администратор» за2004 год).

В целом из машины на основе OpenDarwin вполне воз-можно сделать почтовый релей, систему доступа из Интер-нета по ppp-соединению, веб-сервер. Основные компонен-ты по созданию сетевой инфраструктуры уже присутству-ют в системе: postfix, mysql, php, apache, bind, perl. Относи-тельно сетевой безопасности – код стека TCP/IP, как гово-рилось в начале статьи, основан на оригинальном BSD-коде.Если каких-либо программ не хватает, то следует обратить-ся на сайт [7]. Вполне возможно, что эти программы ужепортированы и доступны, как ports.

Несколько слов о компиляции программ под Darwin. Всистеме используется GNU Compiler Collection. Поэтомуособых проблем при сборке возникнуть не должно. В Darwinя встретил такое понятие, как «толстые» файлы («fat»-files).То есть при сборке файлы компилируются под несколькоархитектур, например, под PPC и под x86. И собираются водин бинарный файл. При выполнении файла происходитопределение архитектуры и передается управление на не-обходимый участок кода. В частности, ядро, идущее с сис-темой, собрано с поддержкой как PowerPC (PPC) архитек-туры, так и x86.

Конечно же, статья не претендует на полное освещениевсех нюансов «фруктовой» ОС. Она предназначена в пер-вую очередь для тех, кто собирается расширить свой кру-гозор, и тех, кто неравнодушен к самой компании AppleComputer.

Мечты придуманы для того, чтобы из них делать реаль-ность. И фруктовая компания не забывает об этом.

Ссылки:1. http://developer.apple.com/darwin/history.html2. http://news.com.com/2100-1045_3-5103279.html3. http://www.opendarwin.org/hardware/4. http://blackboxwm.sf.net5. http://www.opendarwin.org/documentation/6. http://www.mymac.ru7. http://darwinports.opendarwin.org8. Потемкин А. Mac OS X или то, что должен знать каждый

про Macintosh, Apple и операционные системы. – // Жур-нал «Системный администратор», №7, июль, 2003 г. –68-77 с.

rootfuji:# /usr/local/nmap/bin/nmap -v -sS -O darwin

DarwinOS:~ root# smbclient -L fuji -I fuji 2> /dev/null

DarwinOS:~ root# fdisk /dev/rdisk1

# ls /System/Library/Filesystems

# mount /dev/hdd2 /mnt/hd �t ufs �o, ufstype=44bsd

34

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

Перед началом рассмотрения процесса профилирования япредполагаю, что вы установили исходные тексты системыи умеете перекомпилировать ядро. Как это сделать, былоописано в статье «Первые шаги в NetBSD. Часть 1», опуб-ликованной в июньском номере журнала.

Целью и задачей профилирования ядра служит сравне-ние производительности старого и нового ядра. Например,вы скомпилировали новое ядро, и по вашим расчетам онодолжно работать быстрее первого, но желаемого резуль-тата получено не было. В чем дело? Где вы ошиблись? Всеэто можно будет выяснить, используя профилированиеядра. Если вы собираете NetBSD для какой-либо встроен-ной системы, без профилирования вам точно не обойтись.Ведь во встроенных системах аппаратные характеристикижелеза, как правило, очень ограниченны. А без использо-вания профилирования вряд ли удастся настроить системуна оптимальную производительность.

Рассмотрим пример профилирования для NetBSD, ра-ботающей на обычном x86-компьютере. Для примера мывозьмем ядро GENERIC, поставляющееся с системой поумолчанию, и сравним его по производительности с собран-ным вами новым ядром.

Соберем профилированное GENERIC-ядро.Переходим в каталог, где располагаются конфигураци-

онные файлы ядра:

Опция –p указывает, что собираемое ядро будет про-филироваться:

Запускаем сборку ядра:

Сохраняем старое ядро:

ПРАКТИКА РАБОТЫ С NetBSD:ПРОФИЛИРОВАНИЕ ЯДРА

АЛЕКСАНДР БАЙРАК

Копируем в корень новое:

Перезагружаемся:

Сразу после загрузки системы проверим, работает про-филирование или нет.

В ответ мы должны получить:

Теперь отключим профилирование:

В ответ мы получим:

Теперь нам нужно поместить данные kgmon в файл.

После этой команды мы получим файл gmon.out разме-ром около 3 Мб.

Далее нам нужно получить вывод gprof:

Должен заметить, имя файла для вывода можно выб-рать самому. Примерно через 2-3 минуты в текущем ката-логе появится заказанный нами файл gprof.out. После это-

# cd /sys/arch/i386/conf/

# config �p GENERIC

# cp /netbsd /netbsd.old

# cd ../compile/GENERIC.PROF/# make depend && make

# cp netbsd /netbsd

# reboot

# kgmon � b

# kgmon �h

# kgmon �p

#gprof /netbsd > gprof.out

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

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

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

Не забыв в начале функции check_exec, написать:

После внесения этих нехитрых изменений, снова пере-компилируем ядро. Естественно, с профилированием. Пе-резагрузившись, повторяем уже известные нам действияпо созданию файлов gmon.out и gprof.out. И переходим канализу полученных файлов.

В данном случае результат сразу бросается в глаза, вотчто у меня получилось в gprof.out, раздел Flat profile:

Сравним получившиеся результаты функции check_execс теми, которые были получены до модификации после-дней.

До:

После:

Разница, я думаю, всем понятна. А теперь представим,что изменения, аналогичные тем, что мы специально доба-вили в систему для уменьшения производительности, по-пали в код случайно, например, вследствие ошибки про-граммиста. Без профилирования, как мне кажется, будетсложно узнать, что именно «притормаживает» систему. Акакие перспективы открывает профилирование для оцен-ки оптимизации кода ядра! Внесли некоторые изменения впроцедуру, погоняли машину в тестовом режиме, сравни-ли результаты с тем, что было в старом варианте и что по-лучилось в новом.

И все видно сразу как на ладони. Для любителей опти-мизации и разработчиков встроенных систем это простонаходка!

Вообще тема профилирования достаточно обширна, нодумаю, что описанного выше простого примера вполне до-статочно, чтобы понять принцип профилирования ядра.

го переходим непосредственно к процессу анализа полу-ченных данных. Смотрим наш gprof.out (или как вы его на-звали).

Первым разделом идет Flat profile, это список всех выз-ванных функций, время и количество их вызовов.

Вот пример части вывода:

Дальше в том же духе. Давайте рассмотрим содержа-ние этих столбцов более подробно.1. Сколько всего времени (в процентах) исполнялась та или

иная функция.2. Общая сумма времени (в секундах) выполнения всех

функций до текущего момента.3. Время (в секундах) исполнения какой-либо функции. Это

основной показатель данной таблицы.4. Общее количество вызовов некой функции.5. Среднее время (в миллисекундах), истраченное на вы-

зов функции. Если функция не профилируется, тостолбец останется пустым. Например, функцию idle,как вы понимаете, «улучшить» никак нельзя, поэто-му текущий столбец для этой функции оказался не-заполненным.

6. Среднее время (в миллисекундах), истраченное этойфункцией и ее потомками на вызов. Так же, как и в пре-дыдущем столбце, если функция не профилируется, зна-чение остается пустым.

7. Имя функции.

Как видно из этого фрагмента, подавляющее большин-ство времени система бездействовала.

Теперь следует раздел Call Graph Profile. Его задача –показать дальнейшие запросы («потомки») от перечислен-ных функций.

Вот часть вывода:

И так далее. Всего 6 столбцов с данными.1. Уникальное число, присвоенное каждой функции.2. Cколько времени (в процентах) исполнялась некая фун-

кция и все ее «потомки».3. Общий процент времени, истраченный на эту функцию.4. Общее время, занятое «потомками» этой функции.5. Сколько раз функция была вызвана. После «/» идет ко-

личество вызовов этой функции ее потомками. Рекур-сивные вызовы не учитываются.

6. Имя функции.

for (x = 0; x < 100000000; x++){y = x;}

int x;int y;

36

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

Одной из «вечных» проблем является организация учётаинтернет-трафика пользователей. Многие из тех, кто стал-кивался с этой задачей, знают, что решается она не всегдатак просто, как хотелось бы.

Традиционными платформами для прокси-серверов яв-ляются операционные системы семейства UNIX.

Для создания прокси-сервера на базе любой UNIX-по-добной ОС вам необходимо иметь достаточно долгий опытработы с ней, возможно, даже могут понадобиться програм-мистские навыки. И тогда по силам будет решить задачупрактически любой сложности. Но если вы не владеете эти-ми навыками или по каким-либо другим причинам возни-кает «огромная» проблема с подсчётом трафика, то этастатья для вас.

Usergate является одним из самых простых решенийучёта интернет-трафика. Но стоит сразу сказать, что не яв-ляется бесплатным, да и существует только под Windows,что соответственно увеличивает стоимость системы иуменьшает ее надежность.

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

Итак, Usergate представляет собой кэширующий про-кси-сервер, позволяющий гибко управлять разграничени-ем доступа пользователей к сети Интернет. Найти болееподробную информацию об этой программе и узнать рас-ценки можно по адресу: http://usergate.ru.

Остановимся более подробно на возможностях, пре-доставляемых данной системой. Как уже было сказановыше, – это кэширующий прокси-сервер для HTTP, DNS иFTP (через HTTP) запросы.

Имеется поддержка протокола Socks5 и туннелирова-ние POP3- и SMTP-протоколов.

В составе программы находится интегрированный веб-сервер, правда, с очень ограниченными возможностями.Конечно, с полноценными веб-серверами он сравниться неможет, да и, к сожалению, пока и со своей прямой задачей –предоставлением пользователям статистической информа-ции, не всегда справляется (по загадочным причинам про-сто перестаёт работать). В настоящий момент речь ведёт-ся о версии 2.7, как обещают разработчики, в последую-щих версиях все недостатки будут устранены, а также бу-дут включены новые возможности.

Гибкая система разграничения доступа пользователейк сети Интернет включает в себя ограничение доступа какотдельного пользователя, так и группы пользователей. Воз-можен запрет доступа в определённые часы, дни, а такжеограничение пользователя по трафику (в системе можноопределять, каким образом осуществлять подсчёт трафи-

АНДРЕЙ УВАРОВ

ЗНАКОМСТВО С USERGATEЗНАКОМСТВО С USERGATE

ка, то есть для пользователя можно установить учёт кактолько входящего, так и входящего и исходящего трафи-ка). Аналогично Squid Blocks возможно создание или ис-пользование уже готовых списков запрещенных ресурсов,так называемых «черных списков», правда, в отличие отSuid Blocks, все записи вносятся непосредственно в кон-фигурационный файл, что не очень удобно.

Есть ещё некоторое количество дополнительных воз-можностей, таких как автодозвон, portmaping, авторизацияна прокси-сервере каскада и другие.

Установка и настройкаВообще с установкой и настройкой больших сложностейвозникнуть не должно, ввиду того что программа имеетдостаточно удобный интерфейс.

Если вы находитесь за прокси-сервером, соответствен-но указываете его IP-адрес и, если необходима авториза-ция, заполняете поля: имя пользователя и пароль.

Также неотъемлемой частью настройки системы будетсоздание и редактирование групп пользователей и отдель-

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

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

ных пользователей, так как это напрямую связано с основ-ной задачей программы. В первую очередь необходимосоздать группы, а затем в них добавлять пользователей. Ксожалению, нет возможности перемещать пользователейиз одной группы в другую. При добавлении каждой группыпользователей необходимо указать имя создаваемой груп-пы, и если требуется, ограничения и тарифы.

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

При добавлении пользователя необходимо указать егологин и пароль, которые будут использоваться для автори-зации пользователя при выходе в Интернет, и группу, к ко-торой он будет принадлежать. Установленный у пользова-теля атрибут «Администратор» даёт возможность просмат-ривать через веб-интерфейс статистику всех пользовате-лей. Но, к сожалению, веб-интерфейс не предоставляетвозможности администрирования.

Каждому пользователю можно установить следующиеограничения: по трафику, по времени, по скорости досту-па, по упоминавшимся ранее «черным спискам», запрещён-ным часам работы и по длине документа (в данном случаеречь идёт об HTTP-протоколе).

К сожалению, имеющаяся система ограничения досту-па не очень эффективна, ввиду того что лимитирование потрафику осуществляется только за день или месяц. Это со-здаст вам определённые проблемы, если вы решите исполь-

зовать Usergate. Так как вы не сможете устанавливать аб-солютных ограничений, то есть если вы хотите установитьопределённому пользователю лимит, к примеру, на 100Мб, а к концу месяца он израсходует лишь часть своеготрафика, то весь счёт пользователя обнулится, а ограниче-ние будет прежним. Конечно, можно каждый месяц пере-считывать трафик всем пользователям вручную, но как вы,наверное, догадываетесь – это выходом не является. В этомсостоит, на мой взгляд, один из серьёзнейших недостатковсистемы.

Несомненно, удобной и необходимой является возмож-ность видеть в реальном времени работу пользователей вИнтернете (так называемая функция мониторинга работыпользователей).

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

Использовать Usergate или нет – выбор ваш, но всё жехочется ещё раз отметить, что это хорошее решение дляWindows (и крайне простое в реализации), но существуетцелый ряд подобных систем для UNIX, ничуть не уступаю-щих, и даже превосходящих его по своим возможностям.

38

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

В последнее время трудно найти провайдера сетевых ус-луг, который бы не уделял должного внимания IP-телефо-нии. Услуги IP-телефонии или VoIP можно условно разде-лить на 3 группы – это «терминация» трафика удаленныхоператоров в сети PSTN, проксирование VoIP-звонков и пре-доставление услуг связи, используя IP-сети.

Конечного пользователя (клиента некого оператораVoIP), как правило, всегда интересует 3-я группа – для негоона фактически означает получение возможности сделатьмеждугородний или международный звонок по несколькоболее выгодным тарифам, чем те, которые готов предоста-вить ему городской узел связи. Среди множества способовреализации такого сервиса особую популярность занима-ет так называемая IP-карта. Приобретая ее, клиент можетпозвонить по местному (городскому) телефонному номеру,указанному на карте, и, перейдя в тональный режим, ввес-ти по запросу системы пин-код, указанный на карте, и не-посредственно код страны, города и номер телефона, кудаон желает сделать звонок.

Когда клиент дозванивается по номеру, указанному накарточке, начинает работать IVR (Interactive Voice Response) –специальное приложение, подгружаемое или частичновстроенное в голосовой шлюз, и обеспечивающее интерак-тивное «общение» с клиентом: запрос необходимых дан-ных для авторизации, получение информации о том, кудаклиент желает сделать звонок, выдача сведений об ошиб-ках и балансе карты.

Мы рассмотрим создание и настройку IVR для маршру-тизаторов и голосовых шлюзов фирмы CISCO (http://www.cisco.com) с аналоговыми FXO (Foreing ExchangeOffice) портами или цифровыми голосовыми портами. Все

МИХАИЛ ЗАГРАЕВСКИЙ

Президента компании AT&Tна пресс-конференции спросили:

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

-Потому что все изменения претерпелателефонная станция.

СОЗДАНИЕ И НАСТРОЙКА IVRДЛЯ ГОЛОСОВЫХ ШЛЮЗОВ CISCO SYSTEMSСОЗДАНИЕ И НАСТРОЙКА IVRДЛЯ ГОЛОСОВЫХ ШЛЮЗОВ CISCO SYSTEMS

примеры, рассмотренные ниже, были протестированы намаршрутизаторе CISCO 3725 IOS 12.3(7)T, но должны ра-ботать на любом голосовом шлюзе фирмы CISCO с IOS,поддерживающим TCL IVR API 2.0 и авторизацию по прото-колу RADIUS. Нашей целью будет создание и отладка пол-ноценного приложения для предоставления услуги между-городней связи с использованием предоплаченных (дебет-ных) телефонных карт.

IVR для оборудования CISCO представляет собойскрипт, написанный на языке TCL (Tool Command Language)с использованием CISCO TCL IVR API (Application ProgramInterface). Мы будем работать с версией API 2.0.

Итак, давайте прежде всего четко определим наши целии основные стадии звонка.

Предполагается, что информация от клиента к IVR бу-дет поступать с использованием DTMF (Dual Tone Multi-frequency) (тонального донабора).

Клиенту предлагается:! Выбрать язык общения с системой.! Ввести номер (пин-код) его карточки.! Ввести номер телефона, куда он желает сделать звонок.! Клиента соединяют с требуемым номером.

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

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

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

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

По завершении звонка маршрутизатор может предос-тавить информацию о клиенте (номере его карты), длитель-ности звонка и назначении звонка серверу RADIUS для ве-дения статистики.

Как уже говорилось ранее, IVR – это программа, напи-санная на языке TCL с использованием CISCO IVR API, ко-торая начинает свое выполнение при поступлении звонкана маршрутизатор (голосовой порт). Наше TCL-приложе-ние может находиться на различных носителях или удален-ных серверах, а именно FTP, TFTP и FLASH-памяти марш-рутизатора. При поступлении звонка шлюз запускает IVR-скрипт, определенный в конфигурации соответcтвующегоdial-peer, связанного с портом, на который поступил зво-нок. Например, в шлюзе определен голосовой аналоговыйFXO-порт:

С ним связан соответствующий dial-peer:

То есть при поступлении звонка на голосовой порт 1/0/0запустится IVR-приложение с именем debitcard, которое дол-жно быть определено глобально в конфигурации маршру-тизатора:

Первая строчка сообщает маршрутизатору о местона-хождении IVR-скрипта с именем debitcard.tcl, в нашем слу-чае он находится на slot0: – дополнительном модуле Flash-памяти.

Вторая и третья определяют количество цифр в иденти-фикаторе и пин-коде карты.

Четвертая строчка определяет количество секунд доокончания звонка, за которое клиента следует предупре-дить.

Пятая и шестая указывают маршрутизатору, где искатьнеобходимые звуковые файлы, из которых будут форми-роваться голосовые сообщения для клиента.

Вообще говоря, для каждого IVR-скрипта можно опре-делить любой параметр или параметры, значение которыхон потом сможет считать через соответствующие IVR API.

Например, наличие следующей строчки в конфигура-ции шлюза:

определяет для IVR-скрипта debitcard параметр say_

seconds, имеющий значение yes. Позже, в процессе выпол-нения скрипта, мы сможем изменить его поведение в зави-симости от значения этого параметра, что, несомненно, уве-личивает универсальность приложения.

IVR-приложение базируется на трех составляющих: про-цедуры инициализации, функции обработки событий и ко-нечное состояние (FSM – Finite State Machine).

Процедуры или функции инициализации применяютсядля объявления и присвоения значений переменным, ис-пользуемых скриптом.

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

Второй тип процедур инициализации – это те, которыевызываются при каждом поступлении звонка на маршру-тизатор, а именно при получении события ev_setup_indication или ev_handoff. В них, как правило, принято опре-делять и инициализировать глобальные переменные, кото-рые могут меняться при каждом звонке.

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

Для IVR-скрипта назначен четкий набор событий, кото-рый он может обрабатывать, и на каждое из них можно при-вязать функцию-обработчик этого события, которая будетавтоматически выполнена при поступлении этого событияв каждом конечном состоянии скрипта.

FSM – (конечные состояния) выполнения IVR-скриптаопределяют, какие именно обработчики будут вызыватьсяпри получении тех или иных событий, и в какое состояниедолжен перейти скрипт после вызова обработчика.

Итак, приступим к написанию скрипта. Мы создадимтекстовый файл с именем debitcard.tcl. Далее приводитсяполный листинг скрипта с подробным описанием процедури команд. В тексте слова «процедура» и «функция» явля-ются синонимами.

voice-port 1/0/0 cptone RU timeouts initial 20 timeouts interdigit 20 timeouts wait-release 10

dial-peer voice 1 pots application debitcard port 1/0/0

call application voice debitcard slot0:/ivr/debitcard.tclcall application voice debitcard uid-len 8call application voice debitcard pin-len 3call application voice debitcard warning-time 10call application voice debitcard language 1 rucall application voice debitcard language 2 encall application voice debitcard set-location ru 0 ↵↵↵↵↵

slot0:/ivr/prompts/ru/call application voice debitcard set-location en 0 ↵↵↵↵↵

slot0:/ivr/prompts/en/

call application voice debitcard say_seconds yes

proc init { } {global param;global retryCnt;global LangPattern;global ParamForCard;global ParamForDest;global AccountLen;global PinLen;global CardLen;global WarnTime;set param(abortKey) *set param(interruptPrompt) trueset param(ignoreInitialTermKey) trueset LangPattern(1) {[1,2]}set AccountLen [string trim [infotag get cfg_avpair ↵↵↵↵↵

uid-len]];set PinLen [string trim [infotag get cfg_avpair ↵↵↵↵↵

pin-len]];set retryCnt [string trim [infotag get cfg_avpair ↵↵↵↵↵

retry-count]];

40

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

Процедура init вызывается всего один раз при первомзапуске скрипта, и в ней производится объявление и ини-циализация переменных, не меняющих свое значение длявсех звонков, обслуживаемых IVR-скриптом.

Она вызывается в глобальной области TCL-приложениякомандой init, и все переменные, объявленные и установ-ленные таким образом, будут сохранять свои значения навсем протяжении работы шлюза для всех поступающихзвонков. В этой процедуре объявляются следующие пере-менные: param, ParamForDest, ParamForCard – ассоциатив-ные массивы, содержащие параметры для сбора инфор-мации о набранных клиентом цифрах для выбора языка со-общений системы, номере карты и номере телефона на-значения.

Массивы могут содержать следующие именованныеиндексы и значения:! param(abortKey) – определяет клавишу, которая будет

использоваться как клавиша отмены набора (например,если клиент по ошибке нажал не ту клавишу и вовремяэто заметил, то он будет иметь возможность нажатьклавишу, определенную параметром param(abortKey), иIVR-скрипт получит событие ev_collectdigits_done, опре-деляющее завершение процесса сбора информации ополученных от клиента цифрах со статусом «cd_002»,который означает, что клиент нажал клавишу отмены.В нашем случае это клавиша с символом «*» (звездоч-ка).

! param(interDigitTimeout) – устанавливает тайм-аут в се-кундах между нажатиями клавиш. Если за этот проме-жуток времени не будет нажата ни одна клавиша, тоскрипт получит событие ev_collectdigits_done со стату-сом завершения «cd_001».

! param(initialDigitTimeout) – определяет тайм-аут до мо-мента нажатия первой клавиши. По истечении этого вре-мени возникает событие ev_collectdigits_done со стату-сом завершения «cd_001».

! param(interruptPrompt) – может принимать значения trueили false – определяет возможность прерывать клиен-том голосовые сообщения нажатием любой клавиши.

! param(terminationKey) – по аналогии с abortKey позво-ляет задать клавишу, которая будет использоваться вкачестве индикатора завершения клиентов ввода цифр.При нажатии этой клавиши возникает событие ev_collectdigits_done со статусом «cd_005». В нашем слу-чае это клавиша с символом «#» (решетка).

! param(maxDigits) – задает максимальное количествоцифр, по истечении набора которых возникает событиеev_collectdigits_done со статусом «cd_005».

Мы вычисляем этот параметр для количества цифр,ожидаемых от клиента при вводе номера карты, зная дли-ну идентификатора (логина) и пароля. Сначала мы с помо-щью команды infotag get устанавливаем значения перемен-ных AccountLen и PinLen в те значения, которые определе-ны для этих параметров в глобальной конфигурации мар-шрутизатора. Затем вычисляем сумму этих значений, ко-торую присваиваем переменной CardLen. И наконец, пере-менная LangPattern представляет собой массив, содержа-щий шаблоны, при совпадении с которыми процесс сборацифр завершается со статусом «cd_005». В нашем случаемы хотим получить только цифры 1 или 2 для русского ианглийского языков соответственно.

Процедура init_PerCallVars отвечает за объявление иинициализацию глобальных переменных, меняющих своезначение в процессе каждого звонка. В описании другихпроцедур будет дано пояснение каждой из них.

Процедура act_Setup вызывается IVR-скриптом при по-ступлении нового звонка. Именно с нее, по сути, начинает-ся обработка звонка клиента. Почему именно с нее? Имяэтой функции определено как обработчик событияev_setup_indication, которое генерируется при каждом но-вом звонке. За это отвечает строчка:

в конце нашего скрипта.Тут следует ненадолго перейти к теме FSM. Перед вы-

полнением IVR-скрипта требуется определить имя масси-ва, который будет содержать в себе все состояния звонка,функции-обработчики событий, которые могут возникнутьпри этом состоянии, и имя следующего состояния, в кото-

set WarnTime [string trim [infotag get cfg_avpair ↵↵↵↵↵warning-time]];

set CardLen [expr $AccountLen + $PinLen];set ParamForCard(abortKey) *set ParamForCard(initialDigitTimeout) 10set ParamForCard(terminationKey) #set ParamForCard(maxDigits) $CardLen;set ParamForCard(interruptPrompt) trueset ParamForDest(abortKey) *set ParamForDest(initialDigitTimeout) 10set ParamForDest(terminationKey) #set ParamForDest(interruptPrompt) trueset ParamForDest(dialPlanTerm) true;set ParamForDest(ignoreInitialTermKey) true;return;}

proc init_perCallVars { } {global NumLangPrompt;global NumCardPrompt;global NumDestPrompt;global PromptFlag;global DestPromptFlag;global NoPlayWarn;global NoTimeLimit;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;return;}

proc act_Setup { } {init_perCallVars;leg setupack leg_incoming;infotag set med_language prefix "ru";SelectLanguageMenu;return;}

set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup ↵↵↵↵↵same_state";

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

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

рое перейдет звонок при выполнении этой функции, а так-же стартовое состояние звонка. Заметьте: не после выпол-нения функции, а непосредственно сразу (одновременно).Учтите также, что все вызовы функции и команд в TCL IVRAPI «не блокирующие», и для выполнения последующей ко-манды скрипт не будет дожидаться завершения предыду-щей. Это может смутить программистов, работавших ра-нее с такими языками, как Си, но к этому необходимо при-выкнуть. Например, если в процедуре написаны следую-щие команды:

то все они будут выполнены одна за другой, не дожидаясьзавершения предыдущей. В данном случае начнется про-цесс сбора цифр на виртуальных составляющих звонкаleg 1 и leg 2, процесс установки соединения на $callID5 ивыдано отладочное сообщение командой puts. Далее IVR-приложение начнет ожидать получения событий от этихпроцессов и вызвать соответствующие функции-обработ-чики.

Командой fsm define (в конце нашего скрипта) опреде-ляется имя массива, описанного выше ivr_fsm, и первона-чальное состояние звонка CALLCOMES. Командой setivr_fsm мы будем добавлять к этому массиву новые эле-менты, создавая так называемые «FSM-переходы». Общийсинтаксис этой команды таков:

где:! array – это имя определенного командой fsm define мас-

сива.! curr_state – имя текущего состояния скрипта, при кото-

ром получено событие curr_event.! act_proc – имя функции-обработчика события, которую

необходимо выполнить при поступлении событияcurr_event в состоянии curr_state.

! NEXTSTATE – имя состояния, в которое должен перей-ти звонок.

При определении FSM можно использовать следующиемета-определения:! any_state – определяет любое состояние звонка, для ко-

торого не установлен иной обработчик в другом FSM-переходе; может быть использовано в левой части (ин-дексе массива) FSM-перехода.

! same_state – трактуется как «то же состояние»; можетбыть использовано в правой части (значении элементамассива) определения FSM-перехода.

! ev_any_event – определяет любое событие, которое мо-жет получить скрипт.

Теперь, глядя на строчку:

мы легко можем понять, что при получении события ev_setup_indication в состоянии CALLCOMES будет выполнена функ-ция act_Setup и звонок останется в прежнем состоянии(same_state).

Возвращаясь к нашей функции act_Setup, мы видим, чтов ней вызывается процедура init_perCallVars для объявле-ния и инициализации глобальных переменных, которымнеобходимо иметь возможность менять свое значение прикаждом отдельно взятом звонке. Командой leg setupackпосылается сообщение setup acknowledgement, чтобы пе-ревести наш звонок в состояние, при котором возможнополучение клиентом голосовых сообщений.

Команда infotag set med_language prefix «ru» устанавли-вает текущий язык голосового интерфейса и фактическиуказывает скрипту, где ему следует искать звуковые фай-лы для этого языка. Помните строчку в глобальной конфи-гурации шлюза:

теперь скрипт «знает», что все файлы, из которых следуетсоставлять голосовые сообщения, находятся на носителеslot0:/ivr/prompts/ru/. Перед именем файла при его поискеон автоматически будет добавлять префикс ru, например,команда media play _wecome.au «проиграет» клиенту файл,полный путь, к которому будет slot0:/ivr/prompts/ru/ru_wecome.au.

И в конце этой процедуры вызывается функция SelectLanguageMenu:

В ее задачи входит следующее: пользователю проигры-ваются два приглашения на разных языках с предложени-ем нажать соответствующую клавишу для выбора нужногоязыка. После проигрывания команда leg collectdigits пере-водит скрипт в состояние ожидания поступления события озавершении процесса сбора цифр. Это событие поступитпосле однократного нажатия клиентом любой клавиши –за такое поведение отвечает массив LangPattern, позволя-ющий клиенту нажать только одну клавишу. Но если этаклавиша не была цифрой 1 или 2, то статус завершениясобытия ev_collectdigits_done будет отличен от «cd_005».

При получении этого события определенный нами FSM-переход:

обеспечит вызов функции CheckLangSelection, которая и

leg collectdigits 1 callInfoleg collectdigits 2 callInfoleg setup 295786 setupInfo $callID5puts "\nThis will be executed immediately i.e. before ↵↵↵↵↵

the collect digits or call setup is actually complete"

set array(curr_state,curr_event) �act_proc NEXTSTATE�

set ivr_fsm(CALLCOMES,ev_setup_indication) "act_Setup ↵↵↵↵↵same_state";

call application voice debitcard set-location ru 0 ↵↵↵↵↵slot0:/ivr/prompts/ru/

proc SelectLanguageMenu { } {global param;global retryCnt;global NumLangPrompt;global LangPattern;if {$NumLangPrompt < $retryCnt} {

media play leg_incoming %s2000 _RUS_lang_sel1.au ↵↵↵↵↵%s500 _ENG_lang_sel2.au

leg collectdigits leg_incoming param LangPattern;} else {

media play leg_incoming _final.au;fsm setstate CALLDISCONNECT;

}return;}

set ivr_fsm(CALLCOMES,ev_collectdigits_done) ↵↵↵↵↵"CheckLangSelection CHECKLANG";

42

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

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

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

В процедуре CheckLangSelection мы получаем статуссобытия ev_collectdigits_done командой infotag get evt_status;и набранные цифры(у) командой infotag get evt_dcdigits, за-тем для удобства присваиваем их временным переменнымcollect_status и collect_digits. При получении интересующе-го нас статуса «cd_005» мы командой fsm setstate «вруч-ную» переводим звонок в состояние CARDSELECTION и вы-зываем процедуру act_GetCard для запроса информации опин-коде карты клиента и последующей его авторизации.

Процедура act_GetCard проигрывает клиенту соответ-ствующие звуковые файлы не более $retryCnt раз в зави-симости от значения переменной PromptFlag, которая из-начально установлена в 0 в процедуре init_perCallVars. Со-ответственно при первом вызове этой процедуры клиентслышит приглашение ввести номер (пин-код) карточки.Значение 1 соответствует неверному количеству набран-ных цифр и значение 3 – статусу, при котором клиент ненабрал ни одной цифры. Далее скрипт выполняет командуleg collectdigits и ожидает получения события ev_collectdigits_done, которое проанализирует функция act_GotCardNumber, что обеспечит FSM-переход:

Процедура act_GotCardNumber выполняется при получе-нии события ev_collectdigits_done. Глобальная переменнаяParamForCard, инициализированная в функции init, обес-печивает нам получение определенного количества цифр– в нашем конкретном случае это число 11 ($Cardlen =

proc CheckLangSelection { } {global NumLangPrompt;set collect_status [infotag get evt_status];set collect_digits [infotag get evt_dcdigits];switch $collect_status {

"cd_001" {incr NumLangPrompt;media play leg_incoming _no_digits_entered.au;return;}

"cd_002" {SelectLanguageMenu;fsm setstate CALLCOMES;return;}

"cd_005" {infotag set med_language $collect_digits;fsm setstate CARDSELECTION;act_GetCard;return;}

"cd_006" {incr NumLangPrompt;media play leg_incoming _wrong_lang_sel.au;return;}

default {media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;}

}return;}

proc act_GetCard { } {global ParamForCard;global NumCardPrompt;global PromptFlag;global retryCnt;if {$NumCardPrompt < $retryCnt} {

switch $PromptFlag {0 {media play leg_incoming %s500 ↵↵↵↵↵

_enter_card_num.au; #first play}1 {media play leg_incoming _invalid_digits.au; ↵↵↵↵↵

#Not enuf digits pressed}3 {media play leg_incoming _no_card_entered.au; ↵↵↵↵↵

#Timeout - no digits entered}default {

media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;return;}

}leg collectdigits leg_incoming ParamForCard;} else {

media play leg_incoming _final.au;fsm setstate CALLDISCONNECT;}

return;}

proc act_GotCardNumber { } {global NumCardPromptglobal AccountLen;global PinLen;global CardLen;global PromptFlag;global retryCnt;global account;global pin;set status [infotag get evt_status];switch $status {

"cd_005" {set card_number [infotag get evt_dcdigits];set card_len [string length $card_number];if {$card_len == $CardLen} {

set account [string range $card_number 0 ↵↵↵↵↵[expr $AccountLen - 1]];

set pin [string range $card_number $AccountLen ↵↵↵↵↵[expr $card_len - 1]]

puts "account = $account pin = $pin";aaa authorize $account $pin "" "" leg_incoming;} else {

incr NumCardPrompt;set PromptFlag 1;act_GetCard;return;}

}"cd_001" {

incr NumCardPrompt;set PromptFlag 3;act_GetCard;return;}

"cd_002" {set PromptFlag 0act_GetCard;return;}

}return;}

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

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

$AccountLen + $PinLen). При неудовлетворяющем нас ста-тусе события функция act_GotCardNumber инкремируетпеременную NumCardPrompt для счетчика количества при-глашений ввода карты, устанавливает значение перемен-ной PromptFlag и снова вызывает процедуру act_GetCard,которая проверяет, не превышен ли счетчик приглашений,и указывает клиенту на ошибку, основываясь на значенииPromptFlag. При успешно завершившемся процессе сборацифр (статус «cd_005») функция вычисляет логин и пароль,и с помощью команды:

указывает маршрутизатору отправить запрос на авториза-цию карты RADIUS-серверу. При получении ответа от негоскрипту поступит событие ev_authorize_done. Далее скриптвыполнит функцию act_CardAuthorize и останется в том жесостоянии, для этого используем следующий FSM-переход:

определенный в конце нашего скрипта:

Процедура act_CardAuthorize так же анализирует ста-тус события ev_authorize_done, и при успешной авториза-

ции («ao_000») получает с помощью команды infotag getaaa_avpair h323-credit-amount сумму на счете клиента, со-храняет ее в переменной amt и проигрывает ее клиенту спомощью команды media play %a$amt. Тут необходимо сде-лать небольшое отступление.

TCL IVR API 2.0 предоставляет функции TTS (Text ToSpeech), которые позволяют воспроизводить клиенту голо-совые сообщения, основанные на параметрах командыmedia play. Например, параметр %a<num> считает значе-ние num денежной суммой в центах (в нашем случае в ко-пейках), и клиент услышит эквивалентную сумму в долла-рах и центах или в рублях и копейках. К великому сожале-нию, IVR API могут работать только с несколькими языка-ми, как правило это английский, китайский и испанский, адля русского языка необходимо купить у фирмы CiscoSystems скрипт, обеспечивающий поддержку русского язы-ка. Любителям экстремальных ощущений предлагаетсянаписать такой скрипт самим, который потом можно будетподгрузить в маршрутизатор командой call language voice врежиме глобальной конфигурации.

Скрипт этот должен быть написан с применением «чис-того» TCL без использования CISCO IVR API, и из него бу-дет вызываться функция translate с передачей ей парамет-ров из команды media play. Функция translate должна воз-вратить строку, состоящую из имен *.au-файлов, подлежа-щих воспроизведению, разделенных пробелами. Кромеэтого, по невыяcненной пока причине, TCL IVR-скрипт, по-лучив от TTS TCL-строку с перечисленными аудио-файла-ми, требующими проигрывания, игнорирует весь текст допервого пробела (лидирующие пробелы не учитываются).То есть, если ваш скрипт вернет следующую строку:«_200.au _40.au _7.au», то проиграны будут только файлы_40.au и _7.au.

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

Далее скрипт, вызывая команду fsm state DESTSELEC-TION, устанавливает для звонка новое состояние и, исполь-зуя команду leg collectdigits, переходит в режим ожиданиязавершения сбора цифр от клиента, которые определят но-мер телефона назначения.

При отказе в авторизации (статус события ev_authorize_done не равен «ao_000») функция act_CardAuthorize полу-чает код ответа от RADIUS-сервера командой:

и присваивает его переменной return_code, затем в случае,если количество попыток ввода номера телефона еще непревышено, вызывается функция act_PlayCard ReturnCodec передачей ей переменной return_code в качестве пара-метра для проигрывания соответствующего звукового со-общения об ошибке, и скрипт вызывает команду legcollectdigits leg_incoming ParamForCard для повторного сборацифр – номера карты. Вот тут, кстати, мы и используем

aaa authorize $account $pin "" "" leg_incoming;

set ivr_fsm(CARDSELECTION,ev_authorize_done) ↵↵↵↵↵"act_CardAuthorize same_state";

proc act_CardAuthorize { } {global PromptFlag;global NumCardPrompt;global ParamForDest;global ParamForCard;global retryCnt;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]} else {

media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;return;}

fsm setstate DESTSELECTION;if {$amt <= 999999.99} {

media play leg_incoming _you_have.au %a$amt ↵↵↵↵↵_enter_dest.au;

} else {media play leg_incoming _enter_dest.au;}

leg collectdigits leg_incoming ParamForDest;return;}

if {[infotag get aaa_avpair_exists h323-return-code]} {set return_code [infotag get aaa_avpair ↵↵↵↵↵

h323-return-code];} else {

media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;return;}

incr NumCardPrompt;if {$NumCardPrompt < $retryCnt} {

leg collectdigits leg_incoming ParamForCard;act_PlayCardReturnCode $return_code;} else {

ct_GetCard;}

return;}

infotag getaaa_avpair_exists h323-return-code

44

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

свойство «не блокирующего» вызова функций: legcollectdigits и act_PlayCardReturnCode идут одна за другой,но выполнены они будут условно одновременно – скриптбудет проигрывать голоcовые сообщения об ошибке и ожи-дать события ev_collectdigits_done, после которого опятьотработает процедура act_GotCardNumber, согласно уже ис-пользованному нами ранее FSM-переходу:

поскольку скрипт остался в прежнем состоянии: CARDSELECTION.

Процедура act_GetDestination выполняет в некоторойстепени вспомогательную функцию – она будет вызывать-ся в случае какой-либо ошибки, например, при тайм-аутеввода цифр, нажатии клиентом клавиши, определенной какaborKey, или при несоответствии с планом набора, опреде-ленным в маршрутизаторе.

Процедура act_GotDestination будет выполняться в со-ответствии с FSM-переходом:

Она проверяет статус завершения события ev_collectdigits_done. В случае неустраивающего нас статуса вызы-вает функцию act_GetDestination, установив предваритель-но переменную DestPromptFlag в значение, которое укажетфункции act_GetDestination, какой именно звуковой файлследует проиграть, и, как всегда, следует проверка, не пре-вышен ли счетчик максимально допустимых попыток ввес-ти информацию. При устраивающем нас номере телефо-на, введенном пользователем (статус «cd_004» – совпаде-ние с планом набора) с помощью команды:

RADIUS-серверу будет отправлен запрос на авторизациюзвонка. По завершении авторизации скрипт получит собы-тие ev_authorize_done, которое согласно определенномунами FSM-переходу:

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

set ivr_fsm(CARDSELECTION,ev_collectdigits_done)

proc act_PlayCardReturnCode { return_code } {switch $return_code {

2 {media play leg_incoming _auth_fail.au;}7 {media_play leg_incoming _zero_bal.au;}default {

media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;return;}

}return;}

proc act_GetDestination { } {global retryCnt;global ParamForDest;global DestPromptFlag;global NumDestPrompt;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}2 {media play leg_incoming _reenter_dest.au; ↵↵↵↵↵

#Not mutch to the dial plan}default {

media play leg_incoming _no_aaa.au;fst setstate CALLDISCONNECT;return;}

}leg collectdigits leg_incoming ParamForDest;} else {

media play leg_incoming _dest_collect_fail.au;fsm setstate CALLDISCONNECT;}

return;}

proc act_GotDestination { } {global NumDestPrompt;global DestPromptFlag;global account;global pin;global destination;set status [infotag get evt_status];switch $status {

"cd_001" {incr NumDestPrompt;set DestPromptFlag 1;

act_GetDestination;return;}

"cd_002" {set DestPromptFlag 0;act_GetDestination;return;}

"cd_004" {set destination [infotag get evt_dcdigits];puts "\n************dest_number = $destination";aaa authorize $account $pin "" $destination ↵↵↵↵↵

leg_incoming;return;}

default {incr NumDestPrompt;set DestPromptFlag 2;act_GetDestination;return;}

}return;}

set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵↵↵↵↵"act_GotDestination same_state";

aaa authorize $account $pin "" $destination leg_incoming;

set ivr_fsm(DESTSELECTION,ev_authorize_done) ↵↵↵↵↵"act_CallAuthorize same_state";

proc act_CallAuthorize { } {global NumDestPrompt;global DestPromptFlag;global WarnTime;global NoPlayWarn;global NoTimeLimit;global creditTime;global retryCnt;global ParamForDest;global param;set status [infotag get evt_status];set param(enableReporting) true;set param(interruptPrompt) false;if {$status == "ao_000"} {

if {[infotag get aaa_avpair_exists ↵↵↵↵↵h323-credit-time]} {set creditTime [infotag get aaa_avpair ↵↵↵↵↵

h323-credit-time];} else {

media play leg_incoming _no_aaa.au;

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

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

Процедура act_CallAuthorize анализирует статус собы-тия ev_authorize_done. При успешной авторизации клиентупроговаривается, сколько времени у него на балансе и воз-вращенное скрипту RADIUS-сервером в переменной h323-credit-time, далее скрипт запускает команду leg collectdigitsleg_incoming param и командой fsm setstate PLACECALLпереходит в новое состояние: PLACECAL, в котором по за-вершении проигрывания звуковых файлов обработает со-бытие ev_media_done и через FSM-переход:

будет выполнена функция act_CallSetup, которая наконец-то соединит нашего клиента с требуемым номером.

При ошибке в авторизации функция получит код ошиб-ки из переменной h323-return-code и будет вызвана проце-дура act_PlayDestReturnCode, которая озвучит соответству-ющее сообщение об ошибке.

Если счетчик попыток ввести номер не превышен, скриптбудет ожидать от клиента повторного ввода номера, по за-вершении которого с помощью того же FSM-перехода:

управление будет передано функции act_GotDestination.Теперь объясним, зачем мы используем команду leg

collectdigits при успешной авторизации, ведь казалось быбольше ждать ввода от клиента не стоит.

Это позволит клиенту прервать сообщение о доступномвремени звонка и перейти непосредственно к соединению.Данная возможность будет реализована с помощью команд:

и

и FSM-перехода:

Если клиент в момент прослушивания информации обоставшемся времени звонка нажмет клавишу «#», скриптполучит событие ev_digit_end, сигнализирующее о том, чтобыла нажата некая клавиша, и будет вызвана функцияact_FastSetup, которая проверит эту клавишу на соответ-ствие с символом «#». В случае совпадения прервет проиг-рывание звуковых файлов командой media stop и вызоветпроцедуру act_CallSetup. Переменная SetupDone будет ус-тановлена в 1, чтобы избежать повторного вызова функ-ции act_CallSetup.

Процедура act_CallSetup инициализирует ассоциатив-ный массив callinfo необходимыми значениями – опциямидля будущего соединения, в частности устанавливает мак-симальное время ожидания поднятия трубки вызываемойстороной 60 секунд. И наконец, запускает команду, радикоторой, собственно, и создавался этот скрипт:

с передачей ей в качестве параметров номера назначения,этот массив и условное обозначение голосового канала, ккоторому подключен наш клиент. При завершении коман-ды leg setup скрипт получит событие ev_setup_done, кото-рое будет обработано FSM-переходом:

и вызвана функция act_CallSetupDone:

fsm setstate CALLDISCONNECT;return;}

if {$creditTime <= $WarnTime} {set NoPlayWarn 1;}if {$creditTime == "unlimited"} {set NoTimeLimit 1;}media play leg_incoming _you_have.au %t$creditTime;leg collectdigits leg_incoming param; ↵↵↵↵↵

#For Fast leg setupfsm setstate PLACECALL;return;}

incr NumDestPrompt; #Call authorize failedif {[infotag get aaa_avpair_exists h323-return-code]}

{set return_code [infotag get aaa_avpair ↵↵↵↵↵

h323-return-code];} else {

media play leg_incoming no_aaa.au;fsm setstate CALLDISCONNECT;return;}

if {$NumDestPrompt < $retryCnt} {act_PlayDestReturnCode $return_code;leg collectdigits leg_incoming ParamForDest;} else {

act_GetDestination;}

return;}

set ivr_fsm(PLACECALL,ev_media_done) "act_CallSetup same_state;

set ivr_fsm(DESTSELECTION,ev_collectdigits_done)"act_GotDestination same_state";

set param(enableReporting) true

set param(interruptPrompt) false;

set ivr_fsm(PLACECALL,ev_digit_end) "act_FastSetup same_state";

proc act_FastSetup { } {global SetupDone;if {[infotag get evt_digit] != "#" || $SetupDone} {

return;}

media stop leg_incoming;act_CallSetup;set SetupDone 1;return;}

proc act_CallSetup { } {global destination;global account;global SetupDone;set callinfo(accountNum) $account;set callinfo(alertTime) 60;set SetupDone 1;leg setup $destination callinfo leg_incoming;return;}

leg setup $destination callinfo leg_incoming

set ivr_fsm(PLACECALL,ev_setup_done) "act_CallSetupDone ↵↵↵↵↵same_state";

proc act_CallSetupDone { } {global DestPromptFlag;global ParamForDest;global param;global NoTimeLimit;global creditTime;global WarnTime;

46

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

Процедура act_CallSetupDone получает статус заверше-ния процесса установки соединения и при удачном соеди-нении (статус «ls_000»), если не установлена переменнаяNoTimeLimit, сигнализирующая о неограниченном временизвонка, переводит звонок в состояние CALLACTIVE илиCALLLASTACTIVE, в зависимости от оставшегося времени,и с помощью команды timer start «заводит» таймер, при сра-батывании которого скрипт получит сообщение ev_leg_timer, который будет обработан FSM-переходами:

или

Время таймера рассчитывается как разность значенийпеременной, полученной от RADIUS-сервера h323-credit-time, и глобальной переменной WarnTime, проинициализи-рованной в функции init.

Также мы опять вводим приятную возможность для кли-ента – прервать разговор в любой момент путем длитель-ного (более 300ms) нажатия и удерживания клавиши с изоб-ражением решетки. В этот момент скрипт получит событиеev_digit_end, которое обработает FSM-переход:

При неуспешном завершении процесса установки со-единения процедура act_CallSetupDone проиграет клиентусоответствующее сообщение об ошибке, предложение вве-

сти новый номер телефона назначения и перейдет в состо-яние DESTSELECTION, а согласно FSM-переходу:

после окончания проигрывания файлов при получении со-бытия ev_collectdigits_done будет вызвана функция act_GotDestination:

Процедура act_ActiveTimer выполнится в момент полу-чения скриптом события ev_leg_timer.

В ее задачи входит временно отсоединить вызывающуюи вызываемую стороны звонка (это необходимо, чтобы кли-ент мог получить информацию о том, что время его звонказаканчивается), т.к. нельзя проигрывать звуковые файлыпри установленном звуковом канале между двумя сторо-нами разговора, и установить новый таймер на оставшие-ся у клиента $WarnTime секунд. После разрыва соедине-ния скрипт получит событие ev_destroy_done, обрабатыва-емое FSM-переходом:

Процедура act_LastActiveTimer, так же как и act_ActiveTimer, разрывает голосовой канал между двумя сторонамизвонка согласно FSM-переходу:

После этого скрипт будет ожидать получения событияev_destroy_done, которое обработается FSM-переходом:

set ivr_fsm(CALLACTIVE,ev_leg_timer) ↵↵↵↵↵"act_ActiveTimer CALLWARN";

set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵↵↵↵↵"act_LastActiveTimer same_state";

set ivr_fsm(CALLACTIVE,ev_digit_end) ↵↵↵↵↵"act_LongPound CONNDESTROY";

set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵↵↵↵↵"act_GotDestination same_state";

proc act_PlayDestReturnCode {return_code} {switch $return_code {

9 {media play leg_icoming _dest_blocked.au %s500 ↵↵↵↵↵_enter_dest.au;}

12 {media play leg_incoming _not_enuf.au %s500 ↵↵↵↵↵_enter_dest.au;}

default {media play leg_incoming _no_aaa.au;fsm setstate CALLDISCONNECT;return;}

}return;}

proc act_ActiveTimer { } {global WarnTime;global incoming;global outgoing;set incoming [infotag get leg_incoming];set outgoing [infotag get leg_outgoing];connection destroy con_all;timer start leg_timer [expr $WarnTime - 1] leg_incoming;return;}

set ivr_fsm(CALLWARN,ev_destroy_done) ↵↵↵↵↵"act_CallWarnDestroy same_state";

proc act_LastActiveTimer { } {connection destroy con_all;return;}

set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵↵↵↵↵"act_LastActiveTimer same_state";

set ivr_fsm(CALLLASTACTIVE,ev_destroy_done) ↵↵↵↵↵"act_PlayDisconnect CALLDISCONNECT";

global NoPlayWarn;global SetupDone;set status [infotag get evt_status];switch $status {

"ls_000" {if {!$NoTimeLimit} { #Setting call timer

if {$NoPlayWarn} {timer start leg_timer [expr $creditTime - 1] ↵↵↵↵↵

leg_incoming;fsm setstate CALLLASTACTIVE;} else {

set delay [expr $creditTime - $WarnTime];timer start leg_timer $delay leg_incoming;fsm setstate CALLACTIVE;}

}set param(enableReporting) true;leg collectdigits leg_incoming param; #For long poundreturn;}

"ls_007" {set DestPromptFlag 0;set SetupDone 0;media play leg_incoming _dest_busy.au;leg collectdigits leg_incoming ParamForDest;fsm setstate DESTSELECTION;return;}

default {set DestPromptFlag 0;set SetupDone 0;media play leg_incoming _dest_unreachable.au ↵↵↵↵↵

%s200 _enter_dest.au;leg collectdigits leg_incoming ParamForDest;fsm setstate DESTSELECTION;return;}

}return;}

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

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

и выполнение будет передано процедуре act_PlayDisconnect,которая сообщит клиенту о завершение его звонка и пере-ведет скрипт в состояние CALLDISCONNECT:

Процедуре act_CallWarnDestroy управление передаетсясогласно FSM:

В ее задачи входит сообщить клиенту, что до конца егоразговора осталось $WarnTime секунд. Когда это сообще-ние будет проиграно, скрипт получит событие ev_media_done и FSM-переход:

позаботится о том, чтобы воcстановить канал между сто-ронами звонка, запустив функцию act_CallWarnMedia, и пе-реведет звонок в состояние CALLLASTACTIVE:

Процедура act_LongPound интересна тем, что дает кли-енту возможность во время разговора (состояния CALLACTIVE, CALLLASTACTIVE) прервать звонок и сделать но-вый, не кладя трубки. За это отвечают следующие FSM-переходы:

Если клиент нажмет любую клавишу, скрипт в этих со-

стояниях получит событие ev_digit_end, и будет вызванапроцедура act_LongPound, которая проверит клавишу насовпадение с символом «#». В случае соответствия она раз-рушит канал связи между участниками звонка. При успеш-ном разрушении канала связи скрипт получит событиеev_destroy_done. Поскольку согласно предыдущему FSM-переходу звонок находился в состоянии CONNDESTROY,будет введен в работу FSM-переход:

который вызовет процедуру act_ConnDestroyed. Последняяокончательно и полностью отключит вызываемую сторонуи переведет скрипт в состояние DESTSELECTION, попутнопередав управление функции act_GetDestination и не забывзаново проинициализировать глобальные переменные, не-обходимые для осуществления нового звонка.

proc act_PlayDisconnect { } {media play leg_incoming _disconnected.aufsm setstate CALLDISCONNECT;return;}

proc act_CallWarnDestroy { } {global WarnTime;media play leg_incoming _you_have.au %t$WarnTime;return;}

set ivr_fsm(CALLWARN,ev_destroy_done) ↵↵↵↵↵"act_CallWarnDestroy same_state";

set ivr_fsm(CALLWARN,ev_media_done) ↵↵↵↵↵"act_CallWarnMedia CALLLASTACTIVE";

proc act_CallWarnMedia { } {global incoming;global outgoing;connection create $incoming $outgoing;return;}

proc act_LongPound { } {if {[infotag get evt_digit] != "#"} {

fsm setstate same_state;return; }

set duration [infotag get evt_digit_duration];if {$duration < 300} {

fsm setstate same_state;return;}

connection destroy con_all;return;}

set ivr_fsm(CALLACTIVE,ev_digit_end) ↵↵↵↵↵"act_LongPound CONNDESTROY";

set ivr_fsm(CALLLASTACTIVE,ev_digit_end) ↵↵↵↵↵"act_LongPound CONNDESTROY";

set ivr_fsm(CONNDESTROY,ev_destroy_done) ↵↵↵↵↵"act_ConnDestroyed same_state";

proc act_ConnDestroyed { } {leg disconnect leg_outgoing;init_perCallVars;act_GetDestination;fsm setstate DESTSELECTION;return;}

proc act_Cleanup { } {call close;}

requiredversion 2.0initset ivr_fsm(any_state,ev_disconnected) ↵↵↵↵↵

"act_Cleanup same_state";set ivr_fsm(CALLCOMES,ev_setup_indication) ↵↵↵↵↵

"act_Setup same_state";set ivr_fsm(CALLCOMES,ev_collectdigits_done) ↵↵↵↵↵

"CheckLangSelection CHECKLANG";set ivr_fsm(CHECKLANG,ev_media_done)

"SelectLanguageMenu CALLCOMES";set ivr_fsm(CARDSELECTION,ev_collectdigits_done) ↵↵↵↵↵

"act_GotCardNumber same_state";set ivr_fsm(CARDSELECTION,ev_authorize_done) ↵↵↵↵↵

"act_CardAuthorize same_state";set ivr_fsm(DESTSELECTION,ev_collectdigits_done) ↵↵↵↵↵

"act_GotDestination same_state";set ivr_fsm(DESTSELECTION,ev_authorize_done) ↵↵↵↵↵

"act_CallAuthorize same_state";set ivr_fsm(PLACECALL,ev_media_done) ↵↵↵↵↵

"act_CallSetup same_state";set ivr_fsm(PLACECALL,ev_setup_done) ↵↵↵↵↵

"act_CallSetupDone same_state";set ivr_fsm(PLACECALL,ev_digit_end) ↵↵↵↵↵

"act_FastSetup same_state";set ivr_fsm(CALLACTIVE,ev_digit_end) ↵↵↵↵↵

"act_LongPound CONNDESTROY";set ivr_fsm(CALLACTIVE,ev_leg_timer) ↵↵↵↵↵

"act_ActiveTimer CALLWARN";set ivr_fsm(CALLWARN,ev_destroy_done) ↵↵↵↵↵

"act_CallWarnDestroy same_state";set ivr_fsm(CALLWARN,ev_media_done) ↵↵↵↵↵

"act_CallWarnMedia CALLLASTACTIVE";set ivr_fsm(CALLLASTACTIVE,ev_leg_timer) ↵↵↵↵↵

"act_LastActiveTimer same_state";set ivr_fsm(CALLLASTACTIVE,ev_destroy_done) ↵↵↵↵↵

"act_PlayDisconnect CALLDISCONNECT";set ivr_fsm(CALLLASTACTIVE,ev_digit_end) ↵↵↵↵↵

"act_LongPound CONNDESTROY";set ivr_fsm(CONNDESTROY,ev_destroy_done) ↵↵↵↵↵

"act_ConnDestroyed same_state";set ivr_fsm(CALLDISCONNECT,ev_media_done) ↵↵↵↵↵

"act_Cleanup same_state";set ivr_fsm(CALLDISCONNECT,ev_disconnect_done) ↵↵↵↵↵

"act_Cleanup same_state";fsm define ivr_fsm CALLCOMES;

48

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

Разработчики ядра исполнительной системы говорят, чтооно дает пищу всему остальному. И это отнюдь не преуве-личение! На плохом фундаменте ничего хорошего не пост-роишь, и качество ядра в значительной мере определяетпроизводительность всей операционной системы в целом.В комплект поставки Windows NT входит большое количе-ство разнообразных ядер (в том числе и нашумевшее ядроi486С, по слухам значительно увеличивающее быстродей-ствие системы). Как оценить их производительность? Обыч-ные тестирующие пакеты для этого не подходят, и адекват-ную методику измерений приходится разрабатывать само-стоятельно с учетом архитектуры ядра Windows и специ-фической направленности возложенных на него задач.

Большинство пользователей и системных администра-торов живут с ядром, назначенным операционной системойпри ее инсталляции, совершенно не задумываясь о том, чтоего можно заменить другим. В штатный комплект поставкиWindows NT входит более десятка различных ядер, и ещебольшее их количество может быть найдено на просторахИнтернета. Некоторые производители аппаратного обеспе-чения оптимизируют ядра под свои машины, и зачастую этаоптимальность сохраняется на большинстве остальных. Су-ществует мнение, что древние ядра (все еще совместимыес новомодными версиями Windows) намного производитель-нее современных, хотя и уступают им по функциональнос-ти.

Отдельного разговора заслуживает история с ядромi486С, оптимизированным под 80486-машины. Оно входи-ло во все версии Windows вплоть до NT 4.0, но затем нео-жиданно исчезло, и Windows 2000 вышла уже без него.Однако с Windows XP все вернулось вновь. Говорят, что оноздорово увеличивает производительность системы. Автор,знакомый с ним еще со времен Windows NT 4.0, подтверж-дает – да, увеличивает, особенно на медленных машинах.Для теоретического обоснования данного факта и была на-писана эта статья.

Разумеется, само по себе ядро i486С не настолько ин-тересно, чтобы уделять ему 13 журнальных полос. Давай-те мыслить более глобально. Ядер много, а инструментовдля оценки их производительности не существует (во вся-ком случае в открытом доступе нет ни одного). Тем неменее такой инструмент при желании можно разработатьи самостоятельно, о чем я, собственно, и собираюсь рас-сказать.

Структура ядраЯдро Windows NT состоит из двух ключевых компонентов:executive system – исполнительной системы (далее по тек-сту KERNEL), реализованной в файле ntoskrnl.exe, и биб-

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

КРИС КАСПЕРСКИ

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

лиотеки аппаратных абстракций – Hardware AbstractionLayer (сокращенно HAL), представленной файлом HAL.DLL.На самом деле имена файлов могут быть любыми, и в за-висимости от типа ядра они варьируются в довольно широ-ких пределах.

Исходная концепция построения Windows NT предпола-гала сосредоточить весь системно-зависимый код в HAL,используя его как фундамент для воздвижения системно-независимой исполнительной системы. Тогда для переносаядра на новую платформу было бы достаточно переписатьодин HAL, не трогая ничего остального (по крайней меретеоретически). В действительности же это требование так ине было выполнено, и большое количество системно-зави-симого кода просочилось в исполнительную систему, а HALпревратился в сплошное нагромождение неклассифицируе-мых функций, тесно переплетенных с исполнительной сис-темой, так что двухуровневая схема организации ядра в на-стоящее время выглядит довольно условной.

Исполнительная система Windows NT реализует высо-коуровневые функции управления основными ресурсами(как то: памятью, файлами, процессами и потоками), в оп-ределенном смысле являясь операционной системой в ми-ниатюре. Большинство этих функций слабо связаны с кон-структивными особенностями конкретной аппаратуры. Онипрактически не меняются от одной исполнительной систе-ме к другой и одинаково производительны (или непроизво-дительны) во всех ядрах.

Обособленная часть исполнительной системы, реали-зующая наиболее низкоуровневые операции и тесно взаи-модействующая с библиотекой аппаратных абстракций,называется ядром (KERNEL). Большинство ядерных проце-дур предназначены для сугубо внутреннего использованияи не экспортируются (хотя присутствуют в отладочных сим-волах), а те, что экспортируются, обычно начинаются с пре-фикса Ke (подпрограммы ядра) или Ki (обработка преры-ваний в ядре).

Это уже третье упоминание ядра, которое мы встреча-ем, что создает определенную путаницу. Давайте попробу-ем разложить образовавшийся терминологический кавар-дак по полочкам. На самом верхнем уровне абстракцийядром принято называть совокупность компонентов опера-ционной системы, работающих на привилегированном коль-це нулевого уровня. Спустившись пониже, мы увидим, чтоядро отнюдь не монолитно и состоит как минимум из двухчастей: собственно самого ядра и загружаемых драйверов.Ядро Windows NT реализовано в двух файлах: библиотекеаппаратных абстракций (по сути дела являющееся набо-ром первичных драйверов) и исполнительной системе. Вы-бором исполнительной системы руководит ключ KERNEL

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

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

Типы ядерТип выбираемого ядра определяется как архитектурнымиособенностями конкретной аппаратной платформы, так иличными предпочтениями пользователя системы, обуслов-ленными спецификой решаемых задач. Существует поменьшей мере пять основных критериев классификацииядер:! тип платформы (Intel Pentium/Intel Itanium, Compaq

SystemPro, AST Manhattan);! количество процессоров (однопроцессорные и много-

процессорные ядра);! количество установленной памяти (до 4 Гб, свыше 4 Гб);! тип контроллера прерываний (APIC- и PIC-ядра);! тип корневого перечислителя (ACPI- и не ACPI-ядра).

Очевидно, что ядро должно быть совместимо с целе-вым процессором на уровне двоичного кода и работать внаиболее естественном для него режиме (64-разрядныйпроцессор, поддерживающий архитектуру IA32, сможетработать и со стандартным 32-разрядным ядром, но разум-ным такое решение не назовешь). Данная статья обсужда-ет вопросы оценки сравнительной производительности ядер

файла boot.ini, поэтому многие ассоциируют ее с ядром, хотяэто и не совсем верно, но не будем докапываться до стол-ба, ведь фундамент исполнительной системы – тоже ядро.И это еще далеко не все! Подсистемы окружения (win32,POSIX, OS/2) имеют свои собственные ядра, сосредоточен-ные в прикладных библиотеках непривилегированного ре-жима третьего кольца, и общаются с ядром Windows NTчерез специальную «прослойку», реализованную в файлеNTDLL.DLL. Ядра подсистем окружения представляют со-бой сквозные переходники к ядру Windows NT и практичес-ки полностью абстрагированы от оборудования. Практичес-ки, но не совсем! Некоторая порция системно-зависимогокода присутствует и здесь. Многопроцессорные версиифайлов NTDLL.DLL и KERNEL32.DLL для синхронизациипотоков используют машинную команду LOCK. В однопро-цессорных версиях она теряет смысл и заменяется болеебыстродействующей командой NOP. Наверняка существу-ют и другие различия, но я такие специально не искал, по-скольку их влияние на производительность системы незна-чительно.

Из всего этого зоопарка нас в первую очередь будетинтересовать ядро исполнительной системы и HAL (рис. 1).

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

50

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

в рамках одной аппаратной платформы, и тема выбора про-цессора здесь не затрагивается.

Многопроцессорные ядра отличаются от монопроцес-сорных прежде всего тем, что они «видят» все установлен-ные процессоры и умеют с ними взаимодействовать, воз-лагая эту задачу на специальный драйвер, встроенный вHAL. Также в них кардинально переработаны механизмысинхронизации. Если в монопроцессорных ядрах для пре-дотвращения прерывания критичного участка кода доста-точно всего лишь подтянуть IRQL к верхнему уровню илизаблокировать прерывания командной CLI, то в многопро-цессорных ядрах такая стратегия уже не срабатывает – ведьна всех остальных процессорах прерывания разрешены –и приходится прибегать к спинлокам (от английского spinlock – взаимоблокировка).

Для защиты участка кода от вмешательства извне сис-тема взводит специальный флаг, складывая поступающиезапросы в специальную очередь. Естественно, это требуетнекоторого количества процессорного времени, что нега-тивно сказывается на производительности, но другого вы-хода у нас нет. Значительно усложняется и схема диспет-черизации прерываний, ведь теперь один набор IRQ прихо-дится делить между несколькими процессорами, а табли-цы обработчиков аппаратных/программных прерыванийподдерживать в согласованном состоянии. Изменения кос-нулись и планировщика, а точнее самой стратегии плани-рования потоков, которая может быть реализована как посимметричной, так и по асимметричной схеме. Симметрич-

ные ядра (а их большинство) допускают выполнение каж-дого из потоков на любом свободном процессоре, асиммет-ричные же жестко закрепляют системные потоки за однимиз процессоров, выполняя пользовательские потоки на всехостальных. Асимметричные ядра не входят в стандартныйкомплект поставки Windows NT и обычно предоставляютсяпоставщиками соответствующего оборудования. Асиммет-ричные ядра несколько менее производительны, чем сим-метричные (один из процессоров большую часть своеговремени простаивает), однако их намного сложнее «за-тормозить», и сколько бы прожорливых потоков ни запус-тил злобный хакер, администратор всегда может обезо-ружить их, ведь системные потоки выполняются на отдель-ном процессоре! Многопроцессорные ядра следует исполь-зовать только на многопроцессорных системах, в против-ном случае мы значительно проиграем в производитель-ности, причем многопроцессорные материнские платы содним процессором на борту требуют специального унип-роцессорного ядра (uniprocessor kernel), а работоспособ-ность однопроцессорных ядер в такой конфигурации ни-кем не гарантирована (хотя обычно они все-таки работа-ют) (рис. 2).

Разрядность внешней адресной шины младших моде-лей процессоров Intel Pentium составляет 32 бита, и потомуони не могут адресовать более 4 Гб физической памяти.Поскольку для серьезных серверов и мощных рабочих стан-ций этого оказалось недостаточно, начиная с Pentium Pro,ширина шины была увеличена до 36 бит, в результате чего

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

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

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

мы получили возможность адресовать вплоть до 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, и потому никаких га-рантий, что система не зависнет при загрузке, у нас нет.

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

Подробнее обо всем этом и многом другом я расскажупо ходу углубления внутрь статьи, а пока же сосредоточимсвое внимание на ядре как таковом, приоткрыв черныйящик.

Почему не пригодны тестовые пакетыСреди изобилия тестовых программ, представленных нарынке, можно найти множество утилит для оценки быстро-действия центрального процессора, оперативной памяти,видеокарты или подсистемы ввода/вывода, но мне неизве-стны инструменты, пригодные для определения произво-дительности ядра операционной системы.

А почему бы не воспользоваться WINSTONE илиSPECweb96? Первый имитирует запуск реальных офисныхприложений, второй – веб-сервера. Разве их показания небудут отражать объективное влияние конкретного ядра напроизводительность всей операционной системы в целом?Нет, не будут. И вот почему. WINSTONE (как и большин-ство его соплеменников) прогоняет тесты в идеализирован-ных условиях при минимальном количестве потоков, поэто-му накладные расходы на переключение контекста не учи-

тываются. К тому же степень «отзывчивости» системы (под-сознательно ассоциируемая с ее производительностью)обусловливается отнюдь не скоростью выполнения кода, аинтеллектуальностью планировщика, повышающего при-оритет потока в тот момент, когда он больше всех осталь-ных нуждается в процессорном времени (например, призахвате фокуса, завершении ожидаемой операции ввода/вывода и т. д.). Но тесты этого не учитывают.

Допустим, пересчет миллиона ячеек электронной таб-лицы, независимо от версии ядра, длится ровно один час.Означает ли это, что все ядра равноценны? Вовсе нет! Закадром остались такие жизненно важные аспекты, как «под-вижность» фоновых потоков системы, «чувствительность»к прерываниям и т. д. Одно ядро достойно обрабатываетклавиатурный ввод одновременно с расчетом, а другоежутко тормозит, реагируя на нажатия с задержкой в не-сколько секунд. Ну и с каким из них вам будет приятнееработать? А ведь тестирующей программе все равно…

Зайдем с другой стороны. Обычно тестовое заданиесостоит из серии повторяющихся замеров, время выпол-нения которых усредняется. Замеры со значительными от-клонениями от средневзвешенного значения отбрасыва-ются (ну мало ли, может, в этот момент началась отгрузкакэша на диск). Если продолжительность одного замера со-ставляет не более 20 мс (целая вечность для процессо-ра!), за это время может вообще не произойти ни одногопереключения контекста, а если оно и произойдет, то бу-дет безжалостно отбраковано при обработке результатов,в итоге мы получим «чистую» производительность маши-ны за вычетом вклада операционной системы. Можно лиэтого избежать? Увы! В противном случае результаты те-стирования будут варьироваться от прогона к прогону, ипользователь растеряется – какое же значение ему выб-рать? Ведь на коротком промежутке времени (порядка 10 –20 мс) издержки от побочных эффектов крайне непосто-янны и неоднородны, поэтому выдавать неочищенный ре-зультат нельзя.

Если же продолжительность замера превышает 20 мс,планировщик Windows автоматически перераспределяетпроцессорное время так, чтобы переключение контекстаосновного потока (и этим потоком будет тестовый поток!)происходило как можно реже, а его накладные расходыстремились к нулю. Естественно, остальные потоки систе-мы сажаются на голодный паек, работая рывками (и, какмы увидим далее, даже при умеренной загрузке системына «плохих» ядрах потоки получают управление не чащечем один раз… в десять секунд. Не «тиков», а именно се-кунд! И хотя интегральная производительность системы нетолько не уменьшается, но даже возрастает, работать с нейстановится невозможно).

То же самое относится и к имитатору веб-сервера. До-пустим, одно ядро обрабатывает сто тысяч запросов за ми-нуту, а другое – сто пятьдесят. Какое из них производитель-нее? А если первое обслуживает всех своих клиентов плав-но, а второе отдает информацию «плевками» с паузами подесять-пятнадцать секунд? К сожалению, известные мне те-сты этого обстоятельства не учитывают и потому их пока-зания при всей своей объективности толкают на выбор не-правильного ядра. Помните анекдот про машину, которая

52

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

ездит быстро, но тормозит медленно? Производитель-ность – это не только отношение количества проделаннойработы к затраченному времени, это еще и качество пре-доставляемого сервиса!

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

Обсуждение методик тестированияСкажите, какую физическую величину измеряет обыкно-венный ртутный или спиртовой термометр. Температуру?Нет, объем. Каждый тест по-своему объективен и что-тоизмеряет, но результат измерений в значительной мереопределяется его интерпретацией. Объем ртутного шари-ка прямо пропорционален его температуре. Это хорошо.Но производительность не температура. Это комплекснаявеличина, и в индексах производительности столько жесмысла, сколько и в коэффициенте интеллектуальностиотдельного индивидуума. Кто умнее – Гейзенберг или Га-луа? Кто сильнее – кит или слон? Какое из всех ядер са-мое производительное? Можно спорить до хрипоты, но втакой формулировке вопрос не имеет ответа. Как мини-мум мы должны выявить факторы, в наибольшей степенивлияющие на совокупное быстродействие системы, и раз-работать адекватные методики тестирования, разлагаю-щие векторное понятие производительности на скалярныевеличины.

Никакая методика тестирования не безупречна и отра-жает лишь часть реальной действительности, поэтому кполученным результатам следует относиться с большой ос-торожностью. Если Формула-1 ездит быстрее, чем КамАЗ,отсюда еще не следует, что она сохранит свое преимуще-ство при перевозке двухсот тонн кирпичей.

Методика тестирования тесно связана со спецификойвозлагаемых на систему задач (у автогонщика свои требо-вания к машине, у дальнобойщика – свои), поэтому ника-ких конкретных решений здесь не приводится, напротив,эта статья показывает, как разрабатывать тестовые мето-дики самостоятельно и какие проблемы поджидают вас наэтом пути. А проблем будет много.

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

Первым (а на IBM XT еще и единственным) возник про-граммируемый таймер интервалов – Programmable IntervalTimer, или сокращенно PIT, базирующийся на микросхеме

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 сек, однако имеющиесяу меня материнские платы врут намного сильнее, к томуже некоторые из них обнаруживают значительные «биения»на временных интервалах порядка десятых долей секунды,что отнюдь не способствует точности измерений. К томуже некоторые версии Windows периодически синхронизи-руют системное время с часами реального времени, что ещебольше подрывает доверие к системному времени. Исполь-зуя его для измерения продолжительности тех или иныхпроцессов, вы можете получить очень странный результат.Часы реального времени используют многие тестовые про-граммы и перепрограммирование RTC-таймера позволяетфальсифицировать результат. Windows 2000 используетRTC только для периодической синхронизации с систем-ными часами.

Усовершенствованный контроллер прерываний(Advanced Programmable Interrupt Controller, или сокращен-но APIC), в основном использующийся в многопроцессор-ных системах, помимо прочей оснастки включает в себя инекоторую пародию на таймер, предназначенный для пла-нирования потоков и не пригодный ни для каких измере-ний ввиду своей невысокой точности. Однако по непонят-ным причинам APIC-ядра используют APIC-таймер в каче-стве основного таймера системы, при этом величина одно-го «тика» составляет уже не 10 мс, а 15 мс. Естественно,часы идут с прежней скоростью, но политика планирова-ния существенно изменяется – с увеличением кванта со-

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

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

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

Материнские платы, поддерживающие ACPI (AdvancedConfiguration 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),обогнав своих соплеменников, этот замер должен аннули-роваться как недействительный.

Современные чипсеты (и, в частности, Intel 845) содер-жат специальный высокоточный таймер (High Precision EventTimers, или сокращенно HPET), тактируемый частотой от10 МГц, при которой время одного тика составляет от 0.1 мспри точности порядка ±0.2% на интервалах от 1 мс до100 мс. Это действительно рекордно высокая точность, покрайней мере на порядок прерывающая точность всех ос-тальных таймеров, однако HPET все еще остается завид-ной экзотикой, и чипсеты с его поддержкой пока еще неочень широко распространены.

Помимо этого на материнской плате можно найти мно-жество таймеров, например, PCI Latency Timer или десят-ки таймеров, обслуживающих чипсет, шины, память и про-чие системные устройства. Многие из них тактируются ча-стотами PCI- или AGP-шины, что обеспечивает достаточновысокую точность измерений (ниже, чем у HPET, но суще-ственно выше, чем у PM). К сожалению, они в своей массене стандартизированы и на каждой материнской плате ре-ализуются по-своему, если вообще реализуются.

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

или усыпляют его в паузах между работой для лучшего ох-лаждения. Как следствие – непосредственное преобразо-вание процессорных тактов в истинное время оказываетсяневозможным.

Утилиту для оценки разности хода нескольких таймеровможно скачать, например, отсюда: http://www.overclockers.ru/cgi-bin/files/download.cgi?file=320&filename=timertest.rar. Иесли выяснится, что ваши таймеры идут неодинаково, длясравнения производительности различных ядер будет не-обходимо ограничиться лишь одним из них. Надежнее все-го использовать для снятия показаний свой собственныйдрайвер, поскольку стратегия выбора таймеров ядром сис-темы в общем случае непредсказуема и может отличатьсяот вышеописанной.

Рассмотрим некоторые API-функции, используемые тес-товыми программами для измерения интервалов времени.! GetTickCount. Самая популярная функция. Возвращает

количество миллисекунд, прошедших со времени после-днего старта системы. В зависимости от типа установлен-ного ядра использует либо PIT-, либо APIC-таймеры, в со-ответствии с чем ее разрешение составляет либо 10 мс,либо 15 мс, причем некоторые тики таймера могут бытьпропущены (т.е. за 30 мс не произойдет ни одного увели-чения счетчика). Не рекомендуется к употреблению.

! GetSystemTime. Возвращает истинное время. То естьфункция думает, что оно истинное, а в действительнос-ти – производное от системного счетчика, инкременти-руемого каждые 10 мс или 15 мс за вычетом «съеден-ных» тиков и врожденной неравномерности хода PIT- иAPIC-таймеров. Периодически синхронизирует себя с ча-сами реального времени, а если запущена специаль-ная интернет-служба, то еще и с атомными часами, т.е.системное время продвигается траекторией пьяного гон-щика, едущего по пересеченной местности. Для изме-рений временных промежутков непригодна.

! timeGetTime. То же самое, что и GetTickCount. Докумен-тация утверждает, что разрешающая способность этойфункции составляет 1 мс, в действительности же – 10 мс.Синхронные изменения timeGetTime и GetTickCount под-тверждают, что они питаются от одного источника.

Ðèñóíîê 3. testtimer  çà  ðàáîòîé

54

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

! Sleep. Усыпляет поток на указанное количество милли-секунд, задерживая на время управление. Теоретичес-ки может использоваться для калибровки других тай-меров и вычисления коэффициента перевода таковойпроцессора в секунды. Практически же… Время ожи-дания принудительно округляется до величины, кратной«тику» основного системного таймера, причем на мо-мент выхода из сна данный поток должен находиться всамом начале очереди потоков, ожидающих выполне-ния, в противном случае ему придется подождать. Мо-жет быть, доли секунды, а может, несколько минут – всезависит от размеров очереди, а она непостоянна. На хоро-шо загруженной системе поток, планирующий вздрем-нуть 100 мс, рискует проснуться… через минуту!

! QueryPerformanceCounter. Возвращает количество тиковнаиболее точного таймера, прошедших с момента стар-та системы или… его последнего переполнения. Явля-ется переходником к функции KeQueryPerformanceCounter, реализованной в HAL. Windows XP поддержи-вает HPET, более ранние системы – нет. Если HPET ап-паратно доступен и программно поддерживается, ис-пользуется он, в противном случае ACPI-ядра будут ис-пользовать PM, а не ACPI – либо PIT, либо возвратятноль, сигнализируя об ошибке. Для определения про-должительности одного тика можно использовать фун-кцию QueryPerformanceFrequency, возвращающую егочастоту в герцах. Это самое лучшее средство для про-филировки и хронометража времени из всех имеющих-ся, однако, как уже говорилось, PM-таймеры могут идтинеточно или совершать неожиданные прыжки вперед,поэтому, показания, возращенные QueryPerformanceCounter, требуют некоторой обработки.

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

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

ACPI и IRQПрерывания – их мифическая разводка, неиссякаемый ис-точник слухов, сплетен, легенд и суеверий. Попробуем сними разобраться.

Все x86-процессоры (за исключением старших моделейIntel Pentium-4) управляют прерываниями через специаль-ный интерфейсный вывод, обычно обозначаемый как INTR,

высокий уровень сигнала на котором свидетельствует опоступлении запроса на прерывание. Процессор прекра-щает текущую работу, вырабатывает сигнал подтвержде-ния прерывания (Interrupt Acknowledge) и считывает с шиныданных 8-битный номер вектора перекрывания (INT n), пе-реданный контроллером прерываний, обычно реализуемымна правнучатых племянниках микросхемы i8259 и террито-риально находящийся в южном мосту чипсета.

За вычетом 32 прерываний, зарезервированных разра-ботчиками процессора, мы имеем 224 прерывания, пригод-ных для обработки сигналов от периферийных устройств.Означает ли это, что верхнее ограничение на максималь-ное количество одновременно поддерживаемых устройствравно 224? Нет! Некоторые из древних процессоров имеливсего лишь один вектор прерывания, но это ничуть не ме-шало им управлять десятком устройств одновременно. Как?Да очень просто. Что такое прерывание? Всего лишь спо-соб устройства обратить на себя внимание. При наличиидостаточного количества свободных векторов за каждымиз устройств может быть закреплено свое персональноепрерывание, однозначно указывающее на источник сигна-ла. Это существенно упрощает как проектирование самихустройств, так и разработку обслуживающих их драйверов,однако отнюдь не является необходимым. Получив сигналпрерывания, процессор может опросить все устройства поочереди, выясняя, кто из них затребовал внимания. Есте-ственно, это достаточно медленная операция, выливающа-яся в десятки дополнительных операций ввода/вывода и ктому же потенциально небезопасная, т.к. малейшая ошиб-ка разработчика оборачивается серьезными конфликтами.Короче говоря, для достижения наивысшей стабильностии производительности системы каждое прерывание долж-но использоваться не более чем одним устройством.

Контроллер прерываний, используемый в IBM XT, под-держивал восемь аппаратных прерываний, обозначенныхIRQ (Interrupt Request) и пронумерованных от 0 до 7. НомерIRQ соответствует приоритету прерывания: чем больше но-мер – тем ниже приоритет. Во время обработки более при-оритетных прерываний генерация менее приоритетных по-давляется и соответственно, наоборот, менее приоритет-ные прерывания вытесняются более приоритетными. Счи-тается, чем больше ресурсов требует устройство, тем вышедолжен быть приоритет его IRQ. Это неверно. Выбор пред-почтительного IRQ, определяется отнюдь не «прожорливо-стью» устройства, а критичностью потери прерывания. До-пустим, сетевая карта, видя, что входной буфер практичес-ки полон, а данные по витой паре продолжают поступать,сгенерировала прерывание, которое было вытеснено пре-рыванием звуковой карты, имеющей более высокий при-оритет и сигнализирующей об опустошении выходного бу-фера. Если драйвер звуковой карты ненароком замешка-ется и удержит обработчик прерывания дольше положен-ного, входной буфер сетевой карты переполнится, и частьпакетов окажется безвозвратно утеряна, и данные придет-ся передавать вновь, что несколько снизит быстродействиесети. Является ли эта ситуация нормальной? Для кого-тода, а для кого-то и нет! Потерянных пакетов, конечно, жаль,но если поменять приоритеты местами, звуковая карта от-реагирует на опустошение входного буфера суровым иска-

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

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

жением воспроизводимого сигнала, чего в некоторых ситу-ациях ни в коем случае допускать нельзя. Никакого друго-го влияния на производительность выбор приоритетов неоказывает. Независимо от номера IRQ обработка преры-вания занимает одно и то же время. От обработки осталь-ных прерываний она также не освобождает. При условии,что аппаратные устройства и обслуживающие их драйве-ры реализованы правильно (т.е. более или менее безбо-лезненно переживают потерю IRQ и «отпускают» прерыва-ние практически сразу же после его возникновения), вы-бор приоритетов никакой роли не играет.

Контроллер прерываний позволяет отображать аппарат-ные IRQ0-IRQ7 на 8 любых смежных векторов прерываний,например, на INT 30h – INT 37h. Тогда, при возбужденииIRQ0, процессор сгенерирует прерывание INT 30h, а привозбуждении IRQ3 – INT 33h. В IBM AT количество контрол-леров было увеличено до двух, причем второй был подклю-чен на вход первого, в результате чего количество аппа-ратных прерываний возросло до 15. Почему не 16? Так ведьодна из восьми линий прерываний была израсходована накаскадирование с другим контроллером!

Некоторое количество прерываний разошлось по сис-темным устройствам, некоторое было выделено шине ISA –тогдашнему индустриальному стандарту. Генерация преры-ваний осуществлялась изменением уровня сигнала на ли-нии соответствующего IRQ. Могут ли два или более уст-

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

Шина PCI, пришедшая на смену ISA, работает всего счетырьмя линиями равно приоритетных прерываний, услов-но обозначаемых как INTA, INTB, INTC и INTD. На каждыйслот подведены все четыре прерывания, и устройство мо-жет использовать любое подмножество из них, хотя обыч-но ограничиваются только одним. Линии прерываний одно-го слота соединяются с линями остальных слотов (а в неко-торых дешевых платах все INTA, INTB, INTC и INTD веша-ются на одну линию прерывания). Для равномерного рас-пределения прерывания по устройствам на каждом слотупроисходит ротация прерываний (рис. 5). Допустим, у насесть два слота: в первом слоте прерывание INTA (со сторо-ны устройства) соответствует прерыванию INTA (со сторо-ны шины), прерывание INTB →→→→→ INTB и т. д. Во втором слотепрерыванию INTA (с стороны устройства) соответствуетпрерывание INTB (со стороны шины), INTB →→→→→ INTC, INTC →→→→→INTD и INTD →→→→→ INTA, в результате чего устройства, исполь-

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

56

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

зующие прерывание INTA, оказываются развешаны по пре-рываниям INTA и INTB.

Линии прерываний INTA – INTB соединяются с вывода-ми PIRQ0 – PIRQ3 контроллера PCI-шины, а оттуда черезроутер (PCI Interrupt Router) попадают в контроллер преры-ваний, тем или иным образом отображаясь на четыре ли-нии IRQ, не занятые никакими ISA-устройствами. Посколь-ку количество установленных PCI-устройств обычно многобольше четырех (мы считаем также и внутренние устрой-ства, такие, например, как интегрированный контроллерUSB, чаще всего повешенный на INTD), несколько устройстввынуждены делить одно прерывание между собой. В отли-чие от ISA, в PCI-шине совместное использование преры-ваний является ее нормальным состояниям. Генерация пре-рываний осуществляется не по переходу, а по состоянию,и устройство может удерживать линию прерывания в соот-ветствующем состоянии до тех пор, пока его запрос не бу-дет обработан. Теоретически это легко. Практически же…Даже поверхностное тестирование обнаруживает большоеколичество устройств и драйверов, не вполне соответству-ющих спецификациям и не желающих делить свое PIRQ сдругими (или делающих это настолько неумело, что произ-водительность падает в разы). Следование спецификаци-ям предотвращает конфликты, но оставляет проблему па-дения производительности в силе. При совместном исполь-зовании прерываний драйвера получают сигналы не толь-ко от своих, но и от чужих устройств, заставляя их обра-щаться к своему устройству за подтверждением, и есливыяснится, что прерывание сгенерировало не оно, запроспередается следующему драйверу в цепочке. А теперь пред-ставьте, что произойдет, если на одном прерывании висит

десяток устройств и драйвер наиболее «беспокойного» изних попадет в самый хвост очереди?

Для достижения наивысшей производительности сле-дует, во-первых, оптимально распределить PCI-карты послотам (например, если у вас на шесть PCI-слотов прихо-дятся две PCI-карты, то, втыкая устройства в первый и пя-тый слот, вы вешаете их на одно PIRQ), по возможностисовмещая на одном PIRQ только наименее темперамент-ные устройства, т.е. такие, которые генерируют прерыва-ния реже всего. Во-вторых, каждое PIRQ должно отобра-жаться на свое IRQ. Какое – не суть важно (ведь приоритетPCI-прерываний одинаков), но только свое. Совместное ис-пользование одного IRQ несколькими PIRQ обычно не при-водит к конфликтам, но негативно сказывается на произво-дительности, ведь драйвера работают не с PIRQ, а с IRQ!

ACPI-ядра, работающие с PCI-шиной через ACPI-кон-троллер, лишены возможности управлять отображениемPIRQ на IRQ по своему усмотрению. Не может управлятьэтим и BIOS (во всяком случае легальными средствами).Сам же ACPI стремится повесить все PIRQ на одно IRQ(обычно IRQ9), и помешать ему очень трудно. Если количе-ство установленных PCI-устройств намного больше четы-рех, то разница в производительности между ACPI- и неACPI-ядрами несущественна, поскольку, даже отказавшисьот ACPI, вы все равно будете вынуждены разделять одноPIRQ между несколькими устройствами. Другое дело, есликоличество PCI-устройств невелико и наиболее темпера-ментные из них висят на своих прерываниях, тогда при пе-реходе с ACPI- на не ACPI-ядро разница в быстродействиисистемы может оказаться очень значительной (то же са-мое относится и к неудачно спроектированным устройствам,не умеющим делить прерывания с другими и не имеющихдостойной замены, например, дорогой видеоускоритель,RAID-контроллер и т. д.).

К сожалению, просто взять и отключить ACPI нельзя,поскольку он является не только менеджером питания, рас-пределителем ресурсов, но еще и корневым перечислите-

Ðèñóíîê 5. Ðîòàöèÿ  àïïàðàòíûõ  ïðåðûâàíèé  PCI-øèíû

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

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

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

лем. ACPI- и не ACPI-ядра используют различные деревьяустройств и потому взаимно несовместимы. Смена ядра вобязательном порядке требует переустановки системы, впротивном случае та откажется загружаться. Это суще-ственно затрудняет сравнение быстродействия ACPI- и неACPI-ядер, поскольку переустановка системы радикальными непредсказуемым образом изменяет ее производитель-ность (рис. 6).

Продвинутые материнские платы используют усовер-шенствованный контроллер прерываний (Advanced PIC илисокращенно APIC), поддерживающий 256 IRQ и способныйработать в многопроцессорных системах. Однако в моно-процессорных конфигурациях он не обеспечивает никакихдополнительных преимуществ, т.к. количество свободныхпрерываний ограничивается не контроллером, а PCI-шиной.К тому же APIC-ядра не вполне корректно работают с тай-мером, что сводит на нет все их преимущества.

Переключение контекстаПод «многозадачностью» большинство пользователей под-разумевает возможность параллельного выполнения не-скольких приложений: чтобы в фоне играл WinAmp, скачи-вался mp3 с Интернета, принималась почта, редактирова-лась электронная таблица и т. д. Минимальной единицейисполнения в Windows является поток. Потоки объединя-ются в процессы, а процессы – в задания (jobs). Каждыйпоток обладает собственным стеком и набором регистров,но все потоки одного процесса выполняются в едином ад-ресном пространстве и обладают идентичными квотами.

В любой момент времени на данном процессоре можетвыполняться только один поток, и если количество потоковпревышает количество установленных процессоров, пото-ки вынуждены сражаться за процессорное время. Распре-делением процессорного времени между потоками зани-мается ядро. Вытесняющая многозадачность, реализован-ная в Windows NT, устроена приблизительно так: каждомупотоку выдается определенная порция машинного време-ни, называемая квантом (quantum), по истечении которойпланировщик (dispatcher) принудительно переключает про-цессор на другой поток. Учет процессорного времени обес-печивается за счет таймера. Периодически (раз в 10 мс или15 мс) таймер генерирует аппаратное прерывание, прика-зывающее процессору временно приостановить выполне-ние текущего потока и передать бразды правления диспет-черу. Диспетчер уменьшает квант потока на некоторую ве-личину (обычно равную двум) и либо возобновляет выпол-нение потока, либо (если квант обратился в нуль) сохраня-ет регистры потока в специальной области памяти, назы-ваемой контекстом (context), находит поток, больше всегонуждающийся в процессорном времени, восстанавливаетего контекст вместе с контекстом процесса (если этот по-ток принадлежит другому процессу) и передает ему управ-ление.

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

Алгоритм планирования непрерывно совершенствуется,однако, не все усовершенствования оказывают благопри-ятное влияние на производительность. В общем случаемногопоточные приложения должны исполняться на тех яд-рах, под стратегию планирования которых они оптимизи-ровались, в противном случае можно нарваться на неожи-данное падение производительности.

При небольшом количестве потоков накладные расхо-ды на их переключения довольно невелики, и ими можнопренебречь, но по мере насыщения системы они стреми-тельно растут! На что же расходуется процессорное вре-мя? Прежде всего на служебные нужды самого планиров-щика (анализ очереди, ротацию приоритетов и т. д.), затемна сохранение/восстановление контекста потоков и процес-сов. Посмотрим, как все это устроено изнутри?

Дизассемблирование показывает, что планировщик какбы размазан по всему ядру. Код, прямо или косвенно свя-занный с планированием, рассредоточен по десяткам фун-кций, большинство из которых не документированы и неэкспортируются. Это существенно затрудняет сравнениеразличных ядер друг с другом, но не делает его невозмож-ным. Чуть позже мы покажем, как можно выделить подпрог-раммы профилировщика из ядра, пока же сосредоточимсяна переключении и сохранении/восстановлении контекста.

Процессоры семейства x86 поддерживают аппаратныймеханизм управления контекстами, автоматически сохра-няя/восстанавливая все регистры при переключении надругую задачу, но Windows не использует его, предпочитаяобрабатывать каждый из регистров вручную. Какое-то вре-мя автор думал, что i486С-ядро, ничего не знающее о MMX/SSE-регистрах современных процессоров и не включающееих в контекст, будет выигрывать в скорости, однако парал-лельная работа двух и более мультимедийных приложенийокажется невозможной. В действительности же оказалось,что за сохранение/восстановление регистров сопроцессо-ра (если его можно так назвать) отвечают машинные ко-манды FXSAVE/FXSTOR, обрабатывающие и MMX/SSE-ре-гистры тоже, но чтобы выяснить это, пришлось перерытьвсе ядро – от HAL до исполнительной системы!

Переключение контекста осуществляется служебнойфункцией SwapContext, реализованной в ntoskrnl.exe. Эточисто внутренняя функция, и ядро ее не экспортирует. Темне менее она присутствует в символьных файлах (symbolfile), бесплатно распространяемых фирмой Microsoft. Пол-ный комплект занимает порядка 150 Мб и неподъемно тя-жел для модемного скачивания. Ряд утилит, таких, напри-мер, как Symbol Retriever, от NuMega, позволяют выборочноскачивать необходимые символьные файлы вручную, зна-чительно сокращая время перекачки, однако по непонятнымпричинам они то работают, то нет (Microsoft блокирует дос-туп?), поэтому необходимо уметь находить точку входа вSwapContext самостоятельно. Это легко. SwapContext – един-ственная, кто может приводить к синему экрану смерти снадгробной надписью «ATTEMPTED_SWICH_FROM_DPC»,которой соответствует BugCheck код B8h. Загрузивntoskrnl.exe в ИДУ (или любой другой дизассемблер), пере-числим все перекрестные ссылки, ведущие к функциямKeBugCheck и KeBugCheckEx. В какой-то из них мы найдемPUSH B8h/CALL KeBugCheck или что-то в этом роде. Она-то

58

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

и будет функцией SwapContex. Прокручивая экран дизассем-блера вверх, мы увидим вызов функции HalRequestSoftwareInterrupt, которая, собственно, и переключает контекст,а в многопроцессорной версии ядра еще и машинную ко-манду FXSAVE, которая тут совсем ни к чему и которая от-сутствует в монопроцессорной версии. К тому же много-процессорные версии намного щепетильнее относятся квопросам синхронизации и потому оказываются несколькоменее производительными.

Функция HalRequestSoftwareInterrupt, реализованная вHAL, через короткий патрубок соединяется с функциями_HalpDispatchInterrupt/_HalpDispatchInterrupt, cохраняющими/восстанавливающими регистры в своих локальных перемен-ных (не в контексте потока!) и на определенном этапе пе-редающих управление на KiDispatchInterrupt, вновь возвра-щающую нас в ntoskrnl.exe и рекурсивно вызывающуюSwapContext. Кто же тогда сохраняет/восстанавливает кон-тексты? Оказывается – аппаратные обработчики. Списокуказателей на предустановленные обработчики находитсяв ntoskrnl.exe и содержится в переменной IDT (не путать сIDT-таблицей процессора!), которая, как и следовало ожи-дать, не экспортируется ядром, но присутствует в символь-ных файлах. При их отсутствии найти переменную IDT мож-но так: просматривая таблицу прерываний любых из ядер-ных отладчиков (Soft-Ice, Microsoft Kernel Debugger), опреде-лите адреса нескольких непереназначенных обработчиковпрерываний (т.е. таких, которые указывают на ntoskrnl.exe,а не к драйверу) и, загрузив ntoskrnl.exe в дизассемблер,восстановите перекрестные ссылки, ведущие к ним. Это ибудет структурой IDT.

Другие функции также могут сохранять/восстанавливатьтекущий контекст (это, в частности, делает Kei386EoiHelper,расположенная в ntoskrnl.exe), поэтому накладные расхо-ды на переключение между потоками оказываются доста-точно велики и выливаются в тысячи и тысячи команд ма-шинного кода, причем каждое ядро имеет свои особеннос-ти реализации. Как оценить, насколько одно из них произ-водительнее другого?

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

Сказано – сделано. Создаем большое количество по-токов (по меньшей мере сто или даже триста) и каждый изних заставляем циклически вызывать функцию Sleep(0),приводящую к отдаче квантов времени и как следствие –немедленному переключению на другой поток. Количествопереключений контекста можно определить по содержимо-му специального счетчика производительности, отобража-емого Системным Монитором, утилитой CPUMon МаркаРуссиновича, отладчиком Microsoft Kernel Debugger и мно-гими другими программами.

Сравнение ACPI-ядра с i486С-ядром на машине автора(Intel Pentium-III 733 МГц, 256 Мб SDR-RAM-133) обнаружива-ет значительное расхождение в их производительности.i486С-ядро переключает контекст намного быстрее, особен-но при работе с большим количеством потоков. В общемслучае – количество переключений контекста обратно про-порционально количеству потоков, т.к. контексты надо где-то хранить, а кэш-память не резиновая. Если ядро делаетмного лишних сохранений (о которых мы уже говорили), оносущественно проигрывает в скорости. Тем не менее, все ядраспроектированы достаточно грамотно и сохраняют отличнуюподвижность даже при работе с тысячами потоков.

Переключение процессов требует дополнительных на-кладных расходов и потребляет намного больше памяти,попутно вызывая сброс буфера ассоциативной трансляции,поскольку каждый из процессов обладает своим адреснымпространством. Выделить код, ответственный за переклю-чение контекстов, несложно – он выдает себя обращениемк регистру CR3, загружая в него указатель на каталог стра-ниц (Page Directory Physical Address).

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

Даже при небольшом количестве процессов системазначительно «проседает» под нагрузкой и начинает ощути-мо притормаживать, а количество переключений контекстовсокращаются приблизительно вдвое. i486С-ядро по-прежне-

Ëèñòèíã 1. Èçìåðèòåëü  ñêîðîñòè  ïåðåêëþ÷åíèÿ  êîíòåêñòàthread(){

// îòäàåì ïðîöåññîðíîå âðåìÿ â áåñêîíå÷íîì öèêëåwhile(1)  Sleep(0);

}#define  defNthr 300

Ëèñòèíã 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];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);printf("creating %d proc...", argNthr);for (a = 0; a < argNthr; a++)

CreateProcess(0, buf, 0,0,0, ↵↵↵↵↵NORMAL_PRIORITY_CLASS,0, 0, &st, &pi);

printf("OK\n");  thread();return 0;

}

#define argNthr ((argc > 1)?atol(argv[1]):defNthr)main(int argc, char **argv){

int a, zzz;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;

}

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

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

му держится впереди, что не может не радовать, к тому жес ним система намного более оживленно реагирует на вне-шние раздражители (в частности, клавиатурный ввод).Быстродействие подсистемы ввода/вывода специально нетестировалось, но по субъективным ощущениям i486С и сэтим справляется намного быстрее.

Желающие подкрепить экспериментальные данные доб-рой порцией дизассемблерных листингов могут самостоя-тельно проанализировать функции планировщика, если,конечно, ухитрятся выдрать их из ядра! Далеко не всем ис-следователям недр Windows удалось это сделать…

Задумайтесь: если ядро львиную долю процессорноговремени тратит на переключение контекстов, не означаетли это, что наиболее интенсивно вызываемыми функция-ми окажутся функции, принадлежащие планировщику?Запускаем нашу тестовую программу, подключаем MicrosoftKernel Profiler (или любой другой профилировщик ядра повкусу) и дав ему на сбор статистики порядка десяти секунд,внимательно изучим полученный результат:

Длительность квантовЧастые переключения контекстов отрицательно сказыва-ются на производительности системы, поэтому Windows NTподбирает продолжительность одного кванта с таким рас-четом, чтобы они происходили как можно реже, теряя приэтом подвижность и способность достаточно быстро реа-гировать.

Допустим, у нас имеется 100 потоков, каждому из кото-рых выделяется 100 мс процессорного времени, причем всепотоки используют отведенное им время полностью. Тогдамежду двумя переключениями одного и того же потока прой-дет 10 сек! Вот тебе, бабушка, и многозадачный день…Разве это нормально, когда нажатая клавиша отображает-ся на экране только через 10 сек? Когда сетевые клиентыполучают в час по чайной ложке? А ведь если сервер обра-батывает каждого из клиентов в отдельном потоке (что яв-ляется типичной стратегией программирования вWindows NT), он должен умело распределять процессорноевремя между тысячами потоков!

Разработчики UNIX, программирующие не ради денег,а в силу исторической неизбежности, стремятся выбиратьвеличину кванта так, чтобы сервер не терял отзывчивостидаже при пиковой нагрузке. Разработчики Windows NT, на-против, оптимизировали свою систему под максимальнуюпроизводительность, меняя величину кванта от версии кверсии так, чтобы совокупное количество обработанныхзапросов в единицу времени было максимальным. Ведь про-изводительность – это сила, а комфортабельность и уют –понятия растяжимые. Поднимите компьютерные журналы,полазьте по Интернету – везде лежат только сравнитель-

ные тесты производительности, но нигде – отношение вре-мени простоя клиента ко времени работы. Ладно, оставимлирику и перейдем к делу.

Windows NT поддерживает два типа квантов – длинныеи короткие. Независимо от своего типа кванты могут бытькак фиксированной, так и переменной длины (кванты пе-ременной длины еще называют динамическими). Величи-на кванта выражается в условных единицах, официальноназываемых quantum units. Три квантовых единицы обыч-но равны одному тику таймера.

Управление типом и продолжительностью кванта осу-ществляется через следующий параметр реестра: HKLM\SYSTEM\CurrentControlSet\Control\PriorityControl\Win32PrioritySeparation.

Если 4-й и 5-й биты, считая от нуля, равны 10, системаиспользует короткие кванты. То же самое происходит приоптимизации параметров быстродействия под исполнениеприложений (Панель Управления →→→→→ Система →→→→→ Дополни-тельно →→→→→ Параметры быстродействия). 01 – указывает надлинные кванты (они же используются при оптимизации си-стемы под выполнение служб в фоновом режиме). Любоедругое значение выбирает продолжительность кванта поумолчанию (короткие – в Windows 2000 Professional, длин-ные – в Windows 2000 Server).

Если 2-й и 3-й биты равны 10 – длина квантов фиксиро-ванна; 01 – позволяет планировщику динамически варьи-ровать продолжительность кванта в заранее оговоренныхпределах. Любое другое значение выбирает тип квантов поумолчанию (переменные – в Windows 2000 Professional,фиксированные – в Windows 2000 Server). При использова-нии динамических квантов планировщик пытается автома-тически увеличивать продолжительность квантов некото-рых потоков, тех, которым процессорное время нужнее все-го. Во всяком случае, планировщик думает, что оно им нуж-нее, а думает он приблизительно так: если поток обслужи-вает GUI-окно и это окно находится в фокусе, продолжи-тельность кванта увеличивается. Если поток полностьюиспользует весь отведенный ему квант, его продолжитель-ность увеличивается. Если… Конкретный алгоритм плани-рования зависит от выбранного ядра, и потому одни ядрамогут оказаться намного предпочтительнее других.

Два младших бита задают индекс в двухэлементном мас-сиве PsPrioritySeparation, расположенном внутри ntoskrnl.exeи используемым планировщиком для расчета продолжи-тельности квантов активного процесса. Эта переменная неэкспортируется ядром, но упоминается в символьных фай-лах. Если же они отсутствуют, обратитесь к функции PsSetProcessPriorityByClass, которая использует первый элементэтого массива как указатель на другой массив.

Для экспериментов с квантами слегка модернизируемнашу тестовую утилиту, заставляя потоки (процессы) ис-пользовать отведенный им квант времени целиком. А в пер-вичный поток встроим счетчик времени, вычисляющий про-должительность интервала между двумя соседними пере-ключениями:

Ëèñòèíã 3. Ôóíêöèè ÿäðà, ïðÿìî èëè êîñâåííî îòíîñÿùèåñÿê ïëàíèðîâàíèþ4484 ntoskrnl.exe  ExReleaseResourceForThread4362 ntoskrnl.exe  KiDispatchInterrupt4333 ntoskrnl.exe  SeTokenImpersonationLevel2908 ntoskrnl.exe  KeDelayExecutionThread2815 ntoskrnl.exe  KiIpiServiceRoutine 300 ntoskrnl.exe  RtlPrefetchMemoryNonTemporal  73 ntoskrnl.exe  KeDisconnectInterrupt  41 ntoskrnl.exe  ExFreePoolWithTag

Òàáëèöà 1. Ìîäåëüíûé ðÿä êâàíòîâ â Windows 2000 Professional

60

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

Под Windows 2000 Professional уже при 100 потокахвремя прогона очереди составляет 10 сек, в то же времяпод Windows 2000 Server и того больше. Выглядит это,скажу я вам, очень удручающе, и работать в таких усло-виях становится крайне дискомфортно. Причем приобре-тение более быстродействующего процессора не ускоря-ет обработку очереди, ведь потоки по-прежнему исполь-зуют отведенные им кванты целиком, и хотя успевают пе-реработать намного больше данных, каждый из них, как ираньше, получает управление раз в десять секунд. Пере-ход на двухпроцессорную машину повышает отзывчивостьсистемы приблизительно в 1.3 раза (два процессора умень-шают длину очереди в два раза, но за счет перехода наAPIC продолжительность одного тика таймера увеличи-вается с 10 мс до 15 мс, итого 2/15/10 ~ 1.3). Однако естьи другой, менее дорогостоящий и намного более радикаль-ный способ решения проблемы. Один щелчок мыши уве-личит реакционность системы в пять, а то и более раз. Неверите? Ну так щелкните по рабочему столу, чтобы окнонашего тестового приложения потеряло фокус. Взгляни-те на таблицу временных замеров, которую мне удалосьполучить.

Что произошло? Потоки ушли в фон, их приоритет по-низился, а величина кванта сократилась до минимума. Ихотя накладные расходы на переключение контекста воз-росли, время обработки очереди сократилось до 2 сек, скоторыми вполне можно жить!

А теперь закройте все окна с несохраненными докумен-тами, которые вам жалко потерять, и увеличьте приоритеттестового приложения хотя бы на одну ступень. Висим? Ато! Потоки тестового приложения отбирают процессорноевремя у всех остальных потоков (включая и некоторые сис-темные), и они оказываются нефункциональны. То есть фун-кциональны, но раз за 10 сек, чего для обработки клавиа-турного и мышиного ввода более чем недостаточно. Забав-но, но i486С-ядро при этом продолжает работать более илименее нормально.

Обсуждение полученных результатовРезультаты тестирования i486С-ядра, полученные на маши-не автора, приведены ниже.

Как видно, это ядро имеет множество преимуществ пе-ред стандартным ACPI-ядром. Какое из них использовать –каждый должен решать сам. i486С-ядро не поддерживаетACPI и поэтому не способно в полной мере управлять энер-гопитанием компьютера, однако отрицать его сильные сто-роны, право же, не стоит. Оно действительно увеличива-ет производительность системы, и ничего мифического вэтом разгоне нет. Не верите? Испытайте его сами. Дляэтого в процессе установки (переустановки) операцион-ной системы дождитесь, когда на экране появится сооб-щение «Press F6 if you need to install a third party SCSI orRAID driver» (Нажмите F6, если вам необходимо загрузитьSCSI- или RAID-драйвер стороннего производителя), на-жмите F5 и выберите из списка имеющихся ядер «StandartPC with C-Step i486» (Стандартный компьютер i486 степ-пинг-С). После чего продолжите установку в обычном ре-жиме.

Ëèñòèíã 4. Èçìåðèòåëü  ïðîäîëæèòåëüíîñòè  êâàíòîâthread(){

int a, b;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){

int a, zzz;SYSTEMTIME st;printf("creating  %d  threads...",  argNthr);for (a = 0; a < argNthr; a++)

CreateThread(0, 0, (void*)thread, 0,0, &zzz);f = 1;  printf("OK\n");Sleep(0); f = 2;while(1){

GetSystemTime(&st);printf("*  %02d:%02d:%02d\n",st.wHour,  ↵↵↵↵↵

st.wMinute,  st.wSecond);Sleep(0);

}return 0;

}

Ëèñòèíã 5. Âðåìÿ îáðàáîòêè î÷åðåäè èç 100 ïîòîêîâ ïðè èñ-ïîëíåíèè  íà  ïåðåäíåì  ïëàíå  (ñëåâà)  è  â  ôîíîâîì  ðåæèìå(ñïðàâà)  íà  Windows 2000 Professional

00:14:48 00:23:1000:15:02 00:23:1200:15:16 00:23:1400:15:30 00:23:1600:15:46 00:23:1800:15:59 00:23:2000:16:11 00:23:2200:16:27 00:23:2400:16:41 00:23:2700:16:55 00:23:29

Òàáëèöà 2. Ñêîðîñòü  ïåðåêëþ÷åíèÿ  êîíòåêñòà  ïîòîêîâ  íàWindows 2000 Professional ñ îòäà÷åé êâàíòîâ âðåìåíè (ïî-òîêè  èñïîëüçóþò  íè÷òîæíóþ  ÷àñòü  îòâåäåííîãî  èì  ïðîöåñ-ñîðíîãî  âðåìåíè)

Òàáëèöà 3.  Ñêîðîñòü ïåðåêëþ÷åíèÿ êîíòåêñòà ïðîöåññîâ íàWindows 2000 Professional ñ îòäà÷åé êâàíòîâ âðåìåíè (ïî-òîêè  èñïîëüçóþò  íè÷òîæíóþ  ÷àñòü  îòâåäåííîãî  èì  ïðîöåñ-ñîðíîãî  âðåìåíè)

Òàáëèöà 4. Ñêîðîñòü  ïåðåêëþ÷åíèÿ  êîíòåêñòà  ïîòîêîâ  íàWindows 2000 Professional áåç îòäà÷è êâàíòîâ âðåìåíè (ïî-òîêè èñïîëüçóþò îòâåäåííîå èì ïðîöåññîðíîå âðåìÿ öåëèêîì)

Òàáëèöà 5. Âðåìÿ  îáðàáîòêè  î÷åðåäè  íà  Windows 2000Professional áåç îòäà÷è êâàíòîâ âðåìåíè (ïîòîêè èñïîëüçó-þò îòâåäåííîå èì ïðîöåññîðíîå âðåìÿ öåëèêîì)

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

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

Очень многим системным администраторам задача органи-зации поиска представляется достаточно сложной. Они счи-тают, что для этого необходимо писать какие-то скрипты,поисковые движки и прочее. Ничего этого не нужно. Для по-нимания материала (а соответственно для организации по-иска) вам понадобятся самые элементарные знания по HTMLи функционированию веб-сервера IIS. Почему все так про-сто. Дело в том, что корпорация Microsoft в составе своихоперационных систем имеет стандартное средство для реа-лизации подобных задач – службу индексирования (далееСИ). Эта служба появилась в операционных Windows доста-точно давно – начиная с Windows NT 4.0.

В периодической печати и сети имеется большое коли-

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

АНДРЕЙ САПРОНОВ

Данная статья о том, как очень просто и быстро настроить службу индексирования(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

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

62

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

мощи различных средств разработки (C++, VB, VBS, JS). Впростейшем случае запрос на поиск можно выполнить изоснастки СИ в меню «Опрос каталога».

При помощи оснастки «Производительность» можнооценить (как локально, так и удаленно) работу СИ по мно-жеству параметров. Вот наиболее интересные из них: раз-мер индекса, число активных пользователей, число запро-сов (рис. 1).

IIS и Служба индексированияБуду считать, что у вас установлен и запущен сервер IIS, аСИ настроен на индексацию и поиск нужных вам папок.

По умолчанию CИ индексирует содержимое вашего веб-узла.

Для поиска пользователю, как минимум, необходимаформа для ввода запросов (например, как стандартная,показанная выше). Форму для запроса, а вместе с ней иметоды доступа к СИ можно реализовать двумя путями: ASPили HTML.

В статье я буду рассматривать второй путь. Причин томудве. Во-первых, он на порядок проще как для понимания,так и для реализации. А во-вторых, очень подробные при-меры на ASP имеются в Platform SDK и MSDN.

Кроме того, пример формы и запросов на поиск при-сутствуют в стандартной поставке IIS: Windows\help\iisHelp\iis\misc\search.asp и Windows\help\iisHelp\iis\misc\query.aspсоответственно. Эти примеры имеют подробные коммен-тарии на русском языке.

В случае с HTML форма будет очень простой и до болизнакомой (рис. 2).

В HTML это выглядит примерно так.

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

Краткое описание службыиндексированияСИ является стандартным компонентом. Основными зада-чами этой службы являются индексирование и организа-ция поиска в указанных пользователем каталогах.

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

По умолчанию служба может индексировать (а следо-вательно, осуществлять быстрый поиск) следующие видыфайлов: HTML, все документы MS Office, сообщения MIME.Этого вполне достаточно для веб-сайта. Однако если вамнеобходима большая функциональность вашего поискови-ка, то СИ может индексировать файлы, для которых име-ются специальные фильтры (т.н. IFilter). Фильтр представ-ляет собой dll-библиотеку, которая реализует интерфейсIFilter для определенного типа файлов. Вы можете самиразработать подобную программу или же взять готовую.Последних существует достаточно много. Вот некоторые:! zip – http://www.4-share.com! pdf – http://www.adobe.com/support/downloads/product.jsp?

product=1&platform=Windows! DjVu – http://www.lizardtech.com/download/dl_download.php?

detail=doc_ifilter&platform=win! Jpg – http://www.aimingtech.com/jpeg_ifilter! wmv/wma, shtml, vmf, msg, pdf, zip, xmp, vCard – http://

www.ifiltershop.com

СИ позволяет индексировать документы, находящиеся налокальной машине и на любом доступном удаленном узле.

Запросы к СИ могут осуществляться на различном ло-гическом уровне (OLE, ADO, ISAPI расширения) и при по-

Ðèñóíîê 1

Ëèñòèíã 1. search.html � ôîðìà äëÿ ââîäà çàïðîñà ïîëüçîâàòåëÿ<HTML><HEAD>

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

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

Файл search.idq является скриптом, который содержитвсю необходимую информацию для запроса к СИ посред-ством ISAPI (Internet Server Application Programming Interface)расширения idq.dll. Для того чтобы IIS распознавал этоттекстовый файл как скрипт в свойствах вашего веб-узла,на вкладке «Домашний каталог» нужно нажать кнопку «На-стройка…» и сопоставить файлам с расширением *.idqфильтр Windows\System32\idq.dll.

Также необходимо, чтобы idq-файл находился в папке,имеющей право на исполнение скриптов (сценариев). Пос-ле этого IIS будет обрабатывать IDQ (Internet Data Query)файлы так, как мы этого хотим. А хотеть мы можем при-мерно следующим образом:

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

Небольшие комментарии относительно некоторых пе-ременных:! CiRestriction – строка запроса. В данном случае при-

нимает значение переменной %CiRestriction% из фай-ла search.html (см. Листинг 1). Параметры, передавае-мые в idq-файл, обрамляются с двух сторон знакамипроцента. Если хотите, можете передать больше пара-метров. Например, количество записей на страницупоиска.

! CiSort – признак для сортировки результатов. В этой пе-ременной можно указать любое поле из заданных вCiColumns (доступно около 60 различных полей). Дан-ный параметр можно также сделать настраиваемымпользователем;

! CiTemplate – ссылка на шаблонный htx-файл (HTMLextension) для отображения результатов запроса;

! CiFlags – указывает на необходимость обхода всех вло-женных папок. Можно использовать для поиска тольков указанных папках без обхода всех их подкаталогов.Тогда CiFlags должен быть равен SHALLOW.

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

HTXФайл htx является не чем иным, как HTML-страницей. Од-нако помимо тегов HTML в этом файле доступны специфи-ческие идентификаторы, относящиеся к СИ и являющиесяспецифичными для введенного пользователем запроса. Этипеременные (идентификаторы), как и в idq-файле, обрам-ляются c обеих сторон знаком процента + знаки больше именьше – слева и справа, как у тегов.

Например, имя файла текущей записи находится в пе-ременной <%filename%>. Именно эти идентификаторы по-зволят вам определить количество найденных документов,их ранг, расположение, а также все остальные параметры.Вместо htx-расширения вы можете без ограничений исполь-зовать стандартное расширение html. Предлагаю рассмот-реть пример шаблонной страницы, которая представитпользователю результаты поиска.

Ëèñòèíã 2. search.idq � ñêðèïò äëÿ çàïðîñà íà ïîèñê# [Query] � îáÿçàòåëüíàÿ ñåêöèÿ äëÿ idq-ôàéëà[Query]

<TITLE>Ïðîñòàÿ ôîðìà äëÿ ïîèñêà</TITLE></HEAD><BODY>

<!--Óêàçûâàåì íà ññûëêó, íà âõîä êîòîðîé ïîéäóòïàðàìåòðû ôîðìû� --><FORM ACTION="search.idq" METHOD="GET" ID="Form1">

<!--�à ïàðàìåòð îäèí - ñòðîêà çàïðîñà -->ß èùó: <INPUT TYPE="TEXT" NAME="CiRestriction" ↵↵↵↵↵

SIZE="30" MAXLENGTH="100" VALUE="" ID="Text1"><!--Êíîïêà --><INPUT TYPE="SUBMIT" VALUE="Ïîèñê" ID="Submit1" ↵↵↵↵↵

NAME="Submit1"></FORM>

</BODY></HTML>

Ðèñóíîê 2

Ðèñóíîê 3

# âîçìîæíûå ïîëÿ äëÿ îòîáðàæåíèÿ â ðåçóëüòàòàõ ïîèñêà# èìÿ ôàéëà, ðàçìåð, ðåëåâàíòíîñòü, îïèñàíèå, ïîëíûé ïóòü,# çàãîëîâîê äîêóìåíòà, äàòà ïîñëåäíåé ìîäèôèêàöèèCiColumns=filename, size, rank, characterization, ↵↵↵↵↵

vpath, DocTitle, write# ñòðîêà çàïðîñà (ñóæåíèÿ) � òî, ÷òî õî÷åò íàéòè ïîëüçîâàòåëüCiRestriction=%CiRestriction%# ìàêñèìàëüíî âîçìîæíîå êîëè÷åñòâî äîêóìåíòîâCiMaxRecordsInResultSet=100# ÷èñëî íàéäåííûõ äîêóìåíòîâ íà ñòðàíèöóCiMaxRecordsPerPage=10# êàòàëîã(è) äëÿ ïîèñêà (îòíîñèòåëüíî êîðíåâîé ïàïêè âåá-óçëà)CiScope=/DaT, /# DEEP óêàçûâàåò, ÷òî ïîèñê èäåò âî âñåõ ïîäêàòàëîãàõ# êàòàëîãîâ èç CiScopeCiFlags=DEEP# øàáëîííàÿ ñòðàíèöà äëÿ îòîáðàæåíèÿ ðåçóëüòàòîâ ïîèñêàCiTemplate=/search.htx# ïîðÿäîê ñîðòèðîâêè íàéäåííûõ äîêóìåíòîâ (çäåñü ïî óìåíüøåíèþ# ðåëåâàíòíîñòè)# d - óìåíüøåíèå ïðèçíàêà, a - óâåëè÷åíèåCiSort=rank[d]

64

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

Ëèñòèíã 3. search.htx � øàáëîííàÿ ñòðàíèöà äëÿ ïðåäñòàâëåíèÿðåçóëüòàòîâ ïîèñêà<!--Íîâûé ïîèñê. Ïðèìåíÿåòñÿ âîçìîæíîñòü ïîäêëþ÷åíèÿôàéëîâ --><%include /search.html%><HR width="100%" SIZE="1"><!--â ñëó÷àå åñëè íåò íèêàêèõ äîêóìåíòîâ, ò.å.CiMatchedRecordCount==0, --><!--ãäå CiMatchedRecordCount � ÷èñëî íàéäåííûõ äîêóìåíòîâ --><%if CiMatchedRecordCount eq 0%>

<H4> Íå íàéäåíî äîêóìåíòîâ, ñîîòâåòñòâóþùèõ çàïðîñó ↵↵↵↵↵"<%CiRestriction%>". </H4>

<%else%><!--Åñëè ÷òî-òî íàøëè, òî óêàçûâàåì íîìåð íà÷àëüíîé èêîíå÷íîé çàïèñè íà ñòðàíèöå --><H4>Äîêóìåíòû ñ <%CiFirstRecordNumber%> ↵↵↵↵↵

ïî <%CiLastRecordNumber%> èç ↵↵↵↵↵<%CiMatchedRecordCount%> ñîîòâåòñòâóþùèõ ↵↵↵↵↵çàïðîñó "<%CiRestriction%>". </H4>

<%endif%><!--%begindetail% âûâîäèò çàïèñè î íàéäåííûõ äîêóìåíòàõñ òåêóùåé %CiCurrentRecordNumber% --><!--äî %CiCurrentRecordNumber%+%CiMaxRecordsPerPage%çàïèñè --><%begindetail%> <!--Íå çàáûâàéòå, ÷òî êëèåíò ïîëó÷èò êîììåíòàðèè

èç ýòîé ñåêöèè ìíîãî-ìíîãî ðàç --><br><%CiCurrentRecordNumber%>.<b><a href="<%vpath%>"><%filename%></a></b><b><i>Îïèñàíèå: </i></b><%characterization%><br><!--âûâîäèì ðåëåâàíòíîñòü, òî÷íóþ ññûëêó, ðàçìåð,è äàòó ïîñëåäíåé ìîäèôèêàöèè -->

Ðèñóíîê 4

<font size=-1 color=DarkGreen> <%rank%> - <%vpath%> - ↵↵↵↵↵ðàçìåð <%size%> áàéò - <%write%> GMT</font><br>

<%enddetail%><!--Êîíñòðóèðóåì êíîïêè äëÿ ïåðåìåùåíèÿ ìåæäó ñòðàíèöàìèïîèñêà --><!--Åñëè â âûâåäåííûõ çàïèñÿõ íåò ïåðâîé, çíà÷èò, ìîæíîâåðíóòüñÿ íàçàä, --><!--ò.å. îòîáðàæàåì êíîïêó "Ïðåäûäóùàÿ" --><%if CiContainsFirstRecord eq 0%>

<!--Ïåðåäàåì óæå çíàêîìîìó ñêðèïòó ïàðàìåòðû ñäâèíóòüñÿíà %CiMaxRecordsPerPage% çàïèñåé íàçàä --><!--îò ïåðâîé çàïèñè íà ñòðàíèöå %CiBookmark%><FORM ACTION="search.idq" METHOD="GET">

<INPUT TYPE="HIDDEN" NAME="CiBookmark" ↵↵↵↵↵VALUE="<%CiBookmark%>" >

<INPUT TYPE="HIDDEN" NAME="CiBookmarkSkipCount" ↵↵↵↵↵VALUE="-<%CiMaxRecordsPerPage%>" >

<!--åñëè íà ïðåäûäóùåé ñòðàíèöå âàì íå íóæíîçíà÷åíèå %CiRestriction%, òî ìîæíî--><!--îáîéòèñü è áåç ýòîé ñòðîêè--><INPUT TYPE="HIDDEN" NAME="CiRestriction" ↵↵↵↵↵

VALUE="<%CiRestriction%>" ><INPUT TYPE="SUBMIT" VALUE="Ïðåäûäóùàÿ">

</FORM><%endif%><!--Àíàëîãè÷íî ïîñòóïàåì ñ êíîïêîé "Ñëåäóþùàÿ" --><%if CiContainsLastRecord eq 0%> -->

<FORM ACTION="search.idq" METHOD="GET"><INPUT TYPE="HIDDEN" NAME="CiBookmark" ↵↵↵↵↵

VALUE="<%CiBookmark%>" ><INPUT TYPE="HIDDEN" NAME="CiBookmarkSkipCount" ↵↵↵↵↵

VALUE="<%CiMaxRecordsPerPage%>" > <INPUT TYPE="HIDDEN" NAME="CiRestriction" ↵↵↵↵↵

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

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

Тегами <%begindetail%>…<%enddetail%> результат зап-роса разделяется на страницы с числом записей на каж-дой странице, равным <%CiMaxRecordsPerPage%>», зада-ваемым в idq-файле. Думаю, назначение остальных теговясно из текста программы.

Кроме тегов-переменных, к результатам поиска приме-нимы условные переходы <% if %>…<% else %>…<% endif%> и логические операторы: EQ (равенство), NE (неравен-ство), LT/GT (меньше/больше), LE/GE (меньше/больше либоравно), CONTAINS (содержание одной строки в другой),ISTYPEEQ (принадлежность определенному типу).

Файлы htx поддерживают возможность подключениявнешних html/htx-файлов (см. Листинг 3) при помощи ди-рективы <%include filename%>. Подключать можно не бо-лее 31 файла, включая вложенные.

В этом примере для вывода информации о найденномдокументе использовано свойство документа %Characte-rization% – краткое описание (аннотация). Это описание авто-матически генерируется СИ при индексировании документов.Если вы хотите, чтобы оно генерировалось, то это нужно ука-зать в свойствах СИ на вкладке «Генерация» – «Генериро-вать аннотации». Там же можно указать размер аннотации.

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

Можно сказать, что в минимальном варианте мы имеемрабочую и достаточно функциональную поисковую систему.На следующем рисунке представлена третья страница с ре-зультатами поиска на запрос «Производительность & IIS»(рис. 4).

Hit-HighlightingТрадиционно в поисковых системах возможно отображе-ние тех частей текста, где встречаются искомые слова илифраза. В СИ эту возможность фирма Microsoft назвала Hit-Highlighting. Для этого используется еще один вид скрип-тов – htw. Как и в случае с idq-скриптами, нужно «научить»IIS распознавать файлы htw как файлы сценария. Это де-лается тем же способом, который мы применяли по отно-шению к idq-файлам (см. выше).

Нужно сопоставить файлам с расширением htw ISAPI-фильтр – библиотеку Windows\system32\webhits.dll. Послеэтого IIS при запросе «somefile.htw?параметры» сгенери-рует страницу с результатами «подсветки». Самый элемен-тарный запрос может иметь вид:

В этом запросе два обязательных параметра (все пара-метры указываются поочередно через знак амперсанда):! CiWebhitsFile – указывает на файл, для которого мы хо-

тим сделать выделение участков текста.

! CiRestriction – строка запроса. Это как раз та самая фра-за, по которой и будет происходить отбор. В показан-ном запросе будут выделены все части текста из фай-ла pageforhighlight.html, содержащие слово IIS.

Кроме основных параметров, существуют и дополни-тельные. Практически все они направлены на изменениевида отображаемого текста. Вы можете сделать выделен-ные слова жирными (CiBold), наклонными (CiItalic), указатьцвет подсветки (CiHiliteColor).

Параметр CiHiliteType при передаче ему значения Fullвыводит не куски страницы, а всю ее целиком, подсвечи-вая искомые слова.

Файл null.htw является абстрактным файлом и служитуказанием серверу для форматирования документа поумолчанию. В документации сообщается, что этот файлможет и не существовать, однако без него IIS выдает стан-дартную ошибку – 404. После создания ничего не содержа-щего файла с таким именем все становится на свои места.Этот файл должен располагаться в папке, имеющей правана исполнение сценариев.

Показанный в листинге 3 пример можно дополнить ссыл-кой «подробней», которая и покажет найденные в текстеслова. Сделаем ее красным цветом. В секцию <%begin-detail%>…<%enddetail%> необходимо вставить следующийкод:

Здесь следует обратить внимание на ключевое слово<%EscapeURL%>. Это необходимо для приведения запро-сов к стандартному виду. Например, запрос на поиск всехдокументов с расширением doc «@filename=*.doc» в каче-стве параметра должен быть передан в виде «%40file-name=%2A.doc», а использованный несколько выше зап-рос «Производительность & IIS» будет заменен на:

Похожее назначение и у ключевого слова <%Escape-HTML%>: он заменяет значимые в HTML символы на иханалоги. Таким образом, символ «<» будет заменен на «&lt».

Несколько слов о формате запросовПравила, формат, примеры запросов для поиска исчерпы-вающе представлены во встроенной справке СИ. Руковод-ство написано на удивление хорошо и понятно.

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

За формальным описанием ключевых параметров иконстант советую обратиться к MSDN (msdn.microsoft.com)и к примерам из Platform SDK.

null.htw?CiWebhitsFile=pageforhighlight.html&CiRestriction=IIS

VALUE="<%CiRestriction%>" ><INPUT TYPE="SUBMIT" VALUE="Ñëåäóþùàÿ">

</FORM><%endif%>

Ëèñòèíã 4. Ññûëêà íà ïîäðîáíûé îò÷åò î ñîâïàäåíèÿõ â äîêóìåíòå<!--ññûëêà íà äîêóìåíò, ñîäåðæàùèé ÷àñòè òåêñòà,êîòîðûå îêðóæàþò èñêîìóþ ôðàçó --><a href="Null.htw?CiWebhitsFile=<%escapeURL ↵↵↵↵↵

vpath%>&CiRestriction=<%escapeURL ↵↵↵↵↵CiRestriction%> ">

<font size=-1 color=Red> ïîäðîáíåé </font> </a>

�%EF%F0%EE%E8%E7%E2%EE%E4%E8%F2%E5%EB%FC%ED%EE%F1%F2 ↵↵↵↵↵%FC%20%26%20IIS�

66

web

О шаблонах в веб-программировании знают или слышалибуквально все. Интерпретируемые языки веб-программи-рования, такие как PHP, Perl, ASP VBScript, всегда даваливозможность совмещать код языка и HTML. Это быстро ипросто. Вначале. Как только проект или отдельный файл-сценарий начинают разрастаться, эта смесь так же быстростановится неудобоваримой. Взявшись переписывать ка-кой-либо сценарий, сразу хочется выкинуть HTML-вставкипросто потому, что они начинают мешать читать программ-ный код.

Ради этого, ради разделения HTML-форматирования вы-вода и логики программы и был придуман механизм шаб-лонов. Логика остается программе, верстка – шаблону.

Программа обрабатывает запрос, формирует данные изаполняет шаблон. Шаблон всегда содержит HTML-код инекоторые дополнительные конструкции для вставки дан-ных и управления выводом. Тут необходимо кое-что прояс-нить.

Небольшой взгляд в историю,или Каким должен бытьшаблонный движок?Несомненно, идея шаблонов пришла в голову программис-там. И суть идеи явно была в том, чтобы убрать HTML-кодиз программы. А вот дальше… Дальше все складывалосьпо-разному. Понятно, что первыми «родились» простейшиешаблоны. Это просто HTML-код и специальные указаниядля вставки данных. По сути, переменные.

Такой простой HTML-документ. Можно сохранить и от-крыть в браузере. Будет страничка с заголовком «::title::» исодержимым «::body::».

Есть конструкция (::имя::), которую нужно выделить изHTML-кода, есть некоторое имя, вместо которого нужноподставить данные из программы. Можете называть этопеременной, но лучше назовем это именованной вставкой.

Дальше все стало гораздо более запутанно. Довольнобыстро выяснилось, что есть блоки, которые могут повто-ряться на одной и той же странице. А иногда на этой стра-нице в зависимости от некоторого условия нужно либо вы-водить что-то, либо нет.

Вот тут началось жуткое многообразие. В шаблоны ста-ли добавлять макроязыки. При этом, в зависимости от ква-

HTML-ШАБЛОНЫ ДЛЯ PHP И PERL,ИЛИ НЕ ДЕЛАЙТЕ ИНСТРУМЕНТСАМОЦЕЛЬЮ!

ДМИТРИЙ ГОРЯИНОВлификации авторов системы, в шаблоны внедряли услов-ные операторы, операторы циклов, регулярные выраже-ния, макрофункции и даже объекты (последним сильногрешат системы шаблонов для языка Java). К чему же этопривело?

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

Он должен был предварительно изучить новый для себясинтаксис.

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

чить или не включить в конечный вывод или, допустим,повторить нужное количество раз.

В идеале синтаксис дополнительных конструкций дол-жен позволять работать с шаблоном в традиционном HTML-редакторе и просматривать готовый файл-шаблон в брау-зере.

Систем шаблонов написано действительно много. Мыбудем рассматривать две из них (одну для языка PHP и вто-рую – для Perl). Основная цель статьи – показать, как пра-вильно работать с готовыми движками, чтобы минимизи-ровать изменения в HTML-коде шаблонов и оставить логи-ку за программой.

Добавление шаблонного движкав проектИтак, что вообще требуется от шаблонного движка? Какминимум – установить, подключить и начать использовать.

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

<html><head>

<title>::title::</title></head><body>::body::</body></html>

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

web

Библиотеку PHP XTemplate мы разместили в подката-логе XTemplate каталога lib. Класс XTemplate содержится вфайле xtpl.p.

Для того чтобы создать новый экземпляр класса, намнужно подключить файл и создать объект:

Для подключения библиотеки HTML::Template в Perlследует указать путь к её директории (если, как в нашемслучае, она размещена отдельно от стандартных библио-тек), подключить модуль и так же создать новый объект:

Что же такое шаблон?Вернемся к началу. Для чего затевалась вся эта история сшаблонами? В основном для того, чтобы отделить код HTMLот кода программы. Идеальный шаблон – это файл, содер-жащий HTML-разметку, и программно заполняемый данны-ми.

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

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

Так что сформулируем еще один постулат – движок недолжен требовать дополнительной обработки шаблонов(читай – запретить предварительную компиляцию и прочиедействия).

Шаблон должен редактироваться как HTML-файл, иметьвозможность предварительного просмотра как HTML-файли подключаться «как есть». Мы же не хотим заставить вер-стальщика установить у себя веб-сервер лишь для того,чтобы просматривать промежуточные результаты своей ра-боты?

Вот два простых примера шаблонов для XTemplate иHTML::Template.

Пример шаблона для библиотеки Xtemplate:

Поэтому лучше всего нам подойдет движок, которыйможно скачать, разархивировать и начать использовать безвсякой дополнительной инсталляции.

В качестве движка для PHP мы рассмотрим библиотекуXTemplate (http://sourceforge.net/projects/xtpl), а для Perl – мо-дуль HTML::Template (http://html-template.sourceforge.net).Оба движка достаточно именно скачать и разархивировать.Вы, конечно, можете установить модуль для Perl по всемправилам, но вполне достаточно развернуть из архива ка-талог HTML с файлом-модулем Template.pm в нем.

Куда разархивировать? Для простоты предлагается со-здать отдельный каталог для дополнительных библиотек.Пусть каталог htdocs – это корневой каталог размещениядокументов на веб-сервере. Создадим на одном уровне сним каталог lib, и разархивируем в него наши движки, каж-дый – в отдельный каталог. Получится вот такая структурадиректорий:

В каталоге XTemplate будут файлы PHP-библиотеки,демонстрационные примеры и т. п. В каталоге HTML можетнаходиться один-единственный perl-модуль – Template.pm

Собственно, на этом все необходимые манипуляции поустановке будут закончены. Просто и эффективно, не прав-да ли?

Просто отдельный классИтак, мы решили отказаться от движков, требующихспециальной инсталляции. Установка наших библиотек сво-дится к разархивированию или простому копированию. Пой-дем дальше.

Движок должен быть инкапсулирован в отдельный класс.Он не должен иметь ветвистых зависимостей с другимимодулями. Иначе устанешь их удовлетворять.

Множество хороших веб-программистов создавали ибудут создавать свои, уникальные решения в рамках от-дельных проектов. В таких случаях что-то непременно «за-тачивается» под конкретные нужды, что-то «дергается на-прямую» и т. д. На базе таких решений вырастают системыпубликаций или управления сайтами. Все это плотно упа-ковано, скручено в тугой узел, плохо отделимо друг от дру-га и, возможно, представляет собой законченный продукт,который, скорее всего, больше никому, кроме заказчика,не пригодится.

Если же речь идет именно о подсистеме для работы сшаблонами, то такая система должна представлять собойзаконченный интерфейс, отделимый от остальной систе-мы. Фактически – уникальный класс либо модуль.

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

В нашем случае это требование выполняется полнос-тью. Нам нужно просто подключить новую библиотеку, со-гласуясь с синтаксисом языка.

<?php...include( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );$xtpl=new XTemplate( ïîëíîå_èìÿ_ôàéëà_øàáëîíà );...?>

#!/usr/bin/perl...use lib $ENV{DOCUMENT_ROOT}."/../lib";# Äðóãîé ñïîñîá äîáàâèòü êàòàëîã ðàçìåùåíèÿ áèáëèîòåêè:# unshift( @INC, $ENV{DOCUMENT_ROOT}."/../lib" );use HTML::Template;my $template = HTML::Template->new( filename => ↵↵↵↵↵

ïîëíîå_èìÿ_ôàéëà_øàáëîíà );

<!-- BEGIN: main --><html><head><title>Ïðèìåð 1</title></head><body><dl><dt>Çíà÷åíèÿ ïåðåìåííûõ äîáàâëÿþòñÿ â XTemplate ÷åðåç ìåòîä<i><b>assign( PARAM, value )</I></b>:</dt>

68

web

Если мы сохраним этот код в файле ex1.html и откроемего для просмотра в браузере, мы увидим такую «картинку»:

Пример шаблона для библиотеки HTML::Template:

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

Код подстановки (<TMPL_VAR NAME=VARIABLE>) неотобразится в браузере. Фактически браузер восприметэтот как HTML-тэг, правило отображения которого ему не-известно.

Лучшее – враг хорошего.Минимизируем логику, макроязыкшаблоновТеперь постараемся разобраться с тем, что нам действи-тельно понадобится в шаблонах, кроме HTML-кода.

Есть несколько типичных задач, с которыми мы сталки-ваемся постоянно и для которых мы хотим иметь четкое ипростое решение. Хорошим примером такой задачи можетслужить вывод таблицы с чередованием фона строк. Привыводе таблицы мы, во-первых, имеем дело с повторяющи-мися элементами (строками), во-вторых, чередование цветафона – это выбор оформления в зависимости от условия.

Шаблон «ex2.html» для библиотеки Xtemplate:

Рассмотрим шаблон подробнее. Прежде всего отметим,что весь шаблон для XTemplate заключен внутри именован-ного блока:

Это необходимое условие синтаксиса шаблоновXTemplate. Любой блок (и весь шаблон целиком) начина-ется с конструкции:

И заканчивается конструкцией:

Простая подстановка в шаблоне описывается так:

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

К примеру, это логично сделать при оформлении под-становок значений ячеек табличной строки.

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

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

Внутри блока item мы опишем два других блока – «odd»и «even» – для чередования оформления строк. Програм-ма-обработчик, проверяя условие четности строки, будетвыбирать нужный блок и управлять генерацией конечногоHTML-вывода, формируя содержимое блока item.

В XTemplate вся логика организации циклов и условийложится на код PHP-программы. В шаблоне описываютсятолько блоки и имена для подстановки значений.

Опишем PHP-код, обрабатывающий этот шаблон:

Ðèñóíîê 1. Âèä øàáëîíà äëÿ XTemplate â áðàóçåðå

<html><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></dl></body></html>

Ðèñóíîê 2. Âèä øàáëîíà äëÿ HTML::Template â áðàóçåðå

<!-- BEGIN: main --><table bgcolor="gray" cellspacing=2 border=0 cellpadding=4> <tr><th colspan="2" bgcolor="silver">{TOPIC}</th></tr> <!-- BEGIN: item --> <!-- BEGIN: odd --> <tr bgcolor="white"> <td>{DATA.NAME}</td><td>{DATA.VAL}</td> </tr> <!-- END: odd --> <!-- BEGIN: even --> <tr bgcolor="lightyellow"> <td>{DATA.NAME}</td><td>{DATA.VAL}</td> </tr> <!-- END: even -->

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

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

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

{èìÿ_ïåðåìåííîé_äëÿ_ïîäñòàíîâêè}

{èìÿ_ìàññèâà.èìÿ_êëþ÷à}

<!-- BEGIN: item -->...<!-- END: item -->

<?php// Ïîäêëþ÷åíèå áèáëèîòåêèinclude( getenv("DOCUMENT_ROOT") ↵↵↵↵↵

."/../lib/XTemplate/xtpl.p" );// Ñîçäàíèå íîâîãî îáúåêòà. ôàéë øàáëîíà ex2.html// ðàçìåùàåì â êàòàëîãå htdocs/../data/xtemp

<dd>$VARIABLE = <i>{VARIABLE}</i></dd></dl></body></html><!-- END: main -->

<!-- END: item --></table><!-- END: main -->

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

web

Для того чтобы включить в результат вывода нужныйблок из шаблона, используется метод parse( BLOCK ). Имяблока задается в виде строки, с указанием всех родитель-ских блоков через точку:

Таким образом, блок-шаблон для вывода нечетныхстрок будет именоваться «main.item.odd».

Разумеется, для того чтобы блок был включен в ко-нечный вывод, необходимо вывести и все родительскиеблоки:

Метод assign( PARAM, value ) служит для заполнениязначения переменной шаблона с именем PARAM.

И наконец, метод out(BLOCK) печатает результат раз-бора шаблона. Если нам нужно не выводить, а сохранитьего в виде готового текста, нужно использовать другой ме-тод – text(BLOCK)…».

Обращение к методу out() равносильно такому коду:

Перепишем наш пример с таблицей для библиотекиHTML::Template. Сразу заметим, что изначально синтаксисшаблонов библиотеки HTML::Template более адаптирован«под программистов». Создатели библиотеки остались вер-

ны программированию. Здесь нет таких простых и понят-ных блоков, выделяемых как HTML-комментарии. Увы,здесь есть отдельные конструкции для циклов и условий.Для нас хорошо то, что синтаксис этих конструкций анало-гичен тэговой разметке, и они не отображаются при про-смотре файла-шаблона браузером.

Шаблон «ex2.html» для библиотеки HTML::Template:

В отличие от XTemplate мы не заключаем весь шаблонв блок.

Библиотека HTML::Template не требует заключать весьшаблон в блок. Она работает с целым файлом, не деля егона отдельные блоки.

Простую подстановку (замену) значения в шаблоне опи-сывают так:

Для того чтобы описать в шаблоне повторяющийсяHTML-код, его придется обрамить «оператором цикла»:

В программе для вывода такого цикла нужно сформи-ровать массив и сопоставить его с именем повторяюще-гося блока. Каждый элемент массива соответствует вклю-чению HTML-кода, описанному внутри <TMPL_LOOP> …</TMPL_LOOP>. Элемент массива представляет собой хэш(наборы пар вида «ключ – значение»). То есть если в шаб-лоне мы описываем подстановку вида <TMPL_VARNAME=имя_параметра_подстановки> внутри повторяюще-гося блока, то в хэше, описывающем элемент массива дляподстановки, мы должны создать пару (имя_параметра_подстановки => значение_для_подстановки).

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

Подключение вывода блока зависит от значения, присво-енного обработчиком шаблона имени_блока. Если мы про-инициализируем переменную блока значением «истина» –

$xtpl=new XTemplate( getenv("DOCUMENT_ROOT") ↵↵↵↵↵."/../data/xtemp/ex2.html" );

// Ïðîñòàÿ ïîäñòàíîâêà çíà÷åíèÿ â {TOPIC}$xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵↵↵↵↵

ôîíà ñòðîê" );$even = 0;// Öèêë äëÿ ôîðìèðîâàíèÿ ñòðîê òàáëèöûfor ($i=1; $i<=10; $i++) { $row = array( NAME => "Ñòðîêà", VAL => $i ); $xtpl->assign(DATA, $row); //  çàâèñèìîñòè îò ÷åòíîñòè ñòðîêè óêàçûâàåì // áëîê-øàáëîí âûâîäà «odd» èëè «even» if ( ($even = ($even XOR 1)) ) { $xtpl->parse("main.item.odd"); } else { $xtpl->parse("main.item.even"); } $xtpl->parse("main.item");}// Ðàçáîð ãëàâíîãî áëîêà øàáëîíà$xtpl->parse("main");// Âûâîä ðåçóëüòàòà ðàçáîðà$xtpl->out("main");?>

BLOCK::= [<Èìÿ_áëîêà>.]<Èìÿ_áëîêà>

... $xtpl->parse(�main.item.odd�); ... $xtpl->parse(�main.item�);...$xtpl->parse(�main�);...

...$out = $xtpl->text("main");echo $out;

$xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵↵↵↵↵ôîíà ñòðîê" );

<table bgcolor="gray" cellspacing=2 border=0 ↵↵↵↵↵cellpadding=4>

<tr><th colspan="2" bgcolor="silver"><TMPL_VAR ↵↵↵↵↵ NAME=TOPIC></th></tr>

<TMPL_LOOP NAME=item> <TMPL_IF NAME=odd> <tr bgcolor="white"> <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td> </tr> <TMPL_ELSE> <tr bgcolor="lightyellow"> <td><TMPL_VAR NAME></td><td><TMPL_VAR VAL></td> </tr> </TMPL_IF> </TMPL_LOOP></table>

<TMPL_VAR NAME=èìÿ_ïàðàìåòðà_ïîäñòàíîâêè>

<TMPL_LOOP NAME=èìÿ_ïîâòîðÿþùåãîñÿ_áëîêà>...</TMPL_LOOP>

<TMPL_IF NAME=èìÿ_áëîêà> ...

</TMPL_IF>

70

web

HTML-код, описанный внутри <TMPL_IF> … </TMPL_IF>,будет включен в результат разбора шаблона.

Если мы не определим никакого значения для блока,или определим его как «ложь», блок не будет включен врезультаты разбора шаблона.

Как и в традиционном синтаксисе языков программи-рования, у условного блока (оператора) может существо-вать «альтернативная часть»:

Если переменная блока получила из программы значе-ние «ложь», то HTML-код между <TMPL_ELSE> и </TMPL_IF>будет включаться в результат разбора.

Код для обработки этого шаблона будет выглядеть так:

Вложенные шаблоныЧто еще может пригодиться в работе с шаблонами? Любойсайт содержит повторяющиеся элементы дизайна – шапка,заголовки, меню и так далее.

Хороший движок шаблонов должен иметь возможностьподключать более мелкие шаблоны (назовите их заготов-ками или библиотечными элементами) внутрь более круп-ных, желательно – поддерживать многоуровневое, вложен-ное подключение.

Чуть выше мы написали два шаблона для вывода таб-лицы. Логично не копировать этот код везде, где нам пона-добится отобразить такую таблицу, а иметь возможностьсохранить этот шаблон отдельно и подключать его по меренеобходимости в другие шаблоны.

Обе наши библиотеки такую возможность предостав-ляют. В XTemplate существует конструкция:

В HTML::Template аналогичные функции выполняет кон-струкция:

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

Это плохо. Более того, это как-то противоречит самойидее о легкости подключения (читай – переноса) и замыс-лу о повторном использовании более мелких (библиотеч-ных) шаблонов.

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

В случае HTML::Template эта возможность предусмот-рена изначально. Обратите внимание, мы сразу исполь-зовали инициализацию каталога шаблонов в нашем при-мере:

В библиотеке XTemplate это не предусмотрено (во вся-ком случае в версии 0.2.4-3).

Сформировать имя файла шаблона динамически, на-писав нечто вроде:

и присвоить (проассоциировать) нужное значение подста-

<TMPL_IF NAME=èìÿ_áëîêà>...<TMPL_ELSE>...</TMPL_IF>

#!/usr/bin/perluse strict;# Ïóòü ê êàòàëîãó áèáëèîòåêèuse lib $ENV{DOCUMENT_ROOT}."/../lib";# Ïîäêëþ÷åíèå áèáëèîòåêè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};# Äëÿ íå÷åòíûõ ýëåìåíòîâ óñòàíàâëèâàåì çíà÷åíèå "odd",# ðàâíûì "1", äëÿ ÷åòíûõ - "0"if ( ($even = ($even xor 1)) ) { $row->{"odd"} = 1;} else { $row->{"odd"} = 0;}# Äîáàâëÿåì íîâûé õýø-ýëåìåíò â ìàññèâpush(@{$items}, $row);

}# Àññîöèèðóåì ìàññèâ ñ ïîâòîðÿþùèìñÿ áëîêîì ( <TMPL_LOOP> )$template->param(item => $items);# Ðåçóëüòàò ðàçáîðà øàáëîíà âîçâðàùàåò íàì â âèäå ñòðîêè# âûçîâ ìåòîäà output().# Ìû ìîæåì ñîõðàíèòü åãî â ïåðåìåííóþ, çàïèñàòü â ôàéë# èëè âûâåñòè, êàê ëþáîå äðóãîå çíà÷åíèå:print "Content-type: text/html\n\n";print $template->output();__END__

<TMPL_INCLUDE NAME="èìÿ_âëîæåííîãî_ôàéëà_øàáëîíà">

my $template = HTML::Template->new( filename => "ex2.html", path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/"

);

{FILE "{TEMP_FILE}"}

{FILE "èìÿ_âëîæåííîãî_ôàéëà_øàáëîíà"}

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

web

новкой в {TEMP_FILE} тоже не выйдет. Конструктор классапроизводит рекурсивную inline-подстановку всех вложенныхшаблонов, и только потом производит разбор и обработкуполного шаблона со всеми подстановками.

Что делать? Отказаться от идеи повторного использо-вания вложенных элементов? Ну не все так печально, каккажется. Библиотека XTemplate распространяется по лицен-зии GNU General Public License. Эта лицензия подразуме-вает возможность внесения изменений в исходный продуктпри соблюдении определенных правил1. Нужные нам из-менения ограничатся тремя строчками с добавлением ком-ментариев.

Суть изменения сводится к добавлению еще одного чле-на класса и опциональной инициализации его в конструк-торе. Этот новый член класса – path – будет определятьпуть к директории шаблонов. Остальные модификации вкоде исходного класса ограничиваются конструктором иметодом getfile(...).

После внесения таких изменений в класс XTemplate мы:! Сможем инициализировать единый каталог для хране-

ния шаблонов и использовать в них простые имена дляподключения файлов вложенных шаблонов.

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

Теперь можно нормально работать.Шаблон «ex_inc.html» для библиотеки XTemplate:

Обработчик шаблона выглядит так:

Результат наших действий в браузере отразится при-мерно таким образом:

Для библиотеки HTML::Template подобных модифика-ций исходного кода производить не требуется. Параметри-зованное указание каталога размещения шаблонов тампредусмотрено заранее.

Шаблон «ex_inc.html» для библиотеки HTML::Template:

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

class XTemplate {.../* Íîâûé ÷ëåí êëàññà */var $path = "";/***[ constructor ]*** /function XTemplate ($file,$mainblock="main", $path="") { /* Èíèöèàëèçèðóåì çíà÷åíèå path, åñëè ñîîòâåòñòâóþùèé ïàðàìåòð íå ïóñò */ if ($path) $this->path= str_replace("\\", "/", $path)."/"; �}�/***[ getfile ]*** //* returns the contents of a file*/function getfile($file) { /* Èñïîëüçóåì êîíêàòåíàöèþ çíà÷åíèÿ path è ïåðåäàííîãî èìåíè ôàéëà */ $file = $this->path.$file; if (!isset($file)) { $this->set_error("!isset file name!"); return ""; } ...}...} /* end of XTemplate class. */

<!-- BEGIN: main --><html><head><title>Âëîæåííûå øàáëîíû</title></head><body><p>Ïîäêëþ÷åíèå øàáëîíà òàáëèöû <b><i>"{TEMPLATE}" </i></b>÷åðåç { FILE "{TEMPLATE}" }

</p>{FILE "ex2.html"}</body></html><!-- END: main -->

<?phpinclude( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );/* êîíñòðóêòîð äîáàâèëñÿ äîïîëíèòåëüíûé ïàðàìåòð $path.Äëÿ ñîâìåñòèìîñòè íàì ïðèäåòñÿ âûçûâàòü êîíñòðóêòîð ñ óêà-çàíèåì íàøåãî íîâîãî ïàðàìåòðà è âòîðîãî ïàðàìåòðà âûçîâà �íàçâàíèåì ãëàâíîãî áëîêà øàáëîíà.Åñòåñòâåííî, ýòî ïîíàäîáèòñÿ òîëüêî òàì, ãäå íàì íåîáõî-äèìî óêàçàòü òðåòèé ïàðàìåòð êîíñòðóêòîðó, ò.å. â øàáëî-íàõ ñ âëîæåíèåì.*/$xtpl=new XTemplate( "ex_inc.html",

"main", getenv("DOCUMENT_ROOT")."/../lib/ ↵↵↵↵↵

data/xtemp" );$xtpl->assign( TEMPLATE, "ex2.html");/*Èíèöèàëèçàöèÿ äàííûõ âëîæåííîãî øàáëîíà.Ïðè îáðàùåíèè ê ìåòîäó parse() íóæíî ó÷èòûâàòü, ÷òî ýëå-ìåíòû øàáëîíà ex2.html âëîæåíû â áëîê "main" áîëåå âåðõíå-ãî øàáëîíà � ex_inc.html. Ïîýòîìó óêàçûâàòü ïðèäåòñÿ êàê$xtpl->parse("main.main.item") è ò. ï.*/$xtpl->assign( TOPIC, "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵↵↵↵↵

ôîíà ñòðîê" );$even = 0;for ($i=1; $i<=10; $i++) { $row = array( NAME => "Ñòðîêà", VAL => $i ); $xtpl->assign(DATA, $row); if ( ($even = ($even XOR 1)) ) { $xtpl->parse("main.main.item.odd"); } else { $xtpl->parse("main.main.item.even"); } $xtpl->parse("main.main.item");}/* Ðàçáîð âëîæåííîãî øàáëîíà */$xtpl->parse("main.main");

Ðèñóíîê 3. Ðåçóëüòàò ðàáîòû ñ âëîæåííûìè øàáëîíàìè. Âûâîäòàáëèöû ðåàëèçîâàí îòäåëüíûì øàáëîíîì

72

web

Обработчик шаблона:

Результат работы аналогичен, не будем загромождатьместо еще одной похожей картинкой.

Еще один «интересный случай». Вложенные шаблоныхочется использовать для стандартных элементов. Легкопредположить, что один и тот же элемент может использо-ваться на странице многократно. Если включить его каквложенный шаблон, получится путаница с заполнением. Какпоступить?

Нам нужен управляемый способ многократного вклю-чения вложенных шаблонов (стандартных элементов ди-зайна). Звучит очень серьезно, реализуется – просто.Каждое включение нужно выделить в виде отдельногоблока.

Шаблон «list.html» для библиотеки XTemplate:

Шаблон «ex_inc2.html» для библиотеки XTemplate:

Подключение одного шаблона в разных местах страни-цы и с разным заполнением.

Обрабатываем шаблон на PHP:

Для того чтобы реализовать то же самое с использова-нием библиотеки HTML::Template, придется кое-что уточ-нить. В этой библиотеке нет понятия именованного блока.Как же разделить пространство действия переменных шаб-лона?

Для этого нужно воспользоваться конструкцией<TMPL_LOOP …>…</TMPL_LOOP>. Внутри этого «цикла»переменные-подстановки являются вложенными в цикл.Воспользуемся этим фактом.

Шаблон «list.html» для библиотеки HTML::Template:

#!/usr/bin/perluse strict;# Ïóòü ê êàòàëîãó áèáëèîòåêèuse lib $ENV{DOCUMENT_ROOT}."/../lib";# Ïîäêëþ÷åíèå áèáëèîòåêèuse HTML::Template;# Ñîçäàíèå íîâîãî îáúåêòà.  äàííîì ñëó÷àå ìû îòäåëüíî# óêàçûâàåì â êîíñòðóêòîðå êðàòêîå èìÿ ôàéëà øàáëîíà# ex_inc.html è ïóòü ê êàòàëîãó htdocs/../data/htmltemp,# ãäå ðàçìåùåí øàáëîímy $template = HTML::Template->new( filename => "ex_inc.html", path => $ENV{DOCUMENT_ROOT}."/../data/htmltemp/");$template->param(TEMPLATE => "ex2.html" );# Çàïîëíåíèå äàííûõ âëîæåííîãî øàáëîíà$template->param(TOPIC => "Âûâîä òàáëèöû ñ ÷åðåäîâàíèåì ↵↵↵↵↵

ôîíà ñòðîê");my $even = 0;my $items = ();for (my $i=1; $i<=10; $i++) { my $row = { "NAME" => "Ñòðîêà", "VAL" => $i }; if ( ($even = ($even xor 1)) ) { $row->{"odd"} = 1; } else { $row->{"odd"} = 0; } push(@{$items}, $row);}$template->param(item => $items);print "Content-type: text/html\n\n";print $template->output();__END__

<!-- BEGIN: list --><ol><!-- BEGIN: item --><li>{ITEM}</li>

</p><!-- BEGIN: first -->Ñïèñîê 1:{FILE "list.html"}<!-- END: first --><!-- BEGIN: second -->Ñïèñîê 2:{FILE "list.html"}<!-- END: second --></body></html><!-- END: main -->

<?phpinclude( getenv("DOCUMENT_ROOT")."/../lib/XTemplate/xtpl.p" );$xtpl=new XTemplate( "ex_inc2.html",

"main", getenv("DOCUMENT_ROOT")."/../lib/ ↵↵↵↵↵

data/xtemp" );// Äàííûå äëÿ ïåðâîãî ñïèñêà$list = array("one", "two", "three");foreach($list as $item) { $xtpl->assign(ITEM, $item); $xtpl->parse("main.first.list.item");}// Ðàçáèðàåì áëîê "first"$xtpl->parse("main.first.list");$xtpl->parse("main.first");// Äàííûå äëÿ âòîðîãî ñïèñêà$list = array("ðàç", "äâà", "òðè");foreach($list as $item) { $xtpl->assign(ITEM, $item); $xtpl->parse("main.second.list.item");}// Ðàçáèðàåì áëîê "second"$xtpl->parse("main.second.list");$xtpl->parse("main.second");

$xtpl->parse("main");$xtpl->out("main");?>

<ol><TMPL_LOOP NAME="item"><li><TMPL_VAR NAME="item"></li>

<!� BEGIN: main �><html><head><title>Âëîæåííûå øàáëîíû</title></head><body><p>

<html><head><title>Âëîæåííûå øàáëîíû</title></head><body><p>Ïîäêëþ÷åíèå øàáëîíà òàáëèöû <b><i>"<TMPL_VAR ↵↵↵↵↵

NAME=TEMPLATE>" </i></b>÷åðåç &lt;TMPL_INCLUDE NAME="ex2.html"></p><TMPL_INCLUDE NAME="ex2.html"></body></html>

<!-- END: item --></ol><!-- END: list -->

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

web

Шаблон «ex_inc2.html» для библиотеки HTML::Template:

Подключение одного шаблона в разных местах страни-цы и с разным заполнением.

Вся тонкость обработки сводится к корректному фор-мированию массивов:

Не так уж и страшно, не так ли? Но обратите внима-ние на следующий факт. При заполнении конструкции<TMPL_LOOP …> используется не массив, а ссылка намассив. Это важно!

ЗаключениеВыбирая движок для шаблонов, старайтесь оставить зашаблонами верстку (оформление), а логику – за програм-мой. Не надо перекладывать на шаблоны логику выполне-ния и ни в коем случае не надо внедрять в шаблоны какие-либо объекты!

Все «расширенные» (в сравнении с обычным кодомHTML) возможности шаблонов должны заканчиваться навозможностях:! пометить точку для вставки (переменная шаблона);! пометить блок для выбора (именованный IF);! пометить блок для повтора (именованный LOOP).

И даже это избыточная модель! В PHP-ом XTemplate всееще более «доведено до ума»:

И все!У вас есть блок вида: <— BEGIN: имя —> …<— END:

имя —>. На стороне программы вы можете повторить егонужное количество раз или выбрать из двух смежных тот,который нужно вывести, исходя из логики.

Для библиотеки Perl HTML::Template считайте име-нованным блоком конструкцию <TMPL_IF NAME=имя >…</TMPL_IF>. Все, что нужно повторить несколько раз, дол-жно быть заключено в конструкцию вида <TMPL_LOOPNAME=имя> …</TMPL_LOOP>.

У вас есть место вставки вида {имя} или явная поблаж-ка {имя_структуры.имя}.

И последняя конструкция – включение одного шаблонав другой. По принципу inline-подстановки: {FILE «имя»} или<TMPL_INCLUDE NAME=«имя»>.

Класс (движок) шаблонов должен уметь работать с вло-жениями блоков друг в друга и воспринимать каждый блоккак отдельное пространство имен. Для HTML::Template этобудет верно, если вы начнете рассматривать хэш, заполня-ющий цикл как ограничитель такого пространства.

Этого достаточно, чтобы сформировать любой вывод.Класс (движок) шаблонов должен позволять рекурсив-

ное включение файлов-шаблонов друг в друга.Это позволит вам создавать отдельные мини-шаблоны

для повторяющихся элементов, а не копировать один и тотже HTML-код из шаблона в шаблон.

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

Движок также не должен требовать предварительнойкомпиляции шаблонов ни в какое промежуточное представ-ление. Это позволит вносить изменения в оформлениеименно на уровне шаблонов и полностью отделить HTML-форматирование вывода от программной части.

<html><head><title>Âëîæåííûå øàáëîíû</title></head><body><p>

</p>Ñïèñîê 1:<TMPL_LOOP NAME="first"><TMPL_INCLUDE NAME="list.html"></TMPL_LOOP>Ñïèñîê 2:<TMPL_LOOP NAME="second"><TMPL_INCLUDE NAME="list.html"></TMPL_LOOP></body></html>

</TMPL_LOOP></ol>

#!/usr/bin/perluse strict;# Ïóòü ê êàòàëîãó áèáëèîòåêèuse lib $ENV{DOCUMENT_ROOT}."/../lib";# Ïîäêëþ÷åíèå áèáëèîòåêèuse HTML::Template;my $template = HTML::Template->new( filename => "ex_inc2.html", path => $ENV{DOCUMENT_ROOT}."/../lib/data/htmltemp/");# Çàïîëíÿåì ìàññèâ äëÿ øàáëîíà list.htmlmy $items = ();push( @{$items}, ({"item" => "one"}, {"item" => "two"}, {"item" => "three"}));# Îðãàíèçóåì îáðàìëÿþùèé ìàññèâ äëÿ <TMPL_LOOP NAME=�first�>my $list = ();push(@{$list}, {"item" => $items});# Àññîöèèðóåì çíà÷åíèå$template->param("first" => $list);# Îáíóëÿåì è çàïîëíÿåì ìàññèâû çàíîâî$items = ();push( @{$items}, ({"item" => "ðàç"}, {"item" => "äâà"}, {"item" => "òðè"}));$list = ();push(@{$list}, {"item" => $items});# Àññîöèèðóåì çíà÷åíèå$template->param("second" => $list);print "Content-type: text/html\n\n";print $template->output();__END__

<ul><!-- BEGIN: menulist --><!-- BEGIN: link --><li><a href="{ITEM.URL}">{ITEM.NAME}</a></li><!-- END: link --><!-- BEGIN: active --><li>{ITEM.NAME}</li><!-- END: active --><!-- END: menulist --></ul>

74

web

Сборке HTML-документов по шаблонам посвящено вели-кое множество публикаций самого разного масштаба и ка-чества: от небольших статей до детальных руководств икниг, от дилетантских до весьма профессиональных. Но по-давляющее большинство авторов сосредоточивается на on-line-сборке. Ими рассматривается ситуация, когда на сер-вере лежат не статические документы, а шаблоны: заго-товки и части документов. При запросе клиентом соответ-ствующей страницы на сервере запускается некий меха-низм, собирающий веб-страницу «на лету» (в реальноммасштабе времени) и отправляющий её клиенту. Для ре-шения подобных задач разработано множество инструмен-тов и средств, начиная с несложных и интегрированных глу-боко в сервер (например, SSI) и заканчивая многофункци-ональными самостоятельными модулями и библиотекамис очень богатыми возможностями.

Тема on-line-обработки шаблонов действительно оченьинтересна и воистину неисчерпаема, поскольку в разныхусловиях оказываются уместны разные подходы. Неудиви-тельно, что так много авторов обращается именно к этойтеме. Но я хотел бы уделить немного внимания механиз-мам off-line-сборки. Возможно, не слишком распространён-ным термином «off-line-сборка» я буду называть процесссборки шаблонов на локальной машине, в отсутствие сер-верного ПО и не для передачи клиенту. В результате такойсборки вы получаете набор статических документов (хотяникто не запрещает использовать, например, SSI-инструк-ции), готовых к размещению на сервере. Оказывается, кон-цепция сборки документов по шаблону может быть весьмаполезна не только при сборке документов «на лету», но ипри сборке статических документов.

Прежде чем обсудить конкретную реализацию off-line-процессора шаблонов, нам необходимо сформулироватькритерии, которым он должен соответствовать. Но передэтим давайте определимся, в каких ситуациях off-line-сбор-ка может быть полезна (и для кого написана эта статья).

Когда применима off-line-сборка?Ценность автоматизации сборки статических документов

ОБРАБОТКА HTML-ШАБЛОНОВ OFF-LINE.ВОЗМОЖНОСТИ И ОГРАНИЧЕНИЯ

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

Приведу два, как мне кажется, наиболее житейских при-мера.

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

Здесь возникает возражение: такой поход прожорливдо дискового пространства. Соглашусь с этим, но отвечу,что дисковое пространство стоит копейки и обходится го-раздо дешевле процессорного времени, за одну только воз-можность использования коего обычно приходится допол-нительно доплачивать. При отказе от on-line-сборки в пользуoff-line-сборки мы проигрываем в дисковом пространстве,

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

web

ресобрать документы, и название раздела изменится вовсех оглавлениях, заголовках, элементах <title>...</title> иво всех других местах, где встречалось это название. Также можно будет менять цвета и другие элементы оформле-ния. То есть ресурс становится предельно управляемым вовсех отношениях.! Второе требование: программный код (код, выполняю-

щий сборку) должен быть полностью отделён от кодашаблонов.

Это классическое требование, которое всегда предъяв-ляется к обработчикам шаблонов и нарушается с таким жепостоянством. Иначе и быть не может. С одной стороны,процессор шаблонов должен быть управляемым, то естьпрограммируемым, иначе он сможет собирать только одиндокумент по одному шаблону и утратит всяческий смысл.Но, с другой стороны, программный код в шаблонах дол-жен, как мы только что сказали, отсутствовать. Этот пара-докс каждый из разработчиков решает согласно своим за-дачам. Наши задачи таковы, что в шаблонах должен бытьминимум управляющих конструкций. Мы намеренно не бу-дем реализовывать ни переменных, ни условных перехо-дов. Естественно, у нас не будет ни циклов (частный слу-чай условных переходов), ни вычислений (направленных наобработку переменных)3.

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

мально переносима. Её работоспособность не должназависеть от ОС, ПО, дополнительных модулей и расши-рений. У дизайнера и верстальщика должна быть воз-можность просто носить её на flash-носителе вместе срабочими материалами. Установка на новую машинудолжна быть упрощена до полной незаметности.

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

! Конечно, шаблоны должны обрабатываться рекуррент-но, допуская вставку шаблона в шаблон.

но объем трафика (гораздо более важная характеристика)не меняется, а процессорное время (тоже весьма важнаяхарактеристика) существенно экономится. Таким образом,суммарно мы, скорее всего, получим выигрыш. (Вопросытрудоёмкости загрузки большого объёма данных на сервери другие вопросы я обязательно затрону, но ближе к концустатьи.)

Как видите, в описанной ситуации off-line-сборка вполнеоправданна, а такая ситуация возникает при поддержке прак-тически любого ресурса в Web.

Вторая немаловажная область для применения техни-ки off-line-сборки – разработка. Веб-дизайнер1 должен иметьвозможность оперативно глянуть на дело рук своих. Еслиего работа полностью сосредоточена на разработке шаб-лонов, сборка которых произойдёт только на сервере, тоему (на локальной машине) затруднительно увидеть резуль-тат своих трудов. Сперва он вынужден разместить всё насервере, а это не только трудно, но и потребует от дизайне-ра квалификации, не свойственной для него.

В этой ситуации было бы удобнее, если бы дизайнеримел под рукой процессор обработки шаблонов и мог быс-тро пересобрать весь набор документов, легко «играя» па-литрами или компоновками материала.

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

Разработка тем более упрощается, если программист,дизайнер и верстальщик пользуются одним набором инст-рументов и одним форматом шаблонов.

Итак, задачи намечены, наметим теперь пути их реше-ния.

Требования к аппарату off-line-сборки

Самые общие требования! Первое требование прозвучит почти комично: наша си-

стема сборки должна обеспечивать сборку. То есть онадолжна давать возможность описать, в какое место об-рабатываемого файла содержимое какого файла будетвставляться.

Такие места вставки будем называть точками вставки.Получив возможность вставлять некую информацию во мно-гие места многих файлов, мы получили возможность хра-нить одну информацию в одном месте. Это позволит и из-менять (при необходимости) одну информацию в одномместе. То есть при последовательном проведении этой тех-ники в жизнь вы сможете поменять название раздела водном-единственном файле, одним касанием <Enter> пе-

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

2 Уточню, что под «верстальщиком» я буду понимать человека (или команду), обеспечивающего информационное на-полнение каждой конкретной страницы. Верстальщик получает материалы в естественном для заказчика формате иадаптирует их для размещения на веб-страницах.

3 Это не совсем так, что-то сделать всё-таки придётся. Читайте дальше.

76

web

Требования к шаблонам! Синтаксис оформления точек вставки должен допускать

достаточную гибкость. Разработчик должен иметь воз-можность сделать точку вставки и заметной, и компак-тной, в зависимости от конкретной ситуации.

! Точки вставки не должны быть похожи на HTML-теги.Это позволит беспрепятственно применять программыпроверки HTML-кода к отдельным шаблонам и легко ло-кализовать ошибки типа забытых закрывающих теговили неверных атрибутов.

Возможности управления ходом сборкиПришло время компромиссов и разрешения парадокса, окотором я говорил. Я уже вижу, как взгрустнули програм-мисты. Конечно, отказ от переменных, условных переходов,вычислений и циклов – тяжёлая утрата. Можно ли сочетатьпредельную простоту кода и достаточную управляемость?Думаю, что да.

Я бы назвал предлагаемое решение «условной встав-кой». Каждый шаблон обрабатывается с определённым па-раметром. Фактически параметр – это единственная пере-менная. Но от того, что она одна, ей не нужно имя и синтак-сис её использования становится предельно прост и поня-тен даже человеку, далёкому от программирования (осо-бенно, если не называть её словом «переменная»). В зави-симости от этого параметра в шаблон будет встраиватьсясодержимое того или иного файла.

Итак, ещё одно требование:! Обработчик должен поддерживать сборку с параметром

и условную вставку.

Пока, пожалуй, хватит требований, мелкие замечанияи уточнения разберём на конкретном примере.

Пример реализации off-line-процессораДавайте теперь рассмотрим программу, хоть и далёкую отсовершенства и законченности, но компактно реализующуюизложенные выше идеи и вполне работоспособную, в чёму нас будет возможность убедиться в следующем разделе(где будет создан небольшой сайт из шести страниц).

Выбор языкаПри выборе языка программирования я остановился навездесущем Perl, не устояв перед его широкими возмож-ностями и богатством платформ, на которые он перенесён.

Кроме того, давайте не будем использовать в нашейпрограмме внешних модулей. Такой код можно будет запу-стить под Windows, даже не устанавливая громоздкийActivePerl, а воспользовавшись одним только perl.exe из

коллекции DJGPP4. У пользователей UNIX проблем с уста-новкой Perl вообще не возникнет, поскольку Perl являетсянеотъемлемой частью подобных систем.

Можно было бы поступить ещё концептуальней: напи-сать программу на чём-нибудь компилируемом, например,на C. Такая программа не требовала бы даже интерпрета-тора. Возможно, это хорошая идея. Я не пошёл по этомупути по двум причинам. Во-первых, разница не так принци-пиальна: избавились от интерпретатора – понадобился ком-пилятор. Во-вторых, код получился бы не такой компакт-ный, и приводить его в статье было бы не так удобно. Пос-леднее обстоятельство, как вы понимаете, не должно оста-навливать вас.

Осмотр кода и формат сценария сборкиДавайте пробежим глазами строки кода, который у меняполучился:

Я буду предполагать, что читатель знаком с Perl, и огра-ничусь только краткими пояснениями. Тело программы со-стоит из одной строки 34 (если не считать объявления двухглобальных переменных в строках 5 и 6)5. Здесь в циклеwhile одна за другой обрабатываются строки входного фай-ла. Чтобы отличать его от шаблонов, давайте назовём егогромким словом «сценарий сборки» или просто «сценарий».

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 Это сделано специально, чтобы вам было легче развивать программу, я уже сказал, что приводимая здесь реализа-ция скорее учебная, нежели боевая.

01: #!/usr/bin/perl -w02: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: $text=~s{\(##[#\s]*([^#\s]+)[#\s]*##\)}{17: 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: }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) }

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

web

Каждая строка сценария сборки разбивается на поля (раз-делители – любые пробельные символы в любом количе-стве). Значения этих полей передаются функции assemble,которая инициализирует и запускает процесс сборки шаб-лона, а результат сборки записывает в назначенный файл.

Первое поле каждой строки сценария задаёт имя соби-раемого файла (имя файла, в который будет помещён окон-чательный результат сборки). Второе поле – имя корневогошаблона, с которого начнётся сборка. Третье поле – пара-метр, с которым будет собираться корневой шаблон.

Функция assemble_step, выполняющая саму сборку, вы-зывается рекуррентно (вы видите, что она вызывается изсамой себя в строке 21). Её аргументы таковы: первый –уровень вложенности (глубина рекурсии); второй – имяфайла шаблона, который необходимо обработать; тре-тий – параметр сборки, с которым необходимо обработатьшаблон.

Первое, что делает функция assemble_step, – выдаётстроку протокола сборки (строка 10 листинга). Начальныесимволы строки задают отступ, показывая глубину рекур-сии (чем больше уровень вложенности, тем больше отступ).Далее следует имя файла шаблона и параметр, с которымего предстоит обработать.

Далее (строки 11-15) в переменную $text читается шаб-лон, глубина вложенности увеличивается на единицу (этозначение будет передано «дочерним» assemble_step) и на-чинается самое интересное – обработка шаблона.

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

Что же мы ищем и на что заменяем?

Точки вставкиИщем мы, конечно, точки вставки. Как видите (строка 16),оформлены они у нас будут достаточно гибко: открываю-щая круглая скобка; два знака #; любое количество пробе-лов6 или знаков #; некоторое количество знаков, отличаю-щихся от пробелов и #, они сохранятся в переменной $1 ибудут использованы для определения, что именно необхо-димо вставить в данную точку; далее следует снова произ-вольное количество пробелов и символов #; и наконец за-вершающие два # и закрывающая скобка.

Я не навязываю читателю именно такой стиль, простомне он кажется удобным. Знак # выбран потому, что еголегко заметить в тексте. А формат позволяет оформлятьточки вставки и компактно (полезно, когда их много):

И громоздко (полезно, когда точек вставки мало и ихнадо выделить):

или

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

ВставкаЧто мы будем заменять – теперь понятно, давайте разбе-рёмся с тем, чем мы будем заменять.

В качестве имени файла, который будет вставляться вданную точку, будет использоваться наша переменная $1,но... после небольшой «доработки»:! Первым делом (строка 18) все знаки ? в потенциальном

имени файла заменятся на текущее значение парамет-ра сборки.

То есть если код:

собирается с параметром index, то между тегами <html> и</html> будет вставлен шаблон из файла text-for-index. Еслиже этот же шаблон собирается с параметром paper, то вместо(## text-for-? ##) будет вставлен шаблон из файла text-for-paper.! Далее (в строке 19) в потенциальном имени файла отыс-

киваются все конструкции в круглых скобках. С нимипроизводится следующая замена: если строка в скоб-ках совпадает с текущим параметром сборки, то скоб-ка заменяется на строку «yes», в противном случае оназаменяется на строку «no».

Поясню на примере шаблона:

Если этот шаблон собирается с параметром index, то бу-дет использован файл is-i-home-yes.txt. Во всех других слу-чаях, напротив, будет использован is-i-home-no.txt.! Затем (строка 20) проверяется, содержит ли потенци-

альное имя файла двоеточие. Если ответ утвердитель-ный, то часть имени слева от двоеточия используется вкачестве имени файла, правая часть имени будет ис-пользована как новый параметр сборки.

Это даёт возможность «подменять» параметр сборки впроцессе самой сборки и собирать разные части докумен-та с разными параметрами. Обратите внимание: подменаносит локальный характер, шаблон-«родитель» о ней ни-чего не знает, его обработка продолжается с тем же пара-метром. Подмену замечает только шаблон-«потомок» (и егопотомки, если не произойдёт ещё одной подмены).! Наконец (строка 21) мы вызываем рекуррентно

assemble_step, которая производит сборку того, что намв результате оказалось нужно (и, возможно, с новымипараметрами), и помещаем результат в точку вставки.

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

(## NAME ##)

(############### NAME ###############)

<html>(## text-for-? ##)</html>

<html>(## is-i-home-(index).txt ##)</html>

(## ## ## NAME ## ## ##)

78

web

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

На нашем сервере будет три страницы: index.html,contact.html и about.html. Будем называть их основными. Укаждой из них будет ещё и версия для печати: index-print.html, contact-print.html и about-print.html, соответствен-но. Итого шесть страниц, все они показаны на рисунке суказаниями имён файлов.

Сценарий сборки будет таков7:

Каждому собираемому html-файлу, как вы видите, здесьотвечает своя строка, в которой, кроме имени файла-ми-шени, указываются шаблон и параметр.

Как вы поняли, все основные станицы будут собраныпо шаблону skeleton, а страницы для печати – по шаблонуskeleton-print.

Сборка основных страницРассмотрим сперва сборку основных страниц: index.html,contact.html и about.html. Согласно первым трём строчкамсценария сборки, все они собираются по общему шаблонуskeleton, варьируется только параметр (исключительно дляпростоты чтения он тождественен имени соответствующе-го собираемого файла).

Этот раздел будет фактически посвящён рассмотрениюшаблона skeleton:

В его первой строке находится точка вставки шаблонаbody-open:

В этом шаблоне есть тоже одна точка вставки. В неёвставляется информация одного из трёх файлов index-head,contact-head и about-head. При сборке index.html (с пара-метром index) используется первый – index-head, дляcontact.html и about.html – contact-head и about-head соот-ветственно. В файлах *-head, как вы уже поняли, содер-жатся заголовки соответствующих страниц. Например, вindex-head:

Во второй строке шаблона skeleton (мы продолжаем егорассмотрение) – точка вставки шаблона sep:

Здесь нет ничего хитрого, это чисто декоративный эле-мент – горизонтальная серая полоса. На наших страницахих три на каждой (см. рисунок).

В строках 3-8 шаблона skeleton – код, отвечающий залоготип и баннер.

В строке 9 повторяется вставка декоративного разде-лителя sep.

В строках 10-21 описана таблица, несущая собственнотело страницы. В таблице только две ячейки (одна строка,два столбца). Левая содержит навигационное меню (шаб-лон toc, подключаемый в строке 12), к нему мы вернёмсячуть позже. В правой ячейке содержится несколько точеквставки. В первой из них подключается один из уже знако-мых нам шаблонов *-head, соответствующий параметрусборки (строка 14). Это заголовок. В строке 16 создаётсяссылка на страницу с версией для печати. Адрес этой стра-ницы берётся из файлов index-url-print, contact-url-print иabout-url-print. Например, index-url-print содержит:

Единственное, о чём здесь следует упомянуть: файлы*-url-print не должны содержать ничего, кроме адресов. Тоесть в них не должно быть пробелов, табуляций, символовLF и CR и других невидимых символов.

И наконец в строке 18 шаблона skeleton мы подключа-ем текст страницы из файлов (которые могут оказаться, всвою очередь, шаблонами) index-text, contact-text и about-text. Так, например, в файл index.html вставляется текст изфайла index-text:

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

01: (### body-open ###)02: (### sep #########)03: <table width="100%">04: <tr>05: <td><img src="../img/title.gif"></td>06: <td align="right"><img src="../img/banner.gif"></td>07: </tr>08: </table>09: (####### sep #######)10: <table>11: <tr valign="top">12: <td>(## toc ##)</td>13: <td>14: <big><b>(## ?-head ##)</b></big>15: <br>16: <a href="(## ?-url-print ##)"><b><font size="1">17: âåðñèÿ äëÿ ïå÷àòè</font></b></a>18: <br>(## ?-text ##)19: </td>20: </tr>21: </table>22: (### sep ##########)23: (### body-close ###)

01: <table width="100%">02: <tr bgcolor="#cccccc">03: <td align="right">04: <font size="1" color="#999999">ÑÒÅËÜÊÈ Inc.</font>05: </td>06: </tr>07: </table>

01: index-print.html

01: index.html skeleton index02: contact.html skeleton contact03: about.html skeleton about04: index-print.html skeleton-print index05: contact-print.html skeleton-print contact06: about-print.html skeleton-print about

01: <html>02: <head>03: <title>(## ?-head ##)</title>04: </head>05: <body>

01: Î íàñ

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

web

Шаблон skeleton заканчивается вставкой ещё одногодекоративного разделителя sep и закрывающими тегами,вынесенными в отдельный файл body-close.

Сборка страниц для печатиЕщё проще устроена сборка страниц для печати. Как выпомните, для них используется общий базовый шаблонskeleton-print:

В начале и в конце этого шаблона мы видим уже знако-мые body-open и body-close. Текст заголовка берётся всёиз тех же файлов *-head, а текст – из файлов *-text (обрати-те внимание, как выделена точка вставки текста). Един-ственные новые шаблоны, с которыми мы тут столкнулись,содержат адреса возврата на исходные страницы (не пред-назначенные для печати). Я говорю о шаблонах index-url,contact-url и about-url (они фигурируют в строках 12 и 14).

Понятно, что каждый из них содержит одну строчку. На-пример, index-url содержит:

Перейдём теперь к самому интересному.

Сборка оглавленияВернёмся к шаблону toc, отвечающему за навигационноеменю. Как вы уже видели на рисунке, оглавление у нас сде-лано в лучших традициях гипертекста: ни одна страницанигде не ссылается на саму себя. Для этого нам понадо-бился довольно мудрёный шаблон toc:

Попробуйте ответить на вопрос, сколько шаблонов ис-пользуется шаблоном toc? Правильный ответ: два. Да! Нампонадобится всего два шаблона.

Первый toc-yes:

Второй toc-no:

01: Êîðïîðàöèÿ &laquo;ÑÒÅËÜÊÈ Inc.&raquo; ïðîèçâîäèò02: íåäîðîãèå, íî âûñîêîêà÷åñòâåííûå êîðïîðàòèâíûå ñòåëüêè.03: Ìû âñåãäà äåëàåì óïîð íà èìèäæåâûå ýëåìåíòû, íî04: ïðåäëàãàåì è óíèâåðñàëüíûå ãîòîâûå ðåøåíèÿ...

01: (### body-open ###)02: <hr>03: <big>(## ?-head ##)</big>04: <hr>05: <small>&copy; ÑÒÅËÜÊÈ Inc.</small>06: <hr>07: (##############08: ### ?-text ###09: ##############)10: <hr>11: <small>àäðåñ ðåñóðñà12: <u>http://www.stelki.biz/(## ?-url ##)</u></small>13: <hr>14: <small><a href="(## ?-url ##)">íàçàä</a></small>15: (### body-close ###)

01: index.html

01: <table>02: (## toc-(index):index ##)03: (## toc-(about):about ##)04: (## toc-(contact):contact ##)05: </table>

01: <tr>02: <td nowrap bgcolor="#cccccc"><b>&bull;03: (## ?-head ##)</b></td>04: </tr>

01: <tr>02: <td nowrap><b>&bull;03: <a href="(## ?-url ##)">(## ?-head ##)</a>04: </b></td>05: </tr>

80

web

Шаблон toc-yes описывает одну строку таблицы, в един-ственной ячейке которой находится название соответствую-щего раздела. Шаблон toc-no описывает такую же конструк-цию, но ячейка уже не подкрашена, а текст является ссылкой.

Я думаю, читатель уже начал догадываться, как всё этоработает.

Например, при сборке документа about.html шаблон tocвызывается с параметром about. Во второй его строке бу-дет подключён файл toc-no, так как параметр about не со-впал с круглой скобкой (index). Этот шаблон, в свою оче-редь, вызывается с подменой параметра на index, и в нёмбудут подставлены файлы index-rul и index-head. То естьстрока с названием первой страницы станет ссылкой напервую страницу. Во второй строке шаблона toc будет про-изводиться вставка шаблона toc-yes, который не форми-рует ссылки. То есть мы добились своего – страница нессылается на саму себя. Третья вставка (в четвёртой стро-ке шаблона toc) пройдёт аналогично первой.

Окончательно разобраться в деталях происходящегонам поможет протокол сборки.

Протокол работыМы ещё ничего не сказали о протоколе (необходимостькоего была оговорена в начале статьи), который выдаётсянашей системой сборки. Я не буду приводить его весь. Вотчасть, отвечающая за сборку документа index.html:

Если вы внимательно читали весь предыдущий текст,то он будет вам знаком и понятен. Если же у вас осталосьнекоторое недопонимание, то он поможет разобраться.

Результаты

ДостиженияИтак, что нам удалось сделать? Перечислю кратко основ-ное.! Мы написали и успешно применили обработчик шабло-

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

Даже в нашем маленьком примере вставка шаблонаindex-head происходит семь раз. Это значит, что если бы

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

Обработчик позволяет делать очень многое и не усту-пает большинству подобных инструментов. Фактическилюбое изменение стиля или содержания сайта выполняет-ся редактированием всего одного файла.

Эта управляемость идёт на пользу не только отдельнымразработчикам, но и облегчает взаимодействие в коман-де. Так, дизайнер мог вести разработку так, как это делалимы. Но программист перед окончательным размещениемдокументов на сервере может просто поменять в одномфайле (skeleton) код, отвечающий за баннер, и одним дви-жением подключить баннерную систему на всех страницахресурса.! Шаблоны допускают весьма гибкое форматирование то-

чек вставки, которое может обеспечить и наглядную, икомпактную запись.

! Обработчик выдаёт протокол сборки, позволяющий лег-ко понять коллегу или себя.

! Обработчик компактен и элементарно переносится налюбую машину, работающую под управлением любой ОС.

Но не будем долго распространяться о мощи шаблон-ного подхода. Давайте перейдём к недостаткам.

Слабые стороны подходаКогда вы принимаете решение использовать или не исполь-зовать некий подход (в частности, описываемый в этой ста-тье), важно знать не только достоинства и потенциальныевозможности, но и недостатки подхода. Итак, какие естьнедостатки? Какие из них можно преодолеть, а какие –нельзя?

Относительно работы обработчикаПриведённая здесь реализация практически не содержитмеханизмов обработки нештатных ситуаций. Естественно,было бы полезно добавить следующее:! Конечно, наш код допускает множество очевидных кос-

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

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

! Программа должна корректно себя вести, если требуе-мый файл не существует. Как? Решать вам. Может быть,вы предпочтёте аварийную остановку (сейчас сделаноименно так) или захотите, чтобы эта ситуация была быпроигнорирована.

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

01: skeleton:index02: . body-open:index03: . . index-head:index04: . sep:index05: . sep:index06: . toc:index07: . . toc-yes:index08: . . . index-head:index09: . . toc-no:about10: . . . about-url:about11: . . . about-head:about12: . . toc-no:contact13: . . . contact-url:contact14: . . . contact-head:contact15: . index-head:index16: . index-url-print:index17: . index-text:index18: . sep:index19: . body-close:index

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

web

Плодотворной может оказаться идея «аварийного шаб-лона», который вставляется в те точки, которые не быликорректно обработаны.! Было бы очень уместно сделать кэширование считан-

ных файлов. Тем более, что это совсем не трудно.! После того как вы сделаете кэширование, можно очень

легко сделать предопределённые шаблоны. Например,шаблон DATE может содержать дату сборки.

! Очень полезной была бы и возможность регламентиро-вать дополнительные обработки (или не обработки) фай-лов. Например, можно сделать, чтобы файлы с расши-рениями .file не обрабатывались как шаблоны, а их со-держимое вставлялось «как есть». И наоборот, файлыс расширениями .quot проходили бы дополнительнуюобработку механизмом HTML-квотирования, заменяю-щим «&» на «&amp;» и так далее. Здесь тоже возможныварианты: должен ли, например, подвергаться квотиро-ванию шаблон, вставляемый в квотируемый шаблон?

! Было бы заманчиво хранить информацию с разнымиименами в одном файле, чтобы не заводить множествомаленьких (как это получилось у нас с файлами заго-ловков и адресов). Это, правда, потребует некоторогоусложнения «адресации» данных. Обойтись просто име-нем файла, как сделали мы, будет уже нельзя. Все ли ввашей команде одобрят подобные нововведения?

Относительно загрузки на серверЯ обещал вернуться к вопросам загрузки. Действительно,идея собрать все документы одним махом весьма заман-чива, пока вы не столкнётесь с необходимостью загрузитьих на сервер. Что здесь можно сказать?

Во-первых, для части администраторов эта проблемане будет так остра, потому что большинство измененийвсё-таки не будут затрагивать все документы и, конечно,не будут производиться слишком часто (иначе вам дей-ствительно следует выбрать для сопровождения вашегоресурса другой инструментарий, реализующий on-line-сборку).

Во-вторых, если вам придётся всё-таки решать эту за-дачу, то у вас будет по крайней мере два пути решения.Можно написать CGI-сценарий, принимающий файлы всжатом виде и распаковывающий их на сервере. Так высократите трафик в несколько раз. С другой стороны, мож-но написать сценарий, производящий сборку и размеще-ние на сервере, а передавать ему надо будет только шаб-лоны. Это может очень сильно помочь сократить трафик,но создание такого механизма потребует от вас нешуточ-ных усилий по обеспечению безопасности.

Усовершенствования, которые сложно сделать! Когда начинаешь заботиться о «самодокументируемо-

сти» подобного сборщика, может появиться мысль: пустьон дополняет документы HTML-комментариями. Сделатьэто корректно гораздо сложнее, чем кажется на первыйвзгляд. Вы видели сами, что мы вставляем шаблоны ивнутрь тегов. Если включить аппарат автоматическойвставки комментариев, то код:

после обработки может превратиться в:

что, естественно, не будет работать, как:

! Процесс сборки, на первый взгляд, напоминает работуутилиты make. Возникает искушение реализовать не-что подобное, избежав ненужных действий при внесе-нии в шаблоны небольших изменений. Это тоже трудносделать, так как для определения того, какие файлынуждаются в повторной обработке, надо знать всё «де-рево сборки» – все зависимости файлов. В явном видеони нигде не содержатся. Так что, если не сборка, тохотя бы просмотр всех файлов-шаблонов мне представ-ляется неизбежным.

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

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

ставлять не файл, а сам параметр сборки. Задумайтесь,перед тем как реализовывать и эксплуатировать этуидею! Поверьте, на этой дороге вы не найдёте счастья.У информации должно быть имя и должно быть значе-ние, нельзя объединять эти две материи.

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

Если же вам понадобились циклы или условные пере-ходы, лучше воспользуйтесь готовым решением, трезвовзвесив все «за» и «против». Вероятно, новый инструментпотребует от вас установки сервера на машину дизайнера(или на все машины дизайнеров), не исключено, что вашдизайнер может воспринять в штыки всё это богатство воз-можностей.

В любом случае это будет другой обработчик шаблонови уже совсем другая история.

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

<a href="<!� /file 'url' �>index.html<!� file 'url'/ �>">

<a href="index.html">

84

образование

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

Так или иначе, решение задач этого круга сводится кчтению/записи данных реестра. Управление реестром осу-ществляется с помощью нескольких инструментов, которыебудут рассмотрены в настоящей статье. Основными из нихявляются сценарии регистрации пользователей в сети (сце-нарии загрузки или скрипты) и групповые политики.

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

Однако, примененяя групповые политики, стартующиекаждый раз при регистрации пользователя в сети, исполь-зуя административные шаблоны на основе реестра, можнобезопасно внести изменения в ветви реестра HKLM и HKU.

Политики распространяются на пользователей, являю-щихся членами группы.

Системный администратор может самостоятельно со-здать файлы – административные шаблоны, содержащиегрупповые политики (файл с расширением ADM), и приме-нять их к пользователям, входящим в группу/домен (domain),подразделение (Organization Unit, OU).

Таким образом, групповые политики и сценарии регис-трации пользователей в сети органично дополняют другдруга. Далее рассмотрим подробнее круг вопросов, касаю-щихся создания и применения сценариев загрузки и груп-повых политик, но сначала немного о реестре.

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

В Microsoft Windows 3.x все настройки программногообеспечения располагались в файлах инициализации, ко-торые имели расширение INI. Вся конфигурационная ин-формация располагалась в двух файлах: SYSTEM.INI и

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

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 содержит настройки индивидуального пользо-вателя, а SYSTEM.DAT – настройки компьютера.

Реестры Windows 9x, NT, 2000 несовместимы друг сдругом, однако идея построения реестров едина.

Ветви реестраРеестр состоит из разделов верхнего уровня, называемыхкустами (hives):! HKEY_CLASSES_ROOT (HKCR);! HKEY_CURRENT_USER (HKCU);! HKEY_LOCAL_MACHINE (HKLM);! HKEY_USER (HKU);! HKEY_CURRENT_CONFIG (HKCC).

Структура реестра такова: в каждом из кустов находят-ся ключи, в которых содержатся параметры, имеющие зна-чения.

В разделе HKLM находится информация об аппаратноми программном обеспечении, а также сведения о системебезопасности. Этот раздел является одним из самых боль-ших. Раздел HKCR является виртуальной ссылкой на раз-дел HKLM\Software\Classes. В нем содержатся сведения обовсех расширениях файлов, определениях типов, ярлыках,привязке, классах идентификаторов и т. д.

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

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

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

образование

шения затрат на администрирование и конфигурированиерабочих станций.

Перечислим некоторые задачи, которые могут быть ре-шены с помощью сценария загрузки:! Инвентаризация. Включает в себя сбор информации о

регистрирующемся в сети пользователе, рабочей стан-ции и формировании файла отчета.

! Автоматическое подключение сетевых ресурсов: прин-теров и дисков.

! Автоматическое конфигурирование рабочих станций.! Обеспечение интерактивности работы скрипта. В ходе

выполнения скрипта на экране отображается соответ-ствующая информация.

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

Существует несколько языков, предназначенных для со-здания сценариев загрузки. Среди них стандартными явля-ются сценарии на базе командной строки (файлы с расши-рением BAT, PIF), Windows Script Host (WSH), Microsoft JavaScript (JScript), Microsoft Visual Basic Script Edition (VBScript).

Существуют также языки, специально разработанныедля создания скриптов, такие как KIXtart, AutoIT, CLRScript,WinBatch.

Из всех этих языков рекомендуется остановить свойвыбор на языке KIXtart2, поскольку, обладая огромнымивозможностями, он распространяется бесплатно. KIXtart4.21 поддерживает 48 команд, 56 макросов-функций, бо-лее 100 функций и обладает следующим функционалом:! вывод информации в виде диалоговых сообщений;! подключение сетевых ресурсов;! чтение информации из входного потока;! расширенная поддержка редактирования реестра;! поддержка INI-файлов;! расширенная поддержка операций со строками и мас-

сивами;! сбор информации о пользователе и рабочей станции;! поддержка OLE-объектов;! операции с файлами и каталогами;! создание ярлыков Windows.

Необходимо отметить, что сценарии, созданные наVBScript, Jscript, могут быть легко переписаны на KIXtart.

Рассмотрим подробнее каждую из задач, решаемуюскриптом.

ИнвентаризацияРешение задачи инвентаризации состоит из несколькихчастей – сбора, записи на носитель в определенном фор-мате и обработки информации.

Так или иначе, информация черпается либо из BIOS спомощью программы, либо, что происходит чаще всего, из

Изменения в HKU и HKLM можно сделать только с по-мощью утилиты REGEDT32.EXE в том случае, если у васОС Windows 2000, и REGEDIT.EXE – если Windows XP.Пользователь, от имени которого запускаются эти утили-ты, должен обладать правами системного администрато-ра.

Раздел HKCU содержит сведения о текущем пользова-теле и имеет название, соответствующее значению иден-тификатора безопасности (SID) данного пользователя. Каж-дый раз при перезагрузке компьютера HKCU создаетсязаново.

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

Раздел HKDD (HKEY_DYN_DATA) не хранится в реест-ре, а динамически создается при загрузке операционнойсистемы. В нем содержатся сведения о самонастраиваю-щихся устройствах (Plag-and-Play).

Как и любая база данных, реестр поддерживает несколь-ко типов данных:

Windows 9x поддерживает следующие типы параметров:REG_BINARY и REG_DWORD, REG_SZ и REG_NONE.

Групповые политики, описанные в ADM-файлах, могутпроизводить операции только со следующими типами дан-ных : REG_SZ, REG_EXPAND_SZ, REG_DWORD.

Сценарий регистрациипользователей в сетиОсновной задачей сценария регистрации пользователей всети, далее скрипта, является автоматизация процессов,связанных с подключением рабочих станций к сети, умень-

Òàáëèöà 1

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.

86

образование

реестра с помощью стандартных средств ОС Window. Ре-шение задачи инвентаризации c помощью WMI было опи-сано в [1].

Подключение сетевых ресурсовСетевыми ресурсами являются сетевые диски и принтеры.

Подключение сетевых принтеровВопрос установки, настройки и автоматического подклю-чения сетевых принтеров был подробно освещен в статьях[2], [3].

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

Рассмотрим содержимое конфигурационного файла,который представляет собой текстовый файл с произволь-ным расширением, например INI.

В файле в квадратных скобках перечислены названияразделов, которые включают в себя букву, на которую бу-дут монтироваться ресурс и его порядковый номер, присва-ивающийся для обеспечения возможности подключать наодну и ту же букву разные ресурсы. Каждый раздел содер-жит пять параметров: название сервера (SERVER), путь ксетевой папке (SHARE), группы безопасности, членам ко-торых будет подключаться ресурс (ACCESSGROUP1 иACCESSGROUP2), описание ресурса (DESCRIPTION). При-мер файла приведен ниже:

Таким образом, на основе данных, прочитанных сцена-рием из примера, всем пользователям будет подключен«Консультант+» на букву «L», находящийся по пути \\main\consultant. Пользователям, являющимся членами группdepartament1, departament2, departament3, будет подключен

диск W. Для членов групп departament1, departament3 под-ключается ресурс по адресу: \\second\work\ departament1,для departament2 – \\second\work\departament2.

Сценарий, обеспечивающий автоматическое управле-ние подключением сетевых дисков работает по следующейсхеме:! Чистка локального кэша на рабочей станции, содержащего

список групп, в которые входит пользователь. Удалениеветви реестра HKEY_CURRENT_USER\Software\KiXtart.

! Чтение параметрического файла. Данные рекомендует-ся помещать в соответствующие массивы. Схема чте-ния конфигурационного файла приведена на рис. 1.

! Отключение всех доступных сетевых дисков.! Подключение сетевых дисков, на которые данный поль-

зователь имеет права.! Вывод на экран статистики о подключенных сетевых дис-

ках.

Автоматическое конфигурированиерабочей станцииПоскольку скрипт выполняется от имени пользователя, ко-торый не обладает правами системного администратора,то изменения могут быть внесены только в ветвь HKCU. Вэтом разделе хранятся сведения о текущем зарегистриро-ванном пользователе, и он имеет название, соответствую-щее значению идентификатора безопасности (SID) текуще-го пользователя. Каждый раз при перезагрузке компьюте-ра раздел создается заново на основе данных, считанныхиз HKU. Автоматическое конфигурирование ветви HKU вы-полняется с помощью групповых политик и будет рассмот-ренно позже. Изменения в ветви HKCU осуществляется спомощью скрипта.

Ветвь HKCU не является точной копией HKU. Приведемнаглядный пример. После запуска Windows 2k пользова-тель видит сообщение, в котором предлагается одновре-менно нажать CTRL+ALT+DEL. Нажав эту комбинацию кла-виш, ему предлагается ввести имя учетной записи, пароль.Информация о языковых настройках в этом диалоговомокне, а именно язык по умолчанию и комбинация клавиш, спомощью которой осуществляется переключение междураскладками клавиатуры, хранится в ветви HKEY_USERS\.DEFAULT\Keyboard Layout.

Ïðèìåð 1[L1]SERVER=MainSHARE=ConsultantACCESSGROUP1=everyoneACCESSGROUP2=ÂñåDESCRIPTION="Êîíñóëüòàíò+"[W1]SERVER=SecondSHARE=work\department1ACCESSGROUP1=department1ACCESSGROUP2=department3DESCRIPTION="Ðåñóðñû îòäåëà 1"[W2]SERVER=SecondSHARE=work\department2ACCESSGROUP1=department2ACCESSGROUP2=DESCRIPTION="Ðåñóðñû îòäåëà 2"

Ðèñóíîê 1

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

образование

Однако аналогичная ветвь в ветви HKСU (см. пример 3)отвечает за управление языковыми настройками рабочегостола пользователя. В данном случае нет никакой взаимо-связи между настройками ветвей HKU и HKCU.

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

Обеспечение интерактивности работы скриптаСценарии на языке KIXTart можно визуализировать по край-ней мере тремя способами:! с помощью стандартных диалоговых окон;! с помощью сторонней надстройки KIXTart в виде DLL-

библиотеки;! c помощью сторонней утилиты, передающей парамет-

ры из KIXTart в HTML-файл.

Рассмотрим все три способа.

Визуализация работы скрипта с помощьюстандартных диалоговых оконПользователю выводится информация на экран в виде со-общения. Этот метод рекомендуется использовать в нача-ле сценария, чтобы уведомить пользователя о начале ра-

боты скрипта. Основным недостатком этого метода визуа-лизации является полное отсутствие интерактивности ра-боты сценария.

Визуализация скрипта с помощью стороннейнадстройки KIXTart в виде DLL-библиотекиВизуализация и интерактивность работы скрипта реали-зуются с помощью специально созданной для этих целейнадстройкой – KIXForms 3.2 или KIXGui 1.1.Обе програм-мы можно загрузить с сайта http://www.kixtart.org. У этихпрограмм есть только один недостаток: для корректнойработы визуализационной части необходимо на рабочейстанции зарегистрировать соответствующую DLL-библио-теку. Для регистрации этой библиотеки необходимо об-ладать правами администратора. Конечно, можно создатьMSI-архив, который будет централизованно распростра-няться по сети с помощью групповых политик, но это не-удобно. Визуализировать работу сценария, таким обра-зом, выгодно только в небольших сетях с маленьким ко-личеством рабочих станций.

Визуализация работы скрипта c помощьюсторонней утилиты, передающей параметрыиз KIXTart в HTML-файлЭтот способ мне кажется наиболее оптимальным для реа-лизации визуализации и интерактивности работы скриптав крупных сетях, поскольку утилита самодостаточна и пред-ставляет собой файл с расширением EXE, который реко-мендуется располагать в каталоге Netlogon, вместе соскриптом. В качестве такой утилиты лучше использоватьKixWin 1.1 (http://www.kixtart.org). И вызывать ее из скриптас набором параметров.

Затем она передает эти параметры в DHTML-файл. По-скольку файл пишет данные в файл, прежде чем что-либоотобразить на экране, то необходимо обеспечить правамивозможность записи данных на диск сервера.

Раздробление скрипта на две части является основнымнедостатком данного варианта. DHTML-файл вместе с со-путствующими ему файлами (GIF, JPEG, CSS) рекоменду-ется располагать в скрытой сетевой папке. DHTML-файлсодержит в себе сценарии, созданные с помощью VBScriptили JScript.

Приведем синтаксис утилиты и фрагмент DHTML-файла:

Описание параметров:! “dialog” – строка, содержащая URL, указывающий на

HTML-документ.! “arguments” – строка, содержащая параметры, переда-

ваемые из KIX в HTML. Для разделения параметров вHTML-файле используют строку window.dialogArguments.split(«;»). В данном примере разделителем параметровявляется «;».

! “style” – строка, которая определяет оформление диа-логового окна. Используются один или несколько из сле-дующих параметров стиля:

Ïðèìåð 2. Ïî óìîë÷àíèþ óñòàíîâëåí àíãëèéñêèé ÿçûê, ïåðå-êëþ÷åíèå ìåæäó ðàñêëàäêàìè êëàâèàòóð îñóùåñòâëÿåòñÿ íàæà-òèåì CTRL+SHIFT.Windows Registry Editor Version 5.00[HKEY_USERS\.DEFAULT\Keyboard Layout\Preload]"1"="00000409""2"="00000419"[HKEY_USERS\.DEFAULT\Keyboard Layout\Toggle]"Hotkey"="2"

Ïðèìåð 3. Ïî óìîë÷àíèþ óñòàíîâëåí àíãëèéñêèé ÿçûê, ïåðå-êëþ÷åíèå ìåæäó ðàñêëàäêàìè êëàâèàòóð îñóùåñòâëÿåòñÿ íàæà-òèåì CTRL+SHIFT.Windows Registry Editor Version 5.00[HKEY_CURRENT_USER\Keyboard Layout\Preload]"1"="00000409""2"="00000419"[HKEY_CURRENT_USER\Keyboard Layout\Toggle]"Hotkey"="2"

Òàáëèöà 2

kixwin "dialog" ["arguments"] ["options"]

dialogHeight:sHeight

88

образование

Логическое разделение передаваемых параметров осу-ществляется с помощью заранее оговоренного символа.DHTML возвращает в KIX код ошибки после обработки кодав виде целого числа макросу @ERROR в случае успешно-го завершения операции @ERROR=0.

Передача данных с помощью утилиты KIXWIN осуще-ствляется с помощью сценария загрузки следующим обра-зом:

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

Внедрение скрипта в эксплуатациюСкрипт выполняется каждый раз при регистрации пользо-вателя в сети, если он указан в разделе Profile свойствахпользователя (см. рис. 2) службы Active Directory: Users andComputers.

Для автоматизации установки KIXtart на рабочих стан-циях домена предлагается следующее: в папку Netlogonпоместить файлы:! KIX32.EXE;! SCRIPT.KIX;! START.BAT! KIXWIN.EXE! подкаталог Win9x, содержит файлы KX16.DLL и KX32.DLL

В скрытую сетевую папку скопировать DHTML- файл ивсе сопутствующие ему файлы.

В ходе выполнения файла START.BAT определяется типоперационной системы, установленной на рабочей станции,и запускается скрипт. В зависимости от версии ОС проис-ходит копирование файлов, необходимых для поддержкиKIX этой операционной системой.

Пояснения к синтаксису файла START.BAT:! Принцип определения операционной системы основан

на том, что в Win9x отсутствует переменная окружения%os%; В Windows 2k переменная %os%=WindowsNT.

! С помощью строки «start /wait Kix32.exe Script.kix» добива-ются последовательной загрузки – сначала скрипт, затемрабочий стол и т. д., а не одновременной. То есть на вре-мя выполнения скрипта многозадачность «отключается».

! Это делается для того, чтобы до окончания действияскрипта дальнейшая загрузка операционной системы непроизводилась.

! Для корректной работы Win9x в качестве пути к файлунеобходимо указывать «%0\..\filename.ext». Windows XPне воспринимает относительного пути «%0/./», поэтомудля Windows семейства 2k необходимо указать толькоимя файла, который находится в папке Netlogon.

На время выполнения скрипта необходимо скрыть CMD-панель, в которой выполняется cкрипт, и приостановить заг-рузку рабочего стола до окончания всего скрипта. Этогорезультата добиваются с помощью групповой политики,распространяющейся на домен («Default Domain Controllers

dialogLeft:sXPosdialogTop:sYPosdialogWidth:sWidthcenter:{ yes | no | 1 | 0 | on | off }dialogHide:{ yes | no | 1 | 0 | on | off }edge:{ sunken | raised }help:{ yes | no | 1 | 0 | on | off }resizable:{ yes | no | 1 | 0 | on | off }scroll:{ yes | no | 1 | 0 | on | off }status:{ yes | no | 1 | 0 | on | off }unadorned:{ yes | no | 1 | 0 | on | off }

Ïðèìåð 4shell '%0/../kixwin.exe $html "$system_info ^ $hardware_info ↵↵↵↵↵

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

Ïðèìåð 5�<Script Language="JavaScript">var argv;argv = window.dialogArguments.split("^");document.all.parametr0 = argv[0];</Script> �

Ïðèìåð 6@ECHO OFFif c:\%os%==c:\ goto win9xif not c:\%os%==c:\ goto winnt:winntstart /wait Kix32.exe Script.kixgoto kix:win9xcopy %0\..\win9x\*.dll c:\windows\system /y%0\..\Kix32.exe %0\..\Script.kixgoto kix:kix@echo End Of Batch File

Ðèñóíîê 2

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

образование

Policy»). В разделе групповой политики «User Configuration»необходимо соответственно включить «Run legacy logonscript synhronously» (Запускать сценарий загрузки синхрон-но) и «Run legasy script hidden» (Запускать сценарий скры-то). Для этого необходимо проделать следующее:! Зарегистрироваться на сервере с помощью учетной за-

писи, имеющей административные права.! Загрузить в Active Directory Users and Computers (Start –

Programs – Administrative Tools) и войти в свойства кон-троллера домена (см. рис. 3). Перейти во вкладку вклад-ку «Group Policy» и зарузить «Default Dоmain Policy» (см.рис. 4).

! В загруженной групповой политике (Default DomainPolicy) необходимо в «User Configuration» (настройкахпользователя) войти в «Administrative Templates» (адми-нистративные шаблоны).

! Там выбрать раздел «System» (система), вкладку «logon/logoff» (вход/выход) и включить раннее оговоренныеполитики (см. рис. 5).

Групповые политикиГрупповые политики (Group Policy, GP) представляют со-бой средство, обеспечивающее централизованное управ-ление настройками рабочих станций, профилями пользо-вателей. С их помощью определяют поведение операцион-ной системы, рабочего стола, безопасности ОС, выключе-ния компьютера, приложений и т. д.

Реализации всех возможностей групповых политик до-биваются их совместным использованием с Active Directory(AD) при условии, что в качестве рабочих станций исполь-зуется ОС Windows 2000. Политики могут быть примененык следующим объектам AD: сайт (site), домен (domain), под-разделение (Organization Unit, OU). Дополнительно группо-вые политики могут быть применены к таким объектам, какгруппа безопасности, соответственно к пользователям, вхо-дящим в эту группу.

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

С помощью политик, как говорилось ранее, можно кор-ректировать только ветви реестра HKLM и HKU. В консо-ли групповых политик (см. рис. 5), вызываемой с помо-щью команды GPEDIT.MSC, все политики логически де-лятся на две части: Computer Configuration и UserConfiguration. В разделе Computer Configuration сосредо-точены политики, посредством которых изменяется ветвьреестра HKLM, в разделе User Configuration соответствен-но HKU. Информация о параметрах и конфигурации груп-повых политик сосредоточена в HKLM\Software\Policies(предпочтительное расположение) или HKLM\Software\Microsoft\Windows\CurrentVersion\Policies для разделаComputer Configration; в HKU\Software\Policies (предпочти-тельное расположение) или HKU\Software\Microsoft\Windows\CurrentVersion\Policies для раздела UserConfigration.

Ðèñóíîê 3

Ðèñóíîê 4

Ðèñóíîê 5

Òàáëèöà 3

90

образование

Административные шаблоны. СинтаксисАдминистративные шаблоны представляют собой тексто-вые файлы с расширением ADM. По умолчанию ADM-фай-лы находятся в каталоге %WinDir%\INF. Кратко рассмотримвозможности языка, с помощью которого создаются поли-тики безопасности.

Синтаксис языка включает в себя следующие ключе-вые компоненты:! Комментарии! Строки! CLASS! CATEGORY! POLICY

КомментарииКомментарии полезно использовать для документированиясодержимого создаваемого шаблона. Комментарии предва-ряют точкой с запятой (;) или двумя прямыми слэшами (//).Комментарии также можно помещать в конце строки. При-ведем пример комментария:

СтрокиВсе возможные словесные описания – описание политики,названия разделов, параметров и т. д. рекомендуется рас-полагать в специально предназначенном разделе [strings].В тексте политики перед переменной, ссылающейся натекст в разделе [strings], ставят два восклицательных зна-ка (!!). Каждая строка в разделе [strings] имеет форматname=«строка». Приведем два равнозначных примера. Впервом примере не будем использовать преимущества строк.

Использование строк позволяет создавать шаблоныгрупповых политик, что снижает трудозатраты и ускоряетпроцесс создание политик.

CLASSПервым элементом в файле, содержащем шаблон группо-вой политики, является ключевое слово CLASS, котороеопределяет, к какому типу относится описываемая нижеполитика: компьютерная (Computer Configuration) или

пользовательская (User Configuration). В одном файле до-пускается многократное использование ключевого словаCLASS. Приведем пример синтаксиса элемента CLASS:

где Name может иметь одно из двух значений: USER илиMACHINE. Значение USER определяет, что политикапользовательская. Изменения будут вноситься в реестр вветви HKU, настройку политики следует осуществлять вразделе «User Configuration\Administrative Templates\»; Зна-чение MACHINE соответственно определяет, что политикакомпьютерная. Изменения будут вноситься в реестр в вет-ви HKLM, настройку политики следует осуществлять в раз-деле «Computer Configuration\Administrative Templates\».

CATEGORYПосле определения класса политики необходимо опреде-лить местоположение в подпапке «Administrative Templates».Все, что делает ключевое слово CATEGORY, – это создаетподпапку. В категории может быть ноль и более политик.Те из них, которые не содержат политик, содержат, как пра-вило, одну или несколько подкатегорий. После определе-ния категории ее необходимо закрыть с помощью ENDCATEGORY.

Приведем пример синтаксиса элемента CATEGORY:

где:! Name – это имя папки, которое будет отображаться в

редакторе Group Policy. Используйте строковую пере-мену или строку, заключенную в кавычках.

! SubKey – является необязательным параметром. Инфор-мация о параметрах и конфигурации групповых поли-тик сосредоточена в HKLM\Software\Policies (предпочти-тельное расположение) или HKLM\Software\Microsoft\Windows\CurrentVersion\Policies для раздела ComputerConfigration; в HKU\Software\Policies (предпочтительноерасположение) или HKU\Software\Microsoft\Windows\CurrentVersion\Policies для раздела User Configration. Неиспользуйте в пути корневой ключ (HKLM, HKU), по-скольку он уже описан ключевым словом CLASS. Еслипуть содержит пробелы, заключайте его в кавычки.

Ïðèìåð 7; Ýòî êîììåíòàðèé// È ýòî êîììåíòàðèéCLASS USER // Îïðåäåëåíèå êëàññà USER, îòâå÷àþùåãî

// çà ïîëüçîâàòåëüñêèå íàñòðîéêèCLASS MACHINE // Îïðåäåëåíèå êëàññà MACHINE, îòâå÷àþùåãî

// çà îáùåêîìïüþòåðíûå íàñòðîéêè

Ïðèìåð 8à)CLASS MachinePOLICY "Ïðèìåð ïîëèòèêè"������END POLICYÏðèìåð 8á)CLASS MachinePOLICY !!Pname������END POLICY[Strings]Pname="Ïðèìåð ïîëèòèêè"

CLASS Name

CATEGORY NameKEYMAME SubKey���������..

END CATEGORY

Ïðèìåð 9CLASS USERCATEGORY "POLICIES" CATEGORY !!SubPol1 KEYNAME "Software\Policies\SubPol1" ���������� END CATEGORY CATEGORY !!SubPol2 KEYNAME "Software\Policies\SubPol1" ���������� END CATEGORYEND CATEGORY[strings]SubPol1="Policy1"SubPol2="Policy2"

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

образование

В разделе CATEGORY могут быть использованы следу-ющие ключевые слова:! CATEGORY! END! KEYNAME! POLICY

POLICYИспользуйте ключевое слово POLICY, чтобы определитьполитику. В одной категории может быть включено несколь-ко разделов POLICY, каждый из которых должен заканчи-ваться инструкцией END POLICY. Приведем пример син-таксиса элемента POLICY:

где:! Name – это название политики, отображаемое в редак-

торе Group Policy. Используйте строковую переменнуюили строку, заключенную в кавычках.

! SubKey является необязательным параметром. Инфор-мация о параметрах и конфигурации групповых поли-тик сосредоточена в HKLM\Software\Policies (предпочти-тельное расположение) или HKLM\Software\Microsoft\Windows\CurrentVersion\Policies для раздела ComputerConfigration; в HKU\Software\Policies (предпочтительноерасположение) или HKU\Software\Microsoft\Windows\Current Version\Policies для раздела User Configration. Неиспользуйте в пути корневой ключ (HKLM, HKU), по-скольку он уже описан ключевым словом CLASS. Еслипуть содержит пробелы, заключайте его в кавычки. Ковсем политикам применяется последнее ключевое сло-во KEYNAME.

! Help – эта строка, содержимое которой редактор GroupPolicy отображает в разделе EXTENDED политики. Дляперевода каретки используйте «\n».

! Value – каждая политика содержит ключевое словоVALUENAME, которое связывает с ней значение реест-ра. По умолчанию редактор политик предполагает, чтоэто значение переменной типа REG_DWORD равно 1(0х01), когда политика включена. Редактор политикиудаляет значение, если политика выключена. В том слу-чае, если необходимо его сохранить в реестре после

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

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

Программирование интерфейсаполитик безопасностиПрограммирование интерфейса групповых политик сводит-ся к созданию раскрывающихся списков, флажков, тексто-вых полей и т. д. Рассмотрим по порядку процедуры созда-ния всех элементов интерфейса.

1) В первом варианте, самом простом, нет никаких уп-равляющих элементов, кроме опции «Вкл/Выкл политику».В файле политики записан ключ реестра и значение, име-ющее тип REG_SZ или REG_DWORD, которое может ме-няться в зависимости от того, включена ли политика.

Приведем пример, в котором при включенной политикеустанавливает переключение раскладки клавиатуры в диа-логовом окне регистрации пользователя в сети CTRL+SHIFT,иначе – ALT+SHIFT.

За переключение раскладки языка отвечает параметр«Hotkey», размещающийся в разделе «HKEY_USER\KeyboardLayout\Toggle» и принимающий значение 2 (CTRL+SHIFT) или1 (ALT+SHIFT). Параметр «Hotkey» имеет тип данныхREG_SZ.

Приступим к созданию политики. Поскольку необходи-мо редактировать ветвь реестра HKU, то политика являет-ся пользовательской и будет создана в разделе «UserConfiguration\Administrative Templates\». Исходя из этого, по-литика принадлежит к классу USER.

Параметр KEYNAME раздела CATEGORY, исходя из по-ставленной задачи, принимает значение «Keyboard Layout\Toggle». В разделе POLICY параметр VALUENAME – «Hotkey».VALUEON – 2, а VALUEOFF – 1. Таким образом, политикавыглядит следующим образом:

2) Все остальные элементы интерфейса – флажки, вы-падающие меню и т. д. можно реализовать только в разде-ле PART, являющемся членом раздела POLICY. Рассмот-рим подробнее синтаксис раздела PART:

Ðèñóíîê 6

POLICY Name[KEYNAME SubKey]EXPLAIN HelpVALUENAME Value[PARTS]������.END POLICY

VALUEON [NUMERIC] VValueVALUEOFF [NUMERIC] VValue

Ïðèìåð 10CLASS USERCATEGORY "Admin Policies"

CATEGORY "Keyboard Toggle "KEYNAME ".Default\Keyboard Layout\Toggle"

POLICY "Toggle Policy"EXPLAIN !!helpVALUENAME "Hotkey"VALUEON 2VALUEOFF 1

END POLICYEND CATEGORY

END CATEGORY[strings]help="Óïðàâëåíèå ïåðåêëþ÷åíèåì ðàñêëàäêè êëàâèàòóðû ïðèðåãèñòðàöèè ïîëüçîâàòåëÿ â ñåòè \n\n Ïðè âêëþ÷åííîé ïîëè-òèêå ïåðåêëþ÷åíèå ðàñêëàäêè êëàâèàòóðû îñóùåñòâëÿåòñÿ ñïîìîùüþ êîìáèíàöèè êëàâèø CTRL+SHIFT, ïðè âûêëþ÷åííîé �ALT+SHIFT."

92

образование

где:! Name – указывается название раздела, которое будет

отображено в консоли во время редактирования поли-тики.

! Type – может быть одним из следующих типов:

! Keywords – эта информация специфична для каждогоиз типов. Дополнительная информация приведена ниже.

! Subkey – это необязательный подключ HKLM или HKU.Не используйте в пути корневой ключ (HKLM, HKU), по-скольку он уже описан ключевым словом CLASS. Еслипуть содержит пробелы, заключайте его в кавычки.

! Defaults – это значение параметра по умолчанию. Когдаадминистратор включает политику, то осуществляетсясчитывание параметров по умолчанию.

! Value – модифицируемое значение реестра. Тип и дан-ные значения полностью зависят от типа параметра.

CHECKBOXКлючевое слово CHECKBOX отображает флажок. По умол-чанию флажок сброшен. Если требуется, чтобы изначаль-но флажок был установлен, необходимо в теле разделаPART указать ключевое слово DEFCHECKED. При установ-ленном флажке в реестр записывается значение 1, еслисброшен – 0.

В реестре по умолчанию представлен значением типаREG_SZ. Если необходимо записать данные в реестр вформате REG_DWORD, то необходимо после значений клю-чевых слов VALUEON и VALUEOFF добавить NUMERIC.

Приведем синтаксис элемента CHECKBOX:

где:

! Name – указывается название раздела, которое будетотображено в консоли во время редактирования поли-тики. Если в названии раздела встречаются пробелы,заключите его в кавычки.

! Value – название параметра, которое необходимо из-менить.

! Value1 – значение, задаваемое при установленномфлажке и включенной политике.

! Value2 – значение, задаваемое при снятом флажке ивключенной политике.

COMBOBOXКлючевое слово COMBOBOX добавляет в диалоговое окнополитики список с текстовым полем. Внутри COMBOBOX вклю-чен обязательный блок SUGGESTIONS, заканчивающийся ин-струкцией SUGGESTIONS. Внутри этого блока перечисленыэлементы. Элементы разделяются пробелами, элементы, име-ющие пробелы, помещаются в кавычки (см. синтаксис).

где:! DEFAULT – указывает значение списка по умолчанию.! EXPANDABLETEXT – создает значение типа REG_

EXPAND_SZ.! MAXLENGHT – указывает максимальную длину строки.! NOSORT – отключает сортировку списка редактором по-

литик.! REQUIRED – указывает обязательное значение.! Name – указывается название раздела, которое будет ото-

бражено в консоли во время редактирования политики.! Suggestions – список элементов, помещающихся в рас-

крывающийся список. Все элементы, содержащие про-белы, помещаются в кавычки. Элементы списка разде-ляются пробелами.

! Default – значение по умолчанию. Считывается при вклю-чении политики.

! Max – максимальная длина данных значения.! Value – модифицируемое значение реестра. По умолча-

нию имеет тип данных REG_SZ.

DROPDOWNLISTКлючевое слово DROPDOWNLIST добавляет в диалоговоеокно политики раскрывающийся список. Внутри DROPDOWNLIST включен обязательный блок ITEMLIST, закан-чивающийся инструкцией END ITEMLIST. Внутри этого бло-ка перечислены элементы раскрывающегося списка (см.синтаксис).

Приведем синтаксис элемента DROPDOWNLIST:

PART Name TypeKeywords[KEYNAME Subkey][DEFAULT Default]VALUENAME Name

END PART

Òàáëèöà 4

PART Name CHECKBOX[DEFCHEKED]

VALUENAME ValueVALUEON [NUMERIC] Value1VALUEOFF [NUMERIC] Value2

END PART

PART Name COMBOBOXSUGGESTIONS

�Suggestion1� �Suggestion2� � �Suggestionm�END SUGGESTIONS

[DEFAULT Default][EXPANDABLETEXT][MAXLENGHT] Max[NOSORT][REQUIRED]

VALUENAME ValueEND PART

PART Name DROPDOWNLISTITEMLIST

NAME Item VALUE DataEND ITEMLIST

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

образование

где:! DEFAULT – указывает значение раскрывающегося спис-

ка по умолчанию.! EXPANDABLETEXT – создает значение типа REG_

EXPAND_SZ.! NOSORT – отключает сортировку списка редактором

политик.! REQUIRED – указывает обязательное значение.! Name – указывается название раздела, которое будет ото-

бражено в консоли во время редактирования политики.! Item – имя каждого из элементов раскрывающегося

списка.! Data – значение, соответствующее отображаемому эле-

менту Item.! Default – значение по умолчанию. Считывается при вклю-

чении политики.! Value – модифицируемое значение реестра. По умолча-

нию имеет тип данных REG_SZ.

EDITTEXTКлючевое слово EDITTEXT позволяет вводить в текстовомполе информацию, состоящую из букв и цифр. По умолча-нию редактор политик сохраняет этот текст в значении типаREG_SZ. Приведем синтаксис элемента EDITTEXT:

где:! DEFAULT – указывает значение списка по умолчанию.! EXPANDABLETEXT – создает значение типа REG_

EXPAND_SZ.! MAXLENGHT – указывает максимальную длину строки.! REQUIRED – указывает обязательное значение.! Name – указывается название раздела, которое будет ото-

бражено в консоле во время редактирования политики.! Default – значение по умолчанию. Считывается при вклю-

чении политики.! Max – максимальная длина данных значения.! Value – модифицируемое значение реестра. По умолча-

нию имеет тип данных REG_SZ.

LISTBOXКлючевое слово LISTBOX добавляет в диалоговое окнополитик список с кнопками Add и Remove. Это единствен-ный тип, который может быть использован системным ад-министратором для управления несколькими значениями,содержащимися в одном ключе. В разделе LISTBOX невоз-можно использовать опцию VALUENAME, поскольку он несвязан с каким-либо конкретным значением.

Приведем синтаксис элемента LISTBOX:

где:! EXPANDABLETEXT – создает значение типа REG_

EXPAND_SZ.! NOSORT – отключает сортировку списка редактором по-

литик.! ADDITIVE – считывает значения, уже хранящиеся в рее-

стре.! EXPLICITVALUE – ключевое слово позволяет указать имя

и данные значения. Отображаемый список имеет два стол-бца: один для имени, другой – для данных. Невозможноиспользовать эту инструкцию совместно с VALUEPREFIX.

! VALUEPREFIX – указанный префикс определяет именазначений. Если указывается префикс, то редактор груп-повых политик добавляет к нему возрастающий номер.В том случае, если префикс пустой, генерируются толь-ко возрастающие номера.

! Name – указывается название раздела, которое будет ото-бражено в консоли во время редактирования политики.

! Prefix – используется для генерации последовательнос-ти имен. Если указан префикс, например Sample, то ге-нерируются следующие имена значений: Sample1,Sample2,..Samplen. Если префикс не указан, то генери-руются только порядковые номера: 1,2,3,…n.

NUMERICКлючевое слово NUMERIC позволяет вводить буквенно-цифровой текст, используя элемент управления «счетчик»,который увеличивает или уменьшает число. По умолчаниюпеременные имеют тип данных REG_DWORD, однако, ис-пользуя инструкцию TXTCONVERT, данные могут быть со-хранены в формате REG_SZ.

Приведем синтаксис элемента LISTBOX:

где:! DEFAULT – указывает значение списка по умолчанию.! REQUIRED – указывает обязательное значение.! SPIN – указывается шаг арифметической прогрессии.

По умолчанию равен 1. Указанное значение 0 делаетсчетчик невидимым.

! TXTCONVERT – по умолчанию значения имеют тип дан-ных REG_DWORD. Используйте эту инструкцию, чтобысохранить данные в формате REG_SZ.

! Name – указывается название раздела, которое будет ото-бражено в консоли во время редактирования политики.

! Default – значение по умолчанию. Считывается при вклю-чении политики.

! Max – максимальное значение. По умолчанию равно 9999.

[DEFAULT Default][EXPANDABLETEXT][NOSORT][REQUIRED]

VALUENAME ValueEND PART

PART Name EDITTEXT[DEFAULT Default][EXPANDABLETEXT][MAXLENGHT] Max[NOSORT][REQUIRED]

VALUENAME ValueEND PART

PART Name LISTBOX[EXPANDABLETEXT][NOSORT][ADDITIVE][EXPLICITVALUE| VALUEPREFIX Prefix]

END PART

PART Name NUMERIC[DEFAULT Default][MAX Max][MIN Min][REQUIRED][SPIN][TXTCONVERT]

VALUENAME ValueEND PART

94

образование

! Min – минимальное значение. По умолчанию равно 0.! Value – модифицируемое значение реестра. По умолча-

нию имеет тип данных REG_DWORD.

TEXTКлючевое слово TEXT добавляет в диалоговое окно стати-ческий текст.Приведем синтаксис элемента TEXT:

где Text – статический текст, добавляемый в диалоговоеокно. Если текст имеет пробелы, то его следует заключатьв кавычки.

Практика использованияадминистративных шаблоновПриведем два примера административных шаблонов. В кор-поративной сети, как правило, существуют один или не-сколько файловых серверов, в которых хранятся дистрибу-тивы программного обеспечения, в том числе MicrosoftOffice. В том случае, если пользователю по какой-либо при-чине необходимо перейти с одной рабочей станции на дру-гую, то при первой загрузке для него создается новый про-филь. При первом запуске одного из компонентов MS Office,программа обращается к дистрибутиву, который располо-жен на файловом сервере. При изменении местоположе-ния дистрибутива на сервере пользователю выдается со-общение о невозможности завершить процесс конфигура-ции программы и просьба обратиться к системному адми-нистратору. Во избежание этой неприятной ситуации реко-мендуется использовать групповую политику, которая меня-ет в реестре пути к дистрибутиву MS Office (см. рис. 7):

Внедрение административных шаблоновДля загрузки ADM-файла, содержащего политику, необхо-димо выполнить следующие действия:

! Загрузить консоль групповых политик, выполнив коман-ду GPEDIT.MSC.

! В любом из разделов («Computer Configuration\Administ-rative Templates» или «User Configuration\ AdministrativeTemplates») вызвать контекстное меню, используя од-нократное нажатие на правую кнопку мыши.

! В появившемся меню выбрать «Add/Remove Templates…».! В загрузившемся диалоговом окне нажать кнопку «Add».

В появившемся окне, указав путь к файлу с расшире-нием ADM, нажать кнопку «Open».

! Нажать кнопку «Close». После нажатия на эту кнопкупрограмма проверит синтаксис файла. В том случае,если будет допущена синтаксическая ошибка, интерпре-татор укажет номер строки, в которой она присутству-ет, и возможный вариант исправления.

Удаление административного шаблонаДля удаления политики, подгруженной с помощью ADM-фай-ла, необходимо выполнить действия в следующем порядке:! Загрузить консоль групповых политик, выполнив коман-

ду GPEDIT.MSC.! В любом из разделов («Computer Configuration\Administ-

rative Templates» или «User Configuration\ AdministrativeTemplates») вызвать контекстное меню, используя одно-кратное нажатие на правую кнопку мыши.

! В появившемся меню выбрать «Add/Remove Templates…».! В загрузившемся диалоговом окне выбрать файл, в ко-

тором описана политика, которую необходимо удалить.Нажать кнопку «Remove», затем «Close».

ЗаключениеИспользуя в сценарии регистрации пользователей в сетивместе с административными шаблонами политик безопас-ности, системный администратор автоматизирует большуючасть рутинных операций. Эффективность его работы приграмотном использовании описанных инструментов значи-тельно возрастает.

PART Text TEXTEND PART

Ïðèìåð 11CLASS MachineCATEGORY "Office 200"KEYNAME "Software\Policies"POLICY "Office2000"EXPLAIN !!help

PART "Parametr1" EDITTEXTKEYNAME "SOFTWARE\Classes\Installer\Products\ ↵↵↵↵↵

914000001E872D116BF00006799C897E\SourceList"VALUENAME LastUsedSourceMAXLEN 100EXPANDABLETEXTEND PART

PART !!help1 TEXTEND PART

PART "Parametr2" EDITTEXTKEYNAME "SOFTWARE\Classes\Installer\Products\ ↵↵↵↵↵

914000001E872D116BF00006799C897E\SourceList\Net"VALUENAME 1MAXLEN 100EXPANDABLETEXTEND PART

PART !!help2 TEXTEND PARTEND POLICYEND CATEGORY[strings]help1="Exapmle1: n;3;\\Server\software\Office2000\"help2="Exapmle2: \\Server\software\Office2000\"help="\n\nCorrecting path to Microsoft Office 2000"

Ðèñóíîê 7

подписка на II полугодие 2004

Российская Федерация! Подписной индекс: 81655

Каталог агентства «Роспечать»! Подписной индекс: 87836

Объединенный каталог «Пресса России»Адресный каталог «Подписка за рабочим столом»Адресный каталог «Библиотечный каталог»

! Альтернативные подписные агентства:Агентство «Интер-Почта» (095) 500-00-60, курьерскаядоставка по МосквеАгентство «Вся Пресса» (095) 787-34-47Агентство «Курьер-Прессервис»

! Подписка On-linehttp://www.arzy.ruhttp://www.gazety.ruhttp://www.presscafe.ru

СНГВ странах СНГ подписка принимается в почтовых отделе-ниях по национальным каталогам или по списку номенкла-туры АРЗИ:! Казахстан – по каталогу «Российская Пресса» через

ОАО «Казпочта» и ЗАО «Евразия пресс»! Беларусь – по каталогу изданий стран СНГ через РГО

«Белпочта» (220050, г.Минск, пр-т Ф.Скорины, 10)

! Узбекистан – по каталогу «Davriy nashrlar» российскиеиздания через агентство по распространению печати«Davriy nashrlar» (7000029, Ташкент, пл.Мустакиллик,5/3, офис 33)

! Азербайджан – по объединенному каталогу российскихизданий через предприятие по распространению печа-ти «Гасид» (370102, г. Баку, ул. Джавадхана, 21)

! Армения – по списку номенклатуры «АРЗИ» через ГЗАО«Армпечать» (375005, г.Ереван, пл.Сасунци Давида, д.2)и ЗАО «Контакт-Мамул» (375002, г. Ереван, ул.Сарья-на, 22)

! Грузия – по списку номенклатуры «АРЗИ» через АО«Сакпресса» ( 380019, г.Тбилиси, ул.Хошараульская, 29)и АО «Мацне» (380060, г.Тбилиси, пр-т Гамсахурдия, 42)

! Молдавия – по каталогу через ГП «Пошта Молдавей»(МД-2012, г.Кишинев, бул.Штефан чел Маре, 134)по списку через ГУП «Почта Приднестровья» (МD-3300,г.Тирасполь, ул.Ленина, 17)по прайслисту через ООО Агентство «Editil Periodice»(2012, г.Кишинев, бул. Штефан чел Маре, 134)

! Подписка для Украины:Киевский главпочтампПодписное агентство «KSS»Телефон/факс (044)464-0220

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

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

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

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

96

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

РЕДАКЦИЯИсполнительный директорВладимир ПоложевецОтветственный секретарьНаталья Хвостова[email protected]Технический редакторВладимир ЛукинРедакторАндрей Бешков

РЕКЛАМНАЯ СЛУЖБАтел./факс: (095) 928-8253Константин Меделянreс[email protected]

Верстка и оформление[email protected][email protected]Дизайн обложкиНиколай Петрочук

103045, г. Москва,Ананьевский переулок, дом 4/2 стр. 1тел./факс: (095) 928-8253Е-mail: [email protected]: www.samag.ru

РУКОВОДИТЕЛЬ ПРОЕКТАПетр Положевец

УЧРЕДИТЕЛИВладимир ПоложевецАлександр Михалев

ИЗДАТЕЛЬЗАО «Издательский дом«Учительская газета»

Отпечатано типографиейГП «Московская Типография №13»Тираж 7000 экз.

Журнал зарегистрированв Министерстве РФ по делам печати,телерадиовещания и средств мас-совых коммуникаций (свидетельствоПИ № 77-12542 от 24 апреля 2002г.)

За содержание статьи ответствен-ность несет автор. За содержаниерекламного обьявления ответствен-ность несет рекламодатель. Все пра-ва на опубликованные материалы за-щищены. Редакция оставляет за со-бой право изменять содержание сле-дующих номеров.

ЧИТАЙТЕВ СЛЕДУЮЩЕМНОМЕРЕ:

PHP 5 – пришествиенеизбежноИтак свершилось. 13 июля 2004 годавышла пятая версия самого популяр-ного на сегодняшний день языка веб-разработки – PHP. Его создатель, Рас-мус Лендорф, подчёркивает, что из-менения, привнесённые в язык, не ре-волюционны, а скорее эволюционны,но беглое знакомство с новыми воз-можностями PHP заставляет усом-ниться в словах мэтра. За недолгоевремя своего существования PHP су-мел из набора скриптов для приданияинтерактивности домашней странич-ки автора вырасти в мощное средстворазработки веб-приложений и теперь– новый шаг вперед. Насколько этотшаг важен и какие последствия пере-хода на новую версию грозят програм-мисту и системному администратору,можно оценить, ознакомившись с ос-новными нововведениями PHP 5 ивозможными проблемами, связанны-ми с его использованием.

Роутер без дискаВ работе подчас встречаются ситуа-ции, когда осуществление тех или иныхмероприятий или действий традицион-ным путем если не то, что совсем не-возможно, а просто неудобно, а при-вычные инструменты оказываютсяпрактически бесполезны. Вот и у менявозникла ситуация, когда для показавозможностей необходимо было в ко-роткие сроки собрать и настроить сеть.А так как это была всего лишь презен-тация и все задействованные компью-теры использовались в реальной рабо-те, то одним из требований было по-меньше трогать настройки и ПО, длятого чтобы можно было быстро вернутьвсе в рабочее состояние.

Если с клиентскими компьютерамиболее-менее все решалось просто: гдедоустановкой пары программ, где ис-пользованием GNU/Linux LiveCD-дист-рибутивов, то с роутером вышла замин-ка. Имеющиеся у меня образцы либо не

удовлетворяли по возможностям, либотребовали установки на жесткий диск,либо серьезного изменения настроексистемы, что меня не совсем устраива-ло. Поэтому пришлось искать инстру-мент, который сможет помочь в такойситуации. И он, конечно же, нашелся.

Linux на страже Windows.Обзор и установкасистемы резервногокопирования BackupPCКогда у меня возникло желание сде-лать единое хранилище для ежеднев-ных архивов информации с более чемдесятка серверов своей организации,работающих под управлением не-скольких различных ОС, свой выбор яостановил на платформе Linux. До сихпор каждый сервер с помощью уни-кальных для него скриптов в назначен-ное время сбрасывал по сети на сер-вер резервного копирования или standby-сервер какие-то свои данные, на-пример, пользовательские файлы ссетевых дисков или дампы базы дан-ных. Для этого использовались различ-ные протоколы: ftp, SMB или штатныесредства СУБД. При этом приходилосьследить за уникальным для каждогосервера log-файлом, и в случае каких-либо изменений в стратегии резервно-го копирования править скрипты накаждой машине.

Чтобы как-то упростить админист-рирование и сократить время, затра-чиваемое на поддержку и мониторингвсего этого «зоопарка», я начал искатьсистему, которая бы поддерживалакопирование информации по сети, уда-ленное администрирование, умела де-лать инкрементальные бэкапы и нетребовала установки клиентского про-граммного обеспечения. Кроме того,было важно, чтобы система умела ра-ботать по протоколу SMB, так как частьсерверов, в частности основной файл-сервер работали под управлением ОСWindows. Спустя непродолжительноевремя, такая система была найдена.