> Досистемная загрузка и загрузка Linux
Конспект лекции, прочтённой (в результате оплошности лектора) без слайдов
> Досистемная загрузка
Что происходит после включения компьютера? Как туда попадает ОС? Ведь компьютер -=- это железка, выполняющая программы.И сразу так вот хоп -- загрузить операционку на него нельзя. Более того, а как вообще в компьютер попадает _самая первая_ программа?
>> Загрузчик BIOS
BIOS, Basic Input-Output System, подсистема, распознающая устройства и немножко умеющая с ними работать.
|Возможности| BIOS не может знать, где на диске лежит ОС, не может даже прочитать произвольное количество данных с диска (тем более, непонятно какое). Зато может принять решение о том, чтобы грузиться вообще из сети. Или с дискетки. Вот когда клавишу "Del" нажимаешь, попадаешь в настройку, и там всё это есть в разделе ``Boot''.
|Задача| Выбрать устройство для загрузки и загрузить оттуда <первичный загрузчик|> стандартного размера
>> Первичный загрузчик (BootBlock)
Обычно размером один сектор, а то и меньше: на жёстком диске 512 байтов минус таблица разбиения.
|Возможности| BootBlock размером менее 500 байтов, но зато на каждом устройстве он _свой_. То есть знает, как работать с этим и подобными устройствами, в него можно вбить настройки (например, если надо загрузить программу произвольного размера, в каких секторах диска она лежит). Какие-то мелкие настройки этой программе можно передать.
|Задача| Выбрать и загрузить <вторичный загрузчик|>. Иногда не выбрать, а просто загрузить, не спрашивая.
>> Вторичный загрузчик (BootProg)
Теперь мы не зависим (в пределах разумного от размера). Обычно хранится в специальном месте в начале раздела (предусмотрено ФС).
|Возможности| Он может работать с ФС, в начале которой лежит, много разных полезных вещей может. Загружать ядро, модули к нему. Настраивать ядро.
|Задача| Выбор и загрузка ядра, модулей и старт ОС или загрузка другого загрузчика
Если вторичный загрузчик не умеет загружать ядро этой ОС, он загрузит другой загрузчик, который умеет. Например, Windows и Linux, Linux-загрузчик умеет загружать ядро Linux, но не умеет систем неые файлы Windows, ну и ладно, зато у Windows есть собственный загрузчик, который умеет, и известно, где он лежит. Ну, и загрузим его, пускай разбирается.
В этом месте заканчивается досистемная загрузка. По-хорошему она вообще не зависит от ОС. Сражу скажу -- в Linux не используется теоретически правильная трёхступенчатая последовательность загрузи по причине legacy, тяжёлого наследства длинного исторического процесса разработки IBM PC.
> Разделы жёсткого диска
Разбиение жёсткого диска на разделы устроено нелогично по причине всё того же legacy.
>> Полкадра, которого не было в слайдах
Почему бы не взять диск, завести на нём _одну_ ФС и хранить там файлы?
[Раздел|Логически ``независимая'' часть жёсткого диска.]
Каждый из разделов можно использовать под разные нужды, поэтому их может быть много.
. Разные ОС, каждая на своём разделе. Например, двухсистемные машины в школах.
. Разные способы хранения информации: отдельный swap, ФС разного назначения (допустимо переполнение или нет, надёжность vs. скорость, количество операций записи; с помощью ^mount^ всё это объединяется в одно дерево, а тип ФС можно подбирать разный
. Минимальная ФС, пригодная для ремонта для остальной ФС
Отступление о swap. Зачем он? Когда все процессы вместе используют столько памяти, сколько в системе нет. Но ведь они её и не используют одновременно, большая часть памяти заказана, но не используется. Или уже один раз использовалась, а теперь -- нет. Но выбросить её нельзя, в ней лежат нужные процессу данные. Поэтому эти неиспользованные куски памяти можно записатьо на диск, а когда в них возникнет нужда (процесс к нм обратится) -- прочитать оттуда. Надо отличать swapping, когда процесс выгружается из памяти целиком, чтобы дать место другому процессу, и paging, когда выгружаются только неиспользуемые страницы памяти. Если система начала использовать swapping, надо срочно покупать память. Хранить swap в файле -- значит, попусту использовать механизмы файловой системы и увеличивать паразитную нагрузку.
>> Разбиение диска
# Linux
# Linux swap
# EXT
# ---
# ============
# linux
# EXT
# ==============
# linux
1 MBR (Master Boot Record, место для первичного загрузчика) + в конце её -- HDPT (Hard Disk Partition Table). HDPT содержит место для <4>-х записей о ФС.
1 Четырёх разделов маловато. Придумали новый тип раздела: ``расширение'' таблицы разделов (extended partition). Это -- тоже раздел, он занимает всё оставшееся место диска, и у него в начале опять есть HDPT на четыре элемента. Если не хватит этого расширения, внутрь можно вложить ещё одно с ещё четырьмя записями, и т. п. матрёшка такая.
1 Стандартный способ разбиения диска: 4 записи в главном HDPT -- основные разделы (primary). В расширенном разделе допустимо только две записи: одна ссылается на раздел с файловой системой, а если необходимо ещё раздел сделать, то вторая ссылается на вложенный расширенный раздел, и всё повторяется. Вот это уже настоящая матрёшка. Записи в расширенных разделах -- дополнительные (в linux-овом ^fdisk^ они называются ``logical'').
1 Именование в Linux: основные четыре раздела имеют номера с 1 по 4, независимо от того, это файловые системы, расширение таблицы или пустое место. Номерами с 5 до упора нумеруются _только_ дополнительные разделы, не расширения
>> Пример
Каждый раздел -- это устройство в ^/dev/^, IDE-диски -- "hd", SCSI -- "sd", первый -- "a", второй -- "b" и т. д. Если в компьютере один IDE-диск, он называется обычно ^/dev/hda^, а его разделы ^/dev/hda1^, ^/dev/hfa2^ и т. п.
. MBR + HDPT (главная таблица)
.. Linux1 (^hda1^)
.. Linux Swap (^hda2^)
.. Расширение1 (^hda3^)
.. Пусто
. Linux1 (файловая система)
. Linux Swap (раздел с областью подкачки)
. Расширение1
.. HDPT1 (дополнительная таблица)
... Linux2 (^hda5^)
... Расширение2
... Пусто
... Пусто
.. Linux2
.. Расширение2
... HDPT2 (вторая дополнительная таблица)(?
)
... Linux3 (^hda6^)
... Пусто
... Пусто
... Пусто (?
)
... Linux3 (файловая система)
Среди первичных разделов один может быть загрузочный.
Это разбиение характерно для IBM PC.
> Досистемная загрузка с помощью LILO
Две системы загрузки -- LILO и GRUB.
LILO -- это LInux LOader. Первичный загрузчик -- LI, вторичный -- LO. Приметы: есть при загрузке вывелось только L -- аппаратные проблемы, Li недозагрузилось, если LI -- программные, LO не найден, если LIL -- аппаратные или LO побился, если LILO -- программные: не найдено или побилось ядро.
>> Карта размещения
Загрузчики LI и LO -- простые, они только умеют загружать некоторые последовательности секторов с диска. Каких секторов? Это определяется <картой размещения|карта размещения>.
LI (обычно записывается в MBR) умеет грузить просто несколько секторов с определённого места (обычно -- в начале соотв раздела). Сколько и откуда? А прямо внутри LI это и впишем. Так что получается, что если мы меняем вторичный загрузчик, надо переписать и первичный, вдруг место или размер вторичного изменились.
LO должен загрузить ядро и стартовый виртуальный диск (a.k.a. initrd), передать ядру параметры. Или загрузить вторичный загрузчик. Где лежат ядро и initrd? А где-то в файловой системе, причём необязательно в секторах подряд. Значит, надо составить карту: список секторов, в которых они лежат. Вот эту карту и надо присобачить к LO, и делать это всякий раз, когда то, что надо грузить LILO, как-то меняется.
Обратная зависимость: чтобы установить Linux, надо загрузить весь Linux и запустить утилиту ^lilo^. Если вы изменили ^/etc/lilo.conf^, надо переписывать всё: карты размещения вторичного загрузчика, сам вторичный загрузчик, первичный загрузчик и его карты. Если вы этого не сделали, но обновили ядро, что может быть? Система продолжает загружаться по старой карте (сектора, где _раньше_ лежало старое ядро), до тех пор, пока на это свободное место кто-нибудь что-нибудь не запишет. Потом начнёт грузиться _это_, и всё сломается.
Карта размещения, ядро и стартовый виртуальный диск лежать в ^/boot^.
Если несколько пунктов меню (несколько ядер, загрузка других ОС) -- это всё надо подготовить заранее.
>> Cтартовый виртуальный диск
LILO (точнее, LO) -- не настоящий вторичный загрузчик, скорее, полуторный: он не может подгружать модули ядра и не разбирается в файловых системах. Только сектора грузит в память. Например, для того, чтобы смонтировать файловую систему с жёсткого диска, необходим драйвер жёсткого диска, то есть модуль ядра. Но взять его с этого диска нельзя, потому что он и нужен для доступа к диску. И кто будет компоновать модуль с ядром? LO этого не умеет.
Можно собрать такое ядро, в котором есть все базовые драйверы, как в 90-х. Всякий раз надо пересобирать ядро? Это тяжело, не имея опыта.
Если LO умеет загрузить _один_ файл в память, то несложно загрузить _два_. Первый -- это ядро, а второй -- это образ файловой системы в памяти (стартовый виртуальный диск), ядро его смонтирует, а там -- модули, пара-тройка утилит (^mount^ и ^sh^, например), какие-то мелкие настройки и командный сценарий, который, все нужные действия делает.
Такой вот настоящий, большой вторичный загрузчик, возможности которого не ограничены, потом у что он Linux.
> Системная загрузка
В конце концов initrd смонтирует корневую файловую систему, размонтирует себя (чтобы память не занимать) и запустит оттуда настоящий ^init^ -- папу всех процессов. Linux начинает работать.
. определение устройств
. запуск служб
. запуск getty/login
>> Определение устройств
Далеко не все модули лежат в initrd, например, звуковая карта, видео, всякие не нужные для загрузки автоматически определяемые устройства. Хотя можно всё напихать и в initrd, есть даже такой дистрибутив на базе Sisyphus: RAD Linux, он предназначен для маршрутизаторов и всяких сетевых устройств, загружается по сети или как-то ещё, и представляет собою один сплошной initrd, загружаемый в память, и работающий как есть.
Распознаванием устройства занимается служба ^udev^. Например, если ядро распознало устройство с некоторым идентификатором, но не знает, что это такое, оно посылает ^udevd^ сообщение ``вот, появилось какое-то устройство''. Демон ^udevd^ говорит ``о, так это ж BlueTooth'', и смотрит в специальную таблицу, где написано ``модуль ядра подгрузить такой-то, создать такую-то файл-дырку в ^/dev^ с такими-то правами'' и ещё что-то, например, запуск каких-то программ. И то же самое при удалении устройства.
В старых книжках сказано, что распознаванием устр-в занимается ядро. Сейчас ядро только определяет идентификаторы, а логика работы передана программе.
> Запуска служб
Папа всех процессов управляется файлом ^/etc/inittab^ -- один из самых старых файлов в UNIX, поэтому его синтаксис хитрый. Файл небольшой, потому что он запускает командный сценарий ^rc^, а уже ^rc^ запускает все службы.
Когда-то всё, что надо запускать в системе складывалось в один сценарий ^/etc/rc^. Но это неудобно, все службы в одном сценарии, поэтому при удалении и обновлении служб приходится редактировать этот очень длинный файл.
>> Схема ".d"
Это способ перейти от монолитного файла "{имя}" ^->^ "{имя}.d/{часть1}", "{имя}.d/{часть2}", ...
Например, настроечный файл web-сервера и модули, при добавлении модуля него настройки не вписываются с один файл, а кладутся рядом. Потом администратор эти настройки редактирует. И при удалении модуля вместо того, чтобы как-то искать, а где же эти настройки неузнаваемые, просто удаляется файл с дополнит. настройками.
Или правила для ^udevd^, если поддержка BlueTooth вообще установлена, появляются соотв. правила в ^/etc/udev/rules.d/80-bluetooth.rules^, которые она носит с собой. Нет поддержки BlueTooth в системе -- нет файла.
Получается, что "{имя}.d/" -- это настроечный файл (или сценарий на sehll), распределённый по файлам каталога по принципу ``одна сущность -- один файл''.
>> Start/Stop сценарий
Схема ".d" для старта и останова служб имеет дополнительный плюс -- самостоятельный запуск файлов. Если сценарий старта был монолитный, то отдельную службу остановить или перезапустить модно было бы только руками. А если попилить по принципу ``одна служба -- один сценарий'', можно сделать такой сценарий старт-стопным. То есть оформить как программу на shell, принимающую стандартные параметры ^start^ и ^stop^, что означает ``запустить службу'' и ``остановить службу'' (при этом параметры самих демонов могут быть какими угодно).
Так что при старте системы запускается последовательность файлов из ^/etc/init.d^ с параметром ^start^, а при останове -- ^stop^.
>> Уровни выполнения
Есть несколько т. н. уровней выполнения, то есть профилей запуска системы, изменение состояния системы трактуется как переход с уровня на уровень. Переход на уровень 0 -- это выключение системы, на уровень 6 -- перезагрузка, а остальные -- это профили запуска. 1 -- однопользовательский вариант, запускается только shell, 2 -- многопользовательский без сети (shell плюс несетевые службы), 3 -- многопользовательский с сетью (то же плюс сетевые службы) и 5 -- то же плюс графическая оболочка (4 и 7 можно использовать по своему усмотрению).
Фактически, переход с уровня на уровень просто меняет список служб, которые запущены и остановлены, и просто надо некоторые старт-стопные сценарии запустить с параметром ^start^, а некоторые -- с параметром ^stop^.
Какие и в каком порядке сценарии запускаются при переходе на уровень, скажем, 5? Все эти сценарии лежат в каталоге ^/etc/rc/rc5.d/^, и называются так: сначала "D" или "K", потом -- двузначный номер, потом имя; причём все они -- символьный ссылки на настоящие сценарии в ^init.d^. Старая добрая ".d" схема.
На букву "K" (_K_ill) начинаются файлы, которые надо запустить с параметром ^stop^, а на букву "S" (_S_tart) -- которые надо запустить с параметром ^start^. А цифры нужны для упорядочивания, т. к. сценарии запускаются в _лексикографическом_ порядке, сначала с меньшими числами, затем -- с большими.
Итого, при старте системы выполняются старт-стопные сценарии с параметром ^start^ просто подряд.
>> Getty
В конце концов ^init^ запускает утилиту ^mingetty^ для всех консолей. Mingetty проверяет, есть ли человек за клавиатурой или нет. Если человек есть (то есть он начал нажимать какие-то кнопки), то ^mingetty^ запускает ^login^, пользователь вводит идентификационную информацию, пароль -- система готова к работе.
> Трудности, неприятности и особенности
Многосистемный компьютер:
1 Не менее одного раздела на систему (нельзя удалить файлы и образовать место для установки Linux)
1 Надо настроить загрузчик так, чтобы он загружал все нужные ОС (для виндовз в ^/etc/lilo.conf^ надо просто указывать ^other=/dev/{раздел_с_windows}^).
1 С дополнительных разделов виндовз не грузится.
1 От переразметки диска меняется именование разделов, виндовз при этом может перестать работать. Виндовз2000 и выше умеет переименовывать разделы ``обратно'', но для этого ему надо сперва загрузиться.
1 При установке виндовз затирается LI в MBR, необходимо загрузиться с LiveCD, смонтировать корневой каталог с ^/etc/lilo.conf^ и заново запустить ^lilo^. (Наверное, стоит нарисовать такой сценарий, который это сам делает в LiveCD).
1 Существует несколько вариантов представления одной и той же геометрии диска (попытка впихнуть большое число секторов и дорожек в старый формат, куда эти числа не помещаются). Если старый загрузчик использовал один формат, а новый использует другой, старый может не заработать. То есть Linux грузится, а виндовз -- нет, хотя диск монтируется и все файлы в порядке. Тогда начинается самое неприятное место, потому что тут много знания применять надо и действовать вручную.
Запись на NTFS: ^ntfs3g^ и ^captive^