Процесс загрузки Linux


Хотел бы навести краткое описание уровней загрузки (runlevels) в Linux. А именно на уровнях загрузки дистрибутива Ubuntu. Но для начала пройдемся по самому процессу загрузки системы.

BIOS vs UEFI

Первым делом подгружаеться BIOS, который выполняет базовое тестирование при включении питания POST(power-on self test), т.е. проверка всех подключенных аппаратных средств. Потом выполняется нумерация и инициализации локальных устройств. После этих действий BIOS передает управление MBR.
В современных системах на смену BIOS пришел UEFI (Unified Extensible Firmware Interface). Это программный интерфейс, который работает между операционной системой и прошивкой платформы, что позволяет заменить BIOS. Он же в свою очередь использует GPT (GUID Partition Table) вместо таблиц MBR. Если размер диска превышает 2.2 TB, то без GPT уже не обойтись, потому что MBR поддерживает диски только до 2-х терабайтов.

MBR vs GPT

Типичная загрузка системы производиться HDD, на котором в MBR содержится первичный начальный загрузчик. MBR представляет собой сектор размером 512 байт, который располагается в первом секторе диска (сектор 1 цилиндра 0, головка 0). Для просмотра содержимого MBR используйте следующую команду:

# dd if=/dev/sda of=MBR bs=512 count=1
# od -xa MBR

Первые 446 байт представляют собой собственно первичный загрузчик, который содержит как программный код, так и текст сообщений об ошибках. Следующие 64 байта представляют собой таблицу разделов, которая содержит запись для каждого из четырех разделов диска (по 16 байт каждая). В конце MBR располагаются два байта, которые носят название «магического числа» (0xAA55). Это магическое число служит для целей проверки MBR.
Диски, использующие GPT, в нулевом секторе по-прежнему могут содержать обычную главную загрузочную запись (MBR), используемую для загрузки с этого диска операционной системы в том случае, если компьютер не соответствует спецификации UEFI.
Независимо от того, используется ли на вашем компьютере устаревшая система BIOS или новый интерфейс EFI, таблица разделов GPT позволяет устранить множество ограничений, связанных с главной загрузочной записью:

GRUB

После первичного загрузчика (MBR/GPT) подгружается GRUB (GRand Unified Bootloader), который уже понимает что такое файловая система и может показать показать список имеющихся ядер (которые определяются в /etc/grub.conf, с поддержкой мягких ссылок из /boot/grub/menu.lst и/boot/grub/grub.cfg). К примеру типичное меню с grub.cfg:

menuentry 'Ubuntu, with Linux 3.8.0-26-generic' --class ubuntu --class gnu-linux --class gnu --class os {
        recordfail
        gfxmode $linux_gfx_mode
        insmod gzio
        insmod part_msdos
        insmod ext2
        set root='(hd0,msdos1)'
        search --no-floppy --fs-uuid --set=root 2a1f9055-c025-4dcd-b3c8-b38b9e6ffb9c
        linux   /boot/vmlinuz-3.8.0-26-generic root=UUID=2a1f9055-c025-4dcd-b3c8-b38b9e6ffb9c ro
        initrd  /boot/initrd.img-3.8.0-26-generic
}

Здесь указываться образ временной файловой системы initrd.img и образ ядра vmlinuz, которому передается следующий этап загрузки.

Ядро

После того как образ ядра оказывается в памяти и ему передается управление от загрузчика 2-й ступени. Если имеется образ начального RAM-диска (initrd), то программа также перемещает его в память и помечает для дальнейшего использования, а затем вызывает само ядро, после чего начинается загрузка ядра. Просмотр содержимого начального RAM-диска и способы его редактирование можете посмотреть здесь Пример:

root@:~# lsinitramfs /boot/initrd.img-3.8.0-26-generic | head
/boot/initrd.img-3.8.0-26-generic
.
sbin
sbin/hwclock
sbin/wait-for-root
sbin/mount.ntfs-3g
sbin/mount.ntfs
sbin/dmsetup
sbin/udevd
sbin/udevadm

initrd используется самим ядром в качестве временной корневой файловой системы, пока kernel не загрузится в реальную примонтированную файловую систему. Этот временный диск также содержит необходимые для загрузки драйверы, позволяющие получить доступ к разделам дисков и другому оборудованию. Создать свое примитивное ядро можно по этой инструкции.

Init

После загрузки и инициализации ядра запускается первое приложение в пространстве пользователя /sbin/init, которое обращается к файлу /etc/inittab (или /etc/init/rc-sysinit.conf) для того, чтобы определить уровень выполнения (run level).
Есть следующие уровни выполнения:

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

root@:~# runlevel
N 2
root@:~# vim /etc/init/rc-sysinit.conf
...
env DEFAULT_RUNLEVEL=2
...

Так же можно на прямую вписать в grub.cfg.

Runlevel

Для каждого вышеперечисленного уровня выполнения в Ubuntu есть своя папка со скриптами:

root@:~# ls -ld /etc/rc*
drwxr-xr-x 2 root root 4096 Dec 31 22:48 /etc/rc0.d
drwxr-xr-x 2 root root 4096 Jan 15 13:48 /etc/rc1.d
drwxr-xr-x 2 root root 4096 Jan 15 13:48 /etc/rc2.d
drwxr-xr-x 2 root root 4096 Jan 15 13:48 /etc/rc3.d
drwxr-xr-x 2 root root 4096 Jan 15 13:48 /etc/rc4.d
drwxr-xr-x 2 root root 4096 Jan 15 13:48 /etc/rc5.d
drwxr-xr-x 2 root root 4096 Dec 31 22:48 /etc/rc6.d
-rwxr-xr-x 1 root root  420 Jul 15  2013 /etc/rc.local
drwxr-xr-x 2 root root 4096 Dec 20 16:42 /etc/rcS.d

Когда загружается система — программа init смотрит на уровень выполнения заданный по дефолту и лезет в соответствующею папку со скриптами. В каждой из папок rc*.d есть набор скриптов:

root@:~# ls -l /etc/rc2.d/
total 4
-rw-r--r-- 1 root root 677 Jul 26  2012 README
lrwxrwxrwx 1 root root  21 Jun 18  2013 S01ramfs_mount -> ../init.d/ramfs_mount
lrwxrwxrwx 1 root root  14 Jun 17  2013 S20atop -> ../init.d/atop
lrwxrwxrwx 1 root root  18 Jun 17  2013 S20icecast2 -> ../init.d/icecast2
lrwxrwxrwx 1 root root  19 Jun 17  2013 S20memcached -> ../init.d/memcached
lrwxrwxrwx 1 root root  15 Jun 17  2013 S20mysql -> ../init.d/mysql
lrwxrwxrwx 1 root root  17 Jun 17  2013 S20proftpd -> ../init.d/proftpd
lrwxrwxrwx 1 root root  23 Jun 18  2013 S20smartmontools -> ../init.d/smartmontools
lrwxrwxrwx 1 root root  15 Jun 17  2013 S20snmpd -> ../init.d/snmpd
lrwxrwxrwx 1 root root  17 Jun 17  2013 S20sysstat -> ../init.d/sysstat
lrwxrwxrwx 1 root root  13 Jan 15 13:48 S23ntp -> ../init.d/ntp
lrwxrwxrwx 1 root root  15 Jun 17  2013 S50rsync -> ../init.d/rsync
lrwxrwxrwx 1 root root  19 Jun 17  2013 S70dns-clean -> ../init.d/dns-clean
lrwxrwxrwx 1 root root  18 Jun 17  2013 S70pppd-dns -> ../init.d/pppd-dns
lrwxrwxrwx 1 root root  14 Dec 20 16:47 S75sudo -> ../init.d/sudo
lrwxrwxrwx 1 root root  17 Jun 17  2013 S91apache2 -> ../init.d/apache2
lrwxrwxrwx 1 root root  18 Jun 17  2013 S99fail2ban -> ../init.d/fail2ban
lrwxrwxrwx 1 root root  21 Jun 17  2013 S99grub-common -> ../init.d/grub-common
lrwxrwxrwx 1 root root  18 Jun 17  2013 S99ondemand -> ../init.d/ondemand
lrwxrwxrwx 1 root root  18 Jun 17  2013 S99rc.local -> ../init.d/rc.local
lrwxrwxrwx 1 root root  18 Jun 23  2013 S99webmin -> /etc/init.d/webmin

Скриптам, которые начинаются на S (startup) передается параметр start.
Скриптам, которые начинаются на K (kill) передается параметр stop.
Для наглядного примера, добавим свой скрипт начальной загрузки.

root@:~$ cat /etc/init.d/testapp
#!/bin/bash
DAEMON_PATH="/home/alex"
NAME=testapp
DESC="My test script"

case "$1" in
start)
        printf "%-50s" "Starting $NAME..."
        cd $DAEMON_PATH
        echo "Command START from $0 script at `date +%T` " >> testappfile
;;
status)
        printf "%-50s" "Checking $NAME..."
        cd $DAEMON_PATH
        echo "Command STATUS from $0 script at `date +%T` " >> testappfile
;;
stop)
        printf "%-50s" "Stopping $NAME"
        cd $DAEMON_PATH
        echo "Command STOP from $0 script at `date +%T` " >> testappfile
;;

restart)
        $0 stop
        $0 start
;;

*)
        echo "Usage: $0 {status|start|stop|restart}"
        exit 1
esac

Здесь мы добавили добавили скрипт с стандартными опциями (status|start|stop|restart). При вызове одной из них — мы записываем вывод с указанием времени в файл /home/alex/testappfile чтобы узнать очередность запуска скриптов. Далее нам нужно добавить запуск скрипта на стандартных уровнях выполнения.

root@:~# update-rc.d testapp defaults

Теперь перезагружаем систему и потом смотрим в файл testappfile.

root@:~# cat /home/alex/testappfile
Command STOP from /etc/rc6.d/K20testapp script at 05:50:17
Command START from /etc/rc2.d/S20testapp script at 05:50:32

Как видим, после сигнала reboot система переходит в уровень выполнения «6» и запускает скрипты начинающиеся с «К». Далее система стартует и переходит в стандартный уровень выполнения (уровень 2 для Ubuntu), где запускает скрипты «S».
Если нужно добавить некоторый скрипт в автозагрузку — можно также использовать /etc/rc.local. Команды из этого скрипта запускается самым последним, после запуска всех скриптов из rc*.d. К примеру, добавим запись в тот же файл и перезагрузимся.

root@:~# cat /etc/rc.local
...
echo "Command from $0  at `date +%T` " >> /home/alex/testappfile
exit 0

После загрузки — смотрим в файл.

root@ubuntu:~# cat testappfile
Command STOP from /etc/rc6.d/K20testapp script at 05:50:17
Command START from /etc/rc2.d/S20testapp script at 05:50:32
Command STOP from /etc/rc6.d/K20testapp script at 05:53:43
Command START from /etc/rc2.d/S20testapp script at 05:53:59
Command from /etc/rc.local  at 05:53:59

Как видим — команда из скрипта /etc/rc.local запустилась самой последней.

Автор: admin, 27 января 2014
Рубрики: Linux
Метки: , ,

Написать комментарий

Последние статьи

Яндекс.Метрика
?>