учебники, программирование, основы, введение в,

 

Системная начальная загрузка

Начальную работу ядра если и приходится как-то планировать, то однократно, при установке или настройке системы. С точки зрения работающей системы ядро загружается, определяет устройства и запускает init единым махом. Оттого трудно судить, насколько старт ядра относится еще к досистемной или уже к системной части процедуры загрузки (вообще-то деление чисто условное).
Процесс init - обычный процесс (даром что с PID=1), но именно он, по сути дела, определяет профиль будущей системы. Утилита init была написана в те времена, когда разработчики различных ветвей UNIX еще и не думали договариваться о совместимости. Поэтому поведение init в системах семейства USG сильно отличается от поведения init в системах семейства BSD (о семействах UNIX рассказано в лекции 5). В современных UNIX встречаются и обе схемы в чистом виде, и особые гибриды, так что рассмотрим каждую подробнее.
Файловые системы
Но прежде поясним один термин, который нам пришлось уже использовать ранее. Речь идет о монтировании файловых систем. В лекции 7 уже было рассказано про древовидную структуру каталогов в UNIX. С точки зрения пользователя, все выглядит очень просто: один общий корневой каталог и вложенные в него подкаталоги, в которых лежат файлы. Как в эту стройную схему уложить возможность использования двух и более жестких дисков, равно как и двух или более разделов одного диска? Если, как это сделано в AIX, ввести промежуточный уровень, представляющий содержимое всех доступных разделов всех дисков в виде единого пространства, задача решается сама собой. Однако этот способ имеет свои недостатки и для большинства других веток UNIX нестандартен.
Чаще всего каждый раздел диска содержит свою файловую систему. Чтобы удобно представлять данные в виде дерева, недостаточно хранить содержимое файлов, их имена и структуру этого дерева. Нужно решить массу технических задач, связанных с быстродействием, надежностью, распределением свободного места и т. д. Создание каждой файловой системы - многолетний труд ученых и программистов, поэтому на сегодня известно не так уж много видов файловых систем. Здесь нам придется ограничиться перечислением их названий, более подробные сведения можно почерпнуть из  и статей авторов этих файловых систем. В системах гнезда BSD чаще всего используются наследники Berkeley fast file system - UFS, UFS2 и модификации самой FFS. В Linux - Ext2FS и Ext3FS (вариант Ext2FS с журнализацией), а также ReiserFS. В Linux добавлена поддержка и некоторых других файловых систем, используемых в UNIX гнезда USG: XFS и JFS.
Видимое пользователю дерево каталогов образуется так. Одну из доступных файловых систем ядро считает корневой. Эта файловая система монтируется на корневой каталог, в результате чего ее содержимое становится доступно в виде дерева каталогов, растущего непосредственно из /. Любой из каталогов текущего дерева может служить точкой монтирования другой файловой системы. После выполнения команды вида mount файл-дырка каталог (например, mount /dev/hda5 /usr) содержимое файловой системы, лежащей на разделе, которому соответствует файл-дырка, становится доступно в виде дерева, растущего из каталога. Если до монтирования, скажем, в /usr были какие-то файлы и подкаталоги, принадлежащие корневой файловой системе, они становятся недоступны до выполнения соответствующей команды umount.
Список всех файловых систем, которые монтируются по ходу начальной загрузки, обычно лежит в файле /etc/fstab. Помимо дисковых файловых систем там можно встретить упоминание файловых систем в памяти (временных, разумеется, но какова скорость!) или procfs - остроумное изобретение, позволяющее в виде дерева каталогов представлять структуру процессов UNIX. Некоторые устройства (например, CD-ROM) помечены noauto в знак того, что при старте их монтировать не надо. Запись в fstab служит только напоминанием, какое именно устройство какой точке монтирования соответствует.
Правила прививки ветвей
В UNIX существует довольно строгая договоренность относительно того, как должны называться стандартные каталоги системы и для чего их следует использовать. Регулярно выпускается документ, именуемый FHS (Filesystem Hierarchy Standard), авторы которого сводят воедино пожелания и рекомендации мировой общественности на этот счет (см. http://www.pathname.com/fhs/). Документ этот довольно велик, а приводимые в нем доводы чаще всего весьма разумны. Так что полностью соответствовать FHS как стандарту довольно сложно, хотя современные системы стараются по возможности не противоречить ему. Во многих системах есть отдельная страница руководства hier, подробно описывающая основные каталоги и их назначение.
Если кратко пересказывать man hier, картина получается такая. Содержимого каталогов корневой файловой системы должно быть достаточно для аварийной загрузки и "лечения" UNIX. В /bin и /sbin должны лежать только самые необходимые пользовательские и системные утилиты, а в /lib - все, что необходимо для работы этих утилит; в /dev UNIX хранит всевозможные файл-дырки, в /boot - все, что необходимо для досистемной загрузки. В специальном каталоге /tmp кто угодно и когда угодно может - временно - хранить свои файлы. Очень важен каталог /etc, содержащий все настройки системы (включая файлы паролей и настройки программных продуктов). Содержимое этих каталогов занимает, как правило, не очень много места; его удобно копировать на какой-нибудь резервный носитель (на совсем уж черный день).
Каталог /var предназначен для файлов, размер (и количество) которых все время меняется: для системных журналов (/var/log), почты (/var/mail), очередей (на печать, на выполнение и т. п. - /var/spool) и многого другого. Каталог /mnt содержит временные точки монтирования, то есть пустые подкаталоги, на которые при помощи mount можно временно отобразить содержимое какой-нибудь файловой системы (например, того же CD-ROM), не опасаясь, что какие-то файлы при этом не будут видны. Каталог /home принято отводить под домашние каталоги пользователей.
Наконец, каталог /usr содержит все то, чего не было в /, и что необходимо для штатной работы системы. Многие каталоги называются так же, как и подкаталоги корневого: /usr/bin, /usr/sbin, /usr/lib и другие; их назначение повторяет назначение их тезок. Содержимое /usr/include используется в процессе разработки, а /usr/share содержит файлы, одинаково пригодные на компьютерах любой архитектуры: подкаталог man содержит страницы помощи, info - info-систему, doc - прочую документацию, locale и nls задают язык диалога с пользователем (например, русский) и прочие особенности национальной формы представления данных (даты, времени, денежных единиц и т. д.).
Выносить каталоги UNIX в отдельные файловые системы приходится не только под давлением обстоятельств (не хватило места или нужно использовать несколько жестких дисков), но и по другим соображениям. Поразмыслив над тем, как именно используются файлы и каталоги, стандартные для UNIX, мы можем прийти к выводу, что режимы работы с ними зачастую резко различаются. Учитывая, во-первых, частоту операций записи в каталог, во-вторых, требования к быстродействию и надежности хранения содержимого и, в-третьих, то, обязано ли в каталоге быть свободное место, мы можем составить схему разбиения дерева каталогов по разным файловым системам с различными свойствами.
Типичное решение. Корневая файловая система: запись (как правило, в /etc) происходит, но редко; надежность - самая высокая, в ущерб быстродействию; переполнения происходить не должно. Файловая система в /usr: записи может вообще не быть; надежность - высокая, с учетом быстродействия по чтению. Файловая система в /var: запись происходит постоянно; надежность - не в ущерб скорости записи и чтения; переполнение - штатная ситуация (разбух журнал), система должна продолжать работать. Похожими свойствами обладает и /home, но, поскольку /var заполняет система, а /home - пользователи, смешивать не рекомендуется. Отличие /tmp от /var - в еще более низкой надежности (неиспользуемые файлы могут исчезать из /tmp) и в еще более значительном преимуществе от быстродействия. Поэтому /tmp иногда размещают в памяти.

Гнездо USG
Уровни выполнения
В системах из гнезда USG (к которым по общим признакам относится и Linux, хотя формально Linux не наследует System V) вводится понятие уровня выполнения (run level). Уровень выполнения системы - это некоторый обобщенный профиль ее работы: из всех возможных системных служб на каждом уровне выполнения некоторые должны быть запущены, а некоторые - обязательно остановлены. Уровни выполнения нумеруются, и чем больше номер, тем больше услуг предоставляет система. Некоторые уровни выполнения стандартизованы, в отношении других есть только договоренность.
Уровень 0 соответствует останову системы (такому профилю, в котором все системные службы должны быть остановлены). Уровень 6 соответствует перезагрузке системы: останову с последующим запуском. Перезагрузка может понадобиться, если администратор или иные стихии привели систему в неработоспособное состояние, но чаще - при замене ядра и других подобных операциях.
Уровень 1 - специальный, он называется однопользовательским. Данный уровень предоставляет единственному пользователю - root - единственный канал управления системой (как правило, с системной консоли), при этом другие терминальные линии не обслуживаются и никаких других пользователей в системе зарегистрировать нельзя. Обычно никакие файловые системы, кроме корневой, при этом не смонтированы (что позволяет делать с остальными файловыми системами существенно большее, чем когда смонтированы и они). Иногда в однопользовательском режиме система вообще не спрашивает пароля пользователя root, а сразу запускает на консоли shell с нулевым UID. Это, конечно, стоит допускать только в случае защищенной консоли (secure console), когда к физическому устройству-консоли имеет доступ только тот, кто и так знает пароль root. Например, терминал - консоль сервера, запертого в серверной комнате на ключ, который есть только у системного администратора, можно (но не обязательно!) считать защищенным, а клавиатуру+монитор персональной Linux-станции в офисе - нельзя.
Уровень 2, по договоренности, соответствует многопользовательскому режиму работы, при котором активизируются все терминальные линии со стандартной процедурой аутентификации пользователя (см. лекцию 10) и запускаются кое-какие системные службы.
Уровень 3 принято задействовать для многопользовательского сетевого режима работы: именно при входе на этот уровень выполнения запускаются всевозможные сетевые службы, превращая систему в сетевой сервер (если это нужно). Строгой договоренности, когда именно настраивать сеть (давать возможность работать с ней клиентским программам) нет: иногда это делается на уровне 2, а иногда - на уровне 3.
В Linux часто используется и уровень 5, на котором дополнительно запускаются графические сервисы (обычно графический сервер X11 Window System и служба аутентификации). Можно использовать и другие уровни (4 и 7-9), но для чего они - в точности не определено. В некоторых системах гнезда USG (в частности, в Solaris) для того, чтобы добраться до более высокого уровня выполнения, необходимо последовательно пройти все более низкие, начиная с первого. При этом для перехода с уровня на уровень достаточно только внести изменения в состоянии системы - например, запустить сетевые службы при переходе со второго уровня на третий. С другой стороны, такая схема требует строго вложенных функциональностей, а если более высокий уровень не целиком повторяет возможности более низкого - начинаются неувязки.
Inittab
Стартовав, init читает файл /etc/inittab. В этом файле, довольно замысловатом по структуре, он находит много информации. Там написано, на какой уровень выполнения следует переходить при старте системы (обычно 3 или 5), какие действия выполнять сразу после запуска и при входе на каждый уровень, что делать при выключении или нестабильности питания; возможно, что-то еще. Сами действия могут быть с точки зрения init однократными или повторяемыми (когда соответствующий процесс завершается, init повторно запускает его). Кроме того, init может ждать завершения процесса или запускать его в фоне, умеет запускать определенные процессы в специфических состояниях системы (можно, например, научить его распознавать любимое Ctrl+Alt+Del и переходить на уровень 6 после нажатия этих клавиш).
Особенности поведения init и структура файла inittab описаны в соответствующих руководствах, нам важно только вот что: описанный ниже механизм перехода системы с одного уровня выполнения на другой, хоть и соответствует давней договоренности, но задается отчасти структурой самого inittab, отчасти вызываемым из него универсальным стартовым сценарием. Оба файла - текстовые и по крайней мере один из них - inittab - предназначен для редактирования (системным администратором, конечно). Так что названия самих сценариев могут быть слегка различными в разных системах, и ничего не стоит изменить логику начальной загрузки до неузнаваемости, вот только надо ли?
Итак, сначала init выполняет те строчки из inittab, которые следует выполнять при старте самого init. В Linux, например, это командный сценарий /etc/rc.d/rc.sysinit. Главная задача rc.sysinit - проверить целостность файловых систем, которые должны быть смонтированы, смонтировать их и загрузить все необходимые модули, которых не было в initrd. Во-первых, это модули, ненужные для загрузки ядра - драйверы внешних устройств, разных файловых систем, функциональные расширения ядра и т. п. Во-вторых, это модули, которые нужны для работы необходимых модулей: например, для подключения USB-мыши требуется как минимум два драйвера - самого устройства и шины USB.
Современные внешние устройства умеют сообщать достаточно данных для автоматического их распознавания (некоторое уникальное для однотипных устройств имя и настройки, выданные им шиной. В Linux такая утилита для шины PCI называется lspci, а в FreeBSD - scanpci), однако полностью переложить на систему подбор модулей нельзя. Для этого все изготовители аппаратуры должны давать привязку устройств к драйверам всех операционных систем (или даже изготовлять новые драйверы), что невозможно. По-хорошему, каждое внешнее устройство должно полностью соответствовать какому-нибудь опубликованному стандарту: тогда было бы достаточно одного драйвера, а какой-нибудь ярлычок стандарта определял бы уникальное имя устройства. Это совершенно нереально, потому что множество изготовителей аппаратуры (особенно самой дешевой и самой дорогой) зарабатывают именно тем, что скрывают архитектуру продаваемого устройства, дабы не пострадать от конкурентов.

Схема ".d"
Выполнив rc.sysinit, init выбирает из inittab уровень, на который система должна перейти по умолчанию. В Linux этим занимается командный сценарий /etc/rc.d/rc, которому в качестве параметра командной строки передается номер уровня. Именно запуск rc с параметром и записан в inittab для каждого уровня выполнения, с указанием init дождаться, пока сценарий завершит работу. Переход на уровень - это выполнение соответствующего уровню набора управляющих (или старт-стопных) сценариев, который выбирается и выполняется из-под rc.
Каждый такой сценарий должен распознавать как минимум два параметра командной строки - start и stop. Запущенный с ключом start, сценарий запускает соответствующую службу (например, подсистему печати или WWW-сервер), а запуск с ключом stop эту службу останавливает. При установке некоторой службы в систему ее управляющий сценарий помещается в каталог /etc/init.d. Суффиксом .d обычно отмечается каталог (directory) однотипных файлов, каждый из которых используется какой-нибудь определенной программой. (Очень часто в более ранних версиях UNIX вместо .d-каталога использовался один файл, и при добавлении или удалении программного продукта приходилось этот файл автоматически преобразовывать; если к нему уже успевал приложить руку системный администратор, об автоматизме можно было говорить лишь гипотетически). Каталог /etc/init.d в ALT Linux - символьная ссылка на /etc/rc.d/init.d, а в некоторых версиях Linux /etc/init.d не было вовсе, что слегка нарушало единообразие. Иногда от управляющего сценария требуется еще обрабатывать ключи status, диагностику состояния службы, и restart, перезапуск службы (это не всегда просто stop+start). Перечисленные параметры не играют роли при переходе с одного уровня выполнения на другой, зато их удобно использовать, управляя системой вручную.
В /etc (для Linux - в том же /etc/rc.d) есть каталоги вида /etc/rc номер_уровня.d, в которых помещены специальным образом поименованные символьные ссылки на управляющие сценарии. Если при входе на уровень некоторая служба должна быть остановлена, в соответствующий каталог помещается ссылка на ее управляющий сценарий, имя которой начинается на букву K (от KILL). Если служба должна быть запущена, ссылка на ее управляющий сценарий должна начинаться на S (от Start). Сначала rc выполнит по очереди (в лексикографическом порядке) все начинающиеся на K сценарии из этого каталога, передавая каждому параметр stop. В некоторых системах стоп-сценарий вызывается, только если его служба действительно была запущена; в этом случае значимо сообщение об ошибке: например, служба успела остановиться сама собой, что нехорошо. Потом - упорядоченно и по очереди - выполняются с параметром start все начинающиеся на S сценарии.
В некоторых системах строгих требований, кроме определенной первой буквы, к именам в /etc/rc?.d/ не предъявляется, но считается хорошим тоном второй и третий символы имени сценария делать двузначным числом, а уж потом добавлять имя сервиса. Тогда, во-первых, строго определится очередность выполнения управляющих сценариев, потому что сценарий с меньшим номером выполнится безусловно раньше, а во-вторых, даже при совпадении этих чисел, конфликта имен не будет, потому что разные службы называются по-разному. В тех системах, где флаг запуска и останова службы зависит от имени стартового или остановочного сценария (ALT Linux хранит эти флаги в /var/lock/subsys/), такое именование ссылок на управляющие сценарии обязательно.
Например, в ALT Linux при входе на уровень 2 (многопользовательский без сети) требуется прекратить использовать сетевые файловые системы (netfs) и запустить системную службу выполнения действий по расписанию (crond). На уровне 3 (многопользовательском сетевом) обе службы должны быть запущены, а на уровне 0 (останов системы) - остановлены. Для этого в каталогах /etc/rc.d/rc0.d, /etc/rc.d/rc2.d и /etc/rc.d/rc3.d помещено по две ссылки на управляющие сценарии. (К нашей теме это не относится, но о том, что такое ls -og и egrep, можно почитать в руководствах, а о *grep - еще и в лекции 14; к теме относится, что приведенные ссылки указывают не в /etc/init.d, а в /etc/rc.d/init.d, как это сложилось в Linux):
linux$ /bin/ls -og /etc/rc.d/rc[023].d | egrep ^/|crond|netfs"
/etc/rc.d/rc0.d:
lrwxrwxrwx  1   15 Oct 31 16:41 K60crond -> ../init.d/crond
lrwxrwxrwx  1   15 Oct 31 16:41 K75netfs -> ../init.d/netfs
/etc/rc.d/rc2.d:
lrwxrwxrwx  1   15 Oct 31 16:41 K75netfs -> ../init.d/netfs
lrwxrwxrwx  1   15 Oct 31 16:41 S40crond -> ../init.d/crond
/etc/rc.d/rc3.d:
lrwxrwxrwx  1   15 Oct 31 16:41 S25netfs -> ../init.d/netfs
lrwxrwxrwx  1   15 Oct 31 16:41 S40crond -> ../init.d/crond
Обратите внимание, что останов служб происходит обычно не в том же порядке, что запуск; логичнее всего предположить, что порядок будет обратным, раз уж сумма чисел при K и S у одинаковых сценариев равна 100, однако бывают ситуации, когда порядок останова следует выстраивать специально.
Если понятие уровней выполнения представляется чем-то слегка надуманным, то описанная .d-схема хранения и запуска сценариев выглядит весьма удобной. У нее есть, по крайней мере, три достоинства.
Первое - соответствие У, поскольку в каждом сценарии сосредоточено все, относящееся только к конкретной службе. Второе - соответствие З, поскольку общие для всех сценариев части, вроде оформления вывода, журнализации запуска и останова и т. п., обычно уже включены в /etc/rc.d/rc или хранятся во включаемом сценарии (в Linux - /etc/rc.d/init.d/functions), так что на долю разработчика сценария остается только логика запуска, а пользователю-администратору для запуска или останова службы достаточно просто запустить сценарий. Третье - независимость служб на уровне размещения в файловой системе: поводом для включения некоторой службы в работу будет появившийся в /etc/init.d файл. Указание запускать или не запускать сервис на том или ином уровне тоже сведено до одной операции - заведения соответствующей ссылки в соответствующем каталоге /etc/rc?.d.
Дождавшись выполнения rc, init переходит к другим записям в inittab, относящимся к текущему уровню выполнения. Обычно там остаются только перезапускаемые (respawn) действия: процессы, которые init запускает в фоне, а когда какой-нибудь из них завершается, запускает вновь. Так, например, устроен запуск getty на обслуживаемых терминальных линиях: как известно из главы 8, getty определяет активность на терминальной линии и запускает login; login проверяет входное имя и пароль пользователя и запускает shell, а shell завершается при выходе пользователя из системы. Тогда init замечает, что процесс с указанным PID умер, и перезапускает getty. В качестве параметра getty в inittab вписано имя терминала и его тип.
Системная начальная загрузка считается законченной, когда все перезапускаемые процессы из initab запущены и init ничего не остается, как следить за ними. В это время пользователи уже радостно вводят пароль программе login...

Гнездо BSD

"Линейный" стартовый сценарий

Рассмотрим теперь, как ведет себя init в системах семейства BSD. В этих системах нет понятия уровня выполнения, поэтому в целом init работает очень просто: сначала выполняет стартовый сценарий /etc/rc, после чего разбирает файл /etc/ttys, в котором сказано, на каких терминальных линиях какие обработчики должны быть запущены (в основном это getty, а сам файл, как видно, выполняет одну из ролей inittab). Когда процесс из ttys завершается, init запускает его повторно. Кстати сказать, если вы изменили /etc/ttys (или /etc/inittab) и хотите, чтобы init прочитал заново настройки, ему, как и большинству демонов UNIX, можно послать сигнал HUP (Hang UP): kill -HUP 1 (конечно, это может сделать только тот, кто имеет право изменять inittab - суперпользователь).
Несмотря на то что в BSD нет понятия уровня выполнения, понятие однопользовательского режима там есть. Соответствующий параметр передается ядру, а оно передает аналогичный параметр init. В BSD init, запущенный в однопользовательском режиме, не выполняет /etc/rc и не разбирает строки /etc/ttys, не относящиеся к системной консоли. Вместо этого init непосредственно запускает /bin/sh на консоли, если она защищенная, или вдобавок предварительно спрашивает пароль суперпользователя, если она незащищенная. После того как shell завершается, init переходит в многопользовательский режим и работает так, как описано в этой лекции.
Видимо, когда-то давно /etc/rc был единственным стартовым сценарием, в котором сначала определялись в виде переменных shell разнообразные настройки системы, а потом, в зависимости от их значения, производились необходимые для начала работы действия, вроде проверки и монтирования дисков, настройки сети, запуска основных системных демонов и т. п. Очень скоро стало понятно, что кусок с настройками системы системный администратор редактирует довольно часто, а кусок со сценарием - почти никогда. Тогда этот файл был поделен на две части: сам /etc/rc - сценарий и включаемые в него командой "." (см. лекцию 11) /etc/rc.conf (в некоторых системах - /etc/rc.config) - настройки.
Однако через некоторое время выяснилось, что и rc.config не слишком хорош, потому что перечисленные в нем, иногда безо всякого порядка, все системные настройки (числом более трех сотен) - прямое нарушение У. Порядок строк вида переменная=значение в этом файле сам по себе неважен, да еще средство автоматической настройки в FreeBSD, /stand/sysinstall, дописывает все сделанные с его помощью изменения в конец rc.config. Затем в каталоге /etc появился подкаталог /etc/defaults, в котором хранится в неизменном виде хорошо откомментированный rc.conf и некоторые другие настройки системы. В rc.conf из /etc записываются теперь только различия желаемой конфигурации системы и /etc/defaults/rc.conf, а их, как правило, раз в десять меньше. Итак, всякий сценарий, желающий использовать настройки системы, выполняет сначала команду . /etc/defaults/rc.conf, а затем . /etc/rc.conf.
С вынесением настроек в отдельный файл появилась возможность разбить /etc/rc на несколько сценариев, отвечающих за различные стадии загрузки и различные подсистемы. Некоторые подсистемы при разных вариантах настройки могут быть незадействованы, а значит, их сценарии запускать незачем. Так появились /etc/rc.network (настройка сети и сетевых служб), /etc/rc.firewall (настройки межсетевого экрана), /etc/rc.i386 (настройка параметров, специфичных для i386), /etc/rc.syscons (настройка виртуальных консолей), /etc/rc.diskless[12] (для бездисковых систем) и некоторые другие (в FreeBSD4 - порядка 20 штук). В этих сценариях тоже включаются оба файла rc.conf, так что, если это имеет смысл, их можно запускать отдельно, не из /etc/rc. Например, для перезапуска firewall с консоли можно использовать команду sh /etc/rc.firewall тип.
Эти сценарии вызываются (или не вызываются) из /etc/rc в том порядке, в котором они там встречаются. Предполагается, что все упомянутые в них службы уже установлены в системе, а если они не установлены, то и в rc.conf не включены.
Использовать стартовые сценарии BSD для останова служб не представляется возможным. Вручную службы можно останавливать и перезапускать сигналами (TERM и HUP) - если, конечно, демоны написаны в соответствии с договоренностями - или специальными утилитами вроде lpc, управляющей подсистемой печати. Операции, необходимые для останова всей системы (размонтировать сетевые диски, сохранить динамические настройки некоторых служб, остановить штатным способом службы, которые нельзя завершить сигналом и дождаться их полного останова и т. п.), выполняет сценарий /etc/rc.shutdown.

Недостатки линейной схемы загрузки

Линейная схема неплохо работает только в случае так называемой базовой системы (core system) - системы, все программные продукты которой известны, оттестированы на совместимость друг с другом и отражены в совместных стартовых сценариях вроде /etc/rc.network. Еще одно, ныне утерянное преимущество линейной схемы - на медленных старых машинах она работала существенно быстрее, чем схема .d. В самом деле, при вызове из rc множества других сценариев было бы непоправимой ошибкой выполнять их тем же интерпретатором, что и rc (т. е. использовать команду "."): синтаксическая ошибка в любом из них приведет к сбою самого rc, и процедура начальной загрузки аварийно остановится. Значит, для интерпретации каждого управляющего сценария нужно запустить собственную копию shell - 30 лет назад эта операция требовала очень много системного времени: новый процесс, дополнительная оперативная память, открытие нового файла...
Сейчас эти различия уже неактуальны: размер однотипных программ растет линейно (если, конечно, не раздувать объем кода искусственно), а производительность компьютеров - экспоненциально (см. ), так что сами стартовые сценарии и упомянутые системные вызовы работают уже слишком быстро по сравнению с обработкой пользовательских данных. Дело в том, что объем хранилищ данных растет вместе с быстродействием компьютера: наибольший размер хранилища данных зависит от того, как долго пользователь согласен ждать окончания операции выборки. Поэтому, например, скорость работы команды fsck, проверяющей цельность файловой системы, почти не увеличилась.
Если речь идет о необязательных компонентах - таких как WWW-сервер или какой-нибудь экзотический telserver (talk via telnet), которые тем не менее должны запускаться при старте системы и останавливаться при останове, ничего лучше .d-схемы все равно нет. Поэтому в FreeBSD4 есть и каталог rc.d, только лежит он, согласно правилам FreeBSD, не в /etc, а в /usr/local/etc. Этот каталог - аналог init.d из USG, с той лишь разницей, что USG-система самостоятельно запускает только ссылки на содержимое init.d из каталогов уровней, а BSD-система запускает их непосредственно из rc.d, все с параметром start при старте системы и с параметром stop при останове.
Требования BSD к управляющему сценарию такие: он должен быть исполняемым (иметь x-бит), обрабатывать ключи start и stop, и его имя должно заканчиваться на .sh. Последнее требование позволяет при установке необязательного сервиса класть в /usr/local/etc/rc.d управляющий сценарий с именем, скажем, myservise.sh.sample, который не будет запускаться, пока системный администратор не посмотрит внутрь него и не переименует в myservise.sh. Очередность выполнения сценариев из rc.d при старте FreeBSD4 - лексикографическая, при останове - обратная лексикографическая, поэтому надо быть внимательным при запуске зависящих друг от друга подсистем.

Схема ".d"

Несмотря на очевидные недостатки "линейной" схемы стартовых сценариев в стиле BSD4.4 по сравнению с .d-схемой, в FreeBSD и некоторых других системах не решались на нее переходить. Главная трудность заключалась в выборе того самого двузначного числа, которое в .d-схеме задает порядок выполнения сценариев. Включение очередного сценария в rc.d требует некоторого размышления, каким именно по порядку он должен идти. В случае необязательных служб этот порядок почти неважен, так как они мало зависят друг от друга. Но если этот сценарий запускает часть самой системы, нарушение порядка может привести ее в нерабочее состояние. В конце концов разработчики NetBSD решили вообще отказаться от ручного, заранее заданного определения очередности выполнения сценариев - и от числа в имени сценария тоже.
Пришлось ввести новую сущность - заголовок командного сценария. В заголовке указано, как называется сервис, которым этот сценарий управляет, какие сервисы уже должны быть запущены к моменту его запуска и какие должны быть запущены после. Попутно в NetBSD увеличили число распознаваемых управляющим сценарием ключей (для удобства согласования и управления вручную). Утилита rcorder анализирует заголовки всех управляющих сценариев и выстраивает их в порядке, который удовлетворяет всех. В этом-то порядке /etc/rc их и запускает. Если в зависимостях встречаются циклы, это уже ошибка разработчика, и rcorder выдаст соответствующую диагностику.
Схема NetBSD оказалась удачной и в новой ветке FreeBSD - FreeBSD5 - ее скопировали. Количество "линейных" сценариев резко сократилось (из них некоторые остались по соображениям совместимости; скорее всего, их в последующих версиях FreeBSD5 ликвидируют). Появился каталог /etc/rc.d, в котором и лежат управляющие сценарии (числом более сотни) и некоторая политика оформления их заголовков. Подробнее обо всем этом расскажет руководство по rc и rcorder.

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г.Яндекс.Метрика