Как пишутся операционные системы

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

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

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

Мы будем предполагать, что читатель уже знаком с основами языков ассемблер и Си, а также элементарными понятиями архитектуры ЭВМ. То есть, мы не будем объяснять, что такое регистр или, скажем, оперативная память. Если вам не будет хватать знаний, вы всегда можете обратиться к дополнительной литературе. Краткий список литературы и ссылки на сайты с хорошими статьями есть в конце статьи. Также желательно уметь пользоваться 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» производится заполнение остатка кода программы (за исключением последних двух байт) нулями. Делается это для того, чтобы после компиляции в последних двух байтах программы оказалась сигнатура загрузочного сектора.

С кодом программы вроде разобрались, давайте теперь попробуем скомпилировать это счастье. Для компиляции нам понадобится, собственно говоря, ассемблер — выше упомянутый yasm. Он есть в большинстве репозиториев Linux. Программу можно откомпилировать следующим образом:

$ yasm -f bin -o hello.bin hello.asm

Полученный файл hello.bin нужно записать в зарузочный сектор дискеты. Делается это примерно так (разумеется, вместо fd нужно подставить имя своего дисковода).

$ dd if=hello.bin of=/dev/fd

Поскольку далеко не у всех остались дисководы и дискеты, можно воспользоваться виртуальной машиной, например, qemu или VirtualBox. Для этого придётся сделать образ дискеты с нашим загрузчиком и вставить его в «виртуальный дисковод».
Создаём образ диска и заполняем его нулями:

$ dd if=/dev/zero of=disk.img bs=1024 count=1440

Записываем в самое начало образа нашу программу:

$ dd if=hello.bin of=disk.img conv=notrunc

Запускаем полученный образ в qemu:

$ qemu -fda disk.img -boot a

После запуска вы должны увидеть окошко qemu с радостной строчкой «Hello World!». На этом первая статья заканчивается. Будем рады увидеть ваши отзывы и пожелания.

Литература

  1. По языку ассемблера:
    • Зубков С. В. «Assembler для DOS, Windows и Unix»
    • Введение в машинный код
    • Программирование на ассемблере под DOS
  2. По языку Си:
    • Керниган Б., Ритчи Д. «Язык программирования C»
    • Шилдт Г. «Полный справочник по C»
  3. По устройству операционных систем:
    • Таненбаум Э. «Современные операционные системы»
    • Таненбаум Э. «Операционные системы: Разработка и реализация»
    • Олифер В., Олифер Н. «Сетевые операционные системы»
    • http://osdev.org
  4. По архитектуре ЭВМ:
    • Таненбаум Э. «Архитектура компьютера»
    • Гук М. «Аппаратные средства IBM PC. Энциклопедия»
    • Петцольд Ч. «Код. Тайный язык информатики»
  5. Справочная информация
    • Dan Rollins’s TechHelp
    • Intel Software Developer Manual

Создание операционной системы — одна из сложнейших задач в программировании, поскольку требует обширных и комплексных знаний о работе компьютера. Каких именно? Разбираемся ниже.

Что такое ОС

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

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

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

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

Кратко об истории операционных систем

Первая операционная система была создана в 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-х годов книга была признана определяющей в этой сфере.

Если вдруг вышеописанные материалы вам не подошли, вот ещё пара отличных книг:

  • «Assembly Language Step-by-Step: Programming with Linux» Джефа Дантеманна;
  • «Modern X86 Assembly Language Programming» Дэниэля Кассварма.

Кроме того, на нашем сайте есть статья о написании простейшего ядра под x86 на C.

Язык Cи

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

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

«Learn C the Hard Way» — название ещё одной книги. Кроме привычной теории в ней собрано много практических решений. Этот учебник расскажет обо всех аспектах языка.

Либо же можете выбрать одну из этих книг:

  • «The C Programming Language» Кернигхана и Ритчи;
  • «C Programming Absolute Beginner’s Guide» Пэрри и Миллера.

Разработка ОС

После освоения всего необходимого, что касается информатики, языка ассемблера и 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и.

Вот ещё три книги, которые помогут вам ознакомиться с дисциплиной разработки ОС:

  • «Modern Operating Systems» Таненбаума и Боза;
  • «The Design of the UNIX Operating System» Маурайса Баха;
  • «Operating System Concepts» Сибершатза, Гальвина и Ганге.

Сообщества, посвященные разработке ОС

Заниматься разработкой ОС в одиночку смело, но сложно. Гораздо проще найти единомышленников, которые, как и вы, решили попытать удачу в этом нелёгком деле. Существует пара подходящих мест:

  • OSDev.org — это Вики с исчерпывающей информацией о разработке ОС и отличным форумом, где вы можете попросить о помощи или же наоборот сами кого-нибудь выручить.
  • OS Development Channel на Реддите. Канал, также посвящённый разработке ОС.
  • Computer Science, Programmers, StackOverflow от StackExchange — площадки, где вы можете задавать различные технические вопросы.

Заключение

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

Перевод статьи «How To Program Your Very Own Operating Systems (OS)»

UNIX-подобная операционная система интересна для разбора, а также для написания собственного ядра, которое выведет сообщение. Ну что, напишем?

UNIX-подобная операционная система

UNIX-подобная операционная система и загрузка x86 машины

Что такое UNIX-подобная операционка? Это ОС, созданная под влиянием UNIX. Но прежде чем заняться написанием ядра для нее, давайте посмотрим, как машина загружается и передает управление ядру.

Большинство регистров x86 процессора имеют четко определенные значения после включения питания. Регистр указателя инструкций (EIP) содержит адрес памяти для команды, выполняемой процессором. EIP жестко закодирован на значение 0xFFFFFFF0. Таким образом, у процессора есть четкие инструкции по физическому адресу 0xFFFFFFF0, что, по сути, – последние 16 байт 32-разрядного адресного пространства. Этот адрес называется вектором сброса.

Теперь карта памяти чипсета гарантирует, что 0xFFFFFFF0 сопоставляется с определенной частью BIOS, а не с ОЗУ. Между тем, BIOS копирует себя в ОЗУ для более быстрого доступа. Это называется затенением (shadowing). Адрес 0xFFFFFFF0 будет содержать только инструкцию перехода к адресу в памяти, где BIOS скопировал себя.

Таким образом, код BIOS начинает свое выполнение. Сначала BIOS ищет загрузочное устройство в соответствии с настроенным порядком загрузочных устройств. Он ищет определенное магическое число, чтобы определить, является устройство загрузочным или нет (байты 511 и 512 первого сектора равны 0xAA55).

После того, как BIOS обнаружил загрузочное устройство, он копирует содержимое первого сектора устройства в оперативную память, начиная с физического адреса 0x7c00; затем переходит по адресу и выполняет только что загруженный код. Этот код называется системным загрузчиком (bootloader).

Затем bootloader загружает ядро ​​по физическому адресу 0x100000. Адрес 0x100000 используется как стартовый адрес для всех больших ядер на x86 машинах.

Все x86 процессоры стартуют в упрощенном 16-битном режиме, называемом режимом реальных адресов. Загрузчик GRUB переключается в 32-битный защищенный режим, устанавливая младший бит регистра CR0 равным 1. Таким образом, ядро ​​загружается в 32-разрядный защищенный режим.

Обратите внимание, что в случае обнаружения ядра Linux, GRUB получит протокол загрузки и загрузит ​​Linux-ядро в реальном режиме. А ядро Linux сделает переключение в защищенный режим.

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

  • x86 компьютер (разумеется)
  • Linux
  • Ассемблер NASM
  • GCC
  • ld (GNU Linker)
  • GRUB
  • Исходный код

Ну и неплохо было бы иметь представление о том, как работает UNIX-подобная ОС. Исходный код можно найти в репозитории на Github.

Точка входа и запуск ядра

Для начала напишем небольшой файл в x86 ассемблере, который будет отправной точкой для запуска ядра. Этот файл будет вызывать внешнюю функцию на C, а затем остановит поток программы.

Как убедиться, что этот код послужит отправной точкой для ядра?

Мы будем использовать скрипт компоновщика, который связывает объектные файлы с целью создания окончательного исполняемого файла ядра. В этом скрипте явно укажем, что бинарный файл должен быть загружен по адресу 0x100000. Этот адрес, является тем местом, где должно быть ядро.

Вот код сборки:

;;kernel.asm
bits 32			;директива nasm - 32 bit
section .text

global start
extern kmain	        ;kmain определена в C-файле

start:
  cli 			;блокировка прерываний
  mov esp, stack_space	;установка указателя стека
  call kmain
  hlt		 	;остановка процессора

section .bss
resb 8192		;8KB на стек
stack_space:

Первая инструкция bits 32 не является инструкцией сборки x86. Это директива для ассемблера NASM, которая указывает, что он должен генерировать код для работы на процессоре, работающем в 32-битном режиме. Это не обязательно требуется в нашем примере, однако это хорошая практика – указывать такие вещи явно.

Вторая строка начинается с текстового раздела. Здесь мы разместим весь наш код.

global — еще одна директива NASM, служит для установки символов исходного кода как глобальных.

kmain — это собственная функция, которая будет определена в нашем файле kernel.c. extern объявляет, что функция определена ​​в другом месте.

Функция start вызывает функцию kmain и останавливает CPU с помощью команды hlt. Прерывания могут пробудить CPU из выполнения инструкции hlt. Поэтому мы предварительно отключаем прерывания, используя инструкцию cli.

В идеале необходимо выделить некоторый объем памяти для стека и указать на нее с помощью указателя стека (esp). Однако, GRUB делает это за нас, и указатель стека уже установлен. Тем не менее, для верности, мы выделим некоторое пространство в разделе BSS и поместим указатель стека в начало выделенной памяти. Для этого используем команду resb, которая резервирует память в байтах. После этого остается метка, которая указывает на край зарезервированного фрагмента памяти. Перед вызовом kmain указатель стека (esp) используется для указания этого пространства с помощью команды mov.

В kernel.asm мы сделали вызов функции kmain(). Таким образом, код на C начнет выполнятся в kmain():

/*
*  kernel.c
*/
void kmain(void)
{
	const char *str = "my first kernel";
	char *vidptr = (char*)0xb8000; 	//видео пямять начинается здесь
	unsigned int i = 0;
	unsigned int j = 0;

	/* этот цикл очищает экран*/
	while(j < 80 * 25 * 2) {
		/* пустой символ */
		vidptr[j] = ' ';
		/* байт атрибутов */
		vidptr[j+1] = 0x07; 		
		j = j + 2;
	}

	j = 0;

	/* в этом цикле строка записывается в видео память */
	while(str[j] != '') {
		/* ascii отображение */
		vidptr[i] = str[j];
		vidptr[i+1] = 0x07;
		++j;
		i = i + 2;
	}
	return;
}

Наше ядро ​​будет очищать экран и выводить на него строку «my first kernel».

Для начала мы создаем указатель vidptr, который указывает на адрес 0xb8000. Этот адрес является началом видеопамяти в защищенном режиме. Текстовая память экрана – это просто кусок памяти в нашем адресном пространстве. Ввод/вывод для экрана на карте памяти начинается с 0xb8000 и поддерживает 25 строк по 80 ascii символов каждая.

Каждый элемент символа в этой текстовой памяти представлен 16 битами (2 байта), а не 8 битами (1 байт), к которым мы привыкли. Первый байт должен иметь представление символа, как в ASCII. Второй байт является атрибутным байтом. Он описывает форматирование символа, включая разные атрибуты, например цвет.

Чтобы напечатать символ с зеленым цветом на черном фоне, мы сохраним символ s в первом байте адреса видеопамяти и значение 0x02 во втором байте.

0 — черный фон, а 2 — зеленый.

Ниже приведена таблица кодов для разных цветов:

0 - Black, 1 - Blue, 2 - Green, 3 - Cyan, 4 - Red, 5 - Magenta, 6 - Brown, 7 - Light Grey, 8 - Dark Grey, 9 - Light Blue, 10/a - Light Green, 11/b - Light Cyan, 12/c - Light Red, 13/d - Light Magenta, 14/e - Light Brown, 15/f – White.

В нашем ядре мы будем использовать светло-серые символы на черном фоне. Поэтому наш байт атрибутов должен иметь значение 0x07.

В первом цикле while программа записывает пустой символ с атрибутом 0x07 по всем 80 столбцам из 25 строк. Таким образом, экран очищается.

Во втором цикле while символы строки «my first kernel» записываются в кусок видеопамяти. Для каждого символа атрибутный байт содержит значение 0x07.

Таким образом, строка отобразится на экране.

Связующая часть

Мы собираем kernel.asm и NASM в объектный файл, а затем с помощью GCC компилируем kernel.c в другой объектный файл. Теперь наша задача – связать эти объекты с исполняемым загрузочным ядром.

Для этого мы используем явный скрипт компоновщика, который можно передать как аргумент ld (наш компоновщик).

/*
*  link.ld
*/
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
 {
   . = 0x100000;
   .text : { *(.text) }
   .data : { *(.data) }
   .bss  : { *(.bss)  }
 }

Во-первых, мы устанавливаем выходной формат исполняемого файла как 32-битный исполняемый (ELF). ELF – стандартный формат двоичного файла для Unix-подобных систем на архитектуре x86.

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

SECTIONS – самая важная часть, где мы определяем разметку исполняемого файла. Здесь указывается, как должны быть объединены различные разделы и в каком месте они будут размещаться.

В фигурных скобках, следующих за инструкцией SECTIONS, символ периода (.) – представляет собой счетчик местоположения.

Счетчик местоположения всегда инициализируется до 0x0 в начале блока SECTIONS. Его можно изменить, присвоив ему новое значение.

Как уже говорилось, код ядра должен начинаться с адреса 0x100000. Таким образом, мы установили счетчик местоположения в 0x100000.

Посмотрите на следующую строку .text: {*(.text)}

Звездочка (*) является спецсимволом, который будет соответствовать любому имени файла. То есть, выражение *(.text) означает все секции ввода .text из всех входных файлов.

Таким образом, компоновщик объединяет все текстовые разделы объектных файлов в текстовый раздел исполняемого файла по адресу, хранящемуся в счетчике местоположения. Раздел кода исполняемого файла начинается с 0x100000.

После того, как компоновщик разместит секцию вывода текста, значение счетчика местоположения установится в 0x1000000 + размер раздела вывода текста.

Аналогично, разделы данных и bss объединяются и помещаются на значения счетчика местоположения.

Grub и Multiboot

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

Существует стандарт для загрузки различных x86 ядер с использованием загрузчика, называемый спецификацией Multiboot.

GRUB загрузит ядро только в том случае, если оно соответствует Multiboot-спецификации.

Согласно ей, ядро должно содержать заголовок в пределах его первых 8 килобайт.

Кроме того, этот заголовок должен содержать дополнительно 3 поля:

  • поле магического числа: содержит магическое число 0x1BADB002, для идентификации заголовка.
  • поле флагов: сейчас оно не нужно, просто установим его значение в ноль.
  • поле контрольной суммы: когда задано, должно возвращать ноль для суммы с первыми двумя полями.

Итак, kernel.asm будет выглядеть таким образом:

;;kernel.asm

;nasm directive - 32 bit
bits 32
section .text
        ;multiboot spec
        align 4
        dd 0x1BADB002            ;магические числа
        dd 0x00                  ;флаги
        dd - (0x1BADB002 + 0x00) ;контрольная сумма. мч+ф+кс должно равняться нулю

global start
extern kmain	        ;kmain определена во внешнем файле

start:
  cli 			;блокировка прерываний
  mov esp, stack_space	;указатель стека
  call kmain
  hlt		 	;остановка процессора

section .bss
resb 8192		;8KB на стек
stack_space:

Сборка ядра

Теперь создадим объектные файлы из kernel.asm и kernel.c, а затем свяжем их с помощью скрипта компоновщика.

nasm -f elf32 kernel.asm -o kasm.o

запустит ассемблер для создания объектного файла kasm.o в формате 32-битного ELF.

gcc -m32 -c kernel.c -o kc.o

Параметр «-c» гарантирует, что после компиляции связывание не произойдет неявным образом.

ld -m elf_i386 -T link.ld -o kernel kasm.o kc.o

запустит компоновщик с нашим скриптом и сгенерирует исполняемое именованное ядро.

Настройка GRUB и запуск ядра

UNIX-подобная ОС с ее ядром почти поддалась. GRUB требует, чтобы ядро имело имя вида kernel-<version>. Переименуйте ядро, к примеру, в kernel-701.

Теперь поместите его в каталог /boot. Для этого вам потребуются права суперпользователя.

В конфигурационном файле GRUB grub.cfg вы должны добавить запись такого вида:

title myKernel
	root (hd0,0)
	kernel /boot/kernel-701 ro

Не забудьте удалить директиву hiddenmenu, если она существует.

Перезагрузите компьютер, и вы сможете наблюдать список с именем вашего ядра. Выберите его, и вы увидите:

Ядро в работе

Это ваше ядро! Оказывается, UNIX-подобная операционная система и ее составляющие не так уж сложны, верно?

PS:

  • Всегда желательно использовать виртуальную машину для всех видов взлома ядра.
  • Чтобы запустить это ядро на grub2, который является загрузчиком по умолчанию для более новых дистрибутивов, ваша конфигурация должна выглядеть так:
menuentry 'kernel 701' {
	set root='hd0,msdos1'
	multiboot /boot/kernel-701 ro
}

Если вы хотите запустить ядро на эмуляторе qemu вместо загрузки с помощью GRUB, вы можете сделать так:

qemu-system-i386 -kernel kernel

Теперь вы имеете представление о том, как устроены UNIX-подобная ОС и ее ядро, а также сможете без труда написать последнее.

Пишем операционную систему. Часть 1. Загрузчик +34

Из песочницы, Системное программирование


Рекомендация: подборка платных и бесплатных курсов 3D max — https://katalog-kursov.ru/

Всем привет! Сегодня мы напишем загрузчик, который будет выводить «Hello World» и запустим его на VirtualBox. Писать будем на ассемблере FASM. Скачать его можно отсюда. Также нам понадобится собственно VirtualBox и UltraISO. Перед тем как писать код, разберемся как загружаются операционные системы.

Итак, когда мы нажимаем большую кнопку включения на нашем компьютере запускается система, которая есть на любом компьютере — BIOS (Basic Input/Output System или базовая система ввода/вывода). Задача BIOS это:

  1. Обнаружить все подключенные устройства и проверить их на работоспособность. За это отвечает программа POST (Power On Self Test, самотестирование при включении). Если жизненно необходимое железо не обнаружено, то системный динамик (если таковой имеется) пропищит что-то непонятное и дальше загрузка не пойдет.
  2. Предоставить операционной системе функции для работы с железом.
  3. Считать самый первый сектор загрузочного устройства в нулевой сегмент оперативной памяти по смещению 0x7C00h и передать туда управление. 1 сектор на диске равен 512 байтам. Поэтому, наш загрузчик не должен превышать 512 байт. BIOS определяет, что сектор загрузочный по наличию в последних двух его байтах значений 0x55 и 0xAA.

Теперь можно приступать к написанию кода. Запускаем файл FASMW.EXE, который находится в архиве с FASM-мом и вставляем туда следующий код:

org 7C00h

 start:
    cli              ;Запрещаем прерывания (чтобы ничего не отвлекало)
    xor ax, ax       ;Обнуляем регистр ax
    mov ds, ax       ;Настраиваем dataSegment на нулевой адрес
    mov es, ax       ;Настраиваем сегмент es на нулевой адрес
    mov ss, ax       ;Настраиваем StackSegment на нулевой адрес
    mov sp, 07C00h   ;Указываем на текущую вершину стека
    sti              ;Запрещаем прерывания

  ;Очищаем экран
  mov ax, 3
  int 10h

  mov ah, 2h
  mov dh, 0
  mov dl, 0
  xor bh, bh
  int 10h

  ;Печатаем строку
  mov ax, 1301h
  mov bp, message
  mov cx, 12
  mov bl, 02h
  int 10h

  jmp $

message db 'Hello World!',0

times 510 - ($ - $$) db 0 ;Заполнение оставшихся байт нулями до 510-го байта
db 0x55, 0xAA ;Загрузочная сигнатура  

Этот код требует немного пояснений. Командой

org 7C00h

мы говорим, что код нужно загружать в ОЗУ по адресу 0x7C00. В строках

  mov ax, 3
  int 10h

мы устанавливаем видео режим 80х25 (80 символов в строке и 25 строк) и тем самым очищаем экран.

  mov ah, 2h
  mov dh, 0
  mov dl, 0
  xor bh, bh
  int 10h

Здесь мы устанавливаем курсор. За это отвечает функция 2h прерывания 10h. В регистр dh мы помещаем координату курсора по Y, а в регистр dl — по X.

  mov ax, 1301h
  mov bp, message
  mov cx, 12
  mov bl, 02h
  int 10h

Печатаем строку. За это отвечает функция 13h прерывания 10h. В регистр bp мы помещаем саму строку, в регистр cx — число символов в строке, в регистр bl — атрибут, в нашем случае цвет, он будет зеленым. На цвет фона влияют первые 4 бита, на цвет текста — вторые 4 бита. Ниже представлена таблица цветов

0 - черный, 1 - синий, 2 - зеленый, 3 - желтый, 4 - красный, 5 - фиолетовый, 6 - коричневый, 7 - светло-серый, 8 - темно-серый, 9 - светло-синий, A - светло-зеленый, B - светло-желтый, C - светло-красный, D- светло-фиолетовый, E - светло-коричневый, F – Белый.

В строке

jmp $

Программа зависает.

Откомпилируем код нажатием клавиш Ctrl + F9 и сохраним полученный файл как boot.bin.

Запуск

Запускаем UltraISO и перетаскиваем наш бинарник в специальную область (отмечено красной стрелкой).

Далее кликаем правой кнопкой мыши по бинаринку и нажимаем кнопку примерно с такой надписью: «Установить загрузочным файлом». Далее сохраняем наш файл в формате ISO.
Открываем VIrtualBox и создаем новую виртуальную машину (если вы не знаете, как это делается, кликайте сюда). Итак, после того, как вы создали виртуальную машину, нажимаем «Настроить, выбираем пункт „Носители“, нажимаем на „Пусто“, там где „Привод“ есть значок оптического диска. Нажимаем на него и выбираем „Выбрать образ оптического диска“, ищем наш ISO файл, нажимаем „Открыть“. Сохраняем все настройки и запускаем виртуальную машину. На экране появляется наш „Hello World!“.

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


Download Article


Download Article

This wikiHow teaches you how to plan and develop an operating system if you don’t know how to code in C, or C++. Operating systems manage computer hardware and provide the resources that applications need to run. Writing an operating system from scratch requires a strong command of computer science, a programming language like C or C++, assembly, and code management practices.

  1. Image titled Make a Computer Operating System Step 1

    1

    Take some basic Computer Science courses. Unlike developing for the web, creating an operating system requires a strong understanding of algorithms, data structures, computer hardware, and resource management.[1]
    People get degrees in this stuff, so don’t expect to write an entire operating system after reading an online tutorial! Harvard’s Intro to Computer Science course is available online through EDX at no cost.

  2. Image titled Make a Computer Operating System Step 2

    2

    Advertisement

  3. Image titled Make a Computer Operating System Step 4

    3

    Learn an assembly language. Assembly languages are low-level languages designed to communicate with different processors. Since assembly is different for different processor types (e.g., x86 assembly Language for Intel, AMD, VIA, and other processors), you’ll need to learn the version for the type of processor you’re coding for.

    • This open-source book, if read in its entirety, can give you a solid enough understanding for building an operating system.[2]
    • The Art of Assembly Language is a highly-recommended book about assembly available both on and offline.
    • You should also do a lot of research on the type of processor(s) on which your operating system should run. The manuals for processor architecture can easily be found using a Google search («Intel Manuals,» «ARM manuals,» etc.).
  4. Image titled Make a Computer Operating System Step 5

    4

    Complete an operating system tutorial. OS tutorials will walk you through the process of creating a basic operating system. This gets you used to the process and helps you figure out if there are any pieces you’re missing. Once you’ve completed a tutorial or two, you’ll be well on your way to creating your very own operating system.

    • Bare Bones is a tutorial that helps you write your first simple 32-bit kernel. After completing the tutorial, the next step is to use Meaty Skeleton to structure your own operating system.
    • Linux from Scratch is an online book that walks you through creating your own Linux operating system.
    • Operating systems from 0 to 1 is a free book about creating various operating systems from start to finish.
  5. Image titled Make a Computer Operating System Step 3

    5

    Figure out your operating system goals. What do you want your operating system to do? How should it look? Do you really need to write an entire operating system or are you just looking to create a certain look for your desktop? These are all things to consider before beginning to code.[3]

    • Consider developing with a team of other programmers. Having a team of developers working on the project will cut the development time significantly.
    • Add your project goals, plans, and questions to your public repository so it’s easy for others to help you.
  6. Advertisement

  1. Image titled Have Computer Fun Step 16

    1

    Choose a development environment. This is the platform you’ll be using to code your new operating system. It’s a matter of personal preference, but most people either use Windows or a flavor of UNIX. If you’re using Windows, it’s a good idea to install a UNIX environment like Cygwin or MinGW. You’ll generally want to make sure whatever environment you’re using includes the following:

    • GCC (the Gnu compiler). If you’re using Windows, you can install it in Cygwin or MinGW.
    • Binutils is a collection of tools used for manipulating object files. Again, if you’re using Windows, you can install it in Cygwin.
    • A good text editor. Vim and emacs are commonly used in UNIX environments. Windows users can use Notepad, or check out Notepad++ for a multi-tab upgrade.
    • Perl and/or Python. One or both are recommended for string manipulation.
  2. Image titled Make a Computer Operating System Step 13

    2

    Set up your version control system. Writing an operating system means you’ll likely be creating hundreds (or thousands!) of lines of code. When you’re working on revisions, this can get confusing. Some options to check out are CVS, Mercurial, and Subversion.

  3. Image titled Make a Computer Operating System Step 6

    3

  4. Image titled Make a Computer Operating System Step 9

    4

    Choose a kernel design. The kernel is the core of your operating system, providing an interface between the user and the computer hardware. There are monolithic kernels and micro kernels. Monolithic kernels implement all the services in the kernel, while microkernels have a small kernel combined with user daemons implementing services. In general, monolithic kernels are faster, but microkernels have better fault isolation and reliability.

  5. Image titled Make a Computer Operating System Step 12

    5

    Start small. Begin with small things such as displaying text and interrupts before moving on to things such as memory management and multitasking. You can also try making a simple 16-bit Operating System, instead of taking a big leap.

    • You will not have a full system in two weeks. Start with an OS that boots, then move on to the cool stuff.
  6. Image titled Make a Computer Operating System Step 14

    6

    Test your new operating system with a virtual machine. Rather than rebooting your computer each time you make changes or having to transfer the files from your development computer to your test machine, use a virtual machine application. VMWare is a common option that works with both Windows and Linux, and so is Bochs. Check for potential deadlocks and other bugs and repair them as needed.

  7. Image titled Make a Computer Operating System Step 15

    7

    Release a «release candidate.» You can do this by uploading your tested code to your public repository. This will allow people to test your operating system and report back on any issues they run into.

  8. Image titled Have Computer Fun Step 28

    8

    Network with other operating system developers. There’s an entire community of operating system developers congregating on various forums, including /r/osdev on Reddit and the Software Engineering Stack Exchange. One you’ve gotten a grasp on developing a basic operating system, read existing posts to see what kind of things come up during the development process.

  9. Advertisement

Add New Question

  • Question

    Can I do this if I have no programming experience?

    Community Answer

    Only if you’re willing to learn a lot. You will require a full understanding of at least assembly code, and the motivation to learn more. Building your own OS is not a small task.

  • Question

    What should be my first step?

    Community Answer

    Learn programming languages and concepts, and practice to become a very strong programmer.

  • Question

    Which applications can I use to program my operating system?

    Community Answer

    That depends on the programming language you are using. It is recommended, however, to use an IDE, or Integrated Development Environment, to program your operating system, as IDEs have built-in tools and features to help make development easier. Not all IDEs support every language however, so you will want to specifically search for IDEs that support the language (or languages) you’re using.

See more answers

Ask a Question

200 characters left

Include your email address to get a message when this question is answered.

Submit

Advertisement

Video

  • After all development is done, decide if you want to release the code as open source, or proprietary.

  • To make an operating system able to handle multiple processors, your Memory Manager must have «locking» mechanisms in place to prevent multiple processors from accessing the same resource at the same time. The «locks» used for this will require the presence of a scheduler to make sure that only one processor accesses a critical resource at any given time and all the others are made to wait. Yet the scheduler depends on the presence of a Memory Manager. This is a case of a deadlocked dependency. There is no standard way to solve problems like this; as a programmer, you are expected to be skilled enough to figure out his own way of dealing with it.

  • Be sure to implement security features as your top priority if you ever want your system to be viable.

Show More Tips

Thanks for submitting a tip for review!

Advertisement

About This Article

Article SummaryX

1. Take some computer science courses.
2. Learn a high-level programming language at an advanced level.
3. Learn a low-level assembly language.
4. Complete an operating system tutorial.
5. Plan your operating system.
6. Create your programming environment.
7. Build and test.
8. Release a release candidate.

Did this summary help you?

Thanks to all authors for creating a page that has been read 1,547,651 times.

Is this article up to date?


Download Article


Download Article

This wikiHow teaches you how to plan and develop an operating system if you don’t know how to code in C, or C++. Operating systems manage computer hardware and provide the resources that applications need to run. Writing an operating system from scratch requires a strong command of computer science, a programming language like C or C++, assembly, and code management practices.

  1. Image titled Make a Computer Operating System Step 1

    1

    Take some basic Computer Science courses. Unlike developing for the web, creating an operating system requires a strong understanding of algorithms, data structures, computer hardware, and resource management.[1]
    People get degrees in this stuff, so don’t expect to write an entire operating system after reading an online tutorial! Harvard’s Intro to Computer Science course is available online through EDX at no cost.

  2. Image titled Make a Computer Operating System Step 2

    2

    Advertisement

  3. Image titled Make a Computer Operating System Step 4

    3

    Learn an assembly language. Assembly languages are low-level languages designed to communicate with different processors. Since assembly is different for different processor types (e.g., x86 assembly Language for Intel, AMD, VIA, and other processors), you’ll need to learn the version for the type of processor you’re coding for.

    • This open-source book, if read in its entirety, can give you a solid enough understanding for building an operating system.[2]
    • The Art of Assembly Language is a highly-recommended book about assembly available both on and offline.
    • You should also do a lot of research on the type of processor(s) on which your operating system should run. The manuals for processor architecture can easily be found using a Google search («Intel Manuals,» «ARM manuals,» etc.).
  4. Image titled Make a Computer Operating System Step 5

    4

    Complete an operating system tutorial. OS tutorials will walk you through the process of creating a basic operating system. This gets you used to the process and helps you figure out if there are any pieces you’re missing. Once you’ve completed a tutorial or two, you’ll be well on your way to creating your very own operating system.

    • Bare Bones is a tutorial that helps you write your first simple 32-bit kernel. After completing the tutorial, the next step is to use Meaty Skeleton to structure your own operating system.
    • Linux from Scratch is an online book that walks you through creating your own Linux operating system.
    • Operating systems from 0 to 1 is a free book about creating various operating systems from start to finish.
  5. Image titled Make a Computer Operating System Step 3

    5

    Figure out your operating system goals. What do you want your operating system to do? How should it look? Do you really need to write an entire operating system or are you just looking to create a certain look for your desktop? These are all things to consider before beginning to code.[3]

    • Consider developing with a team of other programmers. Having a team of developers working on the project will cut the development time significantly.
    • Add your project goals, plans, and questions to your public repository so it’s easy for others to help you.
  6. Advertisement

  1. Image titled Have Computer Fun Step 16

    1

    Choose a development environment. This is the platform you’ll be using to code your new operating system. It’s a matter of personal preference, but most people either use Windows or a flavor of UNIX. If you’re using Windows, it’s a good idea to install a UNIX environment like Cygwin or MinGW. You’ll generally want to make sure whatever environment you’re using includes the following:

    • GCC (the Gnu compiler). If you’re using Windows, you can install it in Cygwin or MinGW.
    • Binutils is a collection of tools used for manipulating object files. Again, if you’re using Windows, you can install it in Cygwin.
    • A good text editor. Vim and emacs are commonly used in UNIX environments. Windows users can use Notepad, or check out Notepad++ for a multi-tab upgrade.
    • Perl and/or Python. One or both are recommended for string manipulation.
  2. Image titled Make a Computer Operating System Step 13

    2

    Set up your version control system. Writing an operating system means you’ll likely be creating hundreds (or thousands!) of lines of code. When you’re working on revisions, this can get confusing. Some options to check out are CVS, Mercurial, and Subversion.

  3. Image titled Make a Computer Operating System Step 6

    3

  4. Image titled Make a Computer Operating System Step 9

    4

    Choose a kernel design. The kernel is the core of your operating system, providing an interface between the user and the computer hardware. There are monolithic kernels and micro kernels. Monolithic kernels implement all the services in the kernel, while microkernels have a small kernel combined with user daemons implementing services. In general, monolithic kernels are faster, but microkernels have better fault isolation and reliability.

  5. Image titled Make a Computer Operating System Step 12

    5

    Start small. Begin with small things such as displaying text and interrupts before moving on to things such as memory management and multitasking. You can also try making a simple 16-bit Operating System, instead of taking a big leap.

    • You will not have a full system in two weeks. Start with an OS that boots, then move on to the cool stuff.
  6. Image titled Make a Computer Operating System Step 14

    6

    Test your new operating system with a virtual machine. Rather than rebooting your computer each time you make changes or having to transfer the files from your development computer to your test machine, use a virtual machine application. VMWare is a common option that works with both Windows and Linux, and so is Bochs. Check for potential deadlocks and other bugs and repair them as needed.

  7. Image titled Make a Computer Operating System Step 15

    7

    Release a «release candidate.» You can do this by uploading your tested code to your public repository. This will allow people to test your operating system and report back on any issues they run into.

  8. Image titled Have Computer Fun Step 28

    8

    Network with other operating system developers. There’s an entire community of operating system developers congregating on various forums, including /r/osdev on Reddit and the Software Engineering Stack Exchange. One you’ve gotten a grasp on developing a basic operating system, read existing posts to see what kind of things come up during the development process.

  9. Advertisement

Add New Question

  • Question

    Can I do this if I have no programming experience?

    Community Answer

    Only if you’re willing to learn a lot. You will require a full understanding of at least assembly code, and the motivation to learn more. Building your own OS is not a small task.

  • Question

    What should be my first step?

    Community Answer

    Learn programming languages and concepts, and practice to become a very strong programmer.

  • Question

    Which applications can I use to program my operating system?

    Community Answer

    That depends on the programming language you are using. It is recommended, however, to use an IDE, or Integrated Development Environment, to program your operating system, as IDEs have built-in tools and features to help make development easier. Not all IDEs support every language however, so you will want to specifically search for IDEs that support the language (or languages) you’re using.

See more answers

Ask a Question

200 characters left

Include your email address to get a message when this question is answered.

Submit

Advertisement

Video

  • After all development is done, decide if you want to release the code as open source, or proprietary.

  • To make an operating system able to handle multiple processors, your Memory Manager must have «locking» mechanisms in place to prevent multiple processors from accessing the same resource at the same time. The «locks» used for this will require the presence of a scheduler to make sure that only one processor accesses a critical resource at any given time and all the others are made to wait. Yet the scheduler depends on the presence of a Memory Manager. This is a case of a deadlocked dependency. There is no standard way to solve problems like this; as a programmer, you are expected to be skilled enough to figure out his own way of dealing with it.

  • Be sure to implement security features as your top priority if you ever want your system to be viable.

Show More Tips

Thanks for submitting a tip for review!

Advertisement

About This Article

Article SummaryX

1. Take some computer science courses.
2. Learn a high-level programming language at an advanced level.
3. Learn a low-level assembly language.
4. Complete an operating system tutorial.
5. Plan your operating system.
6. Create your programming environment.
7. Build and test.
8. Release a release candidate.

Did this summary help you?

Thanks to all authors for creating a page that has been read 1,547,651 times.

Is this article up to date?


Загрузить PDF


Загрузить PDF

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

Шаги

  1. Изображение с названием Make a Computer Operating System Step 1

    1

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

  2. Изображение с названием Make a Computer Operating System Step 2

    2

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

  3. Изображение с названием Make a Computer Operating System Step 3

    3

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

  4. Изображение с названием Make a Computer Operating System Step 4

    4

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

  5. Изображение с названием Make a Computer Operating System Step 5

    5

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

  6. Изображение с названием Make a Computer Operating System Step 6

    6

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

  7. Изображение с названием Make a Computer Operating System Step 7

    7

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

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

    8

    Выберите интерфейс программирования приложений (API). Одной из подборок хорошего API является POSIX, так как она хорошо документирован. Все системы Unix имеют, по крайней мере, частичную поддержку POSIX, так что было бы тривиально пристраивать программы Unix на вашу операционную систему.

  9. Изображение с названием Make a Computer Operating System Step 9

    9

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

  10. Изображение с названием Make a Computer Operating System Step 10

    10

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

  11. Изображение с названием Make a Computer Operating System Step 11

    11

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

  12. Изображение с названием Make a Computer Operating System Step 12

    12

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

  13. Изображение с названием Make a Computer Operating System Step 13

    13

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

  14. Изображение с названием Make a Computer Operating System Step 14

    14

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

  15. Изображение с названием Make a Computer Operating System Step 15

    15

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

  16. Изображение с названием Make a Computer Operating System Step 16

    16

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

    Реклама

Советы

  • Когда разработка будет закончена, подумайте, хотите ли вы представить код в свободном доступе либо же установить частные права на него.
  • Обязательно сделайте функции безопасности вашим основным приоритетом, если хотите, чтобы ваша система была жизнеспособной.
  • Не начинайте проект разработки операционной системы с целью обучения программированию. Если вы не знаете 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 привод

Источники

  1. Справочник: Linux From Scratch
  2. Загрузчик: GRUB
  3. Приложения для виртуальной машины: Bochs, VM Ware, XM Virtual Box.
  4. Руководство по работе с процессором: Intel manuals
  5. Сайты по разработке ОС: OSDev, OSDever

Об этой статье

Эту страницу просматривали 74 589 раз.

Была ли эта статья полезной?

Школа ассемблера: разработка операционной системы

Оригинал: AsmSchool: Make an operating system

Автор: Mike Saunders

Дата публикации: 15 апреля 2016 г.

Перевод: А. Панин

Дата перевода: 16 апреля 2016 г.

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

Для чего это нужно?

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

В течение нескольких месяцев мы прошли сложный путь, который начался с разработки простых программ на языке ассемблера для Linux и закончился в прошлом статье серии разработкой самодостаточного кода, исполняющегося на персональном компьютере без операционной системы. Ну а сейчас мы попытаемся собрать всю информацию воедино и создать самую настоящую операционную систему. Да, мы пойдем по стопам Линуса Торвальдса, но для начала стоит ответить на следующие вопросы: «Что же представляет собой операционная система? Какие из ее функций нам придется воссоздать?».

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

Наша операционная система в работе: вывод приветствия, исполнение команды и запуск программы с диска

Наша операционная система в работе: вывод приветствия, исполнение команды и запуск программы с диска

Разработка системного загрузчика

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

Вы можете загрузить исходный код рассмотренных в статье примеров по ссылке www.linuxvoice.com/code/lv015/asmschool.zip. А это код нашего системного загрузчика из файла с именем boot.asm:

BITS 16
jmp short start ; Переход к метке с пропуском описания диска
nop ; Дополнение перед описанием диска
%include "bpb.asm"
start:
mov ax, 07C0h ; Адрес загрузки
mov ds, ax ; Сегмент данных
mov ax, 9000h ; Подготовка стека
mov ss, ax
mov sp, 0FFFFh ; Стек растет вниз!
cld ; Установка флага направления
mov si, kern_filename
call load_file
jmp 2000h:0000h ; Переход к загруженному из файла бинарному коду ядра ОС
kern_filename db "MYKERNELBIN"
%include "disk.asm"
times 510-($-$$) db 0 ; Дополнение бинарного кода нулями до 510 байт
dw 0AA55h ; Метка окончания бинарного кода системного загрузчика
buffer: ; Начало буфера для содержимого диска

В данном коде первой инструкцией центрального процессора является инструкция jmp, которая расположена после директивы BITS, сообщающей ассемблеру NASM о том, что используется 16-битный режим. Как вы наверняка помните из предыдущей статьи серии, исполнение загружаемого средствами BIOS с диска 512-байтного бинарного кода начинается с самого начала, но нам приходится осуществлять переход к метке для пропуска специального набора данных. Очевидно, что в прошлом месяце мы просто записывали код в начало диска (с помощью утилиты dd), а остальное пространство диска оставляли пустым.

Сейчас же нам придется использовать флоппи-диск с подходящей файловой системой MS-DOS (FAT12), а для того, чтобы корректно работать с данной файловой системой, нужно добавить набор специальных данных рядом с началом сектора. Этот набор называется «блоком параметров BIOS» (BIOS Parameter Block — BPB) и содержит такие данные, как метка диска, количество секторов и так далее. Он не должен интересовать нас на данном этапе, так как подобным темам можно посвятить не одну серию статей, именно поэтому мы разместили все связанные с ним инструкции и данные в отдельном файле исходного кода с именем bpb.asm.

Исходя из вышесказанного, данная директива из нашего кода крайне важна:

%include "bpb.asm"

Это директива NASM, позволяющая включить содержимое указанного файла исходного кода в текущий файл исходного кода в процессе ассемблирования. Таким образом мы сможем сделать код нашего системного загрузчика максимально коротким и понятным, вынеся все подробности реализации блока параметров BIOS в отдельный файл. Блок параметров BIOS должен располагаться через три байта после начала сектора, а так как инструкция jmp занимает лишь два байта, нам приходится использовать инструкцию nop (ее название расшифровывается как «no operation» — это инструкция, которая не делает ничего, кроме траты циклов центрального процессора) с целью заполнения оставшегося байта.

Ничто не сравнится с наблюдением за собственноручно созданным программным продуктом, исполняющемся на реальном компьютере (а также за собственным отражением) - это просто круто!

Ничто не сравнится с наблюдением за собственноручно созданным программным продуктом, исполняющемся на реальном компьютере (а также за собственным отражением) — это просто круто!

Работа со стеком

Далее нам придется использовать инструкции, аналогичные рассмотренным в прошлой статье, для подготовки регистров и стека, а также инструкцию cld (расшифровывается как «clear direction»), позволяющую установить флаг направления для определенных инструкций, таких, как инструкция lodsb, которая после ее исполнения будет увеличивать значение в регистре SI, а не уменьшать его.

После этого мы помещаем адрес строки в регистр SI и вызываем нашу функцию load_file. Но задумайтесь на минуту — мы ведь еще не разработали эту функцию! Да, это правда, но ее реализацию можно найти в другом подключаемом нами файле исходного кода с именем disk.asm.

Файловая система FAT12, используемая на флоппи-дисках, которые форматируются в MS-DOS, является одной простейших существующих файловых систем, но для работы с ее содержимым также требуется немалый объем кода. Подпрограмма load_file имеет длину около 200 строк и не будет приведена в данной статье, так как мы рассматриваем процесс разработки операционной системы, а не драйвера для определенной файловой системы, следовательно, не очень разумно тратить таким образом место на страницах журнала. В общем, мы подключили файл исходного кода disk.asm практически перед окончанием текущего файла исходного кода и можем забыть про него. (Если же вас все-таки заинтересовала структура файловой системы FAT12, вы можете ознакомиться с отличным обзором по адресу http://tinyurl.com/fat12spec, после чего заглянуть в файл исходного кода disk.asm — код, содержащийся в нем, хорошо прокомментирован.)

В любом случае, подпрограмма load_file загружает бинарный код из файла с именем, заданном в регистре SI, в сегмент 2000 со сдвигом 0, после чего мы осуществляем переход к его началу для исполнения. И это все — ядро операционной системы загружено и системный загрузчик выполнил свою задачу!

Вы наверняка заметили, что в качестве имени файла ядра операционной системы в нашем коде используется MYKERNELBIN вместо MYKERNEL.BIN, которое вполне вписывается в схему имен 8+3, используемую на флоппи-дисках в DOS. На самом деле, в файловой системе FAT12 используется внутреннее представление имен файлов, а мы экономим место, используя имя файла, которое гарантированно не потребует реализации в рамках нашей подпрограммы load_file механизма поиска символа точки и преобразования имени файла во внутреннее представление файловой системы.

После строки с директивой подключения файла исходного кода disk.asm расположены две строки, предназначенные для дополнения бинарного кода системного загрузчика нулями до 512 байт и включения метки окончания его бинарного кода (об этом говорилось в прошлой статье). Наконец, в самом конце кода расположена метка "buffer", которая используется подпрограммой load_file. В общем, подпрограмме load_file требуется свободное пространство в оперативной памяти для выполнения некоторых промежуточных действий в процессе поиска файла на диске, а у нас есть достаточно свободного пространства после загрузки системного загрузчика, поэтому мы размещаем буфер именно здесь.

Для ассемблирования системного загрузчика следует использовать следующую команду:

nasm -f bin -o boot.bin boot.asm

Теперь нам нужно создать образ виртуального флоппи-диска в формате MS-DOS и добавить бинарный код нашего системного загрузчика в его первые 512 байт с помощью следующих команд:

mkdosfs -C floppy.img 1440
dd conv=notrunc if=boot.bin of=floppy.img

На этом процесс разработки системного загрузчика можно считать оконченным! Теперь у нас есть образ загрузочного флоппи-диска, который позволяет загрузить бинарный код ядра операционной системы из файла с именем mykernel.bin и исполнить его. Далее нас ждет более интересная часть работы — разработка самого ядра операционной системы

Ядро операционной системы

Мы хотим, чтобы наше ядро операционной системы выполняло множество важных задач: выводило приветствие, принимало ввод от пользователя, устанавливало, является ли ввод поддерживаемой командой, а также исполняло программы с диска после указания пользователем их имен. Это код ядра операционной системы из файла mykernel.asm:

mov ax, 2000h
mov ds, ax
mov es, ax
loop:
mov si, prompt
call lib_print_string
mov si, user_input
call lib_input_string
cmp byte [si], 0
je loop
cmp word [si], "ls"
je list_files
mov ax, si
mov cx, 32768
call lib_load_file
jc load_fail
call 32768
jmp loop
load_fail:
mov si, load_fail_msg
call lib_print_string
jmp loop
list_files:
mov si, file_list
call lib_get_file_list
call lib_print_string
jmp loop
prompt db 13, 10, "MyOS > ", 0
load_fail_msg db 13, 10, "Not found!", 0
user_input times 256 db 0
file_list times 1024 db 0
%include "lib.asm"

Перед рассмотрением кода следует обратить внимание на последнюю строку с директивой подключения файла исходного кода lib.asm, который также находится в архиве asmschool.zip с нашего веб-сайта. Это библиотека полезных подпрограмм для работы с экраном, клавиатурой, строками и дисками, которые вы также можете использовать — в данном случае мы подключаем этот файл исходного кода в самом конце основного файла исходного кода ядра операционной системы для того, чтобы сделать последний максимально компактным и красивым. Обратитесь к разделу «Подпрограммы библиотеки lib.asm» для получения дополнительной информации обо всех доступных подпрограммах.

В первых трех строках кода ядра операционной системы мы осуществляем заполнение регистров сегментов данными для указания на сегмент 2000, в который была осуществлена загрузка бинарного кода. Это важно для гарантированной корректной работы таких инструкций, как lodsb, которые должны читать данные из текущего сегмента, а не из какого-либо другого. После этого мы не будем выполнять каких-либо дополнительных операций с сегментами; наша операционная система будет работать с 64 Кб оперативной памяти!

Далее в коде расположена метка, соответствующая началу цикла. В первую очередь мы используем одну из подпрограмм из библиотеки lib.asm, а именно lib_print_string, для вывода приветствия. Байты 13 и 10 перед строкой приветствия являются символами перехода на новую строку, благодаря которым приветствие будет выводиться не сразу же после вывода какой-либо программы, а всегда на новой строке.

После этого мы используем другую подпрограмму из библиотеки lib.asm под названием lib_input_string, которая принимает введенные пользователем с помощью клавиатуры символы и сохраняет их в буфере, указатель на который находится в регистре SI. В нашем случае буфер объявляется ближе к концу кода ядра операционной системы следующим образом:

user_input times 256 db 0

Данное объявление позволяет создать буфер длиной в 256 символов, заполненный нулями — его длины должно быть достаточно для хранения команд такой простой операционной системы, как наша!

Далее мы выполняем проверку пользовательского ввода. Если первый байт буфера user_input является нулевым, то пользователь просто нажал клавишу Enter, не вводя какой-либо команды; не забывайте о том, что все строки оканчиваются нулевыми символами. Таким образом, в данном случае мы должны просто перейти к началу цикла и снова вывести приветствие. Однако, в том случае, если пользователь вводит какую-либо команду, нам придется сначала проверить, не ввел ли он команду ls. До текущего момента вы могли наблюдать в наших программах на языке ассемблера лишь сравнения отдельных байт, но не стоит забывать о том, что также имеется возможность осуществления сравнения двухбайтовых значений или машинных слов. В данном коде мы сравниваем первое машинное слово из буфера user_input с машинным словом, соответствующим строке ls и в том случае, если они идентичны, перемещаемся к расположенному ниже блоку кода. В рамках этого блока кода мы используем другую подпрограмму из библиотеки lib.asm для получения разделенного запятыми списка расположенных на диске файлов (для хранения которого должен использоваться буфер file_list), выводим этот список на экран и перемещаемся назад в цикл для обработки пользовательского ввода.

Исполнение сторонних программ

Если пользователь не вводит команду ls, мы предполагаем, что он ввел имя программы с диска, поэтому имеет смысл попытаться загрузить ее. Наша библиотека lib.asm содержит реализацию полезной подпрограммы lib_load_file, которая осуществляет разбор таблиц файловой системы FAT12 диска: она принимает указатель на начало строки с именем файла посредством регистра AX, а также значение смещения для загрузки бинарного кода из файла программы посредством регистра CX. Мы уже используем регистр SI для хранения указателя на строку с пользовательским вводом, поэтому мы копируем этот указатель в регистр AX, после чего помещаем значение 32768, используемое в качестве смещения для загрузки бинарного кода из файла программы, в регистр CX.

Но почему мы используем именно это значение в качестве смещения для загрузки бинарного кода из файла программы? Ну, это просто один из вариантов карты распределения памяти для нашей операционной системы. Из-за того, что мы работаем в одном сегменте размером в 64 Кб, а бинарный код нашего ядра загружен со смещением 0, нам приходится использовать первые 32 Кб памяти для данных ядра, а остальные 32 Кб — для данных загружаемых программ. Таким образом, смещение 32768 является серединой нашего сегмента и позволяет предоставить достаточный объем оперативной памяти как ядру операционной системы, так и загружаемым программам.

После этого подпрограмма lib_load_file выполняет крайне важную операцию: если она не может найти файл с заданным именем на диске или по какой-то причине не может считать его с диска, она просто завершает работу и устанавливает специальный флаг переноса (carry flag). Это флаг состояния центрального процессора, который устанавливается в процессе выполнения некоторых математических операций и в данный момент не должен нас интересовать, но при этом мы можем определять наличие этого флага для принятия быстрых решений. Если подпрограмма lib_load_asm устанавливает флаг переноса, мы задействуем инструкцию jc (переход при наличии флага переноса — jump if carry) для перехода к блоку кода, в рамках которого осуществляется вывод сообщения об ошибке и возврат в начало цикла обработки пользовательского ввода.

В том же случае, если флаг переноса не установлен, можно сделать вывод, что подпрограмма lib_load_asm успешно загрузила бинарный код из файла программы в оперативную память по адресу 32768. Все что нам нужно в этом случае — это инициировать исполнение бинарного кода, загруженного по этому адресу, то есть начать исполнение указанной пользователем программы! А после того, как в этой программе будет использована инструкция ret (для возврата в вызывающий код), мы должны будем просто вернуться в цикл обработки пользовательского ввода. Таким образом мы создали операционную систему: она состоит из простейших механизмов разбора команд и загрузки программ, реализованных в рамках примерно 40 строк ассемблерного кода, хотя и с большой помощью со стороны подпрограмм из библиотеки lib.asm.

Для ассемблирования кода ядра операционной системы следует использовать следующую команду:

nasm -f bin -o mykernel.bin mykernel.asm

После этого нам придется каким-то образом добавить файл mykernel.bin в файл образа флоппи-диска. Если вы знакомы с приемом монтирования образов дисков с помощью loopback-устройств, вы можете получить доступ к содержимому образа диска floppy.img, воспользовавшись им, но существует и более простой способ, заключающийся в использовании инструментария GNU Mtools (www.gnu.org/software/mtools). Это набор программ для работы с флоппи-дисками, на которых используются файловые системы MS-DOS/FAT12, доступный из репозиториев пакетов программного обеспечения всех популярных дистрибутивов Linux, поэтому вам придется лишь воспользоваться утилитой apt-get, yum, pacman или любой другой утилитой, используемой для установки пакетов программного обеспечения в вашем дистрибутиве.

После установки соответствующего пакета программного обеспечения для добавления файла mykernel.bin в файл образа диска floppy.img вам придется выполнить следующую команду:

mcopy -i floppy.img mykernel.bin ::/

Обратите внимание на забавные символы в конце команды: двоеточие, двоеточие и слэш. Теперь мы почти готовы запуску нашей операционной системы, но какой в этом смысл, пока для нее не существует приложений? Давайте исправим это недоразумение, разработав крайне простое приложение. Да, сейчас вы будете разрабатывать приложение для своей собственной операционной системы — просто представьте, насколько поднимется ваш авторитет в рядах гиков. Сохраните следующий код в файле с именем test.asm:

org 32768 
mov ah, 0Eh 
mov al, 'X' 
int 10h 
ret

Данный код просто использует функцию BIOS для вывода символа ‘X’ на экран, после чего возвращает управление вызвавшему его коду — в нашем случае этим кодом является код операционной системы. Строка org, с которой начинается исходный код приложения, является не инструкцией центрального процессора, а директивой ассемблера NASM, сообщающей ему о том, что бинарный код будет загружен в оперативную память со смещением 32768, следовательно, необходимо пересчитать все смещения с учетом данного обстоятельства.

Данный код также нуждается в ассемблировании, а получившийся в итоге бинарный файл — в добавлении в файл образа флоппи-диска:

nasm -f bin -o test.bin test.asm 
mcopy -i floppy.img test.bin ::/

Теперь глубоко вздохните, приготовьтесь к созерцанию непревзойденных результатов собственной работы и загрузите образ флоппи-диска с помощью эмулятора ПК, такого, как Qemu или VirtualBox. Например, для этой цели может использоваться следующая команда:

qemu-system-i386 -fda floppy.img

Вуаля: системный загрузчик boot.img, который мы интегрировали в первый сектор образа диска, загружает ядро операционной системы mykernel.bin, которое выводит приветствие. Введите команду ls для получения имен двух файлов, расположенных на диске (mykernel.bin и test.bin), после чего введите имя последнего файла для его исполнения и вывода символа X на экран.

Это круто, не правда ли? Теперь вы можете начать дорабатывать командную оболочку вашей операционной системы, добавлять реализации новых команд, а также добавлять файлы дополнительных программ на диск. Если вы желаете запустить данную операционную систему на реальном ПК, вам стоит обратиться к разделу «Запуск системного загрузчика на реальной аппаратной платформе» из предыдущей статьи серии — вам понадобятся точно такие же команды. В следующем месяце мы сделаем нашу операционную систему более мощной, позволив загружаемым программам использовать системные функции и реализовав таким образом концепцию разделения кода, направленную на сокращение его дублирования. Большая часть работы все еще впереди.

Наша операционная система является упрощенной версией операционной системы MikeOS (http://mikeos.sf.net), к исходному коду которой вы можете обращаться в поисках вдохновения

Наша операционная система является упрощенной версией операционной системы MikeOS (http://mikeos.sf.net), к исходному коду которой вы можете обращаться в поисках вдохновения

Подпрограммы библиотеки lib.asm

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

  • lib_print_string — принимает указатель на завершающуюся нулевым символом строку посредством регистра SI и выводит эту строку на экран.
  • lib_input_string — принимает указатель на буфер посредством регистра SI и заполняет этот буфер символами, введенными пользователем с помощью клавиатуры. После того, как пользователь нажимает клавишу Enter, строка в буфере завершается нулевым символом и управление возвращается коду вызывающей программы.
  • lib_move_cursor — перемещает курсор на экране в позицию с координатами, передаваемыми посредством регистров DH (номер строки) и DL (номер столбца).
  • lib_get_cursor_pos — следует вызывать данную подпрограмму для получения номеров текущей строки и столбца посредством регистров DH и DL соответственно.
  • lib_string_uppercase — принимает указатель на начало завершающейся нулевым символом строки посредством регистра AX и переводит символы строки в верхний регистр.
  • lib_string_length — принимает указатель на начало завершающейся нулевым символом строки посредством регистра AX и возвращает ее длину посредством регистра AX.
  • lib_string_compare — принимает указатели на начала двух завершающихся нулевыми символами строк посредством регистров SI и DI и сравнивает эти строки. Устанавливает флаг переноса в том случае, если строки идентичны (для использования инструкции перехода в зависимости от флага переноса jc) или убирает этот флаг, если строки различаются (для использования инструкции jnc).
  • lib_get_file_list — принимает указатель на начало буфера посредством регистра SI и помещает в этот буфер завершающуюся нулевым символом строку, содержащую разделенный запятыми список имен файлов с диска.
  • lib_load_file — принимает указатель на начало строки, содержащей имя файла, посредством регистра AX и загружает содержимое файла по смещению, переданному посредством регистра CX. Возвращает количество скопированных в память байт (то есть, размер файла) посредством регистра BX или устанавливает флаг переноса, если файл с заданным именем не найден.

Попробуйте подключить код библиотеки lib.asm к коду ваших отдельных программ (таким же образом, как в файле test.asm) и протестируйте эти подпрограммы.

В библиотеке lib.asm полно полезных подпрограмм - внимательно присмотритесь к их реализациям

В библиотеке lib.asm полно полезных подпрограмм — внимательно присмотритесь к их реализациям


Предыдущие статьи из серии «Школа ассемблера»:

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

Если вам понравилась статья, поделитесь ею с друзьями:


os-project

Пишем свою собственную операционную систему с нуля!

Идея написать ОС возникла у меня в процессе поиска идеи для сайд-проекта. Это исключительно хобби-проект, не рассчитанный на серьезность и достоверность, и хотя я пытался объяснить многие новые и неочевидные концепты, с которыми я столкнулся в процессе разработки, я мог что-то упустить, так как я сам только учусь — именно поэтому я настоятельно рекомендую пользоваться гуглом и любыми другими источниками информации когда вы познакомитесь с чем-то новым в гайде. Гуглите абсолютно всё. Я серьезно.

**Prerequisites: **Для комфортного прохождения гайда нужно уметь программировать на языке Си на базовом уровне (одно из обязательных требований: понимать принципы работы с указателями), иметь опыт разработки на высокоуровневых ЯП. С синтаксисом ассемблера можно ознакомиться по ссылке ниже, но все же рекомендую побольше почитать или посмотреть по нему туториалов.

Навигация по репозиторию

guide/ — гайд с последовательными уроками, теорией и задокументированным кодом

  • Гайд разделен на главы, например 00-BOOT-SECTOR
  • Главы разделены на упражнения, например ex00
  • Упражнения содержат в себе код и теорию. Выглядят как main.asm

src/ — исходный код ОС

Установка и запуск

  1. Установить эмулятор QEMU (подробнее: https://www.qemu.org/download/)
sudo apt install qemu-kvm qemu
  1. Собрать кросс-компилятор gcc для i386 архитектуры процессора. Удобнее использовать готовый отсюда: https://wiki.osdev.org/GCC_Cross-Compiler#Prebuilt_Toolchains. Для компьютеров на Linux с x86_64 архитектурой:
wget http://newos.org/toolchains/i386-elf-4.9.1-Linux-x86_64.tar.xz
mkdir /usr/local/i386elfgcc
tar -xf i386-elf-4.9.1-Linux-x86_64.tar.xz -C /usr/local/i386elfgcc --strip-components=1
export PATH=$PATH:/usr/local/i386elfgcc/bin
  1. Клонировать и собрать проект
git clone https://github.com/thedenisnikulin/os-project
cd os-project/src/build
make
  1. Запустить образ ОС с помощью эмулятора
qemu-system-i386 -fda os-image.bin

Справочник по синтаксису ассемблера NASM

https://www.opennet.ru/docs/RUS/nasm/nasm_ru3.html


Дополнительная информация

Ссылки на полезный материал которым я пользовался в качестве теории.

На русском языке:

  • Серия статей о ядре Linux и его внутреннем устройстве: https://github.com/proninyaroslav/linux-insides-ru
  • Статья «Давай напишем ядро!»: https://xakep.ru/2018/06/18/lets-write-a-kernel/

На английском языке:

  • Небольшая книга по разработке собственной ОС (70 страниц): https://www.cs.bham.ac.uk/~exr/lectures/opsys/10_11/lectures/os-dev.pdf
  • Общее введене в разработку операционных систем: https://wiki.osdev.org/Getting_Started
  • Туториал по разработке ядра операционной системы для 32-bit x86 архитектуры. Первые шаги в создании собсвтенной ОС: https://wiki.osdev.org/Bare_Bones
  • Продолжение предыдущего туториала: https://wiki.osdev.org/Meaty_Skeleton
  • Про загрузку ОС (booting): https://wiki.osdev.org/Boot_Sequence
  • Список туториалов по написанию ядра и модулей к ОС: https://wiki.osdev.org/Tutorials
  • Внушительных размеров гайд по разработке ОС с нуля: http://www.brokenthorn.com/Resources/OSDevIndex.html
  • Книга, описывающая ОС xv6 (не особо вникал, но должно быть что-то годное): https://github.com/mit-pdos/xv6-riscv-book, сама ОС: https://github.com/mit-pdos/xv6-public
  • «Небольшая книга о разработке операционных систем» https://littleosbook.github.io/
  • Операционная система от 0 до 1 (книга): https://github.com/tuhdo/os01
  • ОС, написанная как пример для предыдущей книги: https://github.com/tuhdo/sample-os
  • Интересная статья про программирование модулей для Линукса и про системное программирование https://jvns.ca/blog/2014/09/18/you-can-be-a-kernel-hacker/
  • Еще одна статья от автора предыдущей https://jvns.ca/blog/2014/01/04/4-paths-to-being-a-kernel-hacker/
  • Пример простого модуля к ядру линукса: https://github.com/jvns/kernel-module-fun/blob/master/hello.c
  • Еще один туториал о том, как написать ОС с нуля: https://github.com/cfenollosa/os-tutorial
  • Статья «Давайте напишем ядро»: https://arjunsreedharan.org/post/82710718100/kernels-101-lets-write-a-kernel
  • Сабреддит по разработке ОС: https://www.reddit.com/r/osdev/
  • Большой список идей для проектов для разных ЯП, включая C/C++: https://github.com/tuvtran/project-based-learning/blob/master/README.md
  • Еще один список идей для проектов https://github.com/danistefanovic/build-your-own-x
  • «Давайте напишем ядро с поддержкой ввода с клавиатуры и экрана»: https://arjunsreedharan.org/post/99370248137/kernel-201-lets-write-a-kernel-with-keyboard-and

Понравилась статья? Поделить с друзьями:

Не пропустите также:

  • Как пишутся приказы в армии
  • Как пишутся команды в майнкрафте
  • Как пишутся команды в майнкрафт
  • Как пишутся клички собак
  • Как пишутся клички людей

  • 0 0 голоса
    Рейтинг статьи
    Подписаться
    Уведомить о
    guest

    0 комментариев
    Старые
    Новые Популярные
    Межтекстовые Отзывы
    Посмотреть все комментарии