Распараллеливайте приложения для ускорения загрузки Linux

Работа с initng и upstart

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

М. Тим Джонс, инженер-консультант, Emulex

M. Тим Джонс (M. Tim Jones) является архитектором встраиваимого программного обеспечения и автором работ: Программирование Приложений под GNU/Linux, Программирование AI-приложений и Использование BSD-сокетов в различных языках программирования. Он имеет опыт разработки процессоров для геостационарных космических летательных аппаратов, а также разработки архитектуры встраиваемых систем и сетевых протоколов. Сейчас Тим работает инженером-консультантом в корпорации Эмулекс (Emulex Corp.) в г.Лонгмонт, Колорадо.



20.07.2007

Распространенная претензия к GNU/Linux (не считая отсутствия корректного отладчика ядра) - это время, необходимое операционной системе для загрузки. Вы можете обобщенно назвать этот процесс загрузкой, но на самом деле в развертывание системы из холодного состояния до того момента, с которого вы можете взаимодействовать с системой при помощи оболочки или оконного менеджера, вовлечено несколько независимых задач. Давайте рассмотрим загрузку Linux и процесс инициализации.

Основные стадии загрузки Linux

Несмотря на то, что загрузка Linux включает в себя много ступеней, вы можете разбить процесс на три основных шага, которые я называю BIOS, загрузка ядра и инициализация системы (см. Рисунок 1).

Рисунок 1. Временная развертка процесса загрузки Linux
процесс загрузки 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:

initctl emit myevent

Скрипты 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, вы сможете понять, на что именно ваша система тратит свое загрузочное время, и еще эффективнее оптимизировать процесс загрузки.

Ресурсы

Научиться

Получить продукты и технологии

  • Следующее поколение системы init (initng) -- основанный на зависимостях вариант замены системы init.
  • Система upstart для Ubuntu -- событийно-ориентированный вариант замены системы init.
  • Bootchart -- инструмент для анализа эффективности и визуализации процесса загрузки. Собирает данные о производительности во время процесса инициализации системы и затем обрабатывает их разверткой по времени.
  • Пакет einit -- еще один вариант скрипта инициализации, использующего Extensible Markup Language (XML) для конфигурационных файлов.
  • Еще одна интересная схема для распараллеливания init -- это Pardus. Этот метод не только избавляется от последовательной структуры загрузки Linux, но также добавляет гибкости, используя язык Python.
  • Пакет runit заменяет схему init супервизором служб.
  • Пакет minit -- маленькая, но полная версия системы init. Вы так же можете исследовать ресурс sysvinit. Пакет minit -- маленькая, но полная версия системы init. Вы так же можете исследовать ресурс sysvinit.
  • Используйте тестовое программное обеспечение IBM, IBM trial software, доступное на developerWorks, и создайте свой следующий проект разработки в Linux.

Обсудить

Комментарии

developerWorks: Войти

Обязательные поля отмечены звездочкой (*).


Нужен IBM ID?
Забыли Ваш IBM ID?


Забыли Ваш пароль?
Изменить пароль

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


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

Вся введенная информация защищена.

Выберите имя, которое будет отображаться на экране



При первом входе в developerWorks для Вас будет создан профиль и Вам нужно будет выбрать Отображаемое имя. Оно будет выводиться рядом с контентом, опубликованным Вами в developerWorks.

Отображаемое имя должно иметь длину от 3 символов до 31 символа. Ваше Имя в системе должно быть уникальным. В качестве имени по соображениям приватности нельзя использовать контактный e-mail.

Обязательные поля отмечены звездочкой (*).

(Отображаемое имя должно иметь длину от 3 символов до 31 символа.)

Нажимая Отправить, Вы принимаете Условия использования developerWorks.

 


Вся введенная информация защищена.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=40
Zone=Linux, Open source
ArticleID=241922
ArticleTitle=Распараллеливайте приложения для ускорения загрузки Linux
publish-date=07202007