Наши партнеры

UnixForum






Книги по Linux (с отзывами читателей)

Библиотека сайта rus-linux.net

Исследуем процесс загрузки Linux

(C) В.А.Костромин, 2007
(последние изменения в файл внесены 29.09.2007 г.)


Назад Оглавление Вперед

Этап 4: Ядро

4.1. Загрузка и инициализация ядра

В сети можно найти несколько статей (см. [8], [38], [39], [40]), содержащих довольно подробное описание процесса загрузки ядра. Хотя в этих статьях рассматривается ядро версии 2.4 (и даже версии 1.0), я полагаю, что с переходом к ядру версии 2.6 в этом процессе изменилось не многое (если я не прав, пишите на kos Y rus-linux DOT net!). Попытаюсь составить из этих источников описание основных действий, выполняемых ядром на этапе его загрузки и инициализации.

Во время загрузки Linux на консоль выводится большое количество сообщений, в том числе и сообщений о действиях, выполняемых ядром при его инициализации. Эти сообщения обычно быстро проскакивают, и вы, вероятно, не сможете прочитать их, несмотря на то, что в некоторые моменты возникают задержки на несколько секунд. А если эти сообщения скрыты за какой-то красивой заставкой, эти сообщения могут накладываться на графический фон или могут быть вообще скрыты. И хотя у вас обычно есть возможность отключить графическую заставку (как это сделать, было рассказано в предыдущем разделе), тем не менее прочитать сообщения на этапе загрузки в любом случае проблематично.

К счастью, все эти сообщения сохраняются и их можно увидеть после завершения загрузки, воспользовавшись командой dmesg. Протоколирование этих сообщений осуществяется демоном протоколирования klogd (он запускается как один из потоков ядра). Klogd сохраняет эти сообщения в специальном буфере ядра, содержимое которого и выдается по команде dmesg. Кроме того, klogd передает эти сообщения демону системного протоколирования syslogd, который записывает их в файл /var/log/messages. Благодаря этому они и доступны для просмотра и анализа после завершения запуска системы. Я попытаюсь дать описание процесса загрузки ядра, используя эти сообщения в качестве ориентиров на пути развития данного процесса (смотрите листинги этих сообщений, взятые с моего домашнего компьютера).

Поскольку демон протоколирования klogd просто не может быть запущен в течение некоторого промежутка времени после передачи управления ядру, самый начальный этап работы ядра не отражен в протоколе. О нем придется рассказывать, ориентируясь только на описания в статьях [38], [39].

Хочу сразу предупредить, что последующий текст досаточно скучен для чтения. Да, скорее всего, простому пользователю Linux можно и не разбираться во всех этих премудростях. Достаточно знать общие принципы и последовательность загрузки. Поэтому я постараюсь где-то в конце дать краткий обзор процесса загрузки, основанный на том детальном разборе, который будет приведен в основной части настоящего иссследования. Так что если вы не сможете набраться терпения для чтения настоящего раздела, потерпите до того момента, когда дело дойдет до краткого обзора.

4.1.1. Получение системной информации от BIOS и разархивация ядра

Ядро хранится на диске в сжатом виде. Только его первая часть не сжата. Когда загрузчик перенесет ядро в память, эта несжатая часть ядра выполняет декомпрессию оставшейся части. Несжатая часть кода, которая содержится в начале ядра, написана на ассемблере, ее можно найти в файлах arch/i386/boot/setup.S, arch/i386/boot/video.S и arch/i386/kernel/head.S.

Код, хранящийся в файлах arch/i386/boot/setup.S и arch/i386/boot/video.S, отвечает за получение системной информации от BIOS и размещение ее в подходящих местах системной памяти. Кратко перечислим основные действия, которые выполняет эта часть ядра:
  • Чтение DASD-типа второго жесткого диска (не знаю, что такое DASD).
  • Проверка того, что ядро загружено правильно. Осуществляется проверкой сигнатуры в конце кода запуска. Если она не найдена, то копируются секторы с запускающим кодом и сигнатура ищется снова. Если опять безуспешно, то выдается сообщение "No setup signature found ...".
  • Проверка на возможность работы с верхней памятью. Если образ ядра слишком большой (и поэтому загружен в верхнюю память), а код несжатой части ядра не может работать с образами, расположенными в верхней памяти, то загрузка прекращается и выдачей сообщения "Wrong loader, giving up...".
  • Выяснение объема памяти системы. Для этого используется вызов трех различных функций BIOS: E820h, которая позволяет собрать карту памяти, затем E801h, которая вернет 32-битный размер памяти и последней вызывается 88h, которая вернет размер в диапазоне 0-64 МБ. Полученные значения сохраняются для дальнейшего использования.
  • Частота повторов клавиатуры устанавливается в максимальное значение при помощи BIOS int 0x16.
  • Настройка режима видеоадаптера. Возможность выбора видеорежима зависит от конфигурации ядра. Можно указать специфичный (предопределенный) режим, который будет использован в процессе загрузки ядра, либо запросить меню со списком режимов, из которого пользователь выберет режим по своему желанию.
  • Считываются (путем вызова прерываний 0x41 и 0x46) и сохраняются параметры жестких дисков hd0 и hd1 (если есть).
  • Считывается информация о шине Micro Channel.
  • Проверяется наличие PS/2 мыши при помощи прерывания BIOS 0x11.
  • Если ядро сконфигурировано с поддержкой APM (Advanced Power Management - улучшенное управление питанием, реализуемая BIOS спецификация управления питанием для персональных компьютеров), проверяется поддержка APM в BIOS .
  • Осуществляется подготовка к переключению в защищенный режим: ядро перемещается в подходящее место (если это необходимо), подправляются адреса и значения регистров.
  • Задействуется адресная линия A20 (что-то связанное с клавиатурой, точно не знаю).
  • Производится сброс математического сопроцессора.
  • Маскируются все прерывания, кроме IRQ2.
  • Производится переключение первого процессора в защищенный 32-битный режим.
  • Код загрузки в "linux/arch/i386/boot/setup.S" перемещает выполнение к началу кода в "linux/arch/i386/kernel/head.S" (помеченному "startup_32:"). Чтобы эта точка стала доступной, небольшая разжатая функция ядра разжимает оставшийся сжатый ядерный образ и затем переходит к полученному коду.

4.1.2. Задание начальных установок процессора(ов)

Далее начинает работать код из файла arch/i386/kernel/head.S. Этот код выполняет следующие действия (регистры обозначаются как '%регистр', константы записываются как номера с или без '$' впереди):
  • Устанавливаются сегментные регистры (%ds = %es = %fs = %gs = __KERNEL_DS = 0x18).
  • Для систем с SMP (Symmetric MultiProcessing) производится проверка того, что работа происходит на загружающем процессоре (BSP - Bootstrap Processor). Если это не так, то инициализация таблицы страниц пропускается, осуществляется переход к пункту "Включение управления страницами".
  • Инициализируются таблицы страниц.
  • Включение управления страницами. Разрешается листание страниц установкой бита PG в %cr0 в состояние "страничная организация задействована".
  • Обнуляется BSS (Block Started by Symbol - блок, начинающийся с символа; неинициализированный сегмент данных), для SMP это действие выполняет только первый CPU.
  • Параметры загрузки заносятся в первые 2 килобайта по адресу _empty_zero_page, а содержимое командной строки (kernel commandline) - в следующие 2 килобайта.
  • Проверяется тип CPU, используя EFLAGS и, если возможно, cpuid, позволяющие обнаружить процессор 386 и выше. Также проверяется наличие сопроцесора 80287 или 80387.
  • Загружается дескриптор таблицы указателя регистров и осуществляется переход к __KERNEL_CS:%eip. Теперь процессор работает в защищенном режиме.
  • Запуск других процессоров. Вторичный(е) процессор(ы) инициализируется (ются) и переводится(ятся) в режим простоя до тех пор, пока процессы его(их) не займут.
  • Первый CPU вызывает функцию start_kernel().

4.1.3. Инициализация ядра

Функция init/main.c:start_kernel() написана на C и выполняет следующие действия:

  • Выполняется глобальная блокировка (BKL: big kernel lock - большая блокировка ядра), необходимая для того, чтобы через процесс инициализации проходил только один CPU. Прерывания в это время еще запрещены.
  • Вывод "баннера" ядра, который содержит версию, компилятор, использованные при сборке, и пр., в кольцевой буфер для сообщений. Текст "баннера" задается в переменной linux_banner, определенной в init/version.c. Текст этот можно вывести на экран командой cat /proc/version. Это фактически первое сообщение, которое выдается ядром. Пример таких сообщений с двух моих компьютеров:
Linux version 2.6.14-1.1653.1asp (build@amd64.asplinux.com.ua) (gcc version 4.0.2 20051125 (Red Hat 4.0.2-8)) #1 Mon Jan 23 19:24:56 EET 2006

Linux version 2.6.18-8.SEL5 (buildsys@pceddyn.internal.startcom.org) (gcc version 4.1.1 20070105 (StartCom Linux 4.1.1-52)) #1 SMP Fri Mar 23 16:08:07 EDT 2007
Замечание: хотя выше и написано про вывод баннера, фактически на консоль еще ничего не выводится, поскольку консоль еще не зарегистрирована в системе. Вывод идет в кольцевой буфер. Как только консоль подключится, сообщения из буфера будут выданы на консоль. Это замечание нужно иметь в виду и всюду ниже, где говорится о выводе каких-либо сообщений.
  • Вызывается функция setup_arch(&command_line) (см. в "linux/arch/i386/kernel/setup.c"), которая выполняет архитектурно-специфичные действия по инициализации, а именно:
    • Системные параметры из 16-битного реального режима копируются и преобразуются в 32-битный запускающий код.

      Для конфигураций с включенным RAMdisk (CONFIG_BLK_DEV_RAM) инициализируются rd_image_start, rd_prompt и rd_doload из параметров реального режима.

    • Карта памяти от BIOS используется для установки областей памяти.
      Вероятно именно в этот момент выводится сообщение вида
      BIOS-provided physical RAM map:
       BIOS-e820: 0000000000000000 - 000000000009fc00 (usable)
       BIOS-e820: 000000000009fc00 - 00000000000a0000 (reserved)
       BIOS-e820: 00000000000e0000 - 0000000000100000 (reserved)
       BIOS-e820: 0000000000100000 - 00000000177f0000 (usable)
       BIOS-e820: 00000000177f0000 - 0000000017800000 (reserved)
       BIOS-e820: 00000000fec00000 - 0000000100000000 (reserved)
      
    • Выставление границ памяти. Устанавливаются значения для начала кода ядра, конца кода ядра, конца данных ядра и "_end" (конец кода ядра = адресу "brk").
    • Устанавливаются значения для начала и конца code_resource и начала и конца data_resource.
    • Разбираются и запоминаются параметры "mem=" командной строки ядра.
    • Установка страничных кадров. Используя карту памяти от BIOS, устанавливаются страничные кадры. Регистрируются доступные страницы нижней RAM в распределителе bootmem. Резервируется физическая страница 0: "это особенная страница BIOS во многих системах, включающая чистые перезагрузки, SMP-операции и laptop-функции."
    • 0MB HIGHMEM available.
      375MB LOWMEM available.
      Using x86 segment limits to approximate NX protection
      On node 0 totalpages: 96240
        DMA zone: 4096 pages, LIFO batch:1
        Normal zone: 92144 pages, LIFO batch:31
        HighMem zone: 0 pages, LIFO batch:1
      
      Адресуемая обычным образом область памяти (располагающаяся в пределах первых 896 MБ) называется нижней памятью (low memory). Подпрограмма распределения памяти ядра, kmalloc(), отвечает за выделение памяти из этой области. Память за пределами 896 MB (называемая верхней памятью - high memory) доступна только путем применения специальных приемов (special mappings).
      В процессе загрузки ядро определяет и выводит общее число страниц, присутствующих в каждой из этих зон. После загрузки системы эта информация доступна через /proc/meminfo.
    • Обработка конфигураций SMP и IO APIC (APIC: Advanced Programmable Interrupt Controller - улучшенный программируемый контроллер прерываний).
      Для CONFIG_SMP резервируем страницу, следующую за страницей 0 для стэка и запуска, затем вызываем smp_alloc_memory() для выделения нижней памяти под запускающий (trampoline) код реального режима для AP процессора(ов).
      Для конфигураций с CONFIG_X86_IO_APIC вызываем find_smp_config() чтобы найти и зарезервировать любую память с SMP-конфигурационной информацией времени загрузки вроде таблицы данных MP (Multi Processor) от BIOS.
    • paging_init() устанавливает страничные таблицы (первые 8 МБ уже зарезервированы в head.S). Эта процедура также освобождает страницу по виртуальному адресу ядра 0, что позволяет вылавливать в ядре надоедливые ошибки с нулевыми ссылками (pesky NULL-reference errors).
    • Сохранение SMP-конфигурации времени загрузки. Для конфигураций с CONFIG_X86_IO_APIC вызываем get_smp_config() для чтения и записи данных конфигурации MP-таблицы маршрутизации прерываний IO APIC.
      Для конфигураций с CONFIG_X86_LOCAL_APIC вызываем init_apic_mappings().
      Local APIC disabled by BIOS -- you can enable it with "lapic"
      mapped APIC to ffffd000 (0138a000)
    • Резервирование памяти для INITRD. Для конфигураций с CONFIG_BLK_DEV_INITRD при наличии достаточного кол-ва памяти под начальный RamDisk вызываем reserve_bootmem() чтобы зарезервировать RAM для него.
    • Поиск и работа с ROM. Вызываем probe_roms() и если находим корректные ресурсы ROM, то резервируем их. Это делается для образа ROM стандартной видеоBIOS, любых других найденных ROM и для расширения ROM системной платы.
    • Резервирование системных ресурсов. Вызываем request_resource() для резервирования видеоRAM. Вызываем request_resource() для резервирования всех стандартных ресурсов ввода-вывода системной платы PC.
    • ACPI: RSDP (v000 IntelR                                ) @ 0x000f7490
      ACPI: RSDT (v001 IntelR AWRDACPI 0x42302e31 AWRD 0x00000000) @ 0x1bff3000
      ACPI: FADT (v001 IntelR AWRDACPI 0x42302e31 AWRD 0x00000000) @ 0x1bff3040
      ACPI: DSDT (v001 INTELR AWRDACPI 0x00001000 MSFT 0x0100000c) @ 0x00000000
      ACPI: PM-Timer IO Port: 0x4008
      Allocating PCI resources starting at 20000000 (gap: 1c000000:e3b00000)
      Detected 1205.234 MHz processor.
      Built 1 zonelists.  Total pages: 114672
  • Выводится содержимое командной строки ядра.
    Kernel command line: ro root=/dev/system/LogVol00 rhgb quiet
    Между прочим командную строку запуска ядра вы всегда можете в работающей системе просмотреть с помощью команды cat /proc/cmdline.
  • Разбор параметров командной строки с помощью функции parse_options(command_line). Эта функция анализирует командную строку и соответственно инициализирует аргументы и окружение init'а (который поток). Каждый аргумент командной строки рассматривается как переменная окружения, если он содержит знак "=". Она также ищет опции, предназначенные для ядра, при помощи вызова checksetup(), который проверяет командную строку на параметры ядра - при их наличии они объявляются при помощи "__setup", например:
    __setup("debug", debug_kernel);
    Если при наличии такого объявления обнаружится строка "debug", то будет вызвана функция debug_kernel(). Список параметров ядра можно найти в "linux/Documentation/kernel-parameters.txt".
    Эти опции не передаются init'у - они предназначены для использования исключительно внутри ядра. Список аргументов по умолчанию для init'а есть {"init", NULL}, максимум с 8-ю аргументами командной строки. Установки окружения по умолчанию для потока init'а есть {"HOME=/", "TERM=linux", NULL}, с максимум 8-ю установками переменных окружения в командной строке. Если LILO грузит нас с командной строкой по умолчанию, то он помещает "auto" перед всей строкой, благодаря чему шелл думает, что он должен выполнить скрипт с таким именем. Так что мы игнорируем все аргументы, введенные _перед_ init=... [MJ]
  • Выделение ресурсов для APIC. Улучшенный программируемый контроллер прерываний (APIC, the Advanced Programmable Interrupt Controller) - это современная реализация программируемого контроллера прерываний 8259. APIC предоставляет большее количество прерываний. Локальный APIC - это часть архитектуры APIC, относящаяся к каждому конкретному ЦПУ системы. I/O APIC - это другая часть архитектуры, обрабатывающая прерывания от устройств ввода-вывода и доставляющая их локальным APIC. Локальные APIC играют существенную роль в мультипроцессорных системах.
    Разделение прерываний с использованием традиционного PIC - это сложная задача. APIC сокращает необходимость в разделении (совместном использовании) прерываний. Кроме того, APIC обрабатывает прерывания быстрее, чем PIC. Некоторые BIOS-ы отключают APIC. Вы можете задействовать APIC, добавив в командную строку ядра опцию lapic.
    Local APIC disabled by BIOS -- you can enable it with "lapic"
    mapped APIC to ffffd000 (0138a000)
  • Вызывается подпрограмма trap_init (из linux/arch/i386/kernel/traps.c), которая:
    • устанавливает обработчики исключений для основных процессорных исключений, (это не обработчики аппаратных прерываний).
    • устанавливает обработчик системного вызова программного прерывания.
    • вызывает cpu_init() чтобы:
      - инициализировать каждый процессор
      - перезагрузить GDT и IDT
      - демаскировать бит NT (Nested Task) регистра eflags
      - установить и загрузить для каждого процессора TSS (Task State Segment - сегмент состояния задачи, i386-специфичная структура данных задачи) и LDT (Local Descriptor Table - локальная дескрипторная таблица, i386-специфичная таблица управления памятью, которая используется для описания памяти для каждого неядерного процесса)
      - очистить 6 отладочных регистров (0, 1, 2, 3, 6 и 7)
      - stts(): установить бит 0x08 (TS: Task Switched) в CR0 для включения задержанной (lazy) записи в регистры при контекстных переключениях.
      Initializing CPU#0
  • Вызывается подпрограмма init_IRQ (в linux/arch/i386/kernel/i8259.c), которая:
    • вызывает init_ISA_irqs() чтобы инициализировать контроллер прерываний 8259A и установить дефолтовые обработчики прерываний для ISA.
    • устанавливает вход прерывания (interrupt gate - ?) для всех неиспользованных векторов прерывания.
      CPU 0 irqstacks, hard=c03fd000 soft=c03fc000
    • для конфигураций с CONFIG_SMP выставляет IRQ 0 заранее, потому как оно используется перед установкой IO APIC.
    • для CONFIG_SMP устанавливает обработчик прерывания для CPU-to-CPU IPI, которые используются для "reschedule helper."
    • для CONFIG_SMP устанавливает обработчик прерывания для IPI, который используется для сброса (здесь - to invalidate) TLB.
    • для CONFIG_SMP устанавливает обработчик прерывания для IPI, который используется для вызовов основных функций.
    • для конфигураций с CONFIG_X86_LOCAL_APIC устанавливает обработчик прерывания для IPI от независимо работающего локального таймера APIC.
    • для конфигураций с CONFIG_X86_LOCAL_APIC устанавливает обработчики прерываний для ложных или ошибочных прерываний.
    • настраивает микросхему системных часов для генерации прерываний каждые HZ герц.
    • если система содержит внешний FPU, то устанавливает обработчик IRQ 13 для обработки исключений при операциях с плавающей точкой.
  • Отрабатывает подпрограмма инициализации данных для планировщика (sched_init, находится в linux/kernel/sched.c)
    • Устанавливаем ID процессора для структур init_task
    • Очищаем таблицу pidhash (хешей PID'ов).
      PID hash table entries: 2048 (order: 11, 32768 bytes)
    • Вызываем init_timervecs()
    • Вызываем init_bh() для инициализации "верхней половины" (bottom half) очередей для timer_bh, tqueue_bh и immediate_bh.
  • Вызывается подпрограмма инициализации данных хранения времени (time_init в linux/arch/i386/kernel/time.c)
    • Выставляем текущее время системы (xtime) из CMOS.
    • Устанавливаем обработчик прерывания irq0 для тиков от таймера.
      Using pmtmr for high-res timesource
  • Инициализация подсистемы программных прерываний ( softirq_init в linux/kernel/softirq.c)
  • Инициализация консоли ( console_init в linux/drivers/char/tty_io.c)
    Console: colour VGA+ 80x25
  • Если ядро было скомпилировано с поддержкой загружаемых модулей, инициализируется подсистема динамической загрузки модулей (init_modules в linux/kernel/module.c). Тем самым выставляется размер (или кол-во символов) таблицы символов ядра.
  • Установка профилирования. Если профилирование включено ("profile=#" в командной строке ядра): вычисляем размер "сегмента" текста (кода) профиля ядра; вычисляем размер буфера профиля в страницах (с округлением); выделяем буфер для профиля: prof_buffer = alloc_bootmem(size);
  • kmem_cache_init (в linux/mm/slab.c), начало инициализации менеджера памяти.
    Dentry cache hash table entries: 65536 (order: 6, 262144 bytes)
    Inode-cache hash table entries: 32768 (order: 5, 131072 bytes)
  • Вызывается mem_init() (в linux/arch/i386/mm/init.c), которая подсчитывает объем доступной оперативной памяти и сравнивает его с объемом памяти, необходимой для работы ядра:
    • Очищаем empty_zero_page.
    • Вызываем free_all_bootmem() и добавляем всю освобожденную память к totalram_pages.
    • Подсчитываем количество зарезервированных страниц RAM.
    • Выводим размер системной памяти (свободно/всего), размер кода ядра, объем зарезервированной памяти, размер данных ядра, "начальный" размер ядра и объем верхней памяти.
      Memory: 376448k/384960k available (2125k kernel code, 7956k reserved, 732k data, 172k init, 0k highmem)
    • Для CONFIG_SMP вызываем zap_low_mappings().
      get_free_pages() можно использовать после mem_init().
  • Завершение инициализации менеджера памяти ( kmem_cache_sizes_init из linux/mm/slab.c)
    Устанавливаем оставшиеся внутренние и основные кэши.
  • Вызывается функция calibrate_delay(), определенная в init/calibrate.c. Эта функция подсчитывает, сколько раз в течение интервала времени между двумя отсчетами таймера процессор выполнит внутренний цикл задержки. Результат сохраняется во внутренней переменной ядра "loops_per_jiffy". Эта переменная используется для генерации задержки на заданное время, например, для генерации задежки на 1 микросекунду нужно выполнить пустой цикл (HZ/1000000)*loops_per_jiffy раз, где HZ - частота микропроцессора в герцах.
    Значение loops_per_jiffy используется для вычисления условной меры быстродействия процессора, известной как BogoMIPS. Значение BogoMIPS можно применять для сравнения производительности разных процессоров. Например, для лэптопа на процессоре Pentium M с частотой 1.6 GHz значение loops_per_jiffy оказалось равным 6385059, а значение BogoMIPS - 3192.52.
    Calibrating delay using timer specific routine.. 3591.60 BogoMIPS (lpj=7183205)
  • Инициализация SELinux
    Security Framework v1.0.0 initialized
    SELinux: Initializing.
    SELinux: Starting in permissive mode
    selinux_register_security: Registering secondary module capability
  • Разрешаются прерывания (sti).
  • Инициализация структур данных для procfs (proc_root_init из linux/fs/proc/root.c)
    Для конфигураций с CONFIG_PROC_FS:
    - вызываем proc_misc_init()
    - mkdir /proc/net
    - для CONFIG_SYSVIPC выполняем mkdir /proc/sysvipc
    - для CONFIG_SYSCTL выполняем mkdir /proc/sys
    - mkdir /proc/fs
    - mkdir /proc/driver
    - вызываем proc_tty_init()
    - mkdir /proc/bus
  • mempages = num_physpages;
  • Устанавливаем максимальное кол-во потоков по умолчанию в безопасное значение: потоковые структуры могут использовать максимум половину памяти.
    fork_init(mempages) (в linux/kernel/fork.c) создает uid_cache, инициализируется max_threads исходя из объема доступной памяти и конфигурируется RLIMIT_NPROC для init_task как max_threads/2.
  • Создаются различные кэши для VFS, VM, кэш буфера и пр..
    • proc_caches_init() (в linux/kernel/fork.c)
      Вызываем kmem_cache_create() для создания отдельных кэшей для signal_act (работа с сигналами), files_cache (files_struct), fs_cache (fs_struct), vm_area_struct и mm_struct.
    • vfs_caches_init(mempages) (в linux/fs/dcache.c)
      Вызываем kmem_cache_create() для создания отдельных кэшей для buffer_head, names_cache, filp и для CONFIG_QUOTA, dquot.
      Вызываем dcache_init() чтобы создать dentry_cache и dentry_hashtable.
    • buffer_init(mempages) (в linux/fs/buffer.c)
      Выделяем память под хэш-таблицу буферного кэша и создаем пустой список. Используем get_free_pages() для хэш-таблицы чтобы уменьшить TLB-промахи; используем SLAB-кэш для заголовков буфера. Устанавливаем цепочки хэша, пустые списки и списки LRU.
    • page_cache_init(mempages) (в linux/mm/filemap.c)
      Выделяем и очищаем память под хэш-таблицу страничного кэша.
    • kiobuf_setup() (в linux/fs/iobuf.c)
      Вызываем kmem_cache_create() чтобы создать кэш буфера ввода-вывода ядра.
    • signals_init() (в linux/kernel/signal.c)
      Вызываем kmem_cache_create() чтобы создать SLAB-кэш "sigqueue" (очереди заданий).
    • bdev_init() (в linux/fs/block_dev.c)
      Инициализируем заголовки списка bdev_hashtable.
      Вызываем kmem_cache_create() чтобы создать SLAB-кэш "bdev_cache".
    • inode_init(mempages) (в linux/fs/inode.c)
      - Выделяем память под inode_hashtable.
      - Инициализируем заголовки списка inode_hashtable.
      - Вызываем kmem_cache_create() SLAB-кэш инодов (inodes).
  • Если имеется поддержка System V IPC, то инициализируется подсистема IPC (ipc_init() в linux/ipc/util.c). Инициализируются различные ресурсы System V IPC (семафоры, сообщения и разделяемая память). Обратите внимание, что для System V shm, это включает монтирование внутреннего (in-kernel) экземпляра файловой системы shmfs.
  • Если включена поддержка квот (quota), создается и инициализируется специальный кэш (dquot_init_hash() в linux/fs/dquot.c)
    VFS: Disk quotas dquot_6.5.1
    Dquot-cache hash table entries: 1024 (order 0, 4096 bytes)
  • Выполняется платформо-зависимая "проверка ошибок" ( check_bugs() в linux/include/asm-i386/bugs.h) и, если это возможно, активируется обработка ошибок процессора/шины/проч.:
    - identify_cpu()
    - Для не-CONFIG_SMP конфигураций вызываем print_cpu_info()
    CPU: After generic identify, caps: 0383f9ff 00000000 00000000 00000000 00000000 00000000 00000000
    CPU: After vendor identify, caps: 0383f9ff 00000000 00000000 00000000 00000000 00000000 00000000
    CPU: L1 I cache: 16K, L1 D cache: 16K
    CPU: L2 cache: 256K
    CPU: After all inits, caps: 0383f1ff 00000000 00000000 00000040 00000000 00000000 00000000
    Intel machine check architecture supported.
    Intel machine check reporting enabled on CPU#0.
    - check_config()
    - check_fpu()
    - check_hlt()
    Checking 'hlt' instruction... OK.
    Инструкция процессора HLT, поддерживаемая процессорами x86, переводит ЦПУ в спящий режим с низким энергопотреблением. Выход из этого режима происходит после поступления следующего аппаратного прерывания. Ядро использует инструкцию HLT для перевода ЦПУ в состояние бездействия (in the idle state). Функция cpu_idle() определена в arch/i386/kernel/process.c.
    Можно отключить использование инструкции HLT, если задать опцию командной строки ядра no-hlt. Если эта опция задана, ядро оставляет ЦПУ в активном состоянии в периоды ожидания вместо того, чтобы переводить его в состояние HLT.

    - check_popad()
    - Корректируем system_utsname.machine{байт 1} в соотв-вии с boot_cpu_data.x86
  • В случае SMP запускаем другие процессоры.
    Выполнение smp_init(), в зависимости от конфигурации ядра, возможно тремя способами.
    Для однопроцессорных (UP) систем без IO APIC (CONFIG_X86_IO_APIC не определена), smp_init() пуста - соответственно ничего не происходит.
    Для однопроцессорных систем с IO APIC для маршрутизации прерываний она вызывает IO_APIC_init_uniprocessor().
    SMP alternatives: switching to UP code
    Freeing SMP alternatives: 16k freed
    SMP motherboard not detected.
    Local APIC not detected. Using dummy APIC emulation.
    Brought up 1 CPUs
    Для многопроцессорных (SMP) систем основная задача smp_init() заключается в вызове архитектурно-специфичной функции "smp_boot_cpus()", которая выполняет следующее:
    • Для ядер с CONFIG_MTRR вызывает mtrr_init_boot_cpu(), которая должна отработать до того, как загрузятся другие процессоры.
    • Сохраняет и выводит информацию о BSP CPU.
    • Сохраняет ID BSP APIC и BSP ID логического CPU (последний равен 0).
    • Если не найдена таблица маршрутизации прерываний MP BIOS, то возвращается к использованию только одного CPU и завершается.
    • Проверяет существование локального APIC для BSP.
    • Если использована опция загрузки "maxcpus" со значением 1 (без SMP), то игнорирует таблицу маршрутизации прерываний MP BIOS.
    • Переключает систему из PIC-режима в режим прерывания симметричного ввода-вывода.
    • Устанавливает локальный APIC BSP.
    • Использует карту наличия (presence map) CPU для последовательной загрузки AP. Ожидает загрузки предыдущего AP перед запуском загрузки следующего.
    • В случае использования IO APIC {что справедливо всегда кроме случаев с опцией загрузки "noapic"} устанавливает IO APIC (или каждый, если их несколько).
  • Инициализация INITRD (INITial Ram-Disk)
    checking if image is initramfs... it is
    Freeing initrd memory: 2693k freed
    О том, что такое виртуальный диск initrd и для чего предназначен, было рассказано в разделе 3.2. После инициализации RAM-диска ядро освобождает ту область памяти, в которую загрузчик перенес образ этого диска (в нашем случае - 2693 КБайт). Освободившиеся страницы памяти раздаются другим частям ядра, которым потребуется память.
  • Инициализация сетевых сокетов.
    NET: Registered protocol family 16
    NET: Registered protocol family 2
    NET: Registered protocol family 1
    (эти сообщения следуют в потоке сообщений, выдаваемых в процессе загрузки, не группой, а с некоторым разбросом)
    Уровень сетевых сокетов (socket layer) в Линукс - это унифицированный интерфейс, с помощью которого пользовательские приложения получают доступ к различным сетевым протоколам. Каждый протокол регистрируется в socket layer, используя назначенный ему номер семейства (определен в include/linux/socket.h). Так, например, номер 2 назначен AF_INET, семейству протоколов Internet IP. Номер 16 в приведенном листинге соответствует семейству AF_NETLINK (family 16).
    Сокеты сетевых соединений - это специальный механизм для организации взаимодействия ядра с процессами пользовательского уровня, используя механизм API сокетов. С помощью сетевых сокетов обеспечивается доступ к таблицам маршрутизации и таблицам протокола разрешения адресов (Address Resolution Protocol, ARP), полный список возможностей, обеспечиваемых сетевыми сокетами, можно найти в include/linux/netlink.h. Сетевые соединения более подходящи для таких задач, чем системные вызовы, поскольку они асинхронны, более просты в применении и могут быть связаны динамически.
    Еще одним широко используемым семейством протоколов является AF_UNIX (семейство с номером 1). Доменные сокеты Unix (Unix domain sockets) используются такими программами как X Window System для организации взаимодействия между процессами одной и той же системы. Они используются даже в том случае, если машина не подключена к сети.
  • На следующем этапе загрузки опробуются и инициализируются шины ввода-вывода и контроллеры периферийных устройств. Ядро опробует аппаратное обеспечение путем опроса шины PCI,
    PCI: Probing PCI hardware (bus 00)
    PCI quirk: region 4000-407f claimed by ICH4 ACPI/GPIO/TCO
    PCI quirk: region 4080-40bf claimed by ICH4 GPIO
    Boot video device is 0000:01:00.0
    PCI: Transparent bridge - 0000:00:1e.0
    после чего опрашивает другие подсистемы:
    - видео чип (part of the 855 North-bridge chipset in this case),
    Linux agpgart interface v0.101 (c) Dave Jones
    agpgart: Detected an Intel i815 Chipset.
    agpgart: AGP aperture is 64M @ 0xd0000000
    - последовательный порт ,
    Serial: 8250/16550 driver $Revision: 1.90 $ 4 ports, IRQ sharing enabled
    serial8250: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
    serial8250: ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A
    00:08: ttyS0 at I/O 0x3f8 (irq = 4) is a 16550A
    00:09: ttyS1 at I/O 0x2f8 (irq = 3) is a 16550A
    - виртуальный диск,
    RAMDISK driver initialized: 16 RAM disks of 16384K size 4096 blocksize
    - IDE-контроллер (в данном случае - часть чипсета южного моста ICH2),
    Uniform Multi-Platform E-IDE driver Revision: 7.00alpha2
    ide: Assuming 33MHz system bus speed for PIO modes; override with idebus=xx
    ICH2: IDE controller at PCI slot 0000:00:1f.1
    ICH2: chipset revision 5
    ICH2: not 100% native mode: will probe irqs later
        ide0: BM-DMA at 0xf000-0xf007, BIOS settings: hda:DMA, hdb:DMA
        ide1: BM-DMA at 0xf008-0xf00f, BIOS settings: hdc:DMA, hdd:DMA
    Probing IDE interface ide0...
    hda: ST340810A, ATA DISK drive
    hdb: ST3160023A, ATA DISK drive
    ide0 at 0x1f0-0x1f7,0x3f6 on irq 14
    Probing IDE interface ide1...
    hdc: FX810T4, ATAPI CD/DVD-ROM drive
    hdd: _NEC DVD_RW ND-3500AG, ATAPI CD/DVD-ROM drive
    ide1 at 0x170-0x177,0x376 on irq 15
    hda: max request size: 128KiB
    hda: 78165360 sectors (40020 MB) w/2048KiB Cache, CHS=65535/16/63, UDMA(100)
    hda: cache flushes not supported
     hda: hda1 hda2 hda3
    hdb: max request size: 512KiB
    hdb: 312581808 sectors (160041 MB) w/8192KiB Cache, CHS=19457/255/63, UDMA(100)
    hdb: cache flushes supported
     hdb: hdb1 hdb2 hdb3
    
    - дисковод гибких дисков,
    ide-floppy driver 0.99.newide
    - USB-контроллер,
    usbcore: registered new driver hiddev
    usbcore: registered new driver usbhid
    drivers/usb/input/hid-core.c: v2.6:USB HID core driver
    - контроллер PS/2 c клавиатурой и мышью,
    PNP: PS/2 Controller [PNP0303:PS2K,PNP0f13:PS2M] at 0x60,0x64 irq 1,12
    serio: i8042 AUX port at 0x60,0x64 irq 12
    serio: i8042 KBD port at 0x60,0x64 irq 1
    mice: PS/2 mouse device common for all mice
    - SCSI,
    SCSI subsystem initialized
    - обратную петлю (the loopback device),
    - контроллер Ethernet
    - контроллер PCMCIA
    и другие устройства. Нужно иметь в виду, что в случае, когда некоторые драйверы сконфигурированы как загружаемые модули, соответствующие сообщения могут появляться немного позже и в другом порядке.
  • Монтирование файловых систем
    EXT3 FS on hda3, internal journal
    kjournald starting. Commit interval 5 seconds
    EXT3 FS on hda4, internal journal
    EXT3-fs: mounted filesystem with ordered data mode.
    Adding 1050800k swap on /dev/hda1. Priority:-1 extents:1 across:1050800k
    Файловая система Ext3 постепенно становится стандартом де-факто для корневой файловой системы Linux. Она добавляет свойство журналируемости к файловой системе Ext2; журналируемость заключается в том, что каждая файловая операция вначале фиксируется в журнале и только после этого производятся действительные изменения на диске. Это позволяет быстро восстановить файловую систему в случае каких-то сбоев.
    Ext3 использует поток ядра kjournald для поддержки журналирования. После того, как Ext3 будет готова к работе, ядро может смонтировать корневую файловую систему.
  • Устанавливается флаг, указывающий на то, что планировщик должен быть вызван "при первой возможности" и создается поток ядра init(), который выполняет команду, указанную в параметре "init=" , если она имеется среди параметров командной строки, или пытается запустить /sbin/init, /etc/init, /bin/init, /bin/sh в указанном порядке; если не удается ни один из запусков, то ядро "впадает в панику" с "предложением" задать параметр "init=".
    Как и бездействующие потоки (idlers), init является неблокируемым потоком ядра, который делает системные вызовы (и поэтому не может быть блокируемым).
  • unlock_kernel() Снимает глобальную блокировку ядра (BKL).
  • current->need_resched = 1;
  • Переход cpu_idle() в фоновый поток с pid=0 Эта функция остается как процесс номер 0. Ее цель заключается в использовании пустых циклов CPU. Если ядро собрано с поддержкой APM или ACPI, то cpu_idle() вызывает поддерживаемые энергосберегающие свойства этих спецификаций. В противном случае она просто выполняет инструкцию "hlt".
--------------------------------------------------------------

Честно сказать, я не совсем уверен, что правильно сопоставил сообщения, выдаваемые при загрузке, с описанием в статьях [20], [21]: просто вставил в текст те строки из вывода команды dmesg, которые мне показались соответствующими. Вообще-то бОльшая часть строк из dmesg не поддается такому простому сопоставлению с описанием.


Назад Оглавление Вперед