Я прохожу курс "Операционная система Unix" и после тестов, вижу в отчете, что этот тест сдало еще 25 человек. Почему так мало, это ведь реально хороший и полезный урок. Здесь естьи теория и практичесские материалы. Сам курс написан хорошо, живым языком. И здесь я получил ответы на вопросы по Linux, которые боялся спросить. Наверное это из-за того, что в названии курса написано не Linux, а Unix и это многих отпугивает. |
Досистемная начальная загрузка
Хотели как лучше
В выключенном состоянии компьютер не может выполнять никаких основных функций, разве что служить подставкой для кофе. На самом деле и во включенном - после нажатия тумблера "Вкл." - он будет способен адекватно отзываться на требования пользователя далеко не сразу. Прежде чем выйти на штатный режим работы, система должна загрузиться: следует разместить в памяти ядро (возможно, с дополнительными модулями) и передать ему управление, ядро должно провести опрос аппаратуры, не помешает быстрый профилактический осмотр состояния системы, затем нужно запустить необходимые для работы демоны и т. д.
Все, что происходит после включения компьютера и до входа системы в штатный режим (пока на терминале не появится "login"), называется начальной загрузкой системы. Процедуру начальной загрузки можно разделить на две стадии: досистемная - от включения до старта ядра и системная - начальная работа ядра и все, что происходит после.
Досистемная начальная загрузка, вообще говоря, системно-независима. Одни и те же действия компьютера с некоторыми подсказками от пользователя (или без них) могут завершаться загрузкой разных ядер в различном формате, а значит, и разных операционных систем. Зато досистемная начальная загрузка аппаратно-зависима, причем работа составляющих ее процедур зависит не только от архитектуры системы, но и от конфигурации аппаратуры, которую очень часто приходится задавать в явном виде.
Поэтому рассмотрим оторванную от конкретной архитектуры, идеальную последовательность начальной загрузки, конечная цель которой - загрузить ядро и передать ему управление. В один присест это сделать не удастся (где уж компьютерному железу знать местоположение ядра неизвестно какой системы, не говоря уже о способе его загрузки!), поэтому вся последовательность разделена на уровни: если какое-то действие нельзя выполнить из-за ограничений текущего уровня, значит, необходимо ввести следующий, в котором подобных ограничений не будет. Цель каждого уровня - выбрать (или позволить пользователю выбрать) следующий.
Первые действия выполняются процедурой, записанной в ПЗУ. Задача этой процедуры - выявить устройства, с которых возможна загрузка, и попытаться загрузиться с них. Программа, записанная в ПЗУ, не может быть слишком уж велика (имеются исключения, о которых см. далее), но ее достаточно для того, чтобы воспользоваться настройками и параметрами из энергонезависимой памяти (non-volatile RAM, nvram; иногда встречается сокращение CMOS). Этот уровень называется BootROM (от ROM - ПЗУ (практически во всех современных компьютерах BootROM хранится не в ПЗУ, а в т. н. ППЗУ - программируемом ПЗУ. В случае крайней необходимости содержимое BootROM можно-таки изменить ("перешить"), однако действие это нештатное). BootROM не обязан, да и не может знать все тонкости всех внешних устройств, с которых можно загружаться, однако простейшие команды вида "считать с устройства один (первый) блок" ему доступны. Уже со вторым блоком устройства может быть не все ладно (а ну как быть если он ровно один?), но первый-то есть всегда (скажем, сектор 0/0/1 на диске в нотации CHS, т. е цилиндр/головка/сектор).
Следующий уровень находится в том самом считанном блоке, поэтому называется он BootBlock или первичный загрузчик. Поскольку BootBlock принадлежит конкретному устройству (например, диску), процедура этого уровня уже умеет считывать любые данные из любого места - вот только размер ее не позволяет быть слишком умной. Поэтому BootBlock знает только, как загрузить следующий уровень. Если речь идет о диске, BootBlock может знать, на сколько - и каких - разделов разбит этот диск и где в них размещается вторичный загрузчик, размер которого может быть любым, лишь бы строго заданным.
Следующий уровень - вторичный загрузчик. Это уже довольно разумная программа, поэтому уровень носит название BootProg. Раз уж программа загружается с определенного раздела диска, она вполне может разбираться в структуре файловой системы, размещенной на этом разделе (да и на других, если они того же типа) и в формате загрузки ядра. BootProg может передавать ядру параметры, ему можно указать в качестве ядра любой файл в файловой системе, настройки вторичного загрузчика можно тоже хранить в файле; скорее всего, BootProg обеспечит даже простейший способ просмотреть содержимое каталога в файловой системе.
Итак, вторичный загрузчик загрузил и настроил ядро, а затем передал ему управление. В работе ядра надо предусмотреть доступ к данным, переданным именно этим типом загрузчика, а остальное будет соответствовать стандартам загружаемой операционной системы.
Достоинство этой схемы в том, что уровни не зависят друг от друга. Мы можем использовать любой тип первичного загрузчика (а так оно и бывает при загрузке с разных устройств), лишь бы он умел находить вторичный. То же можно сказать о паре BootProg - ядро. В идеале трехуровневая схема позволяет избежать обратной зависимости: изменения работы более высокого уровня не требуют исправлений на более низком. Например, если в результате обновления системы изменится BootProg, не надо перенастраивать BootBlock. Что еще важнее, вполне можно обойти и прямую зависимость: чтобы (хотя бы однократно) изменить поведение нижнего уровня, не нужно загружать верхний. Например, для того чтобы "прописать" новое ядро, в BootProg не обязательно сначала грузить старое и часть системы, а потом менять настройки вторичного загрузчика. Для постоянного изменения настройки, скорее всего, придется загружать систему, здесь важна сама возможность выбора между, допустим, старым (работающим) и новым (экспериментальным) вариантами ядра.