Задумывались ли вы когда-нибудь над тем, что заставляет работать ваш ПК? Я имею в виду по-настоящему работать, а не просто дежурную фразу вроде «процессор загружает операционную систему». Сейчас это стало также очевидно, как и то, что предметы состоят из атомов.
Речь идёт о главных компонентах: когда вы нажимаете кнопку включения и блок питания подаёт напряжение через свои 12, 5 и 3-вольтные линии к материнской плате. Откуда процессору вообще знать, что запускать, и как происходит загрузка в память, когда загружать пока, по сути, нечего?
Такой ход мыслей сложился у нас после мысленного эксперимента: окажись вы в комнате с одной только дискетой и ПК с пустым жёстким диском, смогли бы вы загрузить компьютер и запустить операционную систему? И более того, что надо делать, чтобы запустить ОС?
Ответы на эти вопросы содержатся в данной статье. Она состоит из двух частей. Сначала мы взглянем на мысленный эксперимент, о котором писалось выше, и попытаемся ответить на вопрос: что нужно программировать в первую очередь, чтобы заставить систему загружаться? А потом соберём настоящую операционную систему при помощи дистрибутива Arch Linux, чтобы наглядно показать слаженную работу компонентов нижнего уровня. Сделайте глубокий вдох, мы погружаемся…
Часть 1
Начальная загрузка компьютера
Когда вы нажимаете кнопку включения, блок питания подаёт электроэнергию на ATX-кабели (через них подаётся питание к накопителям с интерфейсом SATA), материнскую плату и видеокарту. БП понадобится несколько секунд, чтобы нормализовать электроснабжение, а системный контроллер будет подавать сигнал возврата в исходное состояние для процессора (как если бы вы нажали Reset) до тех пор, пока он не получит сигнал о том, что напряжение в норме. Устройства вроде накопителей имеют неизменяемые логические схемы и потому запускаются самостоятельно. У более сложных компонентов вроде графического процессора или сетевого адаптера имеется собственная периферия, посредством которой и происходит инициализация.
Как правило, раньше всё начиналось с первой инструкции, взятой из энергонезависимой памяти EEPROM и записанной в 0ХFFFF0h (1Мб – максимальный размер памяти для 16-битного адресного пространства), сейчас же происходит перенос в 0ХFFFFFFF0h (4 Гб, то же самое, но для 32-битного адресного пространства). Первая команда – так называемый вектор сброса – это всегда длинный переход к действительному месторасположению BIOS – обычно это 0хF00000.
Теперь ваш ПК готов запустить ПО для самотестирования на старте (процедура POST). POST инициализирует регистры процессора, верифицирует прямой доступ к памяти, BIOS, таймер и контроллер прерываний, проверяет системную память и загрузочные устройства. В современных устройствах также проверяются системные шины (USB, PCIe и т. д.), дисплеи и другое оборудование. Помимо этого, происходит инициализация ACPI (усовершенствованный интерфейс конфигурирования системы и управления энергопитанием). Он помещает данные в память, и впоследствии они используются ядром операционной системы.
В случае возникновения неполадок загрузка останавливается. BIOS может сообщить об этом несколькими способами: надписью с текстом или кодом ошибки, несколькими звуковыми сигналами или же передать сообщение через шину PCI на отладочную карту.
Однако если, как это часто бывает, трудностей не возникло, BIOS передаёт управление ПК операционной системе. Но каким образом? Все эти прошедшие проверку шины и устройства придутся очень кстати на данном этапе, поскольку у ОС будет иметься список поддерживаемых накопителей, закреплённых за обозначенными шинами. В стародавние времена это могли быть обычная дискета или жёсткий диск с интерфейсом IDE. Сейчас, как вы и сами знаете по режиму UEFI, вариантов загрузки огромное множество.
Не важно, будет ли это накопитель с поддержкой NVMe или USB-накопитель, последовательность передачи та же самая: BIOS загружает первые 512 байт из выбранного загрузочного устройства – из так называемой основной загрузочной записи (MBR). Это и есть первый сектор диска. Он загружается в память и адресует процессору выполнение этого кода.
В те же 512 байт должна помещаться таблица разделов. Она в свою очередь состоит из 4-х разделов, один из которых помечен как «Активный» загрузочный раздел. Добавьте к этому подпись 55AAh, которая является идентификатором того, что диск является загрузочным.
Не трудно догадаться, что места в MBR хватает только на то, чтобы перенаправить процессор туда, где есть ещё больше исполняемого кода. И потому MBR часто называют загрузчиком первой ступени. В стандартных системах Windows код начальной загрузки даёт команду BIOS загрузить первый сектор из активного раздела в память и запустить его.
К слову, MBR на современных ПК вытеснил UEFI, поскольку последний может напрямую распознавать диск и схемы размещения разделов, а также сохранять загрузочный код в сектор FAT32. UEFI тем самым избавился от ограничения, которое присутствовало в MBR. Оно заключалось в том, что у основной загрузочной записи имелось ограничение на 2 ТБ для нескольких разделов и 4 ТБ на один раздел. Тем не менее MBR обратно совместима с системами (U)EFI.
Мы уже там?
Наша ОС готова к запуску. На данном этапе MBR загружает первые три байта из активного раздела – инструкцию JMP. Данная команда сообщает системе, какой участок кода нужно пропустить, прежде чем перейти к загрузчику второго уровня. А после мы почти готовы запустить ядро операционной системы.
Главная цель загрузчика второй ступени – загрузить конфигурационный файл, а затем передать его ядру операционной системы со всеми требуемыми опциями, будь то режим восстановления или более старые ядра. Сложности процессу добавляет то обстоятельство, что загрузчику требуется доступ к файлу как к части файловой системы высокого уровня (FAT32, NTFS, ext 2/4).
Вы могли бы подумать, что при таком порядке загрузки было бы удобно использовать жестко заданные настройки. И хотя такая система может быть вполне себе рабочей, отсутствие гибкости привело бы к её поломке. Во всех популярных загрузчиках ОС имеется файл конфигурации, который загружается во время начальной загрузки. На основании его создаются параметры последующей загрузки.
В Windows NT, 2000 и XP использовался загрузчик NTLDR c файлом boot.ini в корневом каталоге активного раздела. Для распознавания устройств использовался инструмент NTDETECT.COM. Начиная с Windows Vista стал использоваться загрузчик BOOTMGR. В его папке загрузки имеются данные конфигурации загрузки (BCD) в двоичном коде. BOOTMGR предназначен для поддержки современных конфигураций GPT и UEFI, а BCD нельзя корректировать вручную.
Большинство ОС с открытым исходным кодом используют выпущенный в 2012 году загрузчик Grub 2 (GRand Unified Bootloader). Его файл конфигурации grub.сfg хранится в /boot/grub/folder Каталог, в свою очередь, находится либо в собственном разделе загрузки, либо в основной папке загрузочной ОС.
И вот наша Windows запустилась. Но надо знать, что загрузчик второго уровня может поддерживать несколько операционных систем в мультизагрузочном формате. Выбор ОС, которую вы желаете загрузить, даёт старт конечной стадии загрузочного процесса.
В зависимости от ОС и типа ядра загрузчик установит в память образ ядра по пути, указанном в файле конфигурации. Он указывает процессору на инструкцию JMP в определённой локации в пределах ядра и запускает операционную систему.
Часть 2
Собираем ОС
Давным-давно на Reddit кто-то спросил: «Если вас закрыть одного в комнате с ПК, но без ОС, смогли бы вы написать её с нуля?» На фоне прочих шутеек выделялся ответ Крагена Хавьера Ситакера, сотрудника Canonical (компания-разработчик Ubuntu).
По задумке Ситакера, чтобы написать и запустить ОС, требуется базовый функционал. А чтобы реализовать этот базовый функционал, нужен первичный загрузчик.
В первой части мы упоминали, что с помощью BIOS обеспечивается базовый доступ к оборудованию. Делается это за счёт прерываний. Получив доступ к устройствам, загрузчик операционной системы инициализирует ядро ОС и запускает его. Ядро же, в свою очередь, выполняет начальную загрузку.
Обработчики прерываний регулируют все: вывод на экран (10h), чтение/запись с устройств хранения (13h), доступ к порту с последовательной выдачей данных (14h), чтение клавиатуры (16h) и доступ к шине PCI (1 Ah).
Посредством регистров al и ah в Intel 8088 можно напечатать на экране символ «!». Для этого нам понадобится функция экрана 0Eh и обработчик прерываний 10h в ассемблере. Выглядеть всё это будет таким образом:
Надеемся, суть вы уловили. А если нет, то просто поверьте, что BIOS может делать элементарные вещи. Иначе, как, по-вашему, создаётся и управляется меню BIOS?
Чтобы с чего-то начать, вам нужно найти способ ввода машинного кода в компьютер и задействовать процессор для его выполнения. Предназначенное для этого ПО называется «машинный код монитора». Одно время оно было очень популярно, но отчего-то впало в немилость. Однако Ситакер написал очень простой код на языке C:
Функции getchar () и putchar () являются обработчиками прерываний в BIOS. Убедитесь сами: функция AH = 01h, Int 21 считывает символ с эхом, в то время как getchar () и AH = 02h, Int 21 записывает символ в стандартный выход.
Согласно этому магическому сценарию теперь вам надо загрузить машинный код монитора в память, запустить и записать на дискету. Однако нужно ещё улучшить его современными фичами вроде клавиши возврата на одну позицию, отображения вводимых символов и другими функциями редактора. Не забудьте добавить функции отображения области памяти, смены целевого адреса и прерывающийся циклический код.
Просто записывать чистый машинный код в восьмеричном формате неинтересно, даже если у вас шикарный многофункциональный монитор. Так что следующим вашим шагом будет создание основного ассемблера.
Но даже самое простое ассемблирование – это боль. И потому, чтобы ускорить развитие прототипа нашей ОС, нужно задействовать высокоуровневый язык программирования. Ситакер использовал Forth, но вы можете использовать любой высокоуровневый язык.
И вот теперь, когда код записан на дискету, вам нужно создать базовую файловую систему. С ней вы можете создавать, загружать и менять команды в редакторе Forth, и при этом вам не придётся перезаписывать данные. Потом вам понадобится создать базовый набор команд для управления этими файлами – «удалить», «переместить», «переименовать», «создать список» и «копировать». Базовый инструмент для сжатия данных мог бы ускорить этот процесс. И почему бы не улучшить программу сборки и не внедрить текстовый редактор вместо того, чтобы редактировать память напрямую?
На данном этапе всё работает в 16-битном режиме «Real Mode». Он схож с режимом нейтрализации неисправности в 8088. И да, это не есть хорошо. Для продолжения вам нужно перейти в 32-битный режим. Для этого требуется повторная компиляция. Данный режим предоставляет полный доступ к памяти и защиту ввода-вывода.
Через BIOS можно задать разрешение экрана 640x480 с глубиной цвета в 16 бит, создать библиотеки для рисования примитивов и контролировать указатель мыши.
Знакомьтесь: Arch
Итак, квест по разработке операционной системы продолжается. И продолжим мы его с Arch Linux. Linux Arch заслужил репутацию самого хардкорного дистрибутива, а мем «I use Arch btw» («Кстати, я юзаю Arch») используют, чтобы показать своё превосходство. Это очень «прикладной» дистрибутив, многое тут нужно настраивать самим. А ещё, чтобы установить его, вам придётся установить рабочий стол GUI. Однако к этому дистрибутиву прилагается лучшая документация в мире, большинство его программных хранилищ постоянно обновляются. Вместе с тем он стал первым дистрибутивом, который стал поставлять обновления для ядер и ПО.
Так что, по правде говоря, данный гайд представляет собой лишь переработанный текст руководства по установке ядра ОС (не забудьте заглянуть туда: https://wiki.archlinux.org/index.php/installation_guide ).
Часть 3
Устанавливаем Arch Linux
Прежде чем начать что-либо делать, нужно скачать Arch Linux в формате ISO-файла. Зайдите на www.archlinux.org/download и выберите один из торрентов или же через зеркало вебсайта скачайте локализованную версию дистрибутива.
Лучше всего запустить проверку целостности скачанного вами файла – так вы сможете убедиться, что при загрузке не возникло никаких ошибок и он не был никем изменён. ISO-файл и хеш-сумму следует хранить в разных системах – так они будут меньше подвержены риску.
Проще всего выполнить проверку с помощью утилиты IgorWare Hasher. Нужно только загрузить ISO-файл и нажать «Calculate». Сгенерированный алгоритмами SHA-1 и MD5 хэш должен совпадать с тем, что на странице загрузки Arch Linux. Его можно просто скопировать и вставить, и тогда вам не придётся высчитывать длинные шестнадцатеричные числа.
Если вы хотите установить образ на ПК, вам понадобится записать ISO на CD/DVD или, вероятнее всего, на USB-накопитель. Для этого можно воспользоваться программой Etcher.io. Останется только перезагрузить систему и активировать меню загрузки через функциональные клавиши F2, F10 или F12 для загрузки ПК с флэшки.
Мы остановим свой выбор на эмуляторе VirtualBox. Для этого нам понадобится 64-битная машина, в идеале с 4 гигабайтами памяти, однако двух или одного гигабайт будет вполне достаточно. Добавьте жесткий диск по меньшей мере на 20 Гб, контроллер ICH9, графический контроллер VMSVGA. Аппаратные средства трёхмерной графики тоже не помешают. Остаётся только записать ISO-образ Arch на оптический диск.
Меню Grub для начала установки назначает верхнюю опцию. Обычно в таких случаях начинают с выбора клавиатурной раскладки. Перечень доступных клавиатур (их много, но, скорее всего, вы выберете qwerty/folder) выводится с помощью команды:
Активировать любой вариант можно с помощью загрузки карты клавиатуры <keymapname>. Например, loadkeys uk/
Ещё надо проверить тип периферии в ПК: EFI или BIOS/CSM. Чтобы узнать, есть ли у вас папка EFI, введите команду: Is/sys/firmware/efi/efivars
Если папка имеется, значит у вас система EFI, если нет – BIOS. Каждый тип периферии задаёт свою схему размещения раздела. Для запуска обновлений система должна иметь подключение к сети. В системах VirtualBox и большинстве ПК c проводным Ethernet оно работает без проблем. DHCP также будет активен. Протестировать его можно через : ip link и: ping archlinux.org
Если вы установили систему на ПК с беспроводным адаптером, нужно проверить, распознала ли она оборудование и работает ли оно. Если нет, то, можно сказать, вы в затруднительном положении, поскольку добавление модулей ядра в данном руководстве не рассматривается. Однако ничего сложного тут нет: надо только узнать производителя оборудования и модель, найти и загрузить модуль ядра (драйвер) и добавить его через modprobe <module name>.
Простой способ проверить работоспособность адаптера PCI — ввести команду lspci и искать беспроводное сетевое устройство. Для USB-адаптеров используйте команду lsusb и проделайте то же самое.
Добавление беспроводной сети требует более обстоятельного подхода. Посредством iwd, управляющей программы от Intel, впишите: iwctl
В программу встроены интерактивные подсказки: в любом месте можно вписать help, чтобы получить дополнительные сведения, а с помощью клавиши Tab названия команд и станций заполняются в автоматическом режиме.
Процесс настройки будет выглядеть примерно так (#комментарии в терминал вводить не нужно):
device list #перечисляет совместимые беспроводные устройства
station <device name> scan #ищет идентификаторы SSID (доступных сетей) на выбранном устройстве
station <device name> get-networks #перечисляет обнаруженные SSID
station <device name>connect <SSID name> #подключение c подтверждением пароля
Exit #возвращает обратно к запросу
На данном этапе нужно подготовить диск, создать разделы и инициализировать файловую систему. Схемы разделов для UEFI и BIOS немного отличаются: для первой требуется отдельный раздел FAT32. Он выступает в качестве загрузчика второй ступени. Для BIOS же нужен корневой раздел и иногда ещё один для подкачки, однако такой расклад выходит из моды.
Чтобы получить доступ к списку блочных устройств, впишите: lsblk
Не обращайте внимание на «loop» «rom», нам нужно только то, что имеет отношение к слову «disk». Если брать VirtualBox и ПК с SATA-накопителем, то устройство обозначается как sda, если речь идёт о NVMe и флэш-накопителях, то как nvme0n1 и mmcblk0 – да, Linux не слишком последовательна в наименованиях.
Для получения более подробной информации воспользуйтесь старым инструментом DOS для разметки диска fdisk, а затем впишите команду fdisk-l. Затем пропишите полный путь до устройства, которое вы хотите установить: fdisk/dev/sda
Нажмите n, чтобы создать новый раздел, p – чтобы выбрать основной раздел, затем Return for default, Return for default, и +512M, чтобы обозначить его размер. Нажмите t, чтобы поменять тип раздела, выберите EFI и впишите ef.
Чтобы создать корневой раздел, впишите n, затем default, default, default, чтобы задействовать основное пространство. Впишите w, чтобы записать эти изменения и выйти. Теперь нужно создать файловые системы; для EFI это FAT32, а для корневого раздела – нативный линуксовый формат ext4.
Итак, разделы созданы, отформатированы, но, поскольку мы работаем с Linux, нам нужно установить ещё один, чтобы получить доступ к первым двум. Для двух разделов EFI это будет раздел /dev/sda2.
Теперь Вам понадобится ввести несколько команд, чтобы ваша установка была готова к обновлениям.
Для этого нам понадобится Pacman – менеджер пакетов для Arch. Его часто используют при работе с этим дистрибутивом. Сначала синхронизируйте базу данных, так чтобы она была готова для установки ПО, установите инструмент под названием Reflector и используйте его для подготовки списка из 12 самых свежих и быстрых зеркал.
Большинство пакетов можно установить через скрипт pacstrap. Подождите, пока загрузятся основные файлы, а сами выпейте чашечку кофе. Если загрузка по непонятным причинам остановится и или затянется, процесс можно перезапустить:
На данном этапе ОС можно считать установленной, однако нужно ещё проделать немного скучной, но всё же необходимой работы.
Перво-наперво необходимо сохранить конфигурацию монтированного раздела в таблице файловой системы (fstab). Так ОС будет знать, что подключать во время начальной загрузки. Сейчас у вас запущен ISO-файл с корневого каталога. Для смены текущего каталога на директорию /mnt впишите команду chroot. Выберите нужный часовой пояс или используйте UTC.
Ещё один набор настроек, который принимают как должное, — это региональный стандарт (локаль). Он задаёт язык, систему счисления, форматы даты и валюту. Чтобы задать региональные настройки, вам потребуется отредактировать файл с локалями.
Впишите строчку #en_US.UTF-8 UTF-8, удалите # и нажмите Ctrl-O для сохранения и Ctrl-X для выхода. Закончите настройку посредством команды:
А теперь пришло время установить локальные сетевые настройки. Сначала создайте имя хост-системы. Оно может быть абсолютно любым.
Последняя команда позволяет отредактировать хост-файл, добавляет вышеуказанные строки и запоминает новое «имя ПК». Нажмите Ctrl-O для сохранения и Ctrl-X для выхода:
Осталось только включить сетевое управление и поставить пароль для учетной записи администратора.
И последнее (на этот раз точно последнее), что нам осталось, — это установить загрузчик GRUB для нашей не-EFI системы. Нам понадобится три команды: первая — для установки GRUB, вторая — для установки GRUB в загрузочный раздел, и последняя — для создания конфигурационного файла загрузчика. Он понадобится, когда система станет жизнеспособной.
Если у вас система EFI, то нужно действовать схожим образом. Однако вам ещё потребуется создать папку boot/efi и вмонтировать загрузочный раздел таким вот образом:
На данном этапе можно перезагрузить ПК (не забудьте удалить файл ISO) и залогиниться в системе. Кроме командной строки вы ничего не увидите. Однако c установленным Linux можно скачать всё остальное. Завершить процесс настройки мы решили с установки стандартной графической оболочки под названием Gnome.
Извлеките USB-накопитель или удалите ISO-образ, запустите Arch, войдите в систему под корневым именем и паролем и поприветствуйте Arch Linux с интерфейсом Gnome.
Надеемся, теперь вы знаете чуть больше о работе ПК и том, что нужно делать, чтобы он загрузился.
Теперь создайте первые два раздела в таком порядке: небольшой по размеру EFI и корневой раздел Linux. Если у вас BIOS, то первый раздел можно не создавать, – ничего в работе системы не изменится.