Рейтинг@Mail.ru

Наши друзья и партнеры

UnixForum



Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

На главную -> MyLDP -> Тематический каталог -> Процесс начальной загрузки системы

Восход солнца вручную

Игорь Облаков

15.02.2001

Один из интереснейших вопросов в ОС - старт системы. Как именно происходит старт? Как оказалось здесь достаточно много вопросов и трудностей. Более точно остановимся на процессе init и группе rc-файлов. Для экспериментов использован диск Red Hat Linux 6.2.

Основной управляющий файл для процесса init - это /etc/inittab. Приведем его с незначительными исключениями и модификациями:

# inittab Файл описывает, как перейти
#            к конкретному уровню
# Author: Miquel van Smoorenburg,
#       <miquels@drinkel.nl.mugnet.org>
# Modified for RHS Linux by Marc Ewing
# and Donnie Barnes
# Уровни выполнения для RHS:
# 0 - останов (не устанавливайте
# initdefault в это значение)
# 1 - Single user mode - однопользова-
# тельский режим
# 2 - Multiuser, многопользовательский без 
# NFS (Аналог 3, если не используете сеть)
# 3 - Полностью многопользовательский
# режим
# 4 - не используется (точнее, не имеет
# смысла по умолчанию)
# 5 - X11 - графический режим со
# стартом оболочки
# 6 - reboot - перезагрузка (не устанавли-
# вайте initdefault в это значение)
id:3:initdefault:
# Инициализация системы
si::sysinit:/etc/rc.d/rc.sysinit
l0:0:wait:/etc/rc.d/rc 0
:
l6:6:wait:/etc/rc.d/rc 6
# Что выполняется на каждом уровне
ud::once:/sbin/update
# Trap CTRL-ALT-DELETE
ca::ctrlaltdel:/sbin/shutdown -t3 -r now
# ИБП подключен и работает корректно
pf::powerfail:/sbin/shutdown -f -h +2
"Power Failure; System Shutting Down>
# Выполнение gettys на стандартных
# уровнях
1:2345:respawn:/sbin/mingetty tty1
:
6:2345:respawn:/sbin/mingetty tty6
# Выполнение xdm на уровне 5,
# xdm - отдельная служба
x:5:respawn:/etc/X11/prefdm -nodaemon

Формат этого файла практически не отличается от других вариантов Unix. Каждый элемент занимает 4 поля: имя, уровни старта, вариант обработки (ждать окончания - wait, рестартовать после окончания - respawn, установить стандартный уровень для старта - initdefault и т.д.), непосредственно выполняемая на данном уровне команда. Из отличий от других вариантов Unix стоит отметить наличие опции ctrlaltdel.

Что произойдет, если в приведенном файле поменять initdefault значение с 3 на 5? Это будет переход от алфавитно-цифрового входа к графическому входу в систему и наоборот. Именно это значение меняет linuxconf при выборе соответствующей опции.

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

Менее известен другой файл, /etc/initscript. Разработчики отводили ему роль, аналогичную /etc/security/limits.conf, который позволяет задать лимиты для всех, группы или конкретного пользователя. /etc/initscript же выполняется перед стартом каждого из процессов в /etc/inittab. При этом собственно выполняемая команда передается как четвертый параметр. В этом файле можно задать там любые специфические действия, в частности, установку тех же самых лимитов, umask или еще что-нибудь, но уже не для пользователя, а для каждой команды из /etc/inittab файла. Обратите внимание на пример (см. man initscript), дабы не попасть в дурную ситуацию. Другой пример:

ulimit -c 0 ##  core-dump прощай
eval exec "$4"

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

Голосовать будем списком

Остановимся на работе rc-файлов. Скажем, для перехода на третий уровень при старте надо выполнить последовательно /etc/rc.d/rc.sysinit, <etc/rc.d/rc 3>, /sbin/update, а затем запустить /sbin/mingetty на всех эмуляторах консоли. Каковы их функции? Файл rc.sysinit выполняет ряд начальных действий, независимых от уровня. Вот точный порядок этих действий (естественно, часть из них выполняется опционно):

  • вывод баннера;
  • чтение конфигурационных данных из /etc/sysconfig/network;
  • установка системных параметров из /etc/sysctl.conf;
  • установка часов по параметрам из /etc/sysconfig/clock;
  • установка параметров клавиатуры консоли с помощью loadkeys в соответствии с /etc/sysconfig/console/default.kmap или /etc/sysconfig/keyboard;
  • загрузка системного шрифта с именем из /etc/sysconfig/i18n и файлом с расширением .pcf.gz или .pcf из каталогов /etc/sysconfig/console или /usr/lib/kbd/consolefonts;
  • активация области подкачки;
  • установка имени хоста;
  • установка имени домена NIS;
  • запуск fsck (см. ниже) для корневой системы (если в процессе fsck обнаружены серьезные проблемы, то выполняется немедленная перезагрузка);
  • старт PNP-устройств в соответствии с /etc/isapnp.conf;
  • перемонтирование корневой файловой системы в режиме записи (поскольку вопрос о том, что делать с системой, доступной только на чтение, типичен, то выполняющую это действие команду стоит привести явно: <mount -n -o remount,rw />);
  • перестроение таблицы монтирования /etc/mtab;
  • проверка квот для корневой файловой системы;
  • проверка необходимости загрузки модулей, нахождение взаимозависимостей, загрузка их и конфигурирование;
  • дополнительная подгрузка из /etc/rc.d/rc.modules;
  • подключение RAID-устройств;
  • запуск fsck для других систем;
  • монтирование локальных файловых систем;
  • запуск ряда конфигурирующих процедур (выполняется при наличии файла /.unconfigured);
  • включение механизма квот;
  • удаление флаговых файлов загрузки;
  • освобождение каталогов /var/lock и /var/run от мусора;
  • очистка файлов /var/run/utmp и /var/run/utmpx;
  • удаление lock-файлов из /tmp;
  • включение подкачки;
  • инициализация последовательных устройств через /etc/rc.d/rc.serial;
  • подгрузка модулей для SCSI-накопителя на магнитных лентах;
  • установка однопроцессорного (UP) или многопроцессорного (SMP) режима и регенерация в соответствии с этим файла /boot/kernel.h;
  • установка корректной ссылки /boot/System.map;
  • проверка использования интерактивного режима (создание файла /var/run/confirm).

Запуск fsck может быть принудительно отключен с целью уменьшения времени рестарта при наличии файла /fastboot, а также включен при наличии /forcefsck; можно <заказать> дополнительные опции (/fsckoptions). Появление этих файлов может быть результатом выполнения shutdown с соответствующими ключами: -f (пропуск fsck при старте); -F (принудительный fsck при старте).

Обратили ли вы внимание на то, что <включение подкачки> присутствует два раза? В действительности, дважды делается попытка выполнить одну и ту же команду: swapon -a. Зачем? Из комментариев можно вычитать, что первая команда присоединяет к области подкачки разделы, а вторая - файлы. Так как команда одна и та же, мы, конечно, не поверим комментариям и попробуем проверить их опытным путем.

Первым делом создаем файл подкачки на отдельно смонтированной системе:

root@localhost /root]# dd if=/dev/
zero bs=1k count=2048 of=/r6/myswap
2048+0 блоков прочитано
2048+0 блоков записано
[root@localhost /root]# mkswap 
/r6/myswap 2048
Setting up swapspace version 0, 
size = 2093056 bytes
[root@localhost /root]#
Добавляем строчку в /etc/fstab:
/r6/myswap	swap	swap
	defaults	0 0

Проверяем результат после перезагрузки, запрашиваем текущее использование устройств подкачки:

[root@localhost /root]# swapon -s
Filename	Type	Size
	Used	Priority
/dev/hda5	partition
	104380	0	-1
/r6/myswap	file	2044
	0	-2
[root@localhost /root]#

Если посмотреть информацию о перезагрузке, обнаружится, что, конечно, swapon попытается подключить /r6/myswap два раза. Первый раз (/r6 еще не смонтирована) будет сообщение об ошибке (и даже FAILED), хотя подключение раздела происходит. Второй раз все проходит нормально (благо еще и диагностика в /dev/null). Обратите внимание на то, что файл для подкачки должен существовать (как-то мне попалась команда mkswap, которая его заполняла, но не создавала) и при этом не содержать <дыр> (с чего бы это?).

Sysctl позволяет зафиксировать ряд параметров и обеспечить (через /etc/sysctl.conf) их установку после перезагрузки. Вот как выглядел /etc/sysctl.conf сразу после инсталяции:

# Disables packet forwarding
net.ipv4.ip_forward = 0
# Enables source route verification
net.ipv4.conf.all.rp_filter = 1
# Disables automatic defragmentation
net.ipv4.ip_always_defrag = 0
# Disables the magic-sysrq key
kernel.sysrq = 0

Замуровали, демоны

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

Разрешение на выполнение нужного набора скриптов можно задавать интерактивно. Для этого достаточно обратить внимание на заголовочек, указывающий на то, что нажатие I можно использовать для управления процессом старта. Реально после нажатия появляется файл /var/run/confirm, который и служит флагом интерактивного режима загрузки. Для перхода на уровень выполняются все файлы в соответствующем подкаталоге rc$runlevel.d. Имена этих файлов, как правило, начинаются с большой (это существенно) буквы S или K. Если первая буква иная, то файл просто не участвует в процессе межуровневого перехода. Файлы на S отвечают за старт службы, а файлы на K - за остановку какой-либо службы. Обратите внимание, что при переходе на уровень 3 прямо со старта, мы никак не используем файлы из rc2.d - только rc3.d. Этот <кумулятивный> вариант отличается от того, который принят, например, в Unix System V. Там при старте, последовательно выполняются стартовые файлы всех промежуточных каталогов, в частности, rc1.d, rc2.d и т.п.

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

Посмотрим порядок выполнения командных файлов более подробно. Сначала выполняется останов <лишних> служб (первая буква K) и только затем старт (соответственно S). Ясно, что одна и та же служба, как правило, нужна на нескольких уровнях. Было бы обидно при каждом переходе (например, с уровня 2 на уровень 3) останавливать и рестартовать ее. От этого надо как-то защищаться. Для System V это получается автоматически, так как старт и останов службы входят в набор конкретного уровня. В Linux же требуются специальные проверки. Роль флагов выполняют файлы в каталоге /var/lock/subsys/${subsys} или /var/lock/ subsys/${subsys}.init, где subsys - имя подсистемы. Если файлов нет, то данная подсистема считается не запущенной (и, соответственно, запуск S-файла имеет смысл), а если есть - запущенной (и, соответственно, запуск K-файла имеет смысл). Поэтому останов службы по команде kill не будет замечен этим механизмом. Если посмотреть подробнее, то окажется, что все файлы в каталогах rcn.d являются символьными ссылками на соответствующие файлы из /etc/rc.d/init.d. Более того, и стартовые файлы, и файлы остановки подсистемы ссылаются на один файл. Отличие обеспечивается за счет передачи параметра start или stop. В некоторых вариантах Unix можно явно использовать тот факт, что стартстопный файл запускается два раза, но лучше этого не делать и честно разбираться с параметрами. Иногда для выдачи красивого листинга старта/останова системы на экран в Unix используется большее количество опций, например, что-либо в стиле start_msg/stop_msg. Если у вас есть <плохие> привычки, то отладка скриптов при переносе может отнять массу времени.

Для управления набором доступных служб можно использовать программу конфигурирования linuxconf. Что означает разрешить или запретить использование какой-либо службы с точки зрения всяких там файлов? Запрещение старта какого-либо сервиса просто приводит к удалению соответствующей ссылки из каталогов rcn.d, а разрешение - к появлению. А как же обеспечивается корректная установка номера у соответствующей ссылки? Мы же должны обеспечить правильный порядок выполнения старта для подсистем. Неужели linuxconf обязан знать про роль всех служб и что-то интеллектуально вычислять?

Все устроено гораздо проще. Обратите внимание на часть заголовка файла /etc/rc.d/init.d/atd:

# Запуск демона
# chkconfig: 345 40 60
# processname: atd

Что-то неуловимо знакомое мы видим в строке с chkconfig. 345 - явно похоже на список уровней. 40 и 60 смахивают на номера для S и K файлов. Сумма номеров 100 достаточно типична (но, естественно, не обязательна). Таким образом, проще добиться того, чтобы порядок останова подсистем был почти в точности обратен стартовому. Попробуйте поменять 40 на 41 и отменить (разрешить) какую-либо службу. Стартовый файл поменяет свое имя. Description в данном файле четко отвечает за комментарий, который linuxconf выдает вам на экран для объяснения роли данной службы.

Заглянем в сам файл /etc/rc.d/init.d/atd. Мы сразу видим, что реально возможных опций больше, чем стандартные start и stop. Имеются еще restart, reload и status. Такая ситуация тоже достаточно типична. Старт, останов и проверка состояния демона выполняется рядом функций типа daemon, killproc, status. Если все происходит по плану, то создается (удаляется) файл /var/lock/subsys/atd.

Рассмотрим теперь подробнее вышеуказанные процедуры daemon, killproc, status. Они определяются (вместе с рядом других) в /etc/rc.d/init.d/functions (а тот пользуется определениями из /etc/sysconfig/init). Уже по названию ясно для чего они предназначены - старт, останов и проверка статуса демона.

Функция daemon обеспечивает старт подсистемы (службы, демона и т.п.). При этом можно учесть особенности поведения демона (требуемые ресурсы процессора и т.п.) и заказать их в виде nice-значения (+/-nicevalue) и конкретного пользователя (-user).

Перед стартом демона всегда делается проверка наличия его в системе. Поскольку демон может вызываться из скрипта, то для проверки текущего состояния можно заказать точное имя (-check name). Так как появление dump-а демона может приводить к проблемам с security, то все демоны запускаются в режиме без core-dump. Сам старт выполняется командой initlog $INITLOG_ARGS -c <$*>, которая и стартует демона и записывает информацию об этом в лог.

Останов демона выполняется процедурой killproc. Данная функция предполагает один аргумент в виде имени демона и, возможно, еще один для указания сигнала, который и будет послан демону для окончания. SIGKILL очень часто может быть не желателен, так как могут возникнуть разнообразные проблемы типа блокирования ресурсов. Поэтому, если сигнал назначен, то используется только он, если не назначен, то сначала SIGTERM и, если данный сигнал не позволит упокоить демон за разумное время, то посылается SIGKILL. Последним действием удаляется флаговый файл /var/run/подсистема.pid.

Наконец status позволяет проверить текущее состояние работы подсистемы. Если процесс работает, то просто сообщается данный факт. Если не работает, то делается проверка на наличие флаговых файлов (/var/run/подсистема.pid и /var/lock/subsys/подсистема), которые должны блокировать повторный запуск. Естественно наличие их диагностируется как ошибка.

Кроме этого, в файле functions есть объявления action - просто выполнить действие (без каких-либо проверок) и зафиксировать его в логе; confirm - генерируемый запрос на подтверждение продолжения работы; другие макросы. Action необходимы в тех случаях, когда работа службы обеспечивается непосредственно ядром системы, т.е. демон для работы не нужен. Соответственно к этой группе можно отнести конфигурирование сетевой подсистемы (например, ifconfig не должен работать непрерывно), монтирование nfs-систем и т.п.

Попробуем теперь добавить новый демон или новое действие в систему. Для этого скопируем /etc/rc.d/init.d/atd в новый файл. Что можно (at/atd), заменим на sleep. Что будет, если в строчке с демоном поставить daemon sleep 120? Потребуем у linuxconf подцепить службу. Да, не долго готов конфигуратор ждать запуска демона. 15 секунд ожидания - уже побег. Другими словами, демон обязан завершиться за разумное время... Но как в таком случае он будет выполнять свои демонические функции? На самом деле, нам требуется ограниченность по времени старта демона. Демон стартует, что-то там проверяет и, выполнив fork-exit, уходит в тину. Если бы у нас имелся файл mysleep с содержимым наподобие sleep 120 &, то старт:

daemon -check sleep /etc/
rc.d/init.d/mysleep

был бы вполне корректным (демон есть, но сидит в тине). В принципе старт демонов в различных вариантах Unix организуется разными способами.

Снова к вопросу об анатомии вампиров

Как оказалось, часть читателей слишком буквально восприняла предыдущую статью о вампирах (см. <Открытые системы>, 2000, ©12). В ней, в действительности, не шло речи о поимке крэкеров. Просто данная ситуация была использована для продолжения разговора о возможностях команд shell, потенциальных проблемах и т.д. Гоняться за крэкерами с командами ls или find занятие любопытное, но не очень эффективное. Опыт, как вы видите не отрицательный - что-то сделать можно (особенно, если крэкер-душка тексты оставляет), но и не положительный - всех фокусов вы не найдете. Если ваша система после крэкера похожа на фаршированную утку и готова быть съеденной любой бездомной собакой, то далеко вы не уедете. Рано или поздно вы поменяете один из конфигурационных файлов (а /etc/passwd и /etc/inetd.conf, например, были изменены администратором после <добавочки>) и дверка будет зафиксирована не только в файле, но, возможно, и в вашем сознании - так было. Можно, конечно, иметь диск с полной копией системы и заниматься сравнением, но это может быть перебором по ресурсам (не путать с backup, который абсолютно необходим). Как правило, используется хранение списка файлов (дабы проверить появление/исчезновение файла), прав доступа и <контрольных сумм> того или иного типа.

Заметим, что для простой проверки можно использовать базу rpm. Например, у нас возникло подозрение, что подменили login (как мы помним, а если не помним, то используем which, - /bin/login):

[root@localhost rpm]# rpm
 -q -f /bin/login
util-linux-2.10f-7
[root@localhost rpm]# rpm
 -V util-linux-2.10f-7
S.5....T c /etc/pam.d/login
[root@localhost rpm]#

Первая из команд позволяет определить пакет, из которого пришел /bin/login, вторая требует проверки всех файлов пакета. В данном случае обнаружена только модификация конфигурационного файла /etc/pam.d/login (S - размер, 5 - контрольная сумма MD5, T - время модификации). Аналогично, конечно, можно попробовать сказать:

[root@localhost rpm]# rpm -V -a
S.5....T c /etc/services
S.5....T c /etc/localtime
.......T c /etc/nsswitch.conf

Это займет... м-м, некоторое время. Ясно, что в основном это будут конфигурационные файлы. При помощи подкоманд типа -l, -dump и т.п. можно посмотреть установочные характеристики файлов пакета. Rpm проверяет только то, что было установлено, но не ваше собственное творчество. Есть и более гибкие средства.

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

Можно использовать и какое-либо средство собственной разработки. В мою копилку недавно прислали соответствующий экземпляр скрипта специально для проверки контрольной суммы на компьютере-источнике и после копирования на новом компьютере. Скрипт прислан, будто специально, для рубрики <найдите 10 ошибок> и, похоже, составлялся под лозунгом <как не надо писать на bash>:

#!/bin/sh
# lspr subDirSrc
Usage () {
   echo <\nUsage: cks_cd directory\n>
   exit 1
}
if [ $# = 0 ] ; then Usage ; fi
###########################
#tdir=/cdrom
tdir=$1
prot=check_summa
if [ ! -d ${tdir} ] ; then
   echo Directory ${tdir} does not
 exist
   Usage
fi
if [ -f $prot ]
then
 rm $prot
fi 
for file in `find ${tdir} -name <*>`
do 
 if [ -f ${file} ]
 then
 cksum ${file} | sed -e < s/[^0-9].
*//g> >> $prot
fi
done
cksum $prot | sed -e < s/[^0-9].*//g>
##rm $prot

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

Читателю предыдущей статьи, обратившему внимание на проверяемый диапазон дат, должно быть совершенно очевидно, что система очень давно <под колпаком>. Соответственно и надежда на записи в регистрационных файлах мала. Правда, с другой стороны, в сети принцип возвращения преступника на место преступления действует значительно лучше, чем в жизни. Поэтому после переустановки (если, конечно, вы не можете сделать более точное определение повреждений при помощи контрольных сумм и т.п.), кроме более корректного конфигурирования, главное - не забыть повесить звоночек на наиболее полюбившиеся ему службы. Можно их даже <приоткрыть>, дабы не слишком огорчать. И... злоумышленник обязательно придет на чашечку чая. Тут, конечно, не без тонкостей типа подмены адресов или адресации через проходной компьютер, что может привести к шумному разбирательству, но многое проверить вполне можно.

Присылайте свои страшные истории по адресу oblakov@bigfoot.com.


Журнал "Открытые системы", #02, 2001 год // Издательство "Открытые системы" (www.osp.ru)
Постоянный адрес статьи: http://www.osp.ru/os/2001/02/073.htm

Поделиться: