Os - Что нужно для написания операционной системы? Как создать свою систему для любой деятельности.

Читая Хабр в течении последних двух лет, я видел только несколько попыток разработки ОС (если конкретно: от пользователей и (отложено на неопределённый срок) и (не заброшено, но пока больше походит на описание работы защищённого режима x86-совместимых процессоров, что бесспорно тоже необходимо знать для написания ОС под x86); и описание готовой системы от (правда не с нуля, хотя в этом нет ничего плохого, может даже наоборот)). Мне почему-то думается, что почти все системные (да и часть прикладных) программисты хотя бы раз, но задумывались о написании собственной операционной системы. В связи с чем, 3 ОС от многочисленного сообщества данного ресурса кажется смешным числом. Видимо, большинство задумывающихся о собственной ОС так никуда дальше идеи и не идёт, малая часть останавливается после написания загрузчика, немногие пишут куски ядра, и только безнадёжно упёртые создают что-то отдалённо напоминающее ОС (если сравнивать с чем-то вроде Windows/Linux). Причин для этого можно найти много, но главной на мой взгляд является то, что люди бросают разработку (некоторые даже не успев начать) из-за небольшого количества описаний самого процесса написания и отладки ОС, который довольно сильно отличается от того, что происходит при разработке прикладного ПО.

Этой небольшой заметкой хотелось бы показать, что, если правильно начать, то в разработке собственной ОС нету ничего особо сложного. Под катом находится краткое и довольно общее руководство к действию по написанию ОС с нуля.

Как не надо начинать
Просьба не воспринимать следующий ниже текст как явную критику чьих-то статей или руководств по написанию ОС. Просто слишком часто в подобных статьях под громкими заголовками акцент делается на реализации какой-то минимальной заготовки, а подаётся она как прототип ядра. На самом деле следует задумываться о структуре ядра и взаимодействии частей ОС в целом, а тот прототип рассматривать как стандартное «Hello, World!»-приложение в мире прикладного ПО. В качестве небольшого оправдания этих замечаний, следует сказать, что ниже есть подраздел «Hello, World!», которому в данном случае уделено ровно столько внимания сколько нужно, и не больше.

Не надо писать загрузчик. Умные люди придумали Multiboot Specification , реализовали и подробно описали, что это такое и как его использовать. Не хочу повторяться, просто скажу, что это работает, облегчает жизнь, и его надо применять. Спецификацию, кстати, лучше прочесть полностью, она небольшая и даже содержит примеры.

Не надо писать ОС полностью на ассемблере. Это не так чтобы плохо, скорее наоборот - быстрые и маленькие программы всегда будут в почёте. Просто так как этот язык требует значительно больших усилий на разработку, то использование ассемблера приведёт только к уменьшению энтузиазма и, как следствие, к забрасыванию исходников ОС в долгий ящик.

Не надо загружать кастомный шрифт в видео память и выводить что-либо на русском. Толку от этого никакого. Гораздо проще и универсальнее использовать английский, а изменение шрифта оставить на потом, загружая его с жёсткого диска через драйвер файловой системы (заодно будет дополнительный стимул сделать больше, чем просто начать).

Подготовка
Для начала как всегда следует ознакомиться с общей теорией, дабы иметь какие-то представления о предстоящем объёме работ. Хорошими источниками по рассматриваемому вопросу являются книги Э. Таненбаума, которые уже упоминались в других статьях о написании ОС на Хабре. Также есть статьи с описанием существующих систем, и есть различные руководства/рассылки/статьи/примеры/сайты с уклоном в разработку ОС, ссылки на часть из которых приведены в конце статьи.

После начального ликбеза необходимо определиться с главными вопросами:

  • целевая архитектура - x86 (real/protected/long mode), PowerPC, ARM, ...
  • архитектура ядра/ОС - монолит, модульный монолит, микроядро, экзоядро, разные гибриды
  • язык и его компилятор - C, C++, ...
  • формат файла ядра - elf, a.out, coff, binary, ...
  • среда разработки (да, это тоже играет не последнюю роль) - IDE, vim, emacs, ...
Далее следует углублять знания согласно выбранному и по следующим направлениям:
  • видео память и работа с ней - вывод в качестве доказательства работы необходим с самого начала
  • HAL (Hardware Abstraction layer) - даже если поддержка нескольких аппаратных архитектур и не планируется грамотное отделение самых низкоуровневых частей ядра от реализации таких абстрактных вещей как процессы, семафоры и так далее лишним не будет
  • управление памятью - физической и виртуальной
  • управление исполнением - процессы и потоки, их планирование
  • управление устройствами - драйвера
  • виртуальные файловые системы - для обеспечения единого интерфейса к содержимому различных ФС
  • API (Application Programming Interface) - как именно приложения будут обращаться к ядру
  • IPC (Interprocess Communication) - рано или поздно процессам придется взаимодействовать
Инструменты
Учитывая выбранные язык и средства разработки следует подобрать такой набор утилит и их настроек, которые в будущем позволят путём написания скриптов, максимально облегчить и ускорить сборку, подготовку образа и запуск виртуальной машины с проектом. Остановимся немного детальнее на каждом из этих пунктов:
  • для сборки подойдут любые стандартные средства, как то make, cmake,… Тут в ход могут пойти скрипты для линкера и (специально написанные) утилиты для добавления Multiboot-заголовка, контрольных сумм или для каких-либо других целей.
  • под подготовкой образа имеется ввиду его монтирование и копирование файлов. Соответственно, формат файла образа надо подбирать так, чтобы его поддерживала как утилита монтирования/копирования, так и виртуальная машина. Естественно, никто не запрещает совершать действия из этого пункта либо как финальную часть сборки, либо как подготовку к запуску эмулятора. Всё зависит от конкретных средств и выбранных вариантов их использования.
  • запуск виртуальной машины труда не представляет, но нужно не забыть сначала отмонтировать образ (отмонтирование в этом пункте, так как до запуска виртуальной машины реального смысла в этой операции нет). Также не лишним будет скрипт для запуска эмулятора в отладочном режиме (если таковой имеется).
Если все предыдущие шаги выполнены, следует написать минимальную программу, которая будет загружаться как ядро и выводить что-нибудь на экран. В случае обнаружения неудобств или недостатков выбранных средств, необходимо их (недостатки) устранить, ну или, в худшем случае, принять как данность.

На данном шаге необходимо проверить как можно больше возможностей средств разработки, которые планируется использовать в будущем. Например, загрузку модулей в GRUB или использование в виртуальной машине физического диска/раздела/флешки вместо образа.

После того как этот этап прошёл успешно, начинается настоящая разработка.

Обеспечение run-time поддержки
Так как предлагается писать на языках высокого уровня, следует позаботиться об обеспечении поддержки части средств языка, которые обычно реализуются авторами пакета компилятора. Например для C++, сюда относятся:
  • функция для динамического выделения блока данных на стеке
  • работа с heap
  • функция копирования блока данных (memcpy)
  • функция-точка входа в программу
  • вызовы конструкторов и деструкторов глобальных объектов
  • ряд функций для работы с исключениями
  • стаб для нереализованных чисто-виртуальных функций
При написании «Hello, World!» отсутствие этих функций может никак не дать о себе знать, но по мере добавления кода, линкер начнёт жаловаться на неудовлетворённые зависимости.

Естественно, тут же следует упомянуть и о стандартной библиотеке. Полная реализация не является необходимой, но основное подмножество функций реализовать стоит. Тогда написание кода будет значительно привычнее и быстрее.

Отладка
Не смотрите, что об отладке говорится ближе к концу статьи. На самом деле это очень серьёзный и непростой вопрос в разработке ОС, так как обычные средства тут неприменимы (за некоторым исключением).

Можно посоветовать следующее:

  • само собой разумеющееся, отладочный вывод
  • assert с немедленным выходом в «отладчик» (см. следующий пункт)
  • некоторое подобие консольного отладчика
  • проверить не позволяет ли эмулятор подключать отладчик, таблицы символов или ещё что-нибудь
Без встроенного в ядро отладчика поиск ошибок имеет вполне реальный шанс превратится в кошмар. Так что от его написания на некотором этапе разработки просто никуда не деться. А раз это неизбежно, то лучше начать его писать заранее и таким образом значительно облегчить себе разработку и сэкономить довольно много времени. Важно суметь реализовать отладчик независимым от ядра образом, чтобы отладка минимальным образом влияла на нормальную работу системы. Вот несколько типов команд, которые могут быть полезны:
  • часть стандартных отладочных операций: точки останова, стек вызовов, вывод значений, печать дампа, ...
  • команды вывода различной полезной информации, вроде очереди исполнения планировщика или различной статистики (она не так бесполезно как может показаться сначала)
  • команды проверки непротиворечивости состояния различных структур: списков свободной/занятой памяти, heap или очереди сообщений
Развитие
Дальше необходимо написать и отладить основные элементы ОС, которые в данный момент должны обеспечить её стабильную работу, а в будущем - лёгкую расширяемость и гибкость. Кроме менеджеров памяти/процессов/(чего-нибудь ещё) очень важным является интерфейс драйверов и файловых систем. К их проектированию следует подходить с особой тщательностью, учитывая всё разнообразие типов устройств/ФС. Со временем их конечно можно будет поменять, но это очень болезненный и подверженный ошибкам процесс (а отладка ядра - занятие не из лёгких), поэтому просто запомните - минимум десять раз подумайте над этими интерфейсами прежде чем возьмётесь за их реализацию.
Подобие SDK
По мере развития проекта в нём должны добавляться новые драйвера и программы. Скорее всего уже на втором драйвере (возможно определённого типа)/программе будут заметны некоторые общие черты (структура каталогов, файлы управления сборкой, спецификация зависимостей между модулями, повторяющийся код в main или в обработчиках системных запросов (например если драйвера сами проверяют их совместимость с устройством)). Если так и есть, то это признак необходимости разработки шаблонов для различного типа программ под вашу ОС.

Необходимости в документации, описывающей процесс написания того или другого типа программы, нет. Но сделать заготовку из типовых элементов стоит. Это не только упростит добавление программ (что можно делать и копированием существующих программ с их последующим изменением, но это потребует больше времени), но также позволит легче их обновлять при изменениях в интерфейсах, форматах или чем-то ещё. Понятно, что таких изменений в идеале быть не должно, но так как разработка ОС - вещь нетипичная, то есть достаточно много мест для потенциально неверных решений. А вот понимание ошибочности принятых решений как всегда придёт через некоторое время после их внедрения.

Дальнейшие действия
Если кратко, то: читать про операционные системы (и в первую очередь именно про их устройство), развивать свою систему (темпы на самом деле не важны, главное - не прекращать совсем и возвращаться к проекту время от времени с новыми силами и идеями) и естественно исправлять в ней ошибки (для нахождения которых надо иногда запускать систему и «играться» с ней). Со временем процесс разработки будет становиться всё легче и легче, ошибки будут встречаться реже, а вы будете зачислены в список «безнадёжно упёртых», тех немногих, которые несмотря на некоторую абсурдность идеи разработки собственной ОС, всё же сделали это.

Илья Александров

Создаём собственную ОС на базе Linux

Дистрибутивов Linux существует сотни, и неизвестно, сколько появится еще. Десятки компаний и тысячи программистов соревнуются в создании лучшего Linux-проекта, а между тем любой опытный пользователь может стать автором системы для домашнего ПК, не уступающей продуктам гигантов IT-индустрии.

За долгие годы работы с Linux мною было использовано огромное количество различных дистрибутивов: Mandriva, Fedora, SlackWare, Debian, Ubuntu и многие другие. Какой-то проект нравился больше, какой-то – меньше. Но во всех дистрибутивах неминуемо приходилось сталкиваться с серьезными недостатками, которые сильно затрудняли работу. Один слишком требователен к ресурсам, в другом нет поддержки всего нужного оборудования, в третьем не хватает различного ПО. Вот тогда я вспомнил известную восточную мудрость: если нужно что-то сделать хорошо, сделай это сам.

Linux from Scratch

Я не единственный, кто решил заняться построением собственной версии Linux – ОС, в которой за основу будет взята базовая часть системы и ядро, но где не будет ни единого лишнего килобайта от разработчика, то есть от вас. Большое количество Linux-дистрибутивов, не соответствующих требованиям пользователей, подтолкнуло Герарда Бикменса (Gerard Beekmans) к созданию дистрибутива, который даст возможность каждому собрать систему, где будут только необходимые ему компоненты и функции.

Стремление талантливого программиста вылилось в проект Linux from Scratch (www.linuxfromscratch.org), сокращенно – LFS. Этот проект, позволяет сконструировать «с нуля», из исходных кодов, свою операционною систему на базе Linux. Компиляция LFS проходит на компьютере с уже установленной Linux-системой, впрочем, подойдет и «продвинутый» Live-CD, например, Knoppix .

При этом Linux-система, используемая для сборки, может быть любой – обязательно лишь наличие компилятора и системных библиотек. Linux From Scratch трудно назвать дистрибутивом в привычном смысле этого слова – это что-то вроде вспомогательного ПО, которое вкупе с базовой частью операционной системы позволит вам создать свою, уникальную версию ОС.

Как известно, Линус Торвальдс разрабатывал свою операционную систему под девизом «Just for fun!» – то есть только ради удовольствия. Нужно признать, что LFS действительно не часто можно встретить на серверах, используют эту систему, как правило, компьютерные энтузиасты. Установка и работа с Linux from Scratch поможет вам разобраться во взаимосвязи компонентов ОС, что пригодится при собственных разработках Linux-дистрибутива, причем не только на базе LFS. Поэтому LFS во многом рассчитан на тех людей, для которых процесс сборки собственного дистрибутива увлекателен и интересен – а таких людей, поверьте, немало.

Итак, если вы готовы потратить на конструирование системы целый день (а то и больше), то рекомендую скачать с сайта (2) LFS-packages-6.0, LFS-book, и продолжить читать эту статью.

Разбиение диска и создание дерева каталогов

Для лучшего понимания материала опишем весь ход процесса в общих чертах (см. рис. 1).

На первом этапе, с помощью уже инсталлированного дистрибутива или LiveCD, разбивается диск. На жестком диске выделяется раздел для новой системы. После чего на этом разделе нужно будет статически скомпилировать все необходимые программы и ядро системы. Далее происходит смена корневого каталога на раздел жесткого диска, отведенный под нашу новою ОС. Потребуется повторить компиляцию, но на этот раз ПО должно быть собрано динамически (отличие динамической компиляции от статической будет описано ниже). Последний этап включает в себя сборку важнейшей библиотеки glibc и конфигурацию установленной ОС. Как видите, ничего особенно сложного делать не придется.

На протяжении всего процесса ваш главный помощник – документация из пакета LFS-book, русский перевод которой можно взять тут: http://multilinux.sakh.com/download/lfsbook.tar.bz2 . В книге подробно описан каждый шаг создания ОС, поэтому обязательно обращайтесь к этому руководству в случае возникновения проблем (данная статья не призвана заменить такую обширную документацию).

Создаем новый раздел – в моем случае это /dev/hda5, так как раздел /dev/hda1 уже занят установленным на жесткий диск Linux Slackware. Рекомендуется предварительно сделать бэкап системы, дабы можно было ее восстановить в случае повреждения, хотя вероятность подобного близка к нулю. И тут, думаю, все понятно: выделяем нужное количество (достаточно 23 Гб) под корневой каталог, пространство, равное удвоенному объему ОЗУ – под swap-раздел, по желанию можно создать отдельные разделы для домашнего каталога (/home) и для /boot. Впрочем, излюбленный многими вариант разбиения – отвести под корневой каталог все доступное пространство минус swap, и последующее создание собственно swap – также вполне допустимо при сборке LFS. На компьютере автора и Linux Slackware, являющийся родительской ОС, и LFS, используют один жесткий диск, впрочем, установить LFS на другой винчестер тоже труда не составит.

Файловую систему выбирайте на ваше усмотрение: и с Ext3, и с ReiserFS никаких проблем под LFS не было. А вот поклонников XFS придется огорчить – попытки заставить Linux From Scratch работать с этой ФС не увенчались успехом.

Теперь монтируем раздел, отведенный под новую ОС:

$ mount /dev/hda5 /mnt/mylin

Для удобства определим переменную MYLIN:

$ export MYLIN=/mnt/mylin

Отлично, для дальнейшей работы лучше создать отдельного пользователя mylin, которого и назначим владельцем смонтированного раздела.

$ useradd mylin

$ chown –R mylin $MYLIN

Нужно создать дерево каталогов в корне нового раздела:

$ cd $MYLIN

$ mkdir –p bin boot dev etc home lib mnt opt root sbin usr/{X11R6,local} var

В каталогах usr, usr/X11R6, usr/local создаем необходимую структуру: подкаталоги bin, etc, include, lib, sbin, share, src.

Затем то же самое проделаем для каталогов /var и /opt будущей системы:

$ mkdir var/{cache,lib,local,lock,log,opt,run,spool}

$ mkdir opt/{bin,doc,include,info,lib,man}

Не будем забывать, что существуют более глубокие иерархии, например, /usr/share/man/man1. Но объем статьи не позволяет привести здесь всю информацию о структуре файлового дерева, поэтому нужно либо воспользоваться документом Filesystem Hierarhy Standart (можно найти по адресу: http://linux-ve.net/MyLDP/file-sys/fhs-2.2-rus), либо внимательно изучить структуру уже установленной у вас ОС семейства Linux. После подготовки жесткого диска приступаем к статической сборке.

Статическая сборка

Зачем мы используем статическую сборку? При статической компиляции исходный код библиотеки присоединяется к коду приложения, что влечет за собой увеличение его размера, но при этом сохраняется целостность. При динамической же компиляции библиотека находится в отдельном файле, к которому по мере необходимости обращаются приложения. В итоге все программы работают с одной версией библиотеки

Но когда мы посредством команды chroot установим корневой каталог для вновь собираемой системы, библиотеки «родительской», установленной системы, находящиеся в /lib, /usr/lib, и прочих, станут уже недоступны, поэтому динамически скомпилированные программы работать откажутся, вдобавок совместимость версий никем не гарантирована.

Чтобы избежать этого, все необходимое программное обеспечение для нашей будущей системы мы для начала соберем статически. Начнем, пожалуй, с командного интерпретатора bash. (Поклонники ZSH или TCSH могут установить любимые интерпретаторы после установки системы, но на этапе сборки их использование не предусмотрено автором LFS). Следует проверить, есть ли у вас файл /usr/lib/libcurses.a и если его нет – установите пакет nсursesdev. Все пакеты надо собирать с флагами статической сборки: «--enable-static-link», «--disable-shared» или «--static». Какой именно подходит в каждом конкретном случае, можно узнать из документации к конкретному пакету или из вывода конфигурационного сценария, запущенного с параметром «--help».

$ ./configure –-help

Чтобы не спутать позже статически скомпилированные программы с «динамическими», создадим для них специальный каталог:

$ mkdir $MYLIN/stat

При сборке и установке пакетов не забываем добавлять параметр «--prefix=$MYLIN/stat» для перемещения файлов именно в этот каталог. И, наконец, ставим bash:

$ ./configure –-enable-static-link --prefix=$MYLIN/stat

$ make

$ make install

По такой же схеме собираем остальные необходимые пакеты: binutils, bzip2, textutils, texinfo, tar, sh-utils, gcc, grep, gzip, gawk, diffutils, fileutils, make, patch, sed, и, собственно, linux-kernel.

Да, при компиляции ядра не забываем, что для старых версий ядер (2.2.x-2.4.x) нужно использовать gcc 2.95, а для текущей версии 2.6.x рекомендуется применить gcc 3.x, дабы не возникло проблем.

Не забываем заглядывать в соответствующие разделы LFS-book, там сказано об этом и многих других нюансах. В целом же компиляция ядра в LFS не отличается от подобной процедуры, проводимой при использовании установленного на HDD дистрибутива. Разархивируем исходники ядра в $MYLIN/usr/src/linux-2.6.xx, после чего конфигурируем, запуская:

$ make menuconfig

Процесс настройки параметров ядра многократно описан в Интернете (6), вряд ли есть необходимость останавливаться на этом подробнее. Далее даем следующие команды в папке с исходными текстами Linux-kernel:

$ make bzImage

$ make modules

Все, по адресу $MYLIN/usr/src/linux-2.6.xx/arch/i386/boot/bzImage находится новое ядро.

Далее создаем файлы $MYLIN/etc/passwd и $MYLIN/etc/group. В первом прописываем пока единственного пользователя – root с любым паролем, а во втором группы пользователей (для начала одной группы root тоже будет достаточно).

На этом наши приготовления к следующему шагу закончились, и мы переходим уже к более тонкой динамической сборке.

Динамическая сборка

Теперь нам нужно сменить корневой каталог на /mnt/mylin, где мы будем пользоваться только статически собранными утилитами – к помощи инструментов из «родительской» ОС мы уже прибегать не сможем. Даем команду в консоли:

$ chroot $MYLIN/usr/bin/env –i

>HOME=/root TERM=$TERM PS1=’u:w$’

>PATH=/bin: /usr/bin: /sbin: /usr/sbin: /stat/sbin

>/stat/bin/bash --login

Этой командой мы указали пути к исполняемым файлам, тип терминала, интерпретатор и вид приглашения командной строки.

Для обеспечения работы некоторых программ, надо установить файловую систему proc в новой системе.

$ mount proc /proc -t proc

Наступил самый ответственный момент. Сборка библиотеки glibc. Самый ответственный он потому, что работать без нее большинство необходимых программ не будет, а в использовании Linux без основной библиотеки смысла нет. Сборка glibc же зачастую доставляет массу проблем.

При сборке мы указывали параметр «--prefix=$MYLIN/stat», поэтому при смене корня все статически собранные пакеты окажутся в каталоге /stat раздела новой ОС.

Итак, распаковываем архив glibc-2.x.x.tar.gz (например, в директорию /usr/src/) и переходим в каталог glibclinuxthreads. Придется немного подправить исходный код ввиду того, что на данном этапе в системе невозможна идентификация пользователя по имени (как раз из-за отсутствия glibc и других библиотек), и того, что для установки glibc нужен интерпретатор Perl, которого у нас нет.

Заменяем имя пользователя root в файле login/Makefile на его uid, то есть 0, а переменную $PERL в файле malloc/Makefile следует заменить на путь к интерпретатору – /usr/bin/perl – и при конфигурировании он просто будет проигнорирован.

$ /usr/src/glibc-2.x.x/configure --prefix=/usr --enable-add-ons --libexecdir=/usr/bin &&

& make

& make install

$ make localedata/install-locales

$ /stat/bash --login

Если вы все сделали правильно, glibc скомпилируется, в строке приглашения наконец-то появится «root», и можно будет динамически перекомпилировать все программы.

Завершим установку ядра:

$ make modules_install

$ make install

Чтобы переместить новое ядро в каталог /boot, выполняем еще одну команду:

$ make unstall

Собираем все установленные и некоторые новые программы, теперь уже без флагов статической компиляции. Нам потребуются (на данном этапе очень важно не забыть скомпилировать все нижеперечисленное) (см. таблицу 1).

Таблица 1. Необходимый набор пакетов для сборки

autoconf

grep

perl

automake

groff

bash

gzip

procinfo

bin86

procps

binutils

less

psmisc

bzip2

reiserfs-progs

diffutils

libtool

e2fsprogs

lilo

sh-utils

shadow

file

make

sysklogd

fileutils

makedev

sysvinit

findutils

man-pages

flex

modutils

texinfo

gawk

ncurses

textutils

netkitbase

util-linux

bison

net-tools

gettext

patch

После выполнения динамической перекомпиляции можно удалить каталог со статически собранными пакетами:

$ rm -rf /stat

Можно облегченно вздохнуть и выпить кофе – самое сложное, рутинное осталось позади. Переходим к следующему этапу – начальному конфигурированию нашей системы.

Начальное конфигурирование системы

Перед тем как приступить к настройке, замечу, что все изменения нужно делать в файлах каталога новой ОС, а не родительской системы.

Для установки системного времени создадим файл /etc/sysconfig/clock, содержащий всего одну строку:

UTC=0

Теперь часы компьютера будут отображать время вашего часового пояса – при условии, что значение времени в BIOS установлено верно.

Дадим компьютеру имя:

echo "HOSTNAME=my_linux" > /etc/sysconfig/network

Теперь разделы, которые система должна монтировать при загрузке, укажем в /etc/fstab:

# filesystem mount-point fs-type options dump fsck-order

/dev/hda5 / ext3 defaults 1 1

/dev/hda3 swap swap pri=1 0 0

proc /proc proc defaults 0 0

Вместо /dev/hda3 и /dev/hda5 напишите ваши разделы (корневой и swap), дополните файл при необходимости точками монтирования других разделов жесткого диска и CD-ROM.

Теперь сделаем нашу систему загружаемой.

Если помимо lFS вы пользуетесь другими дистрибутивами Linux, то сейчас нужно войти в старую систему – для этого выполняем команду:

$ exit

Уже в родительской ОС в файл /etc/lilo.conf добавляем следующее:

# LFS

image=/boot/bzImage

Label=lfs

Root=

Read-only

Понятно, что «/boot/bzImage» – это путь к скомпилированному вами ядру системы, а «partition» – раздел диска, где находится корневой каталог.

Если же вы не планируете пользоваться другими операционными системами и дистрибутивами Linux, то сразу переходите к настройке LILO в LFS.

В этом случае lilo.conf будет выглядеть примерно так:

boot=/dev/hda

Delay=40

Compact

Vga=normal

Root=/dev/hda1

Read-only

Image=/boot/zImage-2.6.12

Label=Linux

Сделайте необходимые изменения в зависимости от вашей конфигурации. Обновляем загрузчик командой:

$ /sbin/lilo –v

И, если все предыдущие этапы были выполнены правильно, мы окажемся в новой системе. Однако долгий этап «тонкой» настройки (отдельное внимание стоит уделить безопасности новой системы, ибо LFS по умолчанию выглядит довольно-таки незащищенным, как и всякая вновь установленная ОС) еще впереди. Зато собственноручно собранная версия Linux у вас уже есть.

Постскриптум

Герард Бикменс – не единственный, кому пришло в голову создать собственный Linux. Другой проект – BYOLinux, руководителем которого являлся Джонатан Торп (Jonatan Thorpe), на сегодняшний день свое развитие прекратил, хотя написанная имдокументация сохраняет актуальность и сейчас, но она не так детальна, как LFS-book и не переведена на русский. Главное отличие метода Джона в том, что библиотека glibc переносится из родительской системы в дочернюю без перекомпиляции, это не столь эффективно, но позволяет избежать многих проблем при сборке. Желание почувствовать себя конструктором ОС испытывают и некоторые пользователи FreeBSD.

Теперь такое вполне возможно – по адресу http://ezine.daemonnews.org/200302/fbsdscratch.html находится статья о сборке FreeBSD из исходников целиком – от distributions до портов, причем методом не похожим на обычный «rebuild» системы, но схожим с методом Герарда Бикменса. Что ж, теперь и у вас есть личная, уникальная система, созданная на базе Linux. В случае возникновения проблем ищите их решение в LFS-book, там все подробно описано. Также рекомендую с портала http://www.tldp.org скачать руководство Linux Network Administrator’s Guide, оно хоть и не относится непосредственно к LFS, но пригодится на этапе настройки системы. Не стоит забывать, что с каждой программой поставляются также различные man и info pages, также призванные облегчить жизнь линуксоида.

  1. LFS-book на русском – http://multilinux.sakh.com/lfs .
  2. Официальный портал проекта LFS – http://www.linuxfromscratch.org .
  3. Портал ByoLinux – http://www.byolinux.org .
  4. Cтатья о FreeBSD from scratch – http://ezine.daemonnews.org/200302/fbsdscratch.html .
  5. Статья о компиляции ядра Linux – http://vikos.lrn.ru/MyLDP/kernel/kompil-2-6.html .
  6. Байрак А. Обзор Knoppix 3.7 Russian Edition. – Журнал «Системный администратор», №3, март 2005 г. – 4-6 с. ().

Для начала обучитесь программированию. Знание ассемблера необходимо; настоятельно рекомендуется также иметь понятие о других дополнительных языках программирования более низкого уровня, например, С.

Определитесь, на какое устройство вы хотите загрузить операционную систему. Это может быть CD-диск, DVD-диск, устройство флэш-памяти, жесткий диск или другой компьютер.

Решите, какой вы хотите видеть вашу операционную систему. Должна ли это быть полная версия ОС с графическим интерфейсом пользователя (GUI) или, может быть, что-нибудь более минималистичное? Вам необходимо знать, в каком направлении двигаться, еще перед началом процесса.

Уточните, какую платформу процессора будет поддерживать ваша операционная система. AI-32 и x86_64 являются двумя наиболее распространенными версиями для персональных компьютеров, так что их можно считать наилучшим выбором.

Определитесь, предпочитаете ли делать все самостоятельно с нуля, или же имеются ядра, на основе которых вы бы хотели надстроить систему. Linux с нуля – проект для тех, кто желает, к примеру, создать свой собственный дистрибутив Linux.

Выберите, собираетесь вы использовать свой собственный загрузчик или предварительно созданный унифицированный системный загрузчик Grand Unified Bootloader (GRUB). Поскольку кодирование своей собственной программы загрузки требует обширных знаний в области компьютерного обеспечения и BIOS, это может отодвинуть график программирования действующего ядра.

Примите решение по поводу языка программирования, который собираетесь использовать. Конечно, вполне возможно разработать ОС на таком языке, как Pascal или BASIC, но предпочтительнее писать на С или ассемблере. Ассемблер совершенно необходим, т. к. некоторые важные части операционной системы требуют знания именно этого языка. C++, с другой стороны, содержит ключевые слова, требуемые для запуска полной версии ОС.

  • Чтобы собрать ОС с помощью кодов C или C++, вы, конечно, будете использовать то один компилятор, то другой. Это означает, что вы должны прочесть руководство/инструкции/документацию для выбранного компилятора C/C++, что поставляется в комплекте с программным обеспечением или доступно на веб-сайте дистрибьютора. Вам придется узнать множество сложных вещей о компиляторе, кроме того, для совершенствования C++ предстоит изучить его схему и ABI. Вы, как ожидается, поймете различные форматы исполнительных задач (ELF, PE, COFF, обычные бинарные и т.д.) и заметите, что собственный формат Windows, PE (.exe) защищен авторским правом.
  • Выберите интерфейс программирования приложений (API). Одной из подборок хорошего API является POSIX, так как она хорошо документирован. Все системы Unix имеют, по крайней мере, частичную поддержку POSIX, так что было бы тривиально пристраивать программы Unix на вашу операционную систему.

    Определитесь с дизайном. Существуют монолитные ядра и микроядра. Монолитные ядра выполняют все службы в ядре, в то время как микроядра имеют маленькое ядро в сочетании с пользовательской реализацией сервисов. В общем, монолитные ядра быстрее, но микроядра имеют лучшую изоляцию и защиту от возможных неисправностей.

    Рассмотрите вопрос о разработке и работе в команде. Таким образом, вам потребуется меньше времени на разрешение больших проблем, что позволит создать операционную систему лучшего качества за более короткие сроки.

    Не стирайте ваш жесткий диск полностью. Помните, форматирование диска необратимо очистит все ваши данные! Используйте GRUB или другой менеджер для дублированной загрузки вашего компьютера с другой ОС, пока ваша версия не будет полностью готова функционально.

    Начните с малого. Обратите внимание в первую очередь на мелочи, такие как отображение текста и прерывания, прежде чем переходить к сложным элементам, к примеру, к управлению памятью и многозадачности.

    Сохраняйте резервную копию последней рабочей версии. Это дает определенное спокойствие в случае, если что-то пойдет абсолютно неправильно в текущей версии вашей ОС или последующих дополнениях. В случае поломки вашего компьютера и невозможности загрузки, как вы и сами понимаете, отличной возможностью станет наличие второй копии для работы, так что вы сможете устранить имеющиеся неисправности.

    Протестируйте вашу новую операционную систему на виртуальной машине. Вместо перезагрузки компьютера каждый раз после внесения изменений или передачи файлов с рабочего компьютера тестовой машине вы можете использовать приложение для запуска ОС на виртуальной машине, в то время как ваша текущая ОС продолжает работать. Приложения VM включают в себя VMWare (которая также имеет сервер в свободном доступе), альтернативный открытый исходный код, Bochs, Microsoft Virtual PC (не совместим с Linux), а также XVM VirtualBox.

    Выпустите релиз-версию. Это позволит пользователям рассказать вам о возможных недостатках в вашей операционной системе.

  • Операционная система также должна быть удобной для пользователя, так что не забудьте добавить полезные функции, которые станут неотъемлемой частью вашего дизайна.

    • Когда разработка будет закончена, подумайте, хотите ли вы представить код в свободном доступе либо же установить частные права на него.
    • Обязательно сделайте функции безопасности вашим основным приоритетом, если хотите, чтобы ваша система была жизнеспособной.
    • Не начинайте проект разработки операционной системы с целью обучения программированию. Если вы не знаете C, C++, Pascal или какие-нибудь другие подходящие языки и свойства, в том числе типы указателя, операции с битами низкого уровня, переключение битов, встроенный ассемблер и т.д., – значит, еще не готовы для создания ОС.
    • Просматривайте такие порталы, как OSDev и OSDever, которые помогут вам улучшить собственную операционную систему. Обратите особое внимание на то, что по большинству вопросов сообщество OSDev.org предпочитает, чтобы вы самостоятельно обращались к содержанию сайта, а не присоединялись к форуму. Если вы все же решили примкнуть к рядам форумчан, для этого должны быть определенные предпосылки. Вы обязаны досконально знать C или C++ и язык x86 ассамблер. Вы также должны понимать общие и комплексные понятия в программировании, такие как Linked Lists, Queues и т.д. Сообщество OSDev в своих правилах прямо говорит о том, что никто не собирается нянчить новых программистов. Если вы пытаетесь разработать ОС, само собой разумеется, что вы «бог» в области программирования. От вас также требуется прочесть руководство по работе с процессором касательно его архитектуры, выбранной вами; например, x86 (Intel), ARM, MIPS, PPC и т.д. Такой справочник по структуре процессора можно легко найти с помощью поиска в Google («Intel Manuals», «ARM manuals» и т.д.). Не регистрируйтесь на форуме OSDev.org, чтобы задавать очевидные вопросы. Это просто приведет к ответам вроде «Read the f*** ing Manual». Для начала вы должны попробовать почитать Википедию, пособия для различных инструментов, которые собираетесь использовать.
    • Проверьте наличие потенциальных мертвых точек и других ошибок. Недочеты, тупики и другие проблемы могут повлиять на проект вашей операционной системы.
    • Если вы хотите способ попроще, представьте дистрибутивы Linux - типа Fedora Revisor, Custom Nimble X, Puppy Remaster, PCLinuxOS mklivecd или SUSE Studio и SUSE KIWI. Тем не менее, создаваемая ОС принадлежит компании, которая первой представила этот сервис (хотя у вас есть права на ее свободное распространение, изменение и запуск, как вам нравится, под GPL).
    • Хорошим решением будет создание совершенно нового раздела для разрабатываемой операционной системы.

    Предупреждения

    • Небрежное переписывание ОС на жесткий диск может повредить его полностью. Будьте осторожны
    • У вас не получится полностью готовая система за две недели. Начните с загружаемой операционной системы, а затем переходите на более интересный материал.
    • Если вы сделаете что-то опрометчивое, как, например, напишите беспорядочные байты в произвольных портах I/O, то уничтожите вашу ОС и можете (в теории) спалить ваше оборудование.
    • Не ожидайте, что будет легко построить качественную операционную систему. Существует множество сложных взаимозависимостей. Например, для того, чтобы ОС была способна работать с несколькими процессорами, ваш диспетчер памяти должен иметь «блокирующие» механизмы для предотвращения доступа лишних процессоров в один и тот же ресурс одновременно. Используемые «блоки» предполагают наличие планировщика, чтобы убедиться, что только один процессор обращается к критическому ресурсу в любой момент времени, а все остальные находятся в режиме ожидания. Тем не менее, работа планировщика зависит от присутствия диспетчера памяти. Это пример зависимости от взаимоблокировки. Нет стандартного способа разрешить подобные проблемы; каждый создатель операционной системы, как ожидается, достаточно квалифицирован, чтобы найти свой собственный вариант их решения.

    Источники

    Что вам понадобится

    • Компьютер
    • Процессор, на котором собираетесь строить
    • Достаточная оперативная память (ОЗУ) для виртуальной машины
    • Основная ОС (используется для разработки исходного кода ассемблера (и др.), а также сборки и упаковки на ранних стадиях работы; в конечном итоге ваша собственная ОС станет первичной)
    • Редактор цветного кода Syntax (применяется при отсутствии Integrated Development Environment)
    • Компилятор
    • CD/DVD привод
  • Книга «Операционная система с 0 до 1» опубликована на GitHub и имеет более 2 000 звездочек и 100 форков. Как понятно из названия, прочитав её, вы сможете создать свою собственную операционную систему - и, пожалуй, мало что в мире программистов может быть круче.

    Благодаря этой книге вы научитесь следующему:

    • Узнаете, как создать операционную систему на основе технической документации железа. В реальном мире это так и работает, вы не сможете использовать Google для быстрых ответов.
    • Поймёте, как компьютерные компоненты взаимодействуют друг с другом, от софта к железу.
    • Научитесь писать код самостоятельно. Слепое копирование кода не есть обучение, вы действительно научитесь решать проблемы. Кстати, слепое копирование еще и опасно.
    • Освоите всем привычные инструменты для низкоуровневой разработки.
    • Познакомитесь с языком ассемблера.
    • Выясните, из чего состоят программы и как операционная система запускает их. Небольшой обзор этой темы для любознательных мы давали в .
    • Разберётесь, как проводить отладку программы прямо на железе с GDB и QEMU.
    • Язык программирования C. Быстро освоить его можно, следуя .
    • Базовые знания Linux. Достаточно изучить на нашем сайте.
    • Базовые знания в физике: атомы, электроны, протоны, нейтроны, напряжение.
    • Закон Ома о соотношении напряжения, силы тока и сопротивления.

    Книга совершенствуется и редактируется почти каждый день: вы и сами можете внести изменения или исправить опечатку. Операционная система, разработка которой послужила «сюжетом» для этой книги,

    Данный цикл статей посвящён низкоуровневому программированию, то есть архитектуре компьютера, устройству операционных систем, программированию на языке ассемблера и смежным областям. Пока что написанием занимаются два хабраюзера - и . Для многих старшеклассников, студентов, да и профессиональных программистов эти темы оказываются весьма сложными при обучении. Существует много литературы и курсов, посвящённых низкоуровневому программированию, но по ним сложно составить полную и всеохватывающую картину. Сложно, прочитав одну-две книги по ассемблеру и операционным системам, хотя бы в общих чертах представить, как же на самом деле работает эта сложная система из железа, кремния и множества программ - компьютер.

    Каждый решает проблему обучения по-своему. Кто-то читает много литературы, кто-то старается поскорее перейти к практике и разбираться по ходу дела, кто-то пытается объяснять друзьям всё, что сам изучает. А мы решили совместить эти подходы. Итак, в этом курсе статей мы будем шаг за шагом демонстрировать, как пишется простая операционная система. Статьи будут носить обзорный характер, то есть в них не будет исчерпывающих теоретических сведений, однако мы будем всегда стараться предоставить ссылки на хорошие теоретические материалы и ответить на все возникающие вопросы. Чёткого плана у нас нет, так что многие важные решения будут приниматься по ходу дела, с учётом ваших отзывов.

    Возможно, мы умышленно будем заводить процесс разработки в тупик, чтобы позволить вам и себе полностью осознать все последствия неверно принятого решения, а также отточить на нем некоторые технические навыки. Так что не стоит воспринимать наши решения как единственно верные и слепо нам верить. Еще раз подчеркнём, что мы ожидаем от читателей активности в обсуждении статей, которая должна сильно влиять на общий процесс разработки и написания последующих статей. В идеале хотелось бы, чтобы со временем некоторые из читателей присоединились к разработке системы.

    Мы будем предполагать, что читатель уже знаком с основами языков ассемблер и Си, а также элементарными понятиями архитектуры ЭВМ. То есть, мы не будем объяснять, что такое регистр или, скажем, оперативная память. Если вам не будет хватать знаний, вы всегда можете обратиться к дополнительной литературе. Краткий список литературы и ссылки на сайты с хорошими статьями есть в конце статьи. Также желательно уметь пользоваться Linux, так как все инструкции по компиляции будут приводиться именно для этой системы.

    А теперь - ближе к делу. В оставшейся части статьи мы с вами напишем классическую программу «Hello World». Наш хеллоуворлд получится немного специфическим. Он будет запускаться не из какой-либо операционной системы, а напрямую, так сказать «на голом железе». Перед тем, как приступить непосредственно к написанию кода, давайте разберёмся, как же конкретно мы пытаемся это сделать. А для этого надо рассмотреть процесс загрузки компьютера.

    Итак, берем свой любимый компьютер и нажимаем самую большую кнопочку на системном блоке. Видим веселую заставку, системный блок радостно пищит спикером и через какое-то время загружается операционная система. Как вы понимаете, операционная система хранится на жёстком диске, и вот тут возникает вопрос: а каким же волшебным образом операционная система загрузилась в ОЗУ и начала выполняться?

    Знайте же: за это отвечает система, которая есть на любом компьютере, и имя ей - нет, не Windows, типун вам на язык - называется она BIOS. Расшифровывается ее название как Basic Input-Output System, то есть базовая система ввода-вывода. Находится BIOS на маленькой микросхемке на материнской плате и запускается сразу после нажатия большой кнопки ВКЛ. У BIOS три главных задачи:

    1. Обнаружить все подключенные устройства (процессор, клавиатуру, монитор, оперативную память, видеокарту, голову, руки, крылья, ноги и хвосты…) и проверить их на работоспособность. Отвечает за это программа POST (Power On Self Test – самотестирование при нажатии ВКЛ). Если жизненно важное железо не обнаружено, то никакой софт помочь не сможет, и на этом месте системный динамик пропищит что-нибудь зловещее и до ОС дело вообще не дойдет. Не будем о печальном, предположим, что у нас есть полностью рабочий компьютер, возрадуемся и перейдем к рассмотрению второй функции BIOS:
    2. Предоставление операционной системе базового набора функций для работы с железом. Например, через функции BIOS можно вывести текст на экране или считать данные с клавиатуры. Потому она и называется базовой системой ввода-вывода. Обычно операционная система получает доступ к этим функциям посредством прерываний.
    3. Запуск загрузчика операционной системы. При этом, как правило, считывается загрузочный сектор - первый сектор носителя информации (дискета, жесткий диск, компакт-диск, флэшка). Порядок опроса носителей можно задать в BIOS SETUP. В загрузочном секторе содержится программа, иногда называемая первичным загрузчиком. Грубо говоря, задача загрузчика - начать запуск операционной системы. Процесс загрузки операционной системы может быть весьма специфичен и сильно зависит от её особенностей. Поэтому первичный загрузчик пишется непосредственно разработчиками ОС и при установке записывается в загрузочный сектор. В момент запуска загрузчика процессор находится в реальном режиме.
    Печальная новость: размер начального загрузчика должен быть всего 512 байт. Почему так мало? Для этого нам надо ознакомиться с устройством дискеты. Вот познавательная картинка:

    На картинке изображена поверхность дискового накопителя. У дискеты 2 поверхности. На каждой поверхности есть кольцеобразные дорожки (треки). Каждый трек делится на маленькие дугообразные кусочки, называемые секторами. Так вот, исторически сложилось, что сектор дискеты имеет размер 512 байт. Самый первый сектор на диске, загрузочный сектор, читается BIOS"ом в нулевой сегмент памяти по смещению 0x7С00, и дальше по этому адресу передается управление. Начальный загрузчик обычно загружает в память не саму ОС, а другую программу-загрузчик, хранящуюся на диске, но по каким-то причинам (скорее всего, эта причина - размер) не влезающую в один сектор. А поскольку пока что роль нашей ОС выполняет банальный хеллоуворлд, наша главная цель - заставить компьютер поверить в существование нашей ОС, пусть даже и на одном секторе, и запустить её.

    Как устроен загрузочный сектор? На PC единственное требование к загрузочному сектору - это содержание в двух его последних байтах значений 0x55 и 0xAA - сигнатуры загрузочного сектора. Итак, уже более-менее понятно, что нам нужно делать. Давайте же писать код! Приведённый код написан для ассемблера yasm .

    section . text

    use16

    org 0x7C00 ; наша программа загружается по адресу 0x7C00

    start:

    mov ax , cs

    mov ds , ax ; выбираем сегмент данных



    mov si , message

    cld ; направление для строковых команд

    mov ah , 0x0E ; номер функции BIOS

    mov bh , 0x00 ; страница видеопамяти

    puts_loop:

    lodsb ; загружаем очередной символ в al

    test al , al ; нулевой символ означает конец строки

    jz puts_loop_exit

    int 0x10 ; вызываем функцию BIOS

    jmp puts_loop

    puts_loop_exit:

    jmp $ ; вечный цикл



    message:

    db "Hello World!" , 0

    finish:

    times 0x1FE - finish+ start db 0

    db 0x55 , 0xAA ; сигнатура загрузочного сектора

    Эта короткая программа требует ряда важных пояснений. Строка org 0x7C00 нужна для того, чтобы ассемблер (имеется в виду программа, а не язык) правильно рассчитал адреса для меток и переменных (puts_loop, puts_loop_exit, message). Вот мы ему и сообщаем, что программа будет загружена в память по адресу 0x7C00.
    В строках
    mov ax , cs

    mov ds , ax
    происходит установка сегмента данных (ds) равным сегменту кода (cs), поскольку в нашей программе и данные, и код хранятся в одном сегменте.

    Далее в цикле посимвольно выводится сообщение «Hello World!». Для этого используется функция 0x0E прерывания 0x10 . Она имеет следующие параметры:
    AH = 0x0E (номер функции)
    BH = номер видеостраницы (пока не заморачиваемся, указываем 0)
    AL = ASCII-код символа

    В строке « jmp $ » программа зависает. И правильно, незачем ей выполнять лишний код. Однако чтобы компьютер опять заработал, придется перезагрузиться.

    В строке « times 0x1FE-finish+start db 0 » производится заполнение остатка кода программы (за исключением последних двух байт) нулями. Делается это для того, чтобы после компиляции в последних двух байтах программы оказалась сигнатура загрузочного сектора.

    С кодом программы вроде разобрались, давайте теперь попробуем скомпилировать это счастье. Для компиляции нам понадобится, собственно говоря, ассемблер - выше упомянутый