3. Настройка программного окружения для работы ядра - завершение.
Итак, ядро распаковано. Теперь можно спокойно завершить инициализацию системных регистров процессора и настройку служебных таблиц.
Эту часть инициализации выполняет функция startup_32() из файла /usr/src/linux-2.4.2/arch/i386/kernel/head.S. Она завершает настройку программного окружения для ядра и осуществляет настройку процессора для работы в многопользовательском режиме.
Сохраняется командная строка, переданная ядру загрузчиком. Она будет анализироваться на следующем этапе инициализации.
Определяется тип процессора (486, Pentium и т.п.), на котором запущен Linux. Это может понадобиться вот для чего. Некоторые версии процессоров могут содержать ошибки. Наиболее известная - это ошибка плавающей точки в математическом сопроцессоре процессора Pentium. Или, более применимая к Linux ошибка в некоторых ревизиях процессоров Duron/Atlon, возникающая при работе с AGP-приложениями и связанная с ошибкой совместимости с 4Мб страницами памяти. Если существует программый патч к ошибке, то операционная система должна применить его.
Дальше управление передается функции start_kernel(). Начало выполнения этой функции будет ознаменовано появлением сообщения "Linux version 2.x.x ... " в консоли.
4. Инициализация ядра. Приготовление к запуску первого процесса.
Функция start_kernel() ("/usr/src/linux-2.4.2/linux/init/main.c") осуществляет инициализацию всех основных частей ядра - обработчики прерываний, менеджера вируальной памяти, планировщика процессов и т.д.
Инициализируются таблица дескрипторов (paging_init()) и дескрипторы страниц (mem_init()), настраиваются обработчики прерываний (функции trap_init() и init_IRQ()), , считывается текущие системные дата и время из CMOS (time_init()).
Осуществляется анализ командной строки, и настройка ядра в соответствии с переданными параметрами. В частности, пользователь может указать путь к программе, которую следует запустить вместо init и командную строку для этого процесса.
Далее ядро готовит структуры данных планировщика процессов. После этого вызывается функция kernel_thread(), которая создает нулевой поток ядра. Это единственный поток, который создается не в результате вызова функции ядра fork или exec. Создается процесс номер 1, который должен выполнить системый вызов exec и трансформироваться в процесс init.
Все остальные процессы создаются за счет деления (fork) процесса init или его потомков.
После того, как инициализация завершена, ядро освобождает память, которую занимают код и данные функций инициализации (free_initmem()). Размер этой памяти зависит от версии ядра, и составляет примерно 200 Кбайт.
Теперь ядро готово для перемещения в пользовательский режим. Нулевой процесс, так называемая идеальная задача, продолжает функционировать в бесконечном цикле. Эта задача запускается всегда, когда в системе нет активных процессов.
После возникновения очередного прерывания таймера (не позднее, чем через 0,01 сек) управление будет передано процессу процессу номер 1. Он загрузит с диска и запустит процесс init. Операционная система начинает жизнь. Еще многое необходимо сделать, но вся остальная инициализация производится уже пользовательскими процессами.
Об этом - в следующем выпуске.