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

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску

Процесс загрузки Linux — последовательность действий, посредством которых приводятся в состояние готовности операционные системы на основе Linux. Этот процесс во многом схож с загрузкой BSD и других Unix-подобных систем, от которых он и происходит.

Общий обзор процесса[править | править код]

При загрузке компьютера происходит последовательная передача управления от системной прошивки компьютера (BIOS или UEFI) к загрузчику, а от него — к ядру. Затем ядро запускает планировщик (для реализации многозадачности) и выполняет программу init (которая настраивает пользовательское окружение и позволяет осуществлять взаимодействие с пользователем и вход в систему), после чего ядро переходит в состояние бездействия до тех пор, пока не получит внешний вызов.

Основные этапы загрузки:

  1. Системная прошивка компьютера выполняет первичную проверку и инициализацию аппаратного обеспечения.
  2. В случае BIOS прошивка загружает в оперативную память и выполняет загрузочный код с одного из разделов заданного загрузочного устройства, который содержит фазу 1 загрузчика Linux. Фаза 1 загружает фазу 2 (значительный по размеру код загрузчика). Некоторые загрузчики могут использовать для этого промежуточный этап (под названием фаза 1,5), поскольку современные диски большого объёма могут некорректно считываться без дальнейшего кода. В случае UEFI запускается загрузчик загруженный со служебного раздела (EFS), который выбирается согласно настройкам приоритета загрузки определенного в энергонезависимой памяти компьютера. При этом возможна загрузка не только специализированного загрузчика, но можно загрузить и непосредственно ядро Linux (для этого ядро должно быть собрано с опцией EFI_STUB).
  3. Загрузчик зачастую предлагает пользователю меню с доступными вариантами загрузки. После выбора или после заданного тайм-аута загрузчик загружает ядро.
  4. Загруженное ядро распаковывается в памяти, настраивает системные функции, такие как работа необходимого оборудования и управление страницами памяти, после чего делает вызов start_kernel().
  5. После этого start_kernel() выполняет основную настройку системы (прерывания, остальные функции управления памятью, инициализацию устройств, драйверов и т. д.), а потом порождает процесс бездействия, диспетчер и отдельно от них — процесс init (выполняющийся в пользовательском пространстве).
  6. Планировщик начинает более эффективно управлять системой, в то время как ядро переходит к бездействию.
  7. Процесс init выполняет необходимые сценарии, которые настраивают все службы и структуры, не относящиеся к уровню ядра, в результате чего будет создано пользовательское окружение, и пользователю будет предоставлен экран входа в систему.

Когда происходит завершение работы, init вызывается для управляемого закрытия программ пользовательского уровня, тоже согласно сценариям. После этого init закрывается, а ядро производит своё собственное завершение работы.

Фаза загрузчика[править | править код]

При загрузкe через BIOS: Фазы загрузчика различаются в зависимости от платформы. Поскольку ранние этапы загрузки не зависят от операционной системы, процесс загрузки обычно начинается следующим образом:

  • Для x86 или x86-64: код с загрузочного сектора раздела диска выполняется в реальном режиме и загружает первую фазу загрузчика (как правило — часть LILO или GRUB).

С этого момента загрузка продолжается. Первая фаза загружает остальной код загрузчика, который обычно спрашивает, какую операционную систему (или вид её сессии) пользователь хочет запустить. Код загрузчика создаётся на основе конфигурационного файла /etc/lilo.conf (для LILO), в котором определены доступные системы. Этот файл содержит, в частности, информацию о загрузочном разделе и расположении ядра для каждой из таких систем, а также дополнительные параметры загрузки, если они заданы. В результате выбора соответствующее ядро загружается в ОЗУ, минимальная начальная файловая система настраивается из файла-образа (initrd), а затем, вместе с соответствующими параметрами управление передаётся новой ОС.

LILO и GRUB имеют определённые различия:[1]

  • LILO не распознаёт файловые системы, поэтому он использует непосредственные (raw) смещения на диске и сведения из BIOS для загрузки данных. Он загружает код меню, а потом, в зависимости от выбора, загружает либо 512-байтные секторы диска для системы, основывающейся на MBR (например, Microsoft Windows), либо образ ядра для Linux.[1]
  • GRUB, наоборот, распознаёт распространённые файловые системы (например, ext2 и ext3).[2] Так как GRUB хранит свои данные в файле конфигурации, а не в загрузочной записи, и имеет интерфейс командной строки, то зачастую параметры GRUB легче поправить или изменить, если они настроены неправильно или повреждены.[3]

При загрузке через UEFI: В UEFI загрузчик сразу запускается в защищенном режиме (32- или 64-битном) и фактически загружаются сразу все фазы загрузчика (с учетом загрузки со служебного раздела для загрузчика нет необходимости разбивать себя на отдельные фазы и размещать их в разных местах). В остальном процесс загрузки и инициализации ядра не отличается от варианта с BIOS.

GRUB[править | править код]

BIOS:

  1. Загрузчик 1-й фазы считывается BIOS из MBR (главной загрузочной записи).[4]
  2. Он загружает оставшуюся часть загрузчика (2-ю фазу). Если вторая фаза находится на большом диске, иногда загружается промежуточная фаза 1,5, которая содержит дополнительный код, позволяющий считывать цилиндры с номерами более 1024 (диски LBA). Загрузчик фазы 1,5 хранится (если это необходимо) в MBR или в загрузочном разделе.[4]
  3. Выполняется вторая фаза загрузчика и отображает меню запуска GRUB. Оно также позволяет выбрать среду выполнения и просмотреть параметры системы.
  4. Когда операционная система выбрана, она загружается и ей передаётся управление.[4]

GRUB поддерживает и прямой, и цепной способ загрузки, а также LBA, ext2, и «истинно командно-ориентированную, дооперационную среду на машинах x86». Он имеет три интерфейса: меню выбора, редактор настроек и командную консоль.[4]

UEFI:

  1. Загруженный со служебного раздела EFS GRUB (специальная версия бинарного файла, который умеет загружать UEFI) содержит в себе все необходимые компоненты для доступа к файловой системе /boot где находятся конфигурация и дополнительные файлы загрузчика.
  2. Отображается меню загрузчика и отображает меню запуска GRUB. Оно также позволяет выбрать среду выполнения и просмотреть параметры системы.
  3. Когда операционная система выбрана, она загружается и ей передаётся управление.[4]

LILO[править | править код]

LILO старше GRUB и практически аналогичен ему в действии, за исключением того, что не содержит интерфейса командной строки. Поэтому все изменения нужно вносить в его настройки и записывать в MBR, после чего систему перезагружают. Таким образом, ошибка в настройках может сделать диск неспособным к загрузке без использования отдельного загрузочного устройства (дискеты и т. п.), содержащего программу для исправления ошибки.[3] Кроме того, LILO не распознаёт файловые системы; вместо этого, адреса файлов-образов хранятся непосредственно в MBR,[3] а BIOS используется для прямого к ним доступа.

Loadlin[править | править код]

Ещё один способ загрузить Linux — из DOS или Windows 9x, где ядро Linux полностью заменит выполняющуюся копию операционной системы. Это может быть целесообразно, если аппаратное обеспечение должно включаться программно, а соответствующие программы существуют только для DOS, а не для Linux, будучи проприетарным ПО производителя и объектом коммерческой тайны. Этот метод загрузки не особо актуален, так как в Linux есть драйверы для множества аппаратных устройств, хотя в прошлом он был весьма полезен.
Другой пример: когда Linux находится на устройстве хранения данных, которое не предназначено для загрузки из BIOS: DOS или Windows могут загрузить соответствующие драйверы, чтобы обойти такое ограничение BIOS, а затем загрузить оттуда Linux.

Фаза ядра[править | править код]

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

Этап загрузки ядра[править | править код]

Ядро при загрузке обычно имеет вид файла-образа, сжатого в формат zImage или bzImage с помощью zlib. В нём содержится головная программа, которая проводит минимальную настройку оборудования, распаковывает образ целиком в верхнюю память и монтирует RAM-диск, если он предусмотрен.[5] После этого она выполняет запуск ядра посредством ./arch/x86/boot/head и процесса startup_32() (для процессоров семейства x86).

Этап запуска ядра[править | править код]

Источник: Описание процесса загрузки Linux фирмой «IBM» (англ.) + переведённая версия с того же сайта Подробности процесса загрузки Linux (рус.)

Функция запуска ядра (также называемая своппер или процесс 0) организует управление памятью (таблицы страниц и страничную организацию памяти), определяет тип процессора и дополнительные возможности (например, наличие математического сопроцессора), а затем переключается к архитектурно-независимому функционалу ядра Linux путём вызова start_kernel().

start_kernel() выполняет множество задач инициализации. Она настраивает обработчики прерываний (IRQ), затем настраивает память, запускает процесс init (первый процесс пользовательского режима), а затем запускает задачу бездействия вызовом cpu_idle(). Следует заметить, что процесс запуска ядра также монтирует инициирующий RAM-диск («initrd»), который ранее был загружен в роли временной корневой файловой системы в фазе загрузки. Это позволяет загружать модули драйверов, не опираясь на другие физические устройства и драйверы, и поддерживать небольшой размер ядра. Корневая файловая система впоследствии подменяется с помощью вызова pivot_root(), который размонтирует временную и заменяет её настоящей корневой ФС, как только последняя станет доступна. Использованная временной системой память затем освобождается.

Таким образом, ядро инициализирует устройства, монтирует указанную загрузчиком файловую систему в режиме «только чтение» и запускает процесс init (/sbin/init), который обозначается как первый процесс, запущенный системой (с идентификатором процесса PID = 1).[1] Соответствующие сообщения выводит ядро (при монтировании файловой системы) и init (при запуске одноимённого процесса). Ядро также может выполнить initrd для обработки настроек и инициализации устройств до монтирования корневой файловой системы.[1]

По заявлению компании «Red Hat», детали процесса загрузки на этом этапе можно подытожить так:[2]

Когда загружается ядро, оно сразу же инициализирует и конфигурирует память компьютера и настраивает различное подключённое к системе оборудование, включая все процессоры, подсистемы ввода-вывода и устройства хранения данных. Затем оно ищет сжатый образ initrd в заранее определённом участке памяти, распаковывает его, монтирует и загружает все необходимые драйверы. Затем оно инициализирует виртуальные устройства, связанные с файловой системой, например LVM или программные RAID-массивы, прежде чем демонтировать образ диска initrd и освободить всю память, ранее занимаемую образом. Потом ядро создает корневое устройство, монтирует корневой раздел только для чтения и освобождает всю неиспользованную память. К этому времени ядро загружено в память и работоспособно. Тем не менее, поскольку нет пользовательских программ для осуществления осмысленного ввода данных в систему, с ней мало что можно делать.

Теперь, когда включены прерывания, диспетчер может принять общее управление системой, чтобы обеспечить вытесняющую многозадачность, а процесс init остается продолжать загрузку пользовательского окружения в пространстве пользователя.

Процесс init (только типа UNIX System V)[править | править код]

Init является родителем всех процессов. Его главная задача — создавать процессы по сценарию из файла /etc/inittab. В этом файле обычно содержатся записи, указывающие init породить getty для каждой линии, по которой пользователи могут входить в систему. Он также контролирует автономные процессы, требуемые какой-либо системе. Уровень выполнения — программная конфигурация системы, которая позволяет существовать только заданной группе процессов. Процессы, порождаемые init на каждом из таких уровней выполнения, определяются в файле /etc/inittab.[6]

По сути init организует и поддерживает всё пользовательское пространство, что включает в себя также проверку и монтирование файловых систем, запуск нужных пользовательских служб и, переключение в пользовательскую среду, когда запуск системы завершится. Он похож на процессы init в Unix и BSD, от которых произошёл, но в некоторых случаях он изменён или переделан. В обычной системе Linux init имеет параметр, известный как уровень выполнения, принимающий значения от 1 до 6 и определяющий, какие подсистемы следует включить. Для каждого уровня выполнения есть собственные сценарии, которые регламентируют различные процессы, участвующие в установлении или снятии данного уровня, и именно эти сценарии считаются необходимыми для процесса загрузки. Сценарии init обычно хранятся в каталогах с именами вида /etc/rc…. Главный файл конфигурации уровней для init — /etc/inittab.[7]

Во время загрузки системы он проверяет, описан ли уровень по умолчанию в /etc/inittab, а если же нет — запрашивает его через системную консоль. Затем он продолжает выполнять все соответствующие сценарии загрузки для этого уровня, включая загрузку модулей, проверку целостности файловой системы (которая монтировалась только для чтения), перемонтирование её для чтения-записи и настройку сети.[1]

В частности, по сообщению Red Hat, процесс init следует такой схеме:[2]

  1. Он просматривает сценарий sysinit, который "устанавливает путь к среде, запускает swap, проверяет файловые системы и делает всё, что необходимо для инициализации системы. Это, в частности, системные и аппаратные часы, специальные процессы для последовательного порта и т. п.
  2. Затем init просматривает конфигурацию, указанную для заданного уровня выполнения.
  3. После этого init устанавливает исходную библиотеку функций для системы. Это определяет, как следует запустить или снять программу и как определить её PID.
  4. Затем он запускает все предусмотренные процессы и создает сессию входа пользователя в систему.

После того, как он породил все заданные процессы, init переходит в режим ожидания и ждет одного из трёх событий:

  1. Нормального или аварийного завершения порождённых процессов.
  2. Сигнала аварии питания.
  3. Запроса от /sbin/telinit на изменение уровня выполнения.[6]

Это относится к программе init в стиле UNIX System V. Другие программы init могут вести себя иначе.

См. также[править | править код]

Примечания[править | править код]

  1. 1 2 3 4 5 Linux Boot Process — by Kim Oldfield (2001). Дата обращения: 10 декабря 2008. Архивировано 18 декабря 2008 года.
  2. 1 2 3 A Detailed Look at the Boot Process. Дата обращения: 10 декабря 2008. Архивировано 3 февраля 2009 года.
  3. 1 2 3 Documentation. Дата обращения: 10 декабря 2008. Архивировано 5 мая 2010 года.
  4. 1 2 3 4 5 Описание GRUB фирмой «Red Hat» (англ.) (HTML). Дата обращения: 8 августа 2010. Архивировано 7 марта 2009 года.
  5. IBM description of Linux boot process. Дата обращения: 10 декабря 2008. Архивировано 31 мая 2008 года.
  6. 1 2 Руководство man по процессу init. Дата обращения: 10 декабря 2008. Архивировано 12 июня 2008 года.
  7. От включения питания до приглашения Bash: Init. Дата обращения: 26 ноября 2010. Архивировано 10 марта 2011 года.

Ссылки[править | править код]