Исследуем процессы FreeBSD. Часть 3. Демон init


Dru Lavigne
перевод Станислава Лапшанского
Впервые опубликован: http://www.computerra.ru.

В последних двух частях этой статьи (см. Исследуем процессы: Часть 1 и Часть 2), мы узнали как получать список запущенных процессов, и как ими управлять. Сегодня мы посмотрим как на самом деле были запущены все эти процессы.

При загрузке FreeBSD происходит масса интересных вещей. Я не буду здесь всех их детально описывать, поскольку существует FreeBSD Handbook, в которой работа по тщательному описанию процесса загрузки уже проделана.

Вероятно, вы замечали, что в процессе загрузки ядро FreeBSD выясняет список доступного оборудования и выводит его на системную консоль. Когда сведения об оборудовании собраны, ядро запускает два процесса: процесс 0 (swapper) и процесс 1 (init).

Процесс init - это демон, который отвечает за инициализацию системы. Без него ни один другой процесс не сможет запуститься. Во время загрузки init выполняет две важные задачи: во-первых он запускает стартовые сценарии rc, а затем инициализирует терминалы, для того что бы в систему могли войти пользователи. Давайте пройдемся по функциям демона init, начав с rc:
$ whatis rc
rc(8) - command scripts for auto-reboot and daemon startup
rc(8) - сценарий для авто-перезагрузки и запуска демонов

На самом деле эти сценарии находятся в каталоге /etc/rc.d/. Обычно конфигурационным файлам, соответствуют страницы раздела номер 5 интерактивного руководства, в котором вы можете найти описания того, как правильно менять конфигурационные файлы. Однако, если вы напечатаете:
$ man 5 rc

то вы получите следующее сообщение:
No entry for rc in section 5 of the manual
В разделе номер 5 руководства, сведений о rc не найдено

Кажется немного странным, что сведения о конфигурационных файлах находятся в восьмом разделе руководства, который обычно содержит сведения о командах причастных к обслуживанию системы или системным операциям, в общем это раздел о демонах. Давайте поглядим повнимательнее на файл /etc/rc и посмотрим что у него внутри:
$ more /etc/rc
# System startup script run by init on autoboot
# or after single-user.
# Output and error are redirected to console by init,
# and the console is the controlling terminal.
# Note that almost all of the user-configurable behavior
# is no longer in # this file, but rather in /etc/defaults/rc.conf.
# Please check that file first before contemplating any changes
# here. If you do need to change this file for some reason, we
# would like to know about it.
# Сценарий начальной загрузки системы, запускаемый
# процессом init в процессе загрузки или после
# однопользовательского режима.
# Результаты работы и ошибки передаются на системную
# консоль при помощи процесса init, в тоже время консоль это
# управляющий терминал.
# Обратите внимание - практически все что захочет
# конфигурировать пользователь вынесено из этого файла и
# находится в /etc/defaults/rc.conf. Пожалуйста посмотрите
# него до того, как задумаете вносить исправления в этот файл.
# Если вы знаете причину по которой вам необходимо внести
# в этот файл изменения, то сообщите ее нам.

Выглядит так как будто мы не должны возиться с этим файлом самостоятельно. Судя по всему здесь должны находиться очень важные вещи для правильной загрузки нашей системы. Для того что бы понять что на самом деле происходит во время этой фазы загрузки, давайте выборочно посмотрим на некоторые важные куски этого файла. Заметьте, что все сообщения и ошибки, выдаваемые сценарием rc, процесс init переправляет на терминал. (Мои комментарии будут отмечены табуляцией, строки комеентариев, прижатые влево, взяты из файла /etc/rc).
# Первым делом сценарий rc устанавливает переменную PATH,
# для того что бы иметь возможность находить исполняемые
# файлы в вашей FreeBSD.

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin

# Затем rc получает содержимое файлов
# /etc/defaults/rc.conf и /etc/rc.conf:

# If there is a global system configuration file, suck it in.
# Если это глобальный конфигурационный файл, то применить его

if [ -f /etc/defaults/rc.conf ]; then
. /etc/defaults/rc.conf
elif [ -f /etc/rc.conf ]; then
. /etc/rc.conf
fi

# Затем делается проверка целостности файловых систем.
# Если вы некорректно завершили работу вашей FreeBSD,
# то вы будете оповещены об этом на данном
# этапе загрузки системы.

echo Automatic boot in progress...
fsck -p

(На самом деле все немного сложнее, поскольку запуск fsck можно запретить командами в файле rc.conf - прим. переводчика.)
# Будем подразумевать что fsck не встретил проблем
# при проверке файловых систем. Затем rc
# смонтирует файловые системы.

# Mount everything except nfs filesystems.
# Смонтировать все исключая nfs.

mount -a -t nonfs

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

adjkerntz -i

# Содержимое некоторых подкаталогов каталога
# /var должно быть очищено от посторонних файлов,
# после чего загрузочные сообщения помещаются
# в файл dmesg.boot:

clean_var() {
if [ ! -f /var/run/clean_var ]; then
rm -rf /var/run/*
find /var/spool/lock ! -type d -delete
rm -rf /var/spool/uucp/.Temp/*
# Keep a copy of the boot messages around
dmesg >/var/run/dmesg.boot

# Затем rc читает содержимое следующих файлов:

/etc/rc.sysctl
/etc/rc.serial
/etc/rc.pccard
/etc/rc.network
/etc/rc.network6

# После этого rc сбрасывает настройки
#прав доступа терминала

# Whack the pty perms back into shape.
chflags 0 /dev/tty[pqrsPQRS]*
chmod 666 /dev/tty[pqrsPQRS]*
chown root:wheel /dev/tty[pqrsPQRS]*

# А затем он подчищает беспорядок и
# очищает каталог /tmp:

# Clean up left-over files
# Удаление ненужных файлов

...

# Clearing /tmp at boot-time seems to have a long tradition. It doesn't
# help in any way for long-living systems, and it might accidentally
# clobber files you would rather like to have preserved after a crash
# (if not using mfs /tmp anyway).
# See also the example of another cleanup policy in /etc/periodic/daily.
# Очистка каталога /tmp во время загрузки, кажется имеет давние традиции.
# Она никогда не помогает в случае редко перегружаемых систем. При
# перезагрузке из-за системного сбоя вы можете потерять нужные файлы,
# оказавшиеся в каталоге /tmp.

...

# Remove X lock files, since they will prevent you from restarting X11
# after a system crash.
# Удаление блокировочных файлов XWindow, поскольку они
# могут помешать их перезапуску после аварийной перезагрузки

...

# Теперь все подготовлено к запуску некоторых демонов
# первыми запускаются syslogd и named:

# Start system logging and name service.
# Named needs to start before syslogd
# if you don't have a /etc/resolv.conf.
# Запуск службы журналлирования и сервера имен
# Сервер имен должен быть запущен перед службой
# журналлирования, если у вас нет файла resolv.conf

# Затем стартуют inetd, cron, lpd, sendmail, sshd и usbd:

# Now start up miscellaneous daemons that don't belong anywhere else
# Теперь запускаются разные демоны, которые нигде больше не указаны

# После этого rc корректирует файл motd, а так же
# выполняет "uname -m" которая выдаст вам
# архитектуру вашего процессора

(Тут /etc/rc кончается.)

Поскольку мы достигли конца /etc/rc, работа сценария rc закончена. Подведем краткий итог тому, что здесь произошло: init вызывает сценарий rc, который читает несколько конфигурационных файлов, для того что бы правильно смонтировать файловые системы и подготовить среду для старта системных демонов. Теперь ваша система загружена и работает, однако в данный момент еще не существует среды в которой пользователь будет общаться с операционной системой. С этого момента начинается второй этап работы процедуры init.

Теперь прочитывается конфигурационный файл /etc/ttys. Файл /etc/ttys содержит важную информацию о терминалах - какие из них (и каким образом - прим. переводчика) должны быть проинициализированы. В отличие от /etc/rc, этот файл может быть отредактирован суперпользователем, если в этом есть необходимость.

Для правильного понимания содержимого этого файла, мы должны уяснить, что в FreeBSD существует три типа терминалов. Название виртуального терминала начинается с последовательности ttyv, за которой следует число или буква, это терминалы которые доступны пользователю непосредственно сидящему за компьютером с FreeBSD. По-умолчанию первый из них, ttyv0, выполняет функции системной консоли. Последовательные, или "телефонные", терминалы называются с ttyd и заканчиваются числом. Эти терминалы предназначены для пользователей, которые получают доступ к вашей FreeBSD удаленно при помощи модема. Последним типом терминалов являются псевдотерминалы (сетевые терминалы), их название начинается на ttyp, заканчиваясь числом или буквой. Такие терминалы используются для работы пользователей, получающих доступ к FreeBSD по сети (например для работы telnet-сессии - прим. переводчика).

Если мы посмотрим файл /etc/ttys:
$ more /etc/ttys

то мы увидим, что он состоит из трех частей, по количеству типов терминалов. Каждая часть состоит из четырех столбцов, сведения о которых собраны в таблице:Столбец Что содержит
name Название терминального устройства в системе
getty Программа которую надо запустить на терминале, чаще всего это getty. Другие примеры: xdm, который запускает систему XWindow, и none, означающий, что на терминале не надо запускать никаких программ.
type Для виртуальных терминалов, корректный тип cons25 (для русскоязычных пользователей - cons25r - прим. переводчика). Другие типы: для псевдотерминала это network, для портов, обслуживающих входящие модемные вызовы - dialup. Если предугадать тип терминала к которому хочет подключиться пользователь нельзя, в столбец типа пишут unknown.
status Может иметь только два значения - on или off. Если значение "on", то init запускает программу указанную в столбце getty. Если добавлен параметр secure, то на этом терминале разрешен вход суперпользователя. Для его предотвращения используйте слово insecure.


Давайте займемся дешифрацией этого файла с секции виртуальных терминалов. Обратите внимание - она начинается с установки параметров системной консоли:
# If console is marked "insecure", then init will ask
# for the root password when going to single-user mode
# Если консоль помечена как "insecure", тогда init будет
# запрашивать пароль, когда система входит в
# однопользовательский режим
console none unknown off secure

Если во время загрузки запуск команды fsck выявил неустранимые ошибки файловой системы, то init запустит операционную систему в однопользовательском режиме, для того что бы суперпользователь мог заняться решением проблемы. Если вы изменили настройку системной консоли со значения "по-умолчанию" secure на insecure, то перед переходом в однопользовательский режим, init потребует от вас ввода пароля суперпользователя.
ttyv0 "/usr/libexec/getty Pc" cons25 on secure
# Virtual terminals
ttyv1 "/usr/libexec/getty Pc" cons25 on secure
ttyv2 "/usr/libexec/getty Pc" cons25 on secure
ttyv3 "/usr/libexec/getty Pc" cons25 on secure
ttyv4 "/usr/libexec/getty Pc" cons25 on secure
ttyv5 "/usr/libexec/getty Pc" cons25 on secure
ttyv6 "/usr/libexec/getty Pc" cons25 on secure
ttyv7 "/usr/libexec/getty Pc" cons25 on secure
ttyv8 "/usr/X11R6/bin/xdm -nodaemon" xterm off secure

Заметьте - на моей FreeBSD имеется восемь виртуальных терминалов в добавок к консоли. Я имею доступ к каждому из них, по нажатию клавиши Alt и одной из функциональных клавиш. Например, Alt+F1 дает доступ к системной консоли, Alt+F2 - к терминалу ttyv1, Alt+F3 - к ttyv2, и т.д. Если я запущу сессию X Window, она будет доступна по нажатию Atl+F8. Если я поменяю параметр off на on для терминала ttyv8, то при старте системы вместо консоли я получу X Window-сессию, хотя я по прежнему сохраню возможность переключения между терминалами по Alt-комбинации (тут автор немного ошибся - из сессии X Window в текстовый терминал можно перейти только по комбинации Ctrl+Alt+F<номер терминала> - прим. переводчика). Все терминалы на моей машины отмечены признаком "secure", поэтому на любом из них я сразу могу зайти в систему от имени суперпользователя. Какое количество виртуальных терминалов создано по-умолчанию на вашей машине - зависит от версии FreeBSD. Если вы хотите создать себе несколько дополнительных виртуальных терминалов, посмотрите эту ссылку: http://www.freebsd.org/doc/en_US.ISO8859-1/books/faq/x.html#VIRTUAL-CONSOLE.

Теперь переместимся ниже, в раздел терминалов обслуживающих входящие звонки на модемы (раздел dial-up терминалов).
# Serial terminals
# The 'dialup' keyword identifies dialin lines to login, fingerd etc.
ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure

Как мы видим, на моей машине четыре dial-up терминала, однако все они находятся в состоянии off. Если я захочу, что бы пользователи входили в мою систему используя модем, то мне придется хотя бы один из этих терминалов переключить в состояние on. Так же мне пришлось бы решать - должны ли эти пользователи иметь возможность входить в систему от имени суперпользователя, и если нет, то я должна буду изменить признак secure на insecure. Вы так же наверняка заметили, что в строках, описывающих dial-up терминалы столбец getty включает в себя число 9600. Это число определяет скорость передачи данных порта в 9600 бит/сек. Поскольку большинство современных модемов могут использовать гораздо более высокие скорости передачи данных, я, вероятно буду вынуждена сменить это число на большее, например на 57600. И, наконец, я должна буду прочитать раздел FreeBSD Handbook, который детально описывает настройку сервиса приема входящих звонков (смотрите например по ссылке: http://www.freebsd.org/handbook/dialup.html).

Последний раздел файла /etc/ttys посвящен описанию сетевых или псевдо терминалов. В этом файле содержится описание очень большого их количества, 255, если быть точным. В диапазоне от:
# Pseudo terminals
ttyp0 none network

до:
ttySv none network

Ни один из них по-умолчанию не активирован.

Даже если вы стали суперпользователем для того, что бы отредактировать файл /etc/ttys, вы все равно не должны забывать послать процессу init сигнал HUP, для того что бы он воспринял внесенные изменения. Для того что бы сделать это, напишите:
$ kill -1 1

где -1 это числовое обозначение сигнала HUP, а 1 - номер процесса init.

Теперь давайте выясним, что это за программа getty, которая присутствует в файле /etc/ttys. Описание взятое из страницы руководства (man 8 getty) дает нам полную картину:
DESCRIPTION

The getty program is called by init(8) to open and initialize
the tty line, read a login name, and invoke login(1).

ОПИСАНИЕ
Программа getty вызывается процессом init для того что бы открыть
и инициализировать терминальный канал, прочитать имя пользователя
и вызвать программу login.

Таким образом init читает /etc/ttys и запускает программу getty на каждом терминале указанном в конфигурационном файле. Задачей getty является постоянный мониторинг терминала на наличие попыток войти в систему. Если кто-нибудь это делает, getty запускает программу login, для того что бы проверить имя пользователя и пароль (getty считывает имя пользователя, а пароль считывает login - прим. переводчика). Если проверка прошла успешно, то login запускает указанный в профиле пользователя интерпретатор команд и помещает пользователя в его домашний каталог. Как только пользователю становится доступен интерпретатор команд, он может взаимодействовать с операционной системой. Теперь только интерпретатор команд разбирает команды пользователя, а так же обеспечивает запуск необходимых программ.

Когда пользователь покидает свой интерпретатор команд, init немедленно запускает новую копию getty, которая продолжает отслеживать попытки входа в систему.

Попробуем связать воедино весь этот процесс, глядя на вывод команды ps в свежее загруженной, установленной по-умолчанию FreeBSD 4.1. Я использовал ключи -ax для того что бы включить в вывод системных демонов:
$ ps -ax
PID TT STAT TIME COMMAND
0 ?? DLs 0:00.01 (swapper)
1 ?? ILs 0:00.16 /sbin/init --
2 ?? DL 0:00.02 (pagedaemon)
3 ?? DL 0:00.00 (vmdaemon)
4 ?? DL 0:00.02 (bufdaemon)
5 ?? DL 0:01.02 (syncer)
1056 ?? Is 0:00.00 adjkerntz -i
1187 ?? Ss 0:00.08 syslogd -s
1206 ?? Is 0:00.05 inetd -wW
1208 ?? Is 0:00.11 cron
1622 ?? Ss 0:00.02 sendmail: accepting connections on port 25 (sendmail)
1621 v0 Ss 0:00.12 -csh (csh)
1701 v0 R+ 0:00.00 ps -ax
1699 v1 Is+ 0:00.01 /usr/libexec/getty Pc ttyv1
1619 v2 Is+ 0:00.01 /usr/libexec/getty Pc ttyv2
1618 v3 Is+ 0:00.01 /usr/libexec/getty Pc ttyv3
1617 v4 Is+ 0:00.01 /usr/libexec/getty Pc ttyv4
1616 v5 Is+ 0:00.01 /usr/libexec/getty Pc ttyv5
1615 v6 Is+ 0:00.01 /usr/libexec/getty Pc ttyv6
1614 v7 Is+ 0:00.01 /usr/libexec/getty Pc ttyv7

Теперь вы должны узнать большинство этих процессов: swapper имеет PID=0, а init - PID=1. Процессы adjkerntz, syslogd, inetd, cron и sendmail были успешно запущены сценарием rc. Что бы выполнить команду ps, мне был необходим интерпретатор команд, в моем случае это был C-shell, запущенный на ttyv0. Процессы getty ожидают входа пользователей на виртуальных терминалах 1-7. На виртуальном терминале номер 8 getty не запущен, поскольку этот терминал отключен в файле /etc/ttys.

В трех частях этой статьи нам удалось достаточно глубоко забраться во внутренности FreeBSD. (Что бы разбудить интерес к собственным исследованиям - прим. переводчика).

http://www.onlamp.com/pub/a/bsd/2000/11/28/FreeBSD_Basics.html

Обновлено: 12.03.2015