Начнем рассмотрение загрузки Windows 2000 с того этапа, как BIOS считывает в память MBR жесткого диска.
Системная BIOS считывает главную загрузочную запись (Master Boot Record), которая располагается в первом секторе жесткого диска. После загрузки MBR в память, управление передается коду, содержащемуся в MBR, который в свою очередь сканирует таблицу разделов в поисках системного раздела. Таблица разделов (partition table) – таблица, хранящаяся в первом секторе жесткого диска, в которой указано, какой из разделов является системным. Системный раздел – раздел, который содержит файлы, необходимые для загрузки Windows 2000. К ним относятся: ntldr – загрузчик ОС, ntdetect.com – программа, предназначенная для сбора информации об аппаратных средствах, bootsect.dos – файл, необходимый для систем с двойной загрузкой, где в качестве альтернативаной ОС используется Windows 9x, boot.ini – файл, который считывает загрузчик и отображает на экране. Когда системный раздел найден, MBR загружает в память его нулевой сектор, который является загрузочным. Загрузочный сектор – сектор, в котором располагается код, предназначенный для нахождения и загрузки в память загрузчика Windows 2000 (NTLDR). После этого, загрузочный сектор должен распознать файловую систему для поиска загрузчика. На томах FAT структура данных, называемая загрузочным сектором, действительно занимает один физический сектор. На томах FAT32 – 2 сектора. На томе NTFS – до 16 секторов. Затем загрузочный сектор загружает в память NTLDR и передает ему управление.
После того, как управление получает NTLDR, он выполняет следующие функции:
- переключает процессор в защищенный режим;
- считывает, находящийся в корневом каталоге системного раздела файл boot.ini и отображает его содержимое на экране;
- если выбрана система Windows 9x, то NTLDR загружает в память файл bootsect.dos, в котором содержится копия загрузочного сектора раздела, находящегося на основном разделе до установки Windows 2000
- если выбрана система Windows 2000, то запускается программа ntdetect.com
- загружает и запускает ядро Windows 2000 (Ntoskrnl.exe) и уровень аппаратных абстракций – HAL.
Рассмотрим подробнее эти этапы.
После того, как управление передается NTLDR, он начинает свою работу, когда система работает в реальном режиме процессора x86. Первое что он делает, переключает процессор в режим использования 32-х разрядной модели памяти с прямой адресацией. С этого момента NTLDR может работать в полнофункциональном режиме. Если загрузочные диски являются SCSI-устройствами, NTLDR загружает в память Ntbootdd.sys и использует его функции обращения к диску вместо аналогичных функций загрузочного кода. После этого NTLDR с помощью встроенного кода файловой системы считывает из корневого каталога boot.ini.
Далее NTLDR очищает экран и, если в файле boot.ini имеется более одной записи о доступных для загрузки операционных системах, выводит загрузочное меню.
Если запись boot.ini ссылается на MS-DOS, NTLDR считывает в память содержимое файла bootsect.dos, переключается обратно в 16-разрядный реальный режим и вызывает из bootsect.dos код MBR. В результате код из bootsect.dos инициирует процесс загрузки, специфичный для MS-DOS. Также происходит загрузка Windows 98 или Windows 95, если они установлены вместе с Windows 2000.
Если до истечения периода ожидания, указанного в boot.ini, пользователь не выбрал ни одной команды загрузочного меню, NTLDR выбирает вариант по умолчанию. После выбора одного из вариантов NTLDR загружает и запускает ntdetect.com, 16-разрядную программу реального режима, которая получает от BIOS сведения о базовых устройствах и конфигурации компьютера:
- время и дату, хранящиеся в энергонезависимой памяти CMOS;
- типы шин в системе и устройствах, подключенных к этим шинам;
- число, емкость и тип дисков, присутствующих в системе;
- тип подключенной мыши;
- число и тип параллельных портов, сконфигурированных в системе.
Эти сведения, записываемые во внутренние структуры данных, на более поздних этапах загрузки будут сохранены в разделе реестра HKEY_LOCAL_MACHINE\HARDWARE\DESCRIPTION.
Затем NTLDR очищает экран и выводит индикатор процесса загрузки с надписью «Starting Windows» («Запуск Windows»). Индикатор остается на нулевой отметке до начала загрузки драйверов устройств. Под индикатором появляется сообщение «For troubleshooting and advanced startup options for Windows 2000, pree F8» («Для выбора особых вариантов загрузки Windows 2000 нажмите F8»). При нажатии F8 выводится дополнительное загрузочное меню, предлагающее выбрать особые варианты загрузки – последнюю удачную конфигурацию, безопасный или отладочный режим и т. д.
Далее NTLDR начинает загружать необходимые для инициализации ядра файлы.
- Загружает Ntoskrnl.exe и Hall.dll. Если NTLDR не удается загрузить какой-либо из этих файлов, он выводит сообщение «Windows 2000 could not start because the following file was missing or corrupt» («Не удается запустить Windows 2000 из-за испорченного или отсутствующего файла»), за которым следует имя файла.
- Для поиска драйверов устройств, которые нужно загрузить, считывает в память содержимое куста реестра SYSTEM.
- Сканирует загруженный в память куст реестра SYSTEM и находит все загрузочные драйверы устройств. Для того, чтобы определить какие драйверы из какого набора управляющих параметров нужно загрузить, загрузчик обращается к разделу HKEY_LOCAL_MACHINE\SYSTEM\Select. Если выбрана конфигурация по умолчанию, то загрузчик берет значение параметра Default и загружает набор управляющих параметров, на который указывает значение этого параметра. Соответственно, если в дополнительном меню был выбран пункт «Последняя удачная конфигурация», то загрузчик использует значение параметра LastKnownGood. Для поиска требуемых драйверов загрузчик просматривает раздел HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services. На данном этапе загружаются только те драйверы, для которых параметр Start равен 0х0.
- Вносит в список загрузочных драйверов устройств драйвер файловой системы, отвечающий за реализацию кода для конкретного типа раздела, на котором находится системный каталог. Ntldr должен загрузить сейчас этот драйвер именно сейчас, иначе ядро будет требовать от драйверов их же загрузки, и получится замкнутый круг.
- Загружает драйвера, обязательные для запуска системы. Ход загрузки отражается индикатором «Starting Windows». Полоска на индикаторе продвигается вперед по мере загрузки драйверов (число загрузочных драйверов считается равным 80, поэтому после успешной загрузки каждого драйвера полоска продвигается на 1,25%). Если в boot.ini указан параметр /SOS, то вместо индикатора NTLDR выводит имя драйвера каждого загрузочного драйвера. На этом этапе драйверы лишь загружаются, а их инициализация проходит позже
- Подготавливает регистры процессора для выполнения Ntoskrnl.exe.
На этом участие Ntldr в процессе загрузки заканчивается. Для инициализации системы Ntldr вызывает главную функцию из Ntoskrnl.exe.
Вызывая Ntoskrnl.exe, Ntldr передает структуру данных с копией строки из Boot.ini (предствляющий выбранный вариант загрузки), с указателем на таблицы памяти (сгенерированные Ntldr для описания физической памяти в системе), с указателем на загруженные в память копии кустов реестра HARDWARE и SYSTEM и суказателем на список загруженных драйверов.
Ntoskrnl начинает первую из двух фаз процесса инициализаци. Большинство компонентов исполнительной системы имеют инициализирующую функцию, которая принимает параметр, определяющий текущую фазу.
В фазе 0 прервывания отключены. Предназначение этой фазы в том, чтобы сформировать необходимые структуры, необходимые для вызова сервисов в фазе 1. Главная функция Ntoskrnl вызывает KiSystemStartup, которая в свою очередь вызывает HalInitializeProcessor и KiInitializeKernel для каждого процессора. Работая на стартовом процессоре, KiInitializeKernel выполняет общесистемную инициализацию ядра, в том числе всех внутренних структур данных, разделяемых всеми процессорами. Затем каждый экземпляр KiInitializeKernel вызывает функцию ExpInitializeExecutive, отвечающую за управление фазой 0.
ExpInitializeExecutive начинает с вызова HAL-функции HalInitSystem, позволяющей HAL взять управление инициализацией системы на себя. Одной из задач HalInitSystem является подготовка системного контролера прерываний каждого процессора к обработке прерываний и конфигурирование таймера, используемого для учета распределяемого процессорного времени.
На стартовом процессоре ExpInitializeExecutive не просто вызвывает HalInitSystem, но и выполняет другие операции по инициализации. Когда HalInitSystem возвращает управление, функция ExpInitializeExecutive, выполняемая на стартовом процессоре, обрабатывает параметр /BURNMEMORY файла Boot.ini (если таковой указан). В соответствии с этим параметром ExpInitializeExecutive исключает указанный объем памяти.
Далее ExpInitializeExecutive вызывает процедуры инициализации для диспетчера памяти, диспетчера объектов, справочного монитора безопасности, диспетчера процессов и диспетчера Plug and Play. Эти компоненты выполняют следующие инициализирующие операции.
- Диспетчер памяти формирует таблицы страниц и внутренние структуры данных, необходимые для предоставления базовых сервисов, связанных с памятью. Резервирует пространство для кэша файловой системы, а также выделяет области для пулов подкачиваемой и неподкачиваемой памяти. Другие компоненты исполнительной системы, ядро и драйверы устройств пользуются этими пулами, выделяя память под собственные структуры данных.
- При инициализации диспетчера объектов определяются объекты, необходимые для создания его пространства имен, чтобы другие компоненты могли помещать в него свои объекты. Также создается таблица описателей для поддержки учета ресурсов.
- Справочный монитор безопасности инициализирует объект типа «маркер доступа» и использует его для создания и подготовки первых маркеров, назначаемых начальным процессам.
- Диспетчер процессов производит большую часть своей инициализации в фазе 0, определяя типы объектов «процесс» и поток и создавая списки для отслеживания активных процессов и потоков. Он также создает объект «процесс» для начального процесса и присваивает ему имя Idle. Наконец, диспетчер процессов создает процесс System и системный поток для выполнения процедуры Phase1Initialization. Этот поток не запускается сразу же после создания, поскольку прерывания пока запрещены.
- Наступает фаза 0 в инициализации диспетера Plug and Play, в ходе которой просто инициализируется ресурс исполнительной системы, используемый для синхронизации ресурсов шин.
Когда на каждом процессоре управление возвращается к функции KiInitializeKernel, она передает его циклу Idle. В результате системный поток, созданный, как было описано в п.4, начинает фазу 1.
Для подготовки статьи использовались материалы книги «Внутреннее устройство Microsoft Windows 2000».