М. Тим Джонс
Распространенная претензия к GNU/Linux (не считая отсутствия корректного отладчика ядра) - это время, необходимое операционной системе для загрузки. Вы можете обобщенно назвать этот процесс загрузкой , но на самом деле в развертывание системы из холодного состояния до того момента, с которого вы можете взаимодействовать с системой при помощи оболочки или оконного менеджера, вовлечено несколько независимых задач. Давайте рассмотрим загрузку Linux и процесс инициализации.
Основные стадии загрузки Linux
Несмотря на то, что загрузка Linux включает в себя много ступеней, вы можете разбить процесс на три основных шага, которые я называю BIOS , загрузка ядра и инициализация системы (см. Рисунок 1).
Рисунок 1. Временная развертка процесса загрузки Linux
BIOS
Когда вы включаете или перезагружаете компьютер, процессор компьютера начинает выполнять команды с хорошо известного адреса в BIOS (базовая система ввода/вывода, basic input/output system) . BIOS обычно записан на устройство флэш-памяти на материнской плате. На BIOS возложено много задач, например, начальное тестирование основных компонент (таких как оперативная память) и определение процедуры загрузки операционной системы. Поскольку персональные компьютеры могут иметь самые разные конфигурации, устройство, с которого грузится система, может быть одним из многих устройств, подключенных к материнской плате, таких как жесткие диски, CD-ROM или другие устройства, например, сетевой интерфейс.
Вы можете ускорить процесс определения загрузочного устройства, выбирая то устройство, с которого вы, как правило, загружаетесь (обычно это жесткий диск). Но то, на что BIOS тратит больше всего времени - это тестирование памяти. Отключение определенных этапов этого теста (например, полного тестирования памяти), безусловно, увеличит скорость загрузки, жертвуя тестом целостности системы при загрузке.
Загрузка ядра
После того, как найдено устройство, с которого производится загрузка, Linux начинает загрузку ядра. Этот процесс происходит (примерно) в два этапа -- первичная загрузка и вторичная загрузка . Первая стадия состоит из стандартного загрузчика (обнаруженного в главной загрузочной записи (master boot record, MBR) загрузочного устройства), задачей которого является загрузка вторичного загрузчика операционной системы. Первичный загрузчик находит вторичный загрузчик, используя таблицу разделов. Первичный загрузчик сканирует таблицу в поисках активного раздела; после обнаружения этого раздела он загружает вторичный загрузчик операционной системы в оперативную память и запускает его.
С помощью вторичного загрузчика образ ядра Linux и исходный образ RAM-диска (initrd
) загружаются в оперативную память. После загрузки ядро самораспаковывается в верхнюю область памяти и копирует initrd
для дальнейшей установки и использования.
|
LILO и GRUB
Первичный и вторичный загрузчики более известны как LInux LOader (LILO) или GRand Unified Bootloader (GRUB), в зависимости от того, что используется в вашей системе. | |
Процесс загрузки ядра довольно сложен, но недолог, поскольку код написан преимущественно на системном машинном языке. В конце процедуры загрузки ядра запускается процесс init
. Так как init
-- это первый процесс, созданный в системе Linux, то он является материнским для всех остальных процессов (все процессы являются дочерними процессами по отношению к init
).
Система init
Основное внимание в данной статье уделено процессу init
-- первому процессу, создаваемому после завершения процедуры загрузки ядра. Linux использует init
для запуска служб и приложений, делающих Linux удобным в использовании.
При запуске init
открывает файл под названием /etc/inittab . Это конфигурационный файл процесса init
, в котором определено, как инициализировать систему. В файле также содержится информация о том, что делать в случае нарушения энергоснабжения (если система поддерживает такой режим) и как реагировать, если произойдет нажатие клавиш Ctrl-Alt-Delete. Чтобы понять, для чего нужен этот файл, взгляните на его маленький кусочек, приведенный в Листинге 1.
Конфигурационный файл inittab
определяет несколько элементов с общим форматом id:runlevels:action:process . id -- это последовательность символов, которая уникальным образом идентифицирует элемент. runlevels определяет уровни запуска, для которых должно быть выполнено указанное действие. action задает действие, которое нужно выполнить. И, наконец, process определяет процесс, который должен быть запущен.
Листинг 1. Выдержка из файла inittab
# Уровень запуска по умолчанию
id:2:initdefault
# Загрузочный скрипт конфигурации/инициализации
si::sysinit:/etc/init.d/rcS
# Уровни запуска
l0:0:wait:/etc/init.d/rc 0
l1:1:wait:/etc/init.d/rc 1
l2:2:wait:/etc/init.d/rc 2
l3:3:wait:/etc/init.d/rc 3
l4:4:wait:/etc/init.d/rc 4
l5:5:wait:/etc/init.d/rc 5
l6:6:wait:/etc/init.d/rc 6
z6:6:respawn:/sbin/sulogin
# Как реагировать на ctrl-alt-del
ca:12345:ctrlaltdel:/sbin/shutdown -t1 -a -r now
|
|
Init и telinit
С процессом init можно взаимодействовать, используя утилиту telinit (которая связана с утилитой init ). Например, если вы находитесь в многопользовательском режиме (уровень запуска 2) и хотите перейти в однопользовательский режим, просто используйте команду telinit 1 (в режиме привилегированного пользователя) | |
После того как init
загрузит /etc/inittab, система будет переведена на уровень запуска, определяемый действием initdefault
. Как видно из Листинга 1, это уровень запуска 2 (runlevel 2). Рассматривайте уровень запуска как состояние системы. Например, уровень запуска 0 определяет остановку системы, уровень запуска 1 -- это однопользовательский режим. Уровни запуска от 2 до 5 -- это многопользовательские режимы, а уровень запуска 6 указывает на перезагрузку. (Заметим, что некоторые дистрибутивы отличаются определением уровней запуска). Другими словами, уровень запуска -- это способ определить, какие процессы могут быть запущены (процессы, которые определяют состояние системы).
Замечание: для того чтобы узнать текущий уровень запуска вашей системы, используйте команду runlevel
.
Как показано в Листинге 1, initdefault
задает по умолчанию второй уровень запуска init
(многопользовательский режим). После того как определен начальный уровень запуска, для развертывания системы запускается сценарий rc
с аргументом 2
(уровень запуска). Этот скрипт активизирует различные служебные и прикладные скрипты для запуска или остановки определенных элементов. В данном случае файлы находятся в /etc/rc2.d/. Например, для запуска приложения MySQL (при загрузке системы) надо было бы выполнить /etc/rc2.d/S20mysql start
. При выключении системы тот же самый набор сценариев выполнялся бы с аргументом stop
.
|
Изменение процесса init
Изменить процесс инициализации довольно просто. Чтобы во время загрузки управлять вручную процессом инициализации системы, определите новый процесс, который должен быть запущен (используя LILO или GRUB). Для того чтобы запустить этот процесс вместо init , принятого по умолчанию, задайте в командной строке загрузки ядра init=/sbin/mynewinit . Вы можете увидеть это в исходнике ядра в ./linux/init/main.c. Если вы добавили команду init в командную строку загрузки ядра, то она и будет использована. В противном случае ядро попытается запустить один из четырех альтернативных процессов (первый из которых /sbin/init). | |
В конечном счете, для запуска различных необходимых служб многие скрипты выполняются последовательно (что, как правило, вы можете видеть на экране загрузки Linux). Даже когда службы не связаны друг с другом, они все равно стартуют по очереди. В результате процесс может занять продолжительное время (особенно в большой системе с большим количеством служб).
Очевидное решение этой проблемы -- избавиться от последовательной архитектуры процесса init
и заменить его чем-то, что выполняется параллельно. Использование такой модели не ограничивается лишь многопроцессорными системами. Одно из решений, основанное на этой идее -- это socket striping , или использование двух или более сокетов для параллельной передачи данных. Также, для увеличения производительности ввода/вывода, в системах RAID-массивов (массивов независимых дисков с избыточностью) данные расщепляются между дисками (обычно параллельно).
Замена демона init
|
Простой способ оптимизации init
Самый простой способ оптимизировать процесс init -- это отключить необязательные службы. Например, если вы запускаете обычную машину (а не сервер), вы можете отключить такие службы как apache, sendmail и mysql, сократив таким образом процесс init . | |
Так как традиционный процесс init
(sysvinit
) является последовательным процессом, то именно эта часть системы может быть оптимизирована. На самом деле, вы можете использовать один из нескольких методов оптимизации процесса init
. Давайте рассмотрим некоторые из этих методов и то, как именно с их помощью можно решить проблему. Первые два метода основаны на зависимостях (то есть они используют зависимости для распараллеливания), а третий метод основан на обработке событий системы (то есть процессы зависят от событий, которые используются для задания времени запуска и остановки процессов).
Initng
Первый вариант, initng
( init
следующего поколения), -- это полная замена init
, которая для более быстрого выполнения процесса инициализации запускает процессы асинхронно. На момент написания данной статьи initng
являлся бета-продуктом, созданным Jimmy Wennlund.
Основная идея, лежащая в основе initng
, -- запуск служб при условии удовлетворения их зависимостей. В результате получается лучшее соотношение между нагрузкой на CPU и систему ввода/вывода. Пока один скрипт грузится с диска или ожидает запуска аппаратного обеспечения, другой скрипт может выполняться и запустить другую службу.
Как работает initng
Основанный на зависимостях initng
использует свой собственный набор инициализирующих скриптов, которые определяют зависимости демонов и служб. Пример приведен в Листинге 2. Этот скрипт описывает службу, которая должна быть запущена на данном уровне запуска. Ключ need
задает для этого сервиса две зависимости -- для system/initial и net/all. Эти службы должны быть доступны перед тем, как будет запущена служба system/my_service. Если эти службы доступны, в игру вступает ключ exec
. Ключевое слово exec
(с опцией start
) определяет, как запустить сервис, используя любые доступные для этого сервиса опции. Если службу следует остановить, ключевое слово exec
используется с опцией stop
.
Листинг 2. Описание службы для initng
service system/my_service {
need = system/initial net/all;
exec start = /sbin/my_service --start --option;
exec stop = /sbin/my_service --stop --option;
}
|
Как показано в Листинге 2, путем задания служб вы можете построить целую систему. Службы, не имеющие зависимостей, могут быть запущены сразу же (и параллельно), тогда как службы, для которых зависимости определены, должны запускаться аккуратно. Вы можете представить initng
как целевую систему. Целями является запуск служб. Явного планирования событий здесь не существует; напротив, зависимости просто определяют последовательность инициализации служб, с распараллеливанием, заложенным в процессе.
Использование initng
Установить пакет initng
в стандартной комплектации довольно просто. Для систем, использующих нестандартную комплектацию (не отраженную в конфигурации по умолчанию), может потребоваться дополнительная сборка.
Для стандартной установки требуется дистрибутивы initng
(исходный или бинарный) и ifiles. Вы можете собрать дистрибутив initng с помощью ./configure, make
и make install
. С помощью cmake
вы должны собрать ifiles (которые являются скриптами). В зависимости от ваших системных требований, может потребоваться создать новые определения служб/демонов (хотя, скорее всего, кто-нибудь из поклонников initng
это уже сделал). Затем, для того чтобы указать на новый /sbin/initng, вам следует изменить конфигурацию LILO или GRUB.
Для управления initng
используйте ngc
(по аналогии c telinit
для традиционного init
). Синтаксис кое-где отличается, но возможности остались те же самые.
Upstart
Еще один вариант замены init
, upstart
, использует подход, немного отличный от того, с которым вы только что познакомились, осваивая initng
. Upstart
-- это ориентированная на события замена init
, то есть запуск и остановка служб основана на обработке событий. Upstart
был разработан для дистрибутива Ubuntu Скотом Джеймсом Ремнантом (Scott James Remnan), но годится в качестве полной замены init
в любом дистрибутиве Linux.
Как работает upstart
Upstart
требует замены инициализирующих скриптов для поддержки событийно-ориентированного режима действий. Upstart
поддерживает свой собственный процесс init
, который запускается при старте системы (как и при всех других методах). Сначала init
генерирует событие startup (запуск) -- одно из двух основных событий. Событие startup генерируется процессом init
при старте системы, а событие shutdown -- при выключении системы. Другие ключевые события -- это ctrlaltdel , которое указывает, на то, что вы нажали клавиши Ctrl-Alt-Delete, и kbdrequest , которое генерируется, если вы нажали комбинацию Alt-Up (клавиши со стрелкой вверх).
Для других целей вы можете создавать новые события. Например, вы можете создать произвольное событие под название myevent и сигнализировать о его получении при помощи команды echo
. Вот коротенький пример выполняемого действия:
on myevent
exec echo myevent received
console output
|
В этом листинге определенная задача ожидает получения события myevent . Затем будет выполнено заданное действие (выведен текст на консоль). При наличии данного файла в составе upstart
(/etc/event.d) вы можете его запустить, используя утилиту initctl
:
Скрипты upstart
работают так же, как и обычные rc init
файлы, за исключением того, что они действуют самостоятельно, основываясь на асинхронных событиях. В Листинге 3 приведен простой пример скрипта, который принимает три события: startup -- для запуска задачи, а shutdown и runlevel-3 -- для остановки выполнения задачи. Оболочка выполняет задачу, описанную в script
(используя опцию -e
для прерывания выполнения скрипта в случае ошибки).
Листинг 3. Упрощенный скрипт upstart для скрипта sysvinit rc 2
start on startup
stop on shutdown
stop on runlevel-3
script
set $(runlevel --set 2 // true)
exec /etc/init.d/rc 2
end script
|
Утилита initctl
предлагает такой же набор функциональных возможностей, что и telinit
, но с некоторыми дополнительными особенностями, присущими upstart
. Как вы видели выше, для генерации события для upstart
, вы можете использовать initctl
с опцией emit
. Опция list
позволит вам быть в курсе действий системы, распознавая состояние задач. Вы увидите, какие операции в данный момент находятся в режиме ожидания, а какие активны. Утилита initctl
может так же отображать отладочные события.
Upstart
-- это интересная альтернатива init
, обладающая некоторыми явными преимуществами. Отпадает реальная необходимость в уровнях запуска, поскольку загрузка системы будет определяться доступным аппаратным обеспечением. Иными словами: нет устройства -- не запустится и процесс, требующий его. Upstart
так же может управлять устройствами "горячей замены". Например, если вы подключили сетевую карту PCMCI через некоторое время после загрузки системы, то будет генерироваться событие network-interface-added . Это событие запустит процесс конфигурирования интерфейса, используя протокол DHCP (Dynamic Host Configuration Protocol ), генерируя событие network-interface-up . Когда в новый интерфейс будет добавлен маршрут по умолчанию, то результатом будет событие default-route-up . С этого момента задачи, требующие использования сетевого интерфейса (такие как почтовый сервер или Web-сервер), будут запускаться автоматически (и останавливаться в случае исчезновения интерфейса).
Использование upstart
Сборка и инсталляция upstart
не представляют сложности и проходят по стандартному шаблону: configure
, make
и make install
. Upstart
предоставляет набор демонстрационных задач, совместимых с обычными уровнями запуска init
. Подобно initng
, новые приложения должны иметь свои собственные задачи, учитывающие их требования (с возможностью добавления новых событий). В любом случае развертывание новой системы init
может быть рискованным. Но преимущества, предоставляемые upstart
, безусловно перевешивают эти риски и дополнительные усилия, которые могут потребоваться.
Как показано выше, утилита initctl
обладает теми же возможностями, что и telinit
. Однако initctl
также предоставляет дополнительные возможности для трассирования и отладки.
Другие варианты
Рассмотренные в этой статье initng
и upstart
-- это далеко не единственные варианты. init
так же может быть заменено, например, на runit
, pardus
, minit
и einit
. Все они поддерживаются и развиваются Linux-сообществом. На настоящий момент upstart
-- возможно лучший из вариантов, поскольку адаптирован для замены init
в популярном дистрибутиве Ubuntu.
Мониторинг производительности init при помощи bootchart
После того как вы изменили процесс загрузки системы, полезно понять, что именно изменилось и как это отражается на полном времени загрузки. Зига Маковец (Ziga Mahkovec) создал очень полезный инструмент под названием bootchart
, предназначенный для визуализации процесса загрузки. Он состоит из нескольких элементов, включая утилиту регистрации данных и утилиту визуализации.
Утилита регистрации данных (bootchartd
) запускается вместо процесса init
(как правило, это задается в файлах grub или lilo.conf). После инициализации bootchartd
возвращает управление процессу init
(обычно /sbin/init). Bootchartd
-- это, по сути, программа для сбора информации, которая просто периодически сканирует окружение (по умолчанию раз в 200 мс). Под сканированием окружения я подразумеваю, что она считывает текущую статистику CPU, время ввода/вывода и простоя, а так же информацию об использовании диска и о каждом активном процессе (используя файловую систему proc
). Эти данные хранятся во временном файле (var/log/bootchart.tgz) для последующей обработки.
После этого bootchart
использует инструмент дальнейшей обработки для преобразования исходных данных в диаграмму загрузки. Этот процесс может выполняться локально, при помощи Java™ приложения (часть дистрибутива bootchart
), однако более простой способ -- использовать Web-форму, находящуюся на домашней странице bootchart
. В качестве примера на Рисунке 2 приведена часть диаграммы загрузки. Заметим, что эти диаграммы, как правило, довольно большие (в зависимости от запускаемых служб и приложений).
Рисунок 2. Фрагмент диаграммы загрузки, созданной bootchartd
Резюме
В соответствии с общей концепцией Linux, существует множество решений и большие возможности для выбора при оптимизации времени загрузки. Вариантов масса: от решений, основанных на зависимостях, подобных initng
, до событийно-ориентированных решений, подобных upstart
, и среди них существует ваш вариант оптимизации, который подойдет именно для ваших нужд. Используя пакет bootchart
, вы сможете понять, на что именно ваша система тратит свое загрузочное время, и еще эффективнее оптимизировать процесс загрузки.
Ссылки по теме