EnglishРусский Map
Linux Kernel Startup

Фазы загрузки Linux

title
Фазы загрузки Linux
type
concept
summary
Шестифазная модель загрузки Linux на x86_64: каждая фаза, от трамплина до kernel_init, открывает возможности, нужные следующей
tags
linux, kernel, boot
created
2026-05-14
updated
2026-05-14
lang
ru
translation_of
linux-boot-phases
source_updated
2026-05-14
translated
2026-09-01
translator
lllm/antigravity/gemini-3.7-flash-medium

Загрузка Linux на x86_64 - это строго упорядоченная последовательность сборки, а не скрипт. У каждой фазы свои границы дозволенного ("что мы сейчас можем делать"), и порядок важен: каждый шаг открывает возможности, необходимые следующему. В схеме Эспино в linux-kernel-startup процесс разбит на шесть фаз.

Фазы

Фаза Точка входа Что становится возможно
1. Ассемблер startup_64 CPU в Long Mode, таблицы страниц выправлены, всё готово к исполнению C
2. Ранний C x86_64_start_kernel Очищена .bss, настроены обработчики исключений, сохранены параметры загрузки, применён микрокод
3. setup_arch setup_arch() Определены возможности CPU, разобрана карта памяти, отображена вся RAM, доступен early printk
4. start_kernel start_kernel() Работает kmalloc, подняты планировщик/RCU/таймеры, включены прерывания, настоящая консоль, ядро пропатчило себя под этот CPU
5. rest_init rest_init() Созданы PID 1 (kernel_init) и PID 2 (kthreadd); поток загрузки становится idle
6. kernel_init kernel_init_freeable() Пробуждены остальные CPU, загружены драйверы, смонтирован корень, освобождена память __init, работает userspace

Почему важен порядок

Большая часть структуры держится на двух правилах упорядочивания:

Прерывания отключены, пока нет всего, что их обрабатывает. Фаза 4 почти целиком проходит с отключёнными прерываниями. Ядро не может принять прерывание, пока не настроен контроллер IRQ, не могут срабатывать таймеры, не поднят RCU, у timekeeper'а нет источника времени, а подсистема printk не может сообщить об ошибке. Вызов local_irq_enable() происходит глубоко внутри start_kernel(), а не в самом его начале.

Каждый аллокатор передаёт дела следующему. На этапе загрузки ядро живёт на memblock (это скорее трекер, чем полноценный аллокатор), пока mm_core_init() не поднимет стек buddy -> SLUB -> vmalloc. Точкой передачи владения служит memblock_free_all(); начиная с этого момента все выделения памяти идут через аллокатор страниц и slab-кэш.

Передача управления: поток загрузки становится idle

Самая важная граница проходит между фазой 4 и фазой 5. На протяжении всей фазы 4 всю работу выполняет единственный поток (init_task). sched_init() тихо переименовывает его в задачу idle для загрузочного CPU - но поток продолжает выполнять код инициализации. Сама передача управления происходит в rest_init(): schedule_preempt_disabled() отдаёт CPU, и когда управление возвращается, исходный поток навсегда уходит в do_idle(). С этого момента работу по загрузке ведёт PID 1 (kernel_init). Единое выполнение разделяется на три потока: PID 0 (idle), PID 1 (init -> userspace), PID 2 (kthreadd).

Правило "двери запирают последними"

Несколько механизмов защиты включаются в фазе 6 после выполнения большей части кода, поскольку эта защита помешала бы его работе:

  • mark_readonly() переводит rodata в режим только для чтения на уровне таблиц страниц - раньше этого сделать нельзя, так как ядро патчило собственный код (alternatives, jump labels).
  • pti_finalize() финализирует Page Table Isolation - по той же причине.
  • free_initmem() освобождает память функций с атрибутом __init - это должно происходить строго после того, как завершится последняя функция __init.

Что полезно перенять из этой модели

Разбиение на шесть фаз специфично для Linux, но два общих паттерна применимы к любому процессу загрузки:

  1. Границы возможностей на каждой фазе. У каждой фазы свой набор доступных функций ("что мы сейчас можем вызывать?"). Документирование этих границ - самое полезное, что может дать карта загрузки.
  2. Передача аллокаторов как дискретное событие. Простой bootstrap-аллокатор уступает место настоящему в конкретный момент. Фазы до этого момента могут работать только с сырыми блоками памяти; фазы после него - с объектами.

Ссылки

  • linux-kernel-startup - статья, из которой взята эта модель
  • kernel-self-patching - перезапись кода во время выполнения в фазе 4
  • linux-preemption-models - что происходит с планированием после завершения загрузки