Как сделать собственную операционную систему
Пишем операционную систему. Часть 1. Загрузчик
Всем привет! Сегодня мы напишем загрузчик, который будет выводить «Hello World» и запустим его на VirtualBox. Писать будем на ассемблере FASM. Скачать его можно отсюда. Также нам понадобится собственно VirtualBox и UltraISO. Перед тем как писать код, разберемся как загружаются операционные системы.
Итак, когда мы нажимаем большую кнопку включения на нашем компьютере запускается система, которая есть на любом компьютере — BIOS (Basic Input/Output System или базовая система ввода/вывода). Задача BIOS это:
Этот код требует немного пояснений. Командой
мы говорим, что код нужно загружать в ОЗУ по адресу 0x7C00. В строках
мы устанавливаем видео режим 80х25 (80 символов в строке и 25 строк) и тем самым очищаем экран.
Здесь мы устанавливаем курсор. За это отвечает функция 2h прерывания 10h. В регистр dh мы помещаем координату курсора по Y, а в регистр dl — по X.
Печатаем строку. За это отвечает функция 13h прерывания 10h. В регистр bp мы помещаем саму строку, в регистр cx — число символов в строке, в регистр bl — атрибут, в нашем случае цвет, он будет зеленым. На цвет фона влияют первые 4 бита, на цвет текста — вторые 4 бита. Ниже представлена таблица цветов
Откомпилируем код нажатием клавиш Ctrl + F9 и сохраним полученный файл как boot.bin.
Запуск
Запускаем UltraISO и перетаскиваем наш бинарник в специальную область (отмечено красной стрелкой).
Далее кликаем правой кнопкой мыши по бинаринку и нажимаем кнопку примерно с такой надписью: «Установить загрузочным файлом». Далее сохраняем наш файл в формате ISO.
Открываем VIrtualBox и создаем новую виртуальную машину (если вы не знаете, как это делается, кликайте сюда). Итак, после того, как вы создали виртуальную машину, нажимаем «Настроить, выбираем пункт „Носители“, нажимаем на „Пусто“, там где „Привод“ есть значок оптического диска. Нажимаем на него и выбираем „Выбрать образ оптического диска“, ищем наш ISO файл, нажимаем „Открыть“. Сохраняем все настройки и запускаем виртуальную машину. На экране появляется наш „Hello World!“.
На этом первый выпуск подходит к концу. В следующей части мы научим наш загрузчик читать сектора диска и загрузим свое первое ядро!
Что нужно знать, чтобы написать операционную систему
Создание операционной системы — одна из сложнейших задач в программировании, поскольку требует обширных и комплексных знаний о работе компьютера. Каких именно? Разбираемся ниже.
Что такое ОС
Операционная система (ОС) — это программное обеспечение, которое работает с компьютерным железом и его ресурсами и является мостом между аппаратной и программной частью компьютера.
Компьютеры первого поколения не имели операционных систем. Программы на первых ЭВМ включали в себя код для непосредственной работы системы, связи с периферийными устройствами и вычислений, для выполнения которых эта программа и писалась. Из-за такого расклада даже простые по логике работы программы были сложны в программной реализации.
По мере того как компьютеры становились более разнообразными и сложными, писать программы, которые работали и как ОС, и как приложение, стало попросту неудобно. Поэтому, чтобы программы было легче писать, владельцы компьютеров начали разрабатывать программное обеспечение. Так и появились операционные системы.
ОС предоставляет всё необходимое для работы пользовательских программ. Их появление означало, что теперь программам не нужно контролировать весь объём работ компьютера (это отличный пример инкапсуляции). Теперь программам нужно было работать именно с операционной системой, а система уже сама заботилась о ресурсах и работе с периферией (клавиатура, принтер).
Кратко об истории операционных систем
Первая операционная система была создана в 1956 году компанией General Motors для единственного компьютера IBM. Остальные владельцы этих компьютеров последовали примеру и начали создавать собственные ОС.
Как можно догадаться, первые ОС сильно отличались друг от друга. Почти на каждом компьютере стояла своя система. Хоть они и облегчали написание программ, использовать такие программы можно было только на одном компьютере.
В 1960-х годах IBM стала первой выпускающей компьютеры компанией, которая взяла на себя процесс написания ОС под свои же компьютеры. Компания начала распространять компьютеры вместе со своей операционной системой.
В эти же годы компании Control Data Corporation, Computer Sciences Corporation, Burroughs Corporation, GE, Digital Equipment Corporation и Xerox тоже начали реализовывать свои ОС.
В конце 60-х была разработана первая версия ОС Unix. Написанная на Си, свободно доступная первые годы, Unix привлекала всё больше внимания и аудитории. Она легко портировалась на новые системы и начала набирать обороты.
Многие современные ОС, включая Apple OS X и все разновидности Linux-систем, являются дальними потомками Unix.
Microsoft Windows, в свою очередь, была написана для ряда персональных компьютеров IBM.
Первая ОС от Microsoft называлась не Windows, а MS-DOS. Эта система была создана в 1981 году, когда систему 86-DOS купили у компании Seattle Computer Products и модифицировали её под требования IBM.
Всем привычное название Windows появилось в 1985, когда MS-DOS обзавелась графическим интерфейсом.
Apple OS X, Microsoft Windows и ряд Linux-систем (включая Android) сейчас контролируют большую часть рынка операционных систем.
Составляющие операционной системы
ОС состоит из двух основных частей:
Ядро (англ. kernel)
Сердце операционной системы. Именно оно запускается первым при включении компьютера (не считая BIOS и загрузчик). Ядро контролирует ресурсы компьютера и обрабатывает запросы от системных программ.
Системные программы
Работают поверх ядра. Такие программы нужны, в целом, не для пользователя, а для связи ядра с пользовательскими приложениями и периферией. Примеры системных программ: драйвера устройств, файловая система, сетевые программы, утилиты для дефрагментации диска.
Пользовательские программы
Не являются частью операционной системы. Именно эти программы уже имеют конкретное назначение. Текстовые редакторы, браузеры, медиа-плееры — всё это пользовательские программы. Они контролируются ядром и используют системные программы для доступа к периферии.
Что необходимо знать
Список вещей, которые необходимо знать для создания собственной ОС, очень длинный. Но в нём есть три основных пункта, на которые нужно обратить внимание в первую очередь:
Изучение информатики
Разработка ОС — это не тоже самое, что, скажем, веб-разработка. Тут нельзя будет просто и быстро разобраться в базовых вещах. Для начала нужно получить крепкий базис информатики и только потом переходить к другим темам.
Coursera: Computer Science 101 — это курс, который подойдёт для тех, кто только начал осваиваться в информатике и программировании. Если у вас уже имеются элементарные знания в в этой области, то пропустите этот курс и переходите к следующему.
Udacity: Intro to Computer Science использует веб-разработку в качестве подхода к обучению компьютерной науке. Хоть курс и не направлен на непосредственную разработку ОС, он даёт мощный базис в области программирования.
edX: Introduction to Computer Science — этот курс является самым исчерпывающим и углублённым в этом списке. Несмотря на это, он полностью бесплатен. Курс был разработан в Гарвардском Университете. В нём вас научат алгоритмам, структурам данных, управлению ресурсами, разработке ПО, а так же познакомят с такими языками программирования, как C, PHP и JavaScript.
Подборка книг для самостоятельного изучения Computer Science.
Изучение программирования
С крепкими знаниями в области информатики и хотя бы базовым пониманием программирования вам нужно набраться опыта в разработке проектов.
Udacity: Software Development Process — отличный курс для тех, кто ещё не принимал участие в крупных проектах. Тут вас ознакомят с подробностями рабочего процесса и работой с инструментарием (например Git), а также расскажут о настройке интегрированной среды разработки.
Изучение языков программирования
Для разработки операционной системы вам понадобится освоить минимум два языка программирования:
Первый используется для работы напрямую с процессором. Процессор «понимает» только машинный код, и для каждого типа процессора есть только один соответствующий язык. Одной из самых популярных архитектур процессора является x86. Она была разработана компанией Intel и на текущий момент широко используется многими производителями компьютерного железа, включая AMD. По этой причине в этой статье акцент будет именно на архитектуру x86.
Высокоуровневые языки программирования, в свою очередь, работают сразу на нескольких архитектурах. Самый популярный из таких языков — Cи. Чаще всего именно на нём и пишутся операционные системы. Но это не означает, что этот язык единственный. Для написания ОС можно использовать и более высокоуровневые языки, например C++ или Python.
Прим. перев. Есть пример написания «игрушечных» операционных систем на C#. Cosmos — некий конструктор ОС. В этой статье на практическом примере показывают написание системы всего в нескольких десятках строк.
Язык ассемблера для x86
«x86 Assembly Guide» — неплохо подойдёт для начального изучения ассемблера. Несмотря на то, что эта статья коротка для полного курса, в ней содержится всё необходимое для дальнейшего углубления.
Книга «Programming from the Ground Up» Джонатана Бартлетта является одной из основных по изучению ассемблера. В ней основами языка программирования подкрепляются базисы информатики.
Для изучения ассемблера есть ещё одна показательная книга — «The Art of Assembly Language», написанная Рэнди Гайдом. Изначально книга писалась специально для курсов, которые вёл автор в Калифорнийском университете Cal Poly и UC Riverside. В середине 90-х годов книга была признана определяющей в этой сфере.
Если вдруг вышеописанные материалы вам не подошли, вот ещё пара отличных книг:
Язык Cи
Как уже упоминалось выше, для написания ОС есть несколько высокоуровневых языков программирования. Однако самый популярный из них — Си.
Начать изучать этот язык можно отсюда. Этот ресурс ознакомит вас с базовыми понятиями и подготовит к более сложным задачам.
«Learn C the Hard Way» — название ещё одной книги. Кроме привычной теории в ней собрано много практических решений. Этот учебник расскажет обо всех аспектах языка.
Либо же можете выбрать одну из этих книг:
Разработка ОС
После освоения всего необходимого, что касается информатики, языка ассемблера и Cи, вам стоит прочесть хотя бы одну или две книги про непосредственную разработку ОС. Вот несколько ресурсов для этого:
«Linux From Scratch». Здесь рассматривается процесс сборки операционной системы Linux (учебник переведён на много языков, в том числе и на русский). Тут, как и в остальных учебниках, вам предоставят все необходимые базовые знания. Полагаясь на них можно попробовать себя в создании ОС. Чтобы сделать программную часть ОС более профессиональной, присутствуют дополнения к учебнику: «Beyond Linux from Scratch», «Automated Linux from Scratch», «Cross Linux from Scratch» или «Hardened Linux from Scratch».
«The little book about OS development» Эрика Хэйлина и Адама Ренберга. Этот учебник разработан в рамках продвинутого курса информатики и на момент его написания авторы являлись студентами Королевского Института Технологий в Стокгольме. В этой книге рассматривается разработка ОС для архитектуры x86 (причём с самого начала — с установки среды разработки). В этом курсе рассказывается о многих интересных темах, включая многозадачность, управление памятью и разработку файловой системы.
«Operation System Development Series» Broken Thorn Entertainment — серия из 25 уроков, посвящённых разработке ОС с нуля.
Примечание Эти уроки рассчитаны на то, что вы уже знакомы с IDE и владеете как языком ассемблера, так и Cи.
Вот ещё три книги, которые помогут вам ознакомиться с дисциплиной разработки ОС:
Сообщества, посвященные разработке ОС
Заниматься разработкой ОС в одиночку смело, но сложно. Гораздо проще найти единомышленников, которые, как и вы, решили попытать удачу в этом нелёгком деле. Существует пара подходящих мест:
Заключение
Обучение разработке ОС — достаточно сложная задача. Однако в процессе вы разберётесь в работе процессора и аппаратного уровня, что поможет лучше понимать работу и оптимизацию высокоуровневых приложений и их взаимодействие друг с другом. Ну и в глазах коллег вы наверняка будете выглядеть очень крутым (но это не точно).
Написание собственной Операционной Системы №1
В этой серии статей я хотел бы рассказать о написании собственной ОС (прошу подождать с истерическим смехом по этому поводу). Конечно же, не на самом подробном уровне, тем не менее, какие-то азы, с которых будет полезно стартовать, я все же постараюсь рассказать. Я сразу хочу заметить, что я предполагаю, что некоторый опыт в программировании на Assembler у вас имеется, а так же вы знаете такие базовые понятия как сегментная память, реальный режим и пр. (Если нет, советую почитать для начала
книгу Зубкова)
Итак, начнем. Давайте рассмотрим приблизительно работу известных ОС.
Адресное пространство в DOS:
Объем | Физ. Адрес | Сегм. Адрес | |
1Кбайт | Векторы прерываний | 00000h | 0000h |
256байт | Область данных BIOS | 00400h | 0040h |
ОС MS-DOS | 00500h | 0050h | |
Область для программ | |||
64Кбайт | Графический видео буфер | A0000h | A000h |
32Кбайт | Свободные адреса | B0000h | B000h |
32Кбайт | Текстовый видеобуфер | B8000h | B800h |
64Кбайт | ПЗУ-расширения BIOS | C0000h | C000h |
128Кбайт | Свободные адреса | D0000h | D000h |
64Кбайт | ПЗУ BIOS | F0000h | F000h |
64Кбайт | HMA | 100000h | |
До4Гбайт | XMS | 10FFF0h |
Первые 640 Кбайт (до графического видеобуфера) называются стандартной (conventional) памятью. Начинается стандартная память с килобайта, который содержит векторы прерываний, их 256 на каждый отводится по 4 байта.
Затем идет область данных BIOS (при включение компьютера BIOS выполняет POST – диагностику, которая проверяет все оборудование на наличие ошибок, если проверка завершилась удачно то BIOS грузит самый первый сектор (там находится загрузочная программа ОС) с выбранного устройства (дискеты, винчестера) по адресу 0x7C00h куда и передает управление). Где находятся данные необходимые для корректной работы функций BIOS. Но также можно модифицировать эту область, тем самым мы влияем на ход выполнения системных функций, по сути дела меняя что либо в этой области мы передаем параметры BIOS и его функциям, которые становятся более гибкими. В случае установленной DOS с сегментного адреса 500h начинается сама операционная система. После ОС до 640 Кбайт находятся прикладные или системные программы, которые были загружены ОС.
За 640 килобайтами начинается старшая память или верхняя (upper) память, она располагается до 1 мегабайта (до HMA), т.е. она составляет 384 Кбайт. Тут располагаются ПЗУ (постоянно запоминающее устройство ) : текстовый видеобуфер (его микросхема рассчитана на диапазон B8000h…BFFFFh) и графический видеобуфер (A0000h…AFFFFh). Если требуется вывести текст то его ASCII коды требуется прописать в текстовый видеобуфер и вы немедленно увидите нужные символы. F0000h…FFFFFh, а вот и сам BIOS. Так же есть еще одна ПЗУ – ПЗУ расширений BIOS (C0000h…CFFFFh), её задача обслуживание графических адаптеров и дисков.
За первым мегабайтом, с адреса 100000h, располагается память именуемая как расширенная память, конец которой до 4 гигабайт. Расширенная память состоит из 2х подуровней: HMA и XMS. Высокая память (High Memory Area, HMA) доступна в реальном режиме, а это еще плюс 64 Кбайт (точнее 64 Кбайт – 16 байт), но для этого надо разрешить линию A20 (открыв вентиль GateA20). Функционирование расширенной памяти подчиняется спецификации расширенной памяти (Expanded Memory Specification, XMS), поэтому саму память назвали XMS-памятью, но она доступна только в защищенном режиме.
Давайте вернемся к началу адресного пространства в представлении DOS и рассмотрим его более подробно.
1) Векторы прерываний таковы (это нам понадобится, когда мы будем составлять свою таблицу прерываний):
IRQ INT Причина возникновения
IRQ0 8h Системный таймер
IRQ1 9h Клавиатура
IRQ2 10h Ведомый контроллер
IRQ3 11h Порт COM2, модем
IRQ4 12h Порт COM1, мышь
IRQ5 13h Порт LPT2
IRQ6 14h Дисковод
IRQ7 15h Порт LPT1, принтер
IRQ8 70h Часы реального времени
IRQ9 71h Прерывание обратного хода луча
IRQ10 72h Для дополнительных устройств
IRQ11 73h Для дополнительных устройств
IRQ12 74h PS мышь
IRQ13 75h Ошибка математического сопроцессора
IRQ14 76h Первый IDE-контроллер
IRQ15 77h Второй IDE-контроллер, жесткий диск
2) BIOS. Это в общем-то лирическое отступление, т.к. здесь мы рассмотрим не столько устройство BIOS, сколько научимся добавлять в него свои функции. Использование таковых, конечно, сделает нашу ОС, непереносимой, но для начального этапа они могут оказаться очень полезными. В написании ОС я больше не буду упоминать об этом, это остается пытливому читателю в виде самостоятельного упражнения. =)
Итак, BIOS (я буду говорить об AWARD BIOS, так как это наиболее популярные версии, поэтому возможно незначительные расхождения с другими
BIOS) – это последовательность запакованных файлов, которые заканчиваются файлом bootblock. Структура первого мегабайта памяти, отведенного под BIOS такова:
17FFEh
1C000* – 1FFFFh
До свободного пространства идет основная часть BIOS, а именно:
original.tmp – главная часть, в которой располагается подпрограмма BIOS Setup, а так же части, необходимые для инициализации.
CRC – контрольная сумма BIOS
awardext.rom – подпрограмма вывода конфигурации компьютера
awardepa.bin – изображение
Так же могут встречаться другие необязательные модули.
Итак, при включении компьютера bootblock инициализирует регистры чипсета, распаковывает заархивированные (с помощью LHA) модули и отправляет их в память.
Соответственно данные файлы можно перепрограммировать, изменив или добавив что-то в BIOS. Таким образом можно изменить все настройки БИОС (начиная от надписей и кончая добавлением возможности работы с новыми устройствами, информации о которых нет в данной версии
BIOS). Делается это достаточно легко: например используя modbin (стандартная программа от Award) можно распаковать данные файлы
(взятые, например, из Интернета), изменить их по своему усмотрению и записать в
BIOS. Только при изменении заархивированных модулей не забывайте исправлять CRC, иначе BIOS подумает, что он испорчен.
Итак, что же нужно для более серьезной перепрошивки BIOS нежели незначительного изменения уже существующих кодов. Во-первых я напомню
вам, что существует множество компаний, производящий материнские платы, а так же процессоры, а единого стандарта для чипсетов не существует, поэтому написать универсальный BIOS ко всем материнским платам не возможно, необходимо написать для каждой материнской платы свои функции и объединить их в единый BIOS. Но для этого необходимо множество человеко-часов, поэтому в одиночку справиться с подобной задачей представляется достаточно трудным или попросту невозможным.
Итак, наша программа будет располагаться в ПЗУ (постоянное запоминающее устройство). BIOS передаст ей управление, но для этого он должен ее найти. Соответственно наша программа должна находиться в области с С800:0 до E000:0 в памяти, так как эта область сканируется BIOS на наличие определенной сигнатуры 0AA55H. В байте за этой подписью количество байт для подсчета их контрольной суммы. Если контрольная сумма равно нулю, то это ПЗУ и управление передается в область памяти, где была найдена данная сигнатура со смещением 3. Для того, чтобы «уровнять» контрольную сумму, необходимо в конце программы дописать байт, в котором будет число, равное разнице 100h и полученной контрольной суммы.
Итак, вот так должна выглядеть ваша программа, которую Вы запишите в ПЗУ.
LENGTHROM EQU 2000H ; Размер ПЗУ в байтах = числу после подписи * 200H
CODE SEGMENT BYTE PUBLIC
ASSUME CS:CODE,DS:CODE
ORG 0
START:
DB 55h
DB 0AAh; Размер ПЗУ по модулю 200H
DB LENGTHROM SHR 9; Первая выполняемая команда
JMP BEGIN
BEGIN:
; Заносим в регистры нужные значения
MOV AX,CS
MOV DS,AX
; Код программы
; Вернуть управление БИОС
RETF
; Сюда запишем дополняющий байт
DB (0)
CodeEnd:
; заполнение оставшегося кода нулями
DB (LENGTHROM-(OFFSET CodeEnd-OFFSET START)) DUP (0FFH)
LastByte:
CODE ENDS
END START
Загрузка Linux и Windows
Это базовая и очень важная тема. Вспомним о БИОС, который загружает самый первый сектор (Master Boot Record) с выбранного в его настройках устройства (дискеты, винчестера, CD-ROM привода и пр.) по адресу 0x7C00h куда и передает управление. Программа, находящаяся в этой памяти называется первичным загрузчиком. У него не очень много возможностей, так как его размер ограничен 512 байтами. Его задачей является подготовка компьютера, а именно: запись в память вторичный загрузчик, предварительно считанный с HDD,
включить линию A20 и перевести процессор в защищенный режим. После этого управление передается вторичному загрузчику, цели работы которого точно не определены. Я считаю, что его главными задачами являются формирование таблицы прерываний, подготовка компьютера к работе с файловой системой, определение периферийных устройств, подключенных к компьютеры, передача управления ядру, скачанному им с диска заложенному в памяти.
Чтобы более точно понять устройство загрузки ОС, перед переходом к исходным тестам рассмотрим принципы загрузки наиболее популярных в наше время ОС: Linux и
Windows.
Linux может загружаться как через специализированный загрузчик (Lilo), так и через boot sector диска. Поскольку загрузчика у нас нет, а есть только желание более полно узнать об устройстве загрузки, рассмотрим второй случай:
1) boot sector записывает свой код в 9000h
2) Загружает с диска Setup, который находится в нескольких последующих секторах (9000h:0200h;)
3) Загружает ядро в 1000h. Ядро так же следует после Setup. Ядро должно быть меньше 508 килобайт
4) Управление передается Setup
5) Setup проверяется на корректность
6) С помощью BIOS определяется оборудование, размер памяти, наличие жестких дисков, наличие шины Micro channel bus, PC/2 mouse, Advanced power management, инициализируются клавиатура и видеосистема
7) Процессор переводится в защищенный режим
8) Управление передается ядру
9) Ядро переписывается по адресу 100000h (если оно было заархивировано, то оно предварительно разархивируется)
10) Управление передается ядру
11) Активируется страничная адресация
12) Происходит инициализация IDT и GDT, при этом в кодовый сегмент и в сегмент данных ядра входит вся виртуальная память
13) Инициализируются драйвера
14) Управление передается процессу init;
15. init запускает все остальные необходимые программы в соответствии с файлами конфигурации(init.X);
Теперь рассмотрим загрузку Windows (NT, так как ранние версии устарели):
1) boot sector загружает NTLDR
2) Процессор переходит в защищенный режим;
3) Делаются таблицы страниц
4) Механизм преобразования страниц;
5) Чтение boot.ini, используя код FS под названием read only. Выводит на экран выбор загрузки ОС (из boot.ini)
6) Из boot.ini считывается адрес директории Windows
7) Управление получает ntdetect.com, определяющий устройства, установленные на компьютере
8) Из %dir%\system32 загружается ntoskrnl.exe, в котором находится ядро.
9) Управление передается hal.dll с информацией об аппаратном обеспечении;
10) Загружаются драйвера и важные файлы
11) Стартует графическая оболочка и пр.
Итак, мы рассмотрели на примерах уже готовых ОС этапы загрузок, а так же устройство памяти. Приступим непосредственно к написанию своей ОС. Начнем мы с написания загрузчика, который должен обеспечить загрузку и подготовить все для старта ОС. Он будет делиться на два (деление условное). Задача первого подготовить базу, а точнее занести в память код с дискеты, после чего передать управление второму загрузчику, задача которого перевести процессор в защищенный режим и сделать другие подготовки для передачи управления уже собственно ядру.
1) Первичный загрузчик
Загружаться мы будем с дискеты, а следовательно нам необходимо читать с нее данные по секторам.
2) Вторичный загрузчик
А теперь вторичный загрузчик:
Оба загрузчика готовы. Осталось лишь откомпилировать их и отправить на bootsector дискеты. За сим с первой частью я заканчиваю, в следующей части мы будем писать ядро системы.