Разное

Как написать свою операционную систему: Пишем свою ОС: Выпуск 1 / Хабр

Содержание

Пишем свою ОС: Выпуск 1 / Хабр

Данный цикл статей посвящён низкоуровневому программированию, то есть архитектуре компьютера, устройству операционных систем, программированию на языке ассемблера и смежным областям. Пока что написанием занимаются два хабраюзера — 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. По языку ассемблера:
  2. По языку Си:
    • Керниган Б., Ритчи Д. «Язык программирования C»
    • Шилдт Г. «Полный справочник по C»
  3. По устройству операционных систем:
    • Таненбаум Э. «Современные операционные системы»
    • Таненбаум Э. «Операционные системы: Разработка и реализация»
    • Олифер В., Олифер Н. «Сетевые операционные системы»
    • http://osdev.org
  4. По архитектуре ЭВМ:
    • Таненбаум Э. «Архитектура компьютера»
    • Гук М. «Аппаратные средства IBM PC. Энциклопедия»
    • Петцольд Ч. «Код. Тайный язык информатики»
  5. Справочная информация

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

Всем привет! Сегодня мы напишем загрузчик, который будет выводить «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!“.

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

Пишем операционную системы своими руками

Пишем ОС

Введение

Не давно мне
стало интересно на сколько тяжело
написать операционную систему, как она
вообще устроенна внутри, что должна
делать, ну или хотя бы какой минимальный
набор функциональности иметь. Так же
было интересно низкоуровневое
программирование, там где нет помощников
и готовых функций, только ты, железо и
BIOS,
без
него было бы совсем туго :).
И
я решил написать простенькую ОС а попутно
подучить уже совсем забытый мной
ассемблер, и архитектуру x86.

Первое
что я решил заделать это поискать статьи,
типа «Пишем простую ОС». Таковых оказалось
довольно много но лично я смог уловить
по ним только основной принцип написания
ОС, довольно поверхностно. Возможно
просто мой уровень IQ
маловат,
хотя признаться я его не когда не измерял,
боюсь результата :).
Ну
так вот открыв кучу вкладок в своём
браузере, а также различных исходников
ОС в notepad++
я
принялся за дело. Ниже я опишу всё что
я выяснил о создании собственной ОС.

Загрузка ОС с дискеты

Нашу
ОС мы разместим на дискете, по этому
давайте рассмотрим процесс загрузки
операционной системы с дискеты. Он
состоит из нескольких этапов, сначала
загружается BIOS
(англ.
basic input/output system — «базовая система
ввода-вывода»), затем
BIOS
определяет
различные устройства в том числе и
блочные устройства ввода-вывода,
К блочным относятся такие устройства,
которые хранят информацию в блоках
фиксированной длины, у каждого из которых
есть свой собственный адрес. Обычно
размеры блоков варьируются от 512 до 32
768 байт. Вся передача данных ведется
пакетами из одного или нескольких целых
(последовательных) блоков. Важным
свойством блочного устройства является
то, что оно способно читать или записывать
каждый блок независимо от всех других
блоков. Среди наиболее распространенных
блочных устройств жесткие диски, приводы
гибких дисков а так же, приводы
компакт-дисков и флэш-накопители USB. Нас
же интересуют приводы гибких дисков а
именно привод для дискет диаметром 3.5
дюйма
и объёмом 1.4МБ(1 474 560 байт)

Немного
об устройстве привода. Привод состоит
из электронной части и механической, в
механической части присутствуют два
двигателя, один из них шаговый, шаговым
его называют по тому что он вращается
не непрерывно, как обычные двигатели,
а маленькими точными шагами, шаг двигателя
3.5
дюймового дисковода
равен 1,8°,что
позволяет с его помощью довольно точно
позиционировать головку записи-чтения
над определённой областью магнитного
диска дискеты. Также к механической
части относятся головки чтения-записи
они то и считывают-записывают данные
на магнитный диск. Их в дисководе две,
одна над магнитным диском другая под
ним. Ниже показано размещение головок
над диском.

Д
иск
имеет магнитное покрытие и разбит на
дорожки и сектора, на одной стороне
диска содержится 80 дорожек, всего сторон
две, нумерация дорожек начинается с 0
по 79, в одной дорожке содержится 18
секторов, емкость одного сектора
составляет 512байт. Нумерация секторов
начинается с первого. Первый сектор
является загрузочным

После
определения всех устройств BIOS
начинает
загрузку с накопителя который выбран
как загрузочный. На этом накопителе
должен присутствовать загрузочный
сектор в котором находится программа
загрузки ядра ОС. После чего BIOS
прочтёт
первый сектор диска, который как правило
является загрузочным и если найдёт на
нём сигнатуру загрузочного сектора,
это два последних байта сектора, имеющие
вид в шестнадцатеричной системе AAh,55h,
то загрузит этот сектор в оперативную
память по адресу 07C00h, и передаст управление
загруженному коду. Далее этот код должен
загрузить ядро ОС в память и передать
ему управление.

Инструментарий

У
меня нет ни дискеты ни дисковода, да и
писать любую программу куда приятнее
если есть возможность запускать её по
сто раз за час, дабы убедится в работе
каждой написанной строчки кода
:).
По этому воспользуемся виртуальной
машиной, я выбрал Virtual
Box. Скачать
Virtual
Box можно
тут https://www.virtualbox.org/
. Моя версия 5.0.0 на Windows
7 64 бит,
сразу работать не захотела, порыскав
на форумах, оказалось проблема заключается
в том, что у многих для установки
расширенных тем оформления пропатчен
фал Windows\system32\uxtheme.dll.
Обычно
оригинальный файл находится в том же
каталоге и имеет имя uxtheme.dll.backup.
Просто
поменяйте между собой их названия.

Далее
нам понадобится компилятор FASM(
flat
assembler
) —
это свободно
распространяемый многопроходной
ассемблер,
написанный Томашем Грыштаром (польск.
Tomasz
Grysztar
).
Скачать его можно с официального сайта
http://flatassembler.net/.
Писать код мы будем в Notepad++
его
скачать можно тут https://notepad-plus-plus.org/.
Установите FASM
в
корневой каталог диска C,
затем установите Notepad++.

Также
нам понадобится программа способная
создать виртуальный образ дискеты, для
этих целей я написал на C#
простенькую
программку, которую назвал imgMaster.
Принцып
её действия довольно прост, после запуска
программы, нажимаем кнопку Add
File, и
выбираем первый файл, его размер должен
составлять 512байт не больше, не меньше,
не больше так как он может не поместится
в загрузочный сектор размер которого,
как мы уже знаем 512байт. А не меньше по
тому что 511 и 512 байты, это сигнатура
загрузочного сектора, без них BIOS
решит
что программа загрузчик отсутствует,
и не станет не чего загружать из сектора.
Далее при необходимости добавляем
остальные файлы, и нажимаем Create
IMG, сохраняем
образ. Программа запишет файлы один за
другим, в той же очерёдности в которой
они расположены в списке, затем оставшееся
место заполнит нулями, чтобы размер
образа был равен ёмкости дискеты 1.4МБ(1
474 560 байт).

Пишем
Hello
World

Можно
приступать к написанию первой тестовой
программы, которая без операционной
системы, будет способна вывести на экран
традиционную надпись «Hello
World!».

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

Адресное
пространство:

Размер

Назначение

Физ.Адрес

1KB

Векторы прерываний

00000h

256B

Область данных BIOS

00400h

638KB

Первая
свободная область

(Суда BIOS
помещает данные из
загрузочного сектора)

00500h

07С00h

64KB

Графический видео
буфер

A0000h

32KB

Вторая свободная
область

B0000h

32KB

Текстовый видео буфер

B8000h

64KB

ПЗУ —
Расширения BIOS

C0000h

128KB

Третья свободная
область

D0000h

64KB

ПЗУ — BIOS

F0000h

64KB

HMA

100000h

До 4GB

XMS

Четвёртая свободная
область

10FFF0h

Первые
640 Кбайт (до графического видео буфера)
называются стандартной (conventional) памятью.
Начинается стандартная память с
килобайта, который содержит векторы
прерываний, их 256 на каждый отводится
по 4 байта.

Затем идет
область данных BIOS. Где находятся данные
необходимые для корректной работы
функций BIOS. Но также можно модифицировать
эту область, тем самым мы влияем на ход
выполнения системных функций, по сути
дела меняя что либо в этой области мы
передаем параметры BIOS и его функциям,
которые становятся более гибкими. С
адреса 500h начинается свободная область
до 640КБ. За 640 килобайтами начинается
старшая память или верхняя (upper) память,
она располагается до 1 мегабайта (до
HMA), т.е. она составляет 384 Кбайт. Тут
располагаются ПЗУ (постоянно запоминающее
устройство ) : текстовый видео буфер
(его микросхема рассчитана на диапазон
B8000h…BFFFFh) и графический видео буфер
(A0000h…AFFFFh). Если требуется вывести текст
то его ASCII коды требуется прописать в
текстовый видео буфер и вы немедленно
увидите нужные символы. F0000h…FFFFFh, а вот
и сам BIOS. Так же есть еще одна ПЗУ – ПЗУ
расширений BIOS (C0000h…CFFFFh), её задача
обслуживание графических адаптеров и
дисков. За первым мегабайтом, с адреса
100000h, располагается память именуемая
как расширенная память, конец которой
до 4 гигабайт. Расширенная память состоит
из 2х подуровней: HMA и XMS. Высокая память
(High MemoryArea, HMA) доступна в реальном режиме,
а это еще плюс 64 Кбайт (точнее 64 Кбайт –
16 байт), но для этого надо разрешить
линию A20 (открыв вентиль GateA20).
Функционирование расширенной памяти
подчиняется спецификации расширенной
памяти (Expanded Memory Specification, XMS), поэтому
саму память назвали XMS- памятью, но она
доступна только в защищенном режиме.

Красным
отмечен адрес начала нашей программы,
теперь мы можем смело указать
компилятору с какого адреса начинать
размещать данные в оперативной памяти.
Для этого служит директива org.
Давайте
приступим к написанию программы.
Запустите Notepad++,
затем
создайте новый файл и сохраните его где
вам удобно, назвав boot.asm.
После чего Notepad++
начнёт
подсвечивать ассемблерный синтаксис.
Теперь набираем данный код, сообщая
компилятору адрес начала нашей программы.

org
07C00h ;BIOS
помещает
нашу программу по адресу 07C00h,
поэтому указываем что все ;данные
программы мы будем размещать начиная
с этого адреса

Далее
необходимо провести инициализацию
регистров данных и стека.

;Инициализация

;————-

cli ;Запретить
прерывания, чтобы
не чего не отвлекало 🙂

xor
ax,ax ;Обнуляем
регистр ax

mov
ds,ax ;Сегмент
данных. Наши
данные размещены от начала программы

mov
ss,ax ;Сегмент
стека.

mov
sp,07C00h ;Стек
начинается в начале программы и растёт
в ;противоположную сторону, в начало
памяти

sti ;Разрешить
прерывания

Давайте
узнаем какой размер стека у нас получится
при такой инициализации. Для этого
выполним следующие действия: 7C00h
– 0500h = 7700h. Где
7C00h
– адрес
начала нашей программы, 0500h
– адрес
начала свободного пространства.
Полученное шестнадцатеричное значение
7700h,
это размер стека в байта, переведём его
в десятичное получим 30464байт, разделим
на 1024 и получим 29КБ. Для наших целей
этого более чем достаточно.

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

macro
displayClean ;Очистка
дисплея

{

mov
ax,
02h

;очищаем экран — функция 02h прерывания
10h

int
10h

}

Макрос
displayClean
служит
для очистки экрана от всего лишнего.

Далее
идёт макрос setCursor
служащий для установки курсора в нужную
нам позицию,
имеющий
два аргумента, координату по оси X,
и
координату по оси Y.
От местоположения курсора зависит,
место куда будет выведен следующий
символ выводимой нами строки.

macro
setCursor x, y ;Установить
курсор

{

mov
dx,x

mov
dh,y

;установка
курсора : функция 02h прерывания 10h

mov
ah,02h

xor
bh,bh

int
10h

}

И
последний необходимый нам макрос Print
будет
выводить нашу строку, у него есть три
аргумента, string
– адрес
первого символа строки,
length —
количество символов строки, и
color —
цвет строки.

macro
print string, lenght, color ;Вывод
строки

{

mov
bp,string
;Адрес
строки

mov
cx,lenght ;Длина
строки

mov
bl,color ;в
регистре bl- атрибут цвета

mov
ah,13h ;функция
13h прерывания 10h

mov
al,01h ;строка
только с символами + смещение курсора

;xor
dx,dx ;dh-номер строки на экране, dl — номер
столбца на экране

int
10h

}

После
определения макросов можно разместить
нашу строку в памяти

msg0
db
«Hello
World!»
;Текст
сообщения

msg0_len
=
$ —
msg0 ;Длина
строки, $
текущий
адрес минус адрес начала строки msg0

Далее следует
этот код:

displayClean
;Очистим
экран с помощью подготовленного нами
макроса

setCursor
0,
0
;Утановим
курсор в верхний левый угол

println
msg0, msg0_len, 03h
;Выведим
приветствие

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

times
(512-2)-($-$$)
db
0 ;Цикл
выполняется (512-2)-($-$$) раз. Где

;512
— размер загрузочного сектора

;2
— два последних байта

;$
— текущий адрес в памяти

;$$
— Начальный адрес в памяти

И
записать два байта сигнатуры, указывающие
BIOS-у
что это кот загрузчика

;Конец
загрузочного сектора

;—————

db
0AAh,55h ;сигнатура,
символизирующая о завершении загрузочного
сектора

;последние
два байта — признак конца таблицы MBR —
код 0xAA55.

;──────────────────────────────END──────────────────────────

Давайте
теперь скомпилируем наш код. Для этого
нажмите
F5,
в
появившемся окне введите,
C:\fasm\FASM.EXE(FULL_CURRENT_PATH),
нажмите
сохранить,
и выберите комбинацию клавиш для быстрого
вызова. Теперь после нажатия выбранной
комбинации, компилятор FASM
скомпилирует
нашу программу, и рядом с исходным кодом
появится бинарный файл, этот файл и
будет нашим загрузчиком, который мы
должны будем записать на первый сектор
дискеты.

Теперь
давайте проверим работу нашей программы.
Запустите imgMaster
и
добавте в список скомпилированный файл,
он должен называется boot.bin,
затем
нажмите Create
IMG и
сохраните образ дискеты. Далее запустите
Virtual
Box и
нажмите Создать, затем заполните поля
как на рисунке ниже:

Д
алее
жмём создать и выставляем:

И

снова жмём создать

После
чего в списке появится наша новенькая
виртуальная машина. Выбираем её и жмём
Настроить, выбираем вкладку Носители,
мы видим что к контроллеру IDE
у
нас подключен один жёсткий диск My_OS.vdi
и
один CD-ROM,
поскольку
CD-ROM
мы
использовать не будем мы можем его
удалить, что бы не мешал. После того как
мы удалили CD-ROM,
жмём
зелёную кнопку Добавить новый контроллер
и выбираем Добавить Floppy
контроллер.
Затем на появившемся в списке контроллере
жмём добавить привод гибких дисков,
откроется окно с предложением выбрать
образ, вбираем сохранённый ранее образ
и жмём ОК. Теперь запускаем нашу
виртуальную машину. После запуска на
экране у нас должно отобразится
приветствие Hello
World!.

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

Написание собственной работоспособной ОС за полгода / Хабр

Предыстория

Здравствуйте! Всех категорически приветствую, сегодня хотел бы рассказать Вам о своём опыте написание работоспособной ОС под архитектуру x86.

Как-то весенней ночью у меня родилась гениальная идея — попробовать себя в написании собственной ОС, которая может позволить запускать программы, работать с устройствами, да и в общем выжимать всю мощь из Intel’овской архитектуры в своих нуждах: к примеру, для своей фабрики или чего-либо иного. Моей целью было и есть написание такой ОС, которая могла бы позволить максимальную производительность для каких-то конкретных задач, не тратя процессорное время на всяческие излишества. В основном я преследую лишь спортивный интерес, получение опыта для себя в системном программировании и написания драйверов для устройств, которые используются повсеместно. Что из этого вышло — решать вам, сразу говорю, что не надо писать комментарии про создание собственного дистрибутива линукса, и преследовал интерес написать всё «From scratch» — с нуля, дабы хорошо погрузиться в тему ОСдева. Сразу хочу выразить огромную благодарность Бенджамину Лунту и форуму OSDev, так же как их Вики. Бен помог мне разобраться с EHCI, что несомненно внесло огромный вклад в мою ОС — USB устройства, они везде! Так же передо мной стояла задача создать собственную архитектуру, удобную мне, не исключая использование стандартов ELF-файлов. Что же, давайте перейдем к сути.

UPD: всю инфу можно найти в группе тык, так же там есть пост с доками и образом(старым, сейчас дописываю доки для stable-версии)

Что сделано?

Сейчас моя ОС умеет работать с USB-флешками, мышками, клавиатурами, AHCI дисками, PCI IDE контроллером, APIC’ом и ACPI, реализована вытесняющая многозадачность, реализован запуск программ, поточная работа с файлами, SVGA драйвер, который работает на 0x118 режиме VBE, работают DNS, DHCP, TCP, UPD, IPv4, HTTP, полный драйвер FAT32, драйвер RTL8139(69) и Intel Gigabit Ethernet.

Оконная система вместе с моей реализацией SVGA позволяет выдать аж 120 FPS при полной перерисовке экрана. Давайте перейдем к тому, как это всё реализовано и вообще может работать.

Как это работает?

Для начала, я написал бутлоадер, который с FAT32 читает вторичный загрузчик с ядром. Второй загрузчик переходит в защищенный режим и прыгает в ядро, там я загружаю и настраиваю IDT, после чего инициализирую PCI-устройства, запускаю ядра и запускаю CMD.

Кто-то спросит, как же ты добился такого перфоманса с SVGA? Ответ прост: ассемблер, ассемблер и еще раз ассемблер. Не обошлось без SSE инструкций, которые очень ускоряют копирование памяти. К примеру, вот код для копирования видеобуфера в LFB(Linear Frame Buffer):

.byte 0x60#Save registers in stack
mov %2,%%ecx 	#Repeat count to ecx
mov %0,%%edi 	#Video memory start to edi
mov %1,%%esi 	#Video buffer start to esi
ww1sse2:
	movaps  (%%esi),%%xmm0 #Copy 16 bytes to xmm0 from buffer
	movaps 	%%xmm0,(%%edi) #Copy from xmm0 to video memory
	movaps  16(%%esi),%%xmm0	#16 again, but + 16 from current
	movaps 	%%xmm0,16(%%edi)	#16 again, but + 16 from current
	movaps  32(%%esi),%%xmm0	#16 again, but + 32 from current
	movaps 	%%xmm0,32(%%edi)	#16 again, but + 32 from current
	movaps  48(%%esi),%%xmm0	#16 again, but + 48 from current
	movaps 	%%xmm0,48(%%edi)	#16 again, but + 48 from current
	add 	$64,%%edi	#Add 64 bytes to edi
	add 	$64,%%esi	#Add 64 bytes to esi
	dec%%ecx#Decrement count
	#test 	%%ecx,%%ecx #Compare ecx with zero
	jnz 	ww1sse2 	#If not zero, repeat again
.byte 0x61	#Restore registers from stack

Оконная система построена на ООП и, думаю, не нуждается в комментариях, всё почти как в шинде.

Менеджер памяти простейший — «Watermark Allocator». Распределение ресурсов осуществляется за счет того, что в ядре нет функций, которые могли бы нагадить друг другу, всё запросы сделаны через очереди и т.п.

Пока что нет никаких потоков ввода-вывода, но в ближайшее время они будут реализованы.

Система логических дисков аналогична MS-DOSу: одна буква — один диск. Поддерживаются как MBR разделы, так и GPT разделы.

Разработка драйверов устройств

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

Если честно, то я сторонник того, что главное в программе — функционал, но в тоже время считаю, что за графику можно и функционалом немного пожертвовать: к примеру, VIM.

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

Отладка

Отладка ОС занимает огромную кучу времени: скомпилить сорсы, скопировать на виртуальный диск, сохранить образ диска, проверить на эмуляторе, после чего скопировать файлы на флешку и проверить уже на реальном железе. А главное — никаких отладчиков, выводи в текстовый режим, либо в окошечки — повисло — ставь return’ы.

Итоги

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

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

Этичного хакинга!

Пишем свою ОС: Выпуск 2 / Хабр

Здравствуйте. Это снова мы, iley и pehat, с долгожданной второй статьёй из цикла «Пишем свою ОС» (первая статья здесь). Извиняемся за большую паузу после первой статьи, нам понадобилось некоторое время, чтобы определить дальнейшее направление нашей работы. В этом выпуске мы кратко рассмотрим защищённый режим 32-битных процессоров Intel. Ещё раз подчеркнём, что мы не ставим себе целью дать исчерпывающие теоретические данные.

Давайте на минуту вспомним нашу программу из предыдущего выпуска. Она запускалась вместо операционной системы и выводила сообщение «Hello world». Программа была написана на 16-битном ассемблере и работала в так называемом реальном режиме. Как вы наверняка знаете, при адресации в реальном режиме физический адрес формируется с помощью сегмента и смещения и имеет размерность 20 бит. Несложная математика подсказывает нам, что таким образом можно обращаться всего к мегабайту оперативной памяти.

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

Чтобы решить эти две проблемы, Intel в своё время разработали новый, значительно более сложный способ адресации оперативной памяти. Точнее, Intel разработали даже несколько способов адресации, и все они известны под собирательным названием защищённый режим.

Итак, адресация в защищённом режиме может происходить в одном из трёх режимов (извините за тавтологию) — в сегментном, страничном и сегментно-страничном. В этом выпуске мы рассмотрим только сегментный режим.

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

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

Итак, как мы уже говорили, в дескрипторе хранится база (base address), размер (segment limit) и всякие дополнительные атрибуты сегмента. Давайте взглянем на схему дескриптора.

Обратите внимание, что под базу выделено 32 бита, а под лимит всего 20. Как же так, — спросите вы — разве нельзя создать сегмент, размер которого будет больше мегабайта? Можно. Для этого используется несложный трюк. Если бит G установлен в единицу, то лимит считается не в байтах, а в блоках по 4 Кбайт. Надо заметить, что лимит содержит размер сегмента минус один в единицах гранулярности, т.е. если лимит равен 0, то сегмент имеет размер 1 байт или 4 Кбайт. Кроме того, как вы видите, дескриптор включает ещё несколько полей. Их подробное описание можно найти, например, здесь.

Как упоминалось, дескрипторы хранятся в специальных таблицах. Таблиц может быть несколько, но в любом случае в памяти обязана присутствовать GDT — глобальная таблица дескрипторов. Она одна на всю операционную систему. В свою очередь, у каждой задачи, то есть процесса может быть ноль, одна или несколько LDT — локальных таблиц дескрипторов. Адрес и размер GDT хранится в регистре GDTR, текущей LDT — в регистре LDTR. Пока нам LDT не понадобится. Кроме того, бывают еще IDT — таблицы дескрипторов прерываний, но их рассмотрение отложим до следующего выпуска.

Для выбора дескриптора из таблицы используется структура данных, называемая селектором. Вот как он выглядит:

Селектор содержит в себе бит, указывающий, в какой таблице искать дескриптор, локальной или глобальной (L/G) и собственно номер дескриптора (descriptor number). Кроме того, у селектора есть поле RPL, но оно нас пока не интересует.

Итак, давайте перейдём к делу!

;16-битная адресация, пока мы находимся в реальном режиме
use16
org 0x7c00

start:

  jmp 0x0000:entry              ;теперь CS=0, IP=0x7c00

entry:

  mov ax, cs

  mov ds, ax

 
;очистить экран

  mov ax, 0x0003

  int 0x10

 
;открыть A20

  in  al, 0x92

  or  al, 2

  out 0x92, al

 
;Загрузить адрес и размер GDT в GDTR

  lgdt  [gdtr]
;Запретить прерывания

  cli
;Запретить немаскируемые прерывания

  in  al, 0x70

  or  al, 0x80

  out 0x70, al

 
;Переключиться в защищенный режим

  mov  eax, cr0

  or   al, 1

  mov  cr0, eax

 
;Загрузить в CS:EIP точку входа в защищенный режим

  O32 jmp 00001000b:pm_entry

 
;32-битная адресация
use32
;Точка входа в защищенный режим

pm_entry:
;Загрузить сегментные регистры (кроме SS)

  mov  ax, cs

  mov  ds, ax

  mov  es, ax

 

  mov  edi, 0xB8000             ;начало видеопамяти в видеорежиме 0x3

  mov  esi, msg                 ;выводимое сообщение

  cld
.loop                           ;цикл вывода сообщения

  lodsb                         ;считываем очередной символ строки

  test al, al                   ;если встретили 0

  jz   .exit                    ;прекращаем вывод

  stosb                         ;иначе выводим очередной символ

  mov  al, 7                    ;и его атрибут в видеопамять

  stosb

  jmp  .loop
.exit

 

  jmp  $                        ;зависаем

 

msg:

  db  'Hello World!', 0

 
;Глобальная таблица дескрипторов.
;Нулевой дескриптор использовать нельзя!

gdt:

  db  0x00, 0x00, 0x00, 0x00, 0x00,      0x00,      0x00, 0x00 

  db  0xFF, 0xFF, 0x00, 0x00, 0x00, 10011010b, 11001111b, 0x00

gdt_size  equ $ - gdt

 
;данные, загружаемые в регистр GDTR

gdtr:

  dw  gdt_size - 1

  dd  gdt

 

finish:

times 0x1FE-finish+start db 0
db   0x55, 0xAA ; сигнатура загрузочного сектора

Теперь немного поясним код.

В строках:

in al, 0x92<br/>
or al, 2<br/>
out 0x92, al

происходит разблокирование адресной линии A20. Что это значит? Вспомним, что в реальном режиме можно адресовать 1 МБ памяти в формате сегмент:смещение (20 бит на адрес). Однако, обратившись, например, по адресу FFFF:FFFF, можно “прыгнуть” немного выше этой планки, и полученный адрес будет иметь длину 21 бит. В процессорах до 80286 старший (двадцатый, если считать от нуля) отбрасывался, и поэтому для совместимости со старыми программами ввели блокировку адресной линии A20. В настоящее время все ОС работают в защищенном режиме, и поэтому для нужд адресации необходимо как можно раньше разблокировать этот бит. Такие дела.

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

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

O32 jmp 00001000b:pm_entry

Здесь используется префикс преобразования разрядности операнда O32, который позволяет делать дальний безусловный переход с 32-битным смещением. Ура, мы наконец-то можем воспользоваться прелестями защищенного режима!

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

Нам не хватило фантазии на что-нибудь, демонстрирующее реальные возможности защищенного режима, поэтому мы снова выводим “Hello, World!”, но уже пользуясь прямым доступом к видеопамяти. Чтобы сделать что-нибудь красивое, было бы удобно воспользоваться прерываниями. Тому, как использовать их в защищенном режиме, будет посвящена следующая статья нашего цикла. А вторая статья на этом заканчивается. Будем рады увидеть ваши отзывы и пожелания.

пишем ядро на языке C

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

Что такое 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] != '\0') {
		/* 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, нужно еще кое-что.

Существует стандарт для загрузки различных 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

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

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-подобная ОС и ее ядро, а также сможете без труда написать последнее.

Как создается операционная система / Хабр

По докладу Ивана Рузанова «Windows – как создается операционная система» с конференции «Платформа 2009».

Как уже писал antonms, стали доступны записи докладов с Платформы. Несколько из них я посмотрел и хочу поделиться наиболее понравившимся. Учитывая занятость рядового хабра-юзера и продолжительность исходной записи доклада (1 час 20 мин.), ниже привожу развернутое изложение материала.

  • История Windows
  • Windows сегодня
  • Разработка Windows
  • Изменения ядра Windows 7
  • Сопровождение Windows, обновления/исправления
История Windows

Мало кто знает, когда началась разработка Windows NT – больше, чем 20 лет назад, в ноябре 1988 года. Ходят слухи, что Microsoft купила/украла Windows NT. Конечно, это всё не так. Для работы над новой операционной системой, Microsoft пригласила Дэйва Катлера (Dave Cutler), который работал в корпорации DEC и был одним из разработчиков операционной системы DEC VAX VMS. В 1988 году группе разработки новой операционной системы было всего 12 человек, среди которых, наряду с Дэйвом Катлером, были также бывшие сотрудники DEC, принимавшие участие в разработке DEC VAX VMS. И весь код новой Windows NT был написан в Microsoft.
Аббревиатура «NT» маркетингом расшифровывается как «New Technologies», но в проектной документации, она означала совсем другое. Дело в том, что Windows NT разрабатывалась для нового, еще не выпущенного в 1988-м году, процессора Intel i860. Его кодовое название было «N10» (N Ten).
Первая версия — Windows NT 3.1, вышла через 5 лет, в 1993 году. На этот момент в команде было уже 250 разработчиков.

Windows сегодня
  • 1 миллиард пользователей
  • 140 миллионов строк кода (включая тестовый код и инструментарий)

    Код Windows очень разный. Какие-то части написаны 20 лет назад, какие-то появились только в текущей версии. Например, код Web Services on Devices (WSD) в Windows Vista существует в своей первой версии, код GDI находится на завершающей стадии своего развития и почти не изменяется, код DirectX уже хорошо разработан, но активно изменяется и в настоящее время.
  • 8000 разработчиков
  • 36 языков локализации
  • 20 лет разработки

Разработка Windows

20-30 лет назад использовалась только одна методология программирования «Водопад». Она представляет собой последовательность:
Спецификации → Дизайн → Реализация → Тестирование → Поставка.
Но такая методология работает только для небольших проектов. Для такого продукта, как Windows сегодня, нужны другие методологии: У всех этих методологий есть и преимущества и недостатки. В зависимости от размера команды и этапа развития компонента разные группы разработчиков Windows применяют разные методологии разработки.
Для Windows, как продукта в целом, используется Product Cycle Model:

  • Периоды по 3-4 месяца
  • Внутри периода – «водопад»

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

  • Требования изменятся
  • Возможности изменятся
  • График работ изменится
  • Проект изменится
  • Пользователи изменятся

Несмотря на то, что разные команды ведут разработку по-разному, существуют «универсальные» правила:

  • Выпуск промежуточных версий (milestones, beta, CTP) для широких масс тестеров
  • Выпуск внутренних сборок с короткими циклами (1 сутки)
  • Простота и надежность дизайна
  • Личные и командные вычитывания кода
  • Unit-тесты
  • Верификационные тесты (Build Verification Tests)
  • Любая промежуточная сборка должна быть качественной (то, что написано, должно работать)

От себя отмечу, что за месяц работы с Windows 7 build 6801 в качестве основной ОС на домашнем компьютере, у меня сформировалось положительное впечатление об этой сборки.

Весь процесс разработки Windows построен вокруг ежедневной сборки:

  • Это пульс продукта
  • Разработка никогда не прекращается
  • Ежедневное автоматическое тестирование
  • Интеграция на ранней стадии
  • Ответственность разработчиков
  • Очевидное состояние продукта

Когда-то раньше была только одна ветка исходного кода, и все разработчики вносили изменения прямо в неё. Сейчас команда разработчиков настолько большая, что это не работает. Поддерживается множество веток, среди которых есть основная – WinMain. У каждой лаборатории есть своя локальная ветка разработки, в которую интегрируются изменения. Проверенные изменения со временем интегрируются в WinMain.
Ежедневный цикл разработки:

  • 15:00 — Допущенные к интеграции изменения в систему контроля исходного кода
  • Сборка 6 версий (Free/Checked – x86, x64, IA64)
  • 18:00 — Новые версии доступны для тестирования
  • Новая версия устанавливается на несколько тысяч рабочих станций и серверов для тестирования
  • Автоматизированный стресс-тест
  • 05:00 — Протоколы тестов анализируются, сбои диагностируются
  • 09:00 — Сводные отчеты автоматически рассылаются командам
  • 09:30 — Сводное совещание руководителей команд для определения целей

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

На чем пишется Windows?

  • C, C++, C#, Ассемблер (x86, x64, IA64)

    Ассемблеры применяются в довольно ограниченном объеме в тех ситуациях, когда без этого не обойтись.
  • Visual Studio, Source Insight, build, nmake
  • Source Depot – система контроля исходных текстов
  • WinDbg, KD, NTSD – отладчики

Многие внутренние инструменты, такие как build, можно скачать с microsoft.com/whdc/devtools.

Изменения ядра Windows 7

Ядро Windows 7 претерпело следующие изменения:

  • Рефакторинг

    Почему в Windows нельзя удалить графическую подсистему?

    Ответ на этот вопрос с технической точки зрения состоит в том, что графическая подсистема в Windows не самостоятельна, это часть подсистемы Win32.

    В Windows 7 произошел рефакторинг многих низкоуровневых компонентов для того, чтобы разбить зависимости. Пользователям это не будет заметно, появятся только новые Dll, например kernel32.dll разделилась на kernel32.dll и kernelbase.dll.

    Это разбиение дало возможность выделить минимальное ядро, называемое MinWin (20 мегабайт на диске).
  • Поддержка EFI для x86 и x64 (как в Vista SP1)

    Многие производители пытаются избавиться от BIOS в пользу EFI.
  • Загрузка с VHD (виртуальный жесткий диск)
  • Параллельная инициализация устройств и старт сервисов

    При загрузке Windows довольно длительное время занимает построение дерева устройств. PNP-менеджер должен опрашивать драйверы шин (PCI, USB, FireWire и др.) на предмет того, какие устройства на них есть. И большую часть времени процессор ждет, пока устройства ответят (или нет). Ведь для того, чтобы определить устройства на шине нужно их опросить. Если они есть, то они ответят, а если нет, то приходится ждать, и процессор простаивает. Параллельное выполнение этих задач сокращает время загрузки.
  • Удаление Dispatcher lock из планировщика и PFN lock из менеджера памяти

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

    Два самых «горячих» лока, которые были в ядре, это Dispatcher lock и PFN lock были удалены.

    Dispatcher lock использовался планировщиком при изменении состояния потоков. Этот лок был удален, и состояние потока «ожидание» разделилось на несколько:

    • Ожидание: В процессе
    • Ожидание: Завершено
    • Ожидание: Отменено

    PFN lock использовался при изменении атрибутов физических страниц памяти. В мультипроцессорной системе каждый процессор запрашивал доступ к этому локу, что вело к большим затратам времени.

  • Поддержка 256 логических процессоров

    Раньше в Windows в качестве affinity mask использовалось машинное слово. Это было сделано из-за того, что так было легко находить свободные процессоры – каждый бит представляет собой процессор. Соответственно, в 32-битной системе поддерживалось 32 логических процессора, а в 64-битной – 64.

    В Windows 7 в результате перехода на сегментную модель affinity mask стала возможна поддержка 256 логических процессоров. Процессоры стали группироваться в группы/сегменты. В каждой группе могут находиться до 64-х процессоров. В результате получается обратная совместимость, старые программы «видят» только процессоры в одной группе, а новые программы, использующие новые интерфейсы, работают со всеми процессорами в системе.
  • Улучшенное энергосбережение: отключение процессорных сокетовСегодня стоит серьезная проблема энергосбережения не только перед владельцами ноутбуков, но и владельцами датацентров. В США 2% электроэнергии потребляются компьютерными датацентрами. Многие из них выключают часть своих серверов на время низкой активности пользователей (выходные дни).

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

Сопровождение Windows, обновления

Раньше обновления зачастую были кумулятивными(накапливаемыми). Это означало, что если ошибочный код содержался в раннем обновлении компонента, то и поздние версии будут содержать этот код. Но не всем пользователям нужны все обновления, у них разная конфигурация.
Теперь после выпуска (RTM) в Windows существует 2 версии исходного кода:

  • RTM GDR (General Distribution Release)

    Включает те немногие изменения, которые предназначены для всех. В основном исправления безопасности.
  • RTM LDR (Limited Distribution Release)

    Во время установки обновления клиент Windows Update выбирает нужную ему ветку и устанавливает код из нее.

Создание обновления безопасности

Работа по созданию обновления безопасности начинается с обнаружения уязвимости. Есть масса разных способов обнаружения – внутренние команды безопасности, партнеры безопасности, разработчики. Когда уязвимость обнаружена, начинается 2 параллельных процесса:

  • Разработка исправления для всех платформ
  • Поиск «вариантов»

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

После разработки исправления, начинаются проверки его кода. Когда они завершатся, исправление интегрируется в сборку, и сборка отправляется на тестирование:

  • Ручное и автоматическое тестирование компонент
  • Автоматическое тестирование искажений форматов файлов, сетевых компонент и т.п. (больше миллиона вариантов)
  • Тестирование системы в целом, включая тестирование обратной совместимости

Только исправления, удовлетворяющие всем критериям качества, допускаются к выпуску на Windows Update и Download Center.

Спасибо всем, кто дочитал до конца =)

c — Как создать небольшую операционную систему на старом настольном компьютере?

Переполнение стека

  1. Около
  2. Продукты

  3. Для команд
  1. Переполнение стека
    Общественные вопросы и ответы

  2. Переполнение стека для команд
    Где разработчики и технологи делятся частными знаниями с коллегами

  3. Вакансии
    Программирование и связанные с ним технические возможности карьерного роста

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

  5. Реклама
    Обратитесь к разработчикам и технологам со всего мира

  6. О компании

.Учебники

— OSDev Wiki

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

— Учебники либо очень простые, либо простые для понимания. Сначала попробуйте это.

— Учебники по более сложным предметам, но все же полезны. Возможно, сначала стоит заняться чем-нибудь попроще.

— Учебники по продвинутым предметам. Не рекомендуется новичкам.

— Учебники по очень сложным предметам. Мастер-класс. Удачи!

— Пока нет оценок.

Основы ядра

Голые кости

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

Bare Bones — Написать базовое 32-битное ядро ​​на C для x86

Пользователь: Zesterer / Bare Bones — Напишите базовое 32-битное ядро ​​на C для x86 (улучшенное руководство от zesterer)

Meaty Skeleton — Шаблон операционной системы

Higher Half x86 Bare Bones — Учебное пособие, показывающее, как написать ядро ​​более высокой половины

Настройка длительного режима — переход в длительный режим

Создание 64-битного ядра — Введение в 64-битные ядра

Сборка в реальном режиме, голые кости — серия руководств по написанию ядра базового языка ассемблера

Pascal Bare Bones — базовое ядро ​​в Pascal

Ada Bare Bones — Учебное пособие по написанию базового ядра в Ada

FreeBasic Bare Bones — базовое ядро ​​FreeBasic

У нас также есть голые кости для других платформ

GameBoy Advance Barebones — Учебник по написанию базового ядра GBA

Sparc Barebones — базовое ядро ​​для SparcStations

Babysteps

Как создать базовое ядро ​​в сборке
Babystep1 — Ваш первый загрузочный сектор.

Babystep2 — Написание сообщения с помощью BIOS.

Babystep3 — взгляд на машинный код

Babystep4 — Печать на экран без BIOS

Babystep5 — прерывания

Babystep6 — Вход в защищенный режим

Babystep7 — Нереальный режим

Babystep8 — 32-битная печать

Приложение A — Дополнительная информация

Другие ядра

В этих руководствах рассматриваются альтернативные конструкции ядра. В основном они предназначены для разработчиков, использующих язык, нигде не описанный, или подходов к дизайну, которые значительно отличаются от тех, которые приведены в руководствах Babysteps и Bare Bones.

Учебник по Java по работе с языками в целом и Java в частности, которые обычно не подходят для разработки ОС.

Учебники от третьих лиц

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

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

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

Os development — для всех разработчиков — Новая страница с обучающими материалами для начинающих.Страница на словацком языке, но вы можете использовать переводчик. На странице также есть ядро ​​LightningOS, которое вы можете использовать для создания собственной операционной системы.

Руководства Джеймса А. Моллоя по ядру — одно из самых популярных руководств в прошлом, оно имеет ряд известных проблем и, похоже, не обновляется активно.

Серия BrokenThorn по разработке операционной системы — Как и серия про Джеймса Моллоя, это очень известная серия руководств, но она очень устарела и имеет большое количество известных недостатков, которые не были исправлены.

Учебное пособие по ядру Брана — очень устаревшее, но все еще часто упоминаемое руководство с ныне умирающего сайта «Bona Fide OS Development». Подобно JAM и BrokenThorn, в примерах кода есть много известных проблем, и большая часть материала давно устарела.

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

Серия видеороликов «Начало работы с FlingOS» — серия видеоуроков от сторонних разработчиков, дающих практическое начало написанию вашей первой ОС (нацелена на x86, полные примеры доступны в ASM, C и C #)

The Little OS Book — сторонний демонстратор ОС, размещенный на GitHub. Проходит периоды обновления, и известные ошибки, которые еще не исправлены, перечислены в репо.

Создание простой операционной системы — с нуля (PDF) — учебное пособие 2010 г., основанное на материалах курса по операционным системам в Университете Бирмингема, Великобритания, написанное доктором Дж.Николас Бланделл, первоначальный инструктор курса. Учебное пособие было написано как дополнительный материал, который студенты могут просмотреть перед курсом, и, по словам Бланделла, «» не предназначен для замены, а скорее как ступенька к отличной работе, такой как проект Minix «».

Итак, вы хотите написать операционную систему и подняться на гору ядра — уже устаревшая серия статей с веб-сайта OS News, начатая в 2002 году, это были введения многих старых разработчиков в разработку ОС.Они хорошо написаны, но содержат лишь поверхностное освещение деталей и сегодня представляют в первую очередь только исторический интерес. Это включено исключительно потому, что на них есть ссылки во многих старых сообщениях на форуме. Обратите внимание, что позже автор написал продолжение, в котором он вообще возражал против разработки нового ядра.

Xv6 в отличие от большинства руководств в этом списке, это полнофункциональная, но простая ОС. Xv6 — это модернизированная версия классической UNIX V6 Денниса Ричи и Кена Томпсона, написанная на ANSI C для защищенного режима x86 и сохраняющая оригинальную философию простоты UNIX.

Основы

GDT Tutorial — Руководство по GDT

Учебник по прерываниям — Как установить прерывания из C

Создание оболочки — Учебное пособие по написанию оболочки

Идем дальше по x86 — руководство, которое должно охватывать основы внутреннего устройства ядра

DEMO — Учебное пособие в коде и в прозе по некоторым основам ‘386 (и выше) и платформы ПК в качестве языка ассемблера DEMO

Управление памятью

Настройка разбиения по страницам — Учебное пособие, посвященное настройке и обслуживанию системы с включенным разбиением по страницам.

Настройка пейджинга с PAE — как указано выше, но с включенным PAE

Руководство по управлению памятью Брендана — Руководство по управлению памятью, объясняющее основные концепции

Написание диспетчера памяти — Учебное пособие по работе с оперативной памятью в компьютере.

Writing A Page Frame Allocator — Как написать простой распределитель страничных кадров

Процессы и потоки

Учебник Брендана по многозадачности — Учебник по многозадачности в пространстве ядра.

Кооперативная многозадачность — Как создать систему многозадачности в пространстве ядра.

Переход в пользовательский режим — Как переключить контекст в пользовательский режим.

Графика и видео

Двойная буферизация — удобный способ предотвратить артефакты.

Загрузка

Загрузочный диск — учебное пособие, объясняющее, как создать образ загрузочного диска (USB-накопителя).

Загрузочный компакт-диск — учебное пособие, объясняющее, как создать загрузочный компакт-диск.

Загрузочный компакт-диск El-Torito с GRUB Legacy — руководство, объясняющее, как создать загрузочный компакт-диск GRUB

Прокрутка собственного загрузчика — описывает шаги, которые необходимо предпринять при написании загрузчика.

Написание загрузчика — Базовое руководство по созданию загрузчика

Написание загрузчика для UEFI — Базовое руководство по созданию загрузчика с использованием UEFI

Написание модулей GRUB — Учебное пособие по написанию модулей, которые добавляют в GRUB пользовательские функции.

Корпус

Makefile — управляемая демонстрация того, как можно использовать Makefile.

OS Specific Toolchain — Руководство по адаптации GCC и Binutils к платформе

CMake Build System — руководство, демонстрирующее адаптацию системы сборки CMake от KitWare для создания операционной системы.

VSCode for Debugging — Настройка VSCode для отладки ядра.

Компиляторы

Кросс-компилятор GCC — руководство, которое помогает создать GCC, ориентированный на другую платформу.

Кросс-компилятор GDC — То же, что и предыдущий, но на этот раз для языка программирования D.

Форматы исполняемых файлов

ELF Tutorial — Руководство, в котором подробно описывается процесс загрузки исполняемых файлов ELF.

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

Программное обеспечение для переноса

Портирование Newlib — Руководство по переносу общей библиотеки C в другую операционную систему

Использование Libsupc ++ — Руководство по портированию libsupc ++, чтобы получить больше возможностей от возможностей C ++

Перенос Python — Руководство по переносу Python на другую операционную систему

.

Добавить комментарий

Ваш адрес email не будет опубликован.