Pull to refresh

Comments 34

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

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

Автор, по-моему, на выходе вы получили просто программу, печатающую "я SimpleOS" без использования страндартной библиотеки - и требующую для запуска и работы наличия ОС Ubuntu и эмулятора QEMU как минимум :D

Назвать это простой ОС с "доступом к "железу" - весьма странно. Любая другая программа user mode имеет ровно такой же "доступ", разве нет?

Для ОС логично было бы начинать со своего собственного системного загрузчика?

А называть методы и классы Kernel и HAL - кажется несколько преждевременным.

Все так. Но начинать с загрузчика не так интересно, нет той обратной связи, слишком много ассемблера для одной статьи в голову:)

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

Интересно будет почитать, если не забросите! Я несколько месяцев назад тоже начал свою ОС делать, но только на C, а не на C++. Столкнулся с довольно интересным моментом, что практически все статьи в интернете расчитывают на запуск через BIOS, а не UEFI, а в long mode нельзя просто так брать и писать в VGA память текст, то есть для простейшего вывода надо писать свой рендер битовых шрифтов. Я пока ограничился выводом через UART.

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

класс, успехов вам. а я наконец-то написал С со своим VM (парсер-лексер - ast - bytecode(highlevel asm emulator) все на С) - это невероятно

А codesite для c проходит?

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

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

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

вот кароче как выглядит байт-код(с циферкой) и исполнение(ниже без циферок но с //)

Скрытый текст
------------------
файл с кодом
int main(){
  int a=4*5+5;
  int b=0;
  while(b<10){
   print(b+a);
   b=b+1;
  }
  return 0;
}
-------------------
отладка визуализация с байткодом
---------------------------------
Function: main
TEST b 	// Отладочный вывод компилятора: 'b' имеет индекс 1
TEST b 	// Отладочный вывод компилятора: 'b' имеет индекс 1
TEST a 	// Отладочный вывод компилятора: 'a' имеет индекс 0
TEST b 	// Отладочный вывод компилятора: 'b' имеет индекс 1

--- Сгенерированный байт-код ---
1	// PUSH 4
1	// PUSH 5
4	// MUL
1	// PUSH 5
2	// ADD
18	// STORE 0
1	// PUSH 0
18	// STORE 1
7	// LOAD 1
1	// PUSH 10
11	// CMPLT
15	// JUMP_IF_FALSE 36 <-
7	// LOAD 1
7	// LOAD 0
2	// ADD
6	// PRINT
7	// LOAD 1
1	// PUSH 1
2	// ADD
18	// STORE 1
16	// JUMP 14
1	// PUSH 0
0	// HALT/RET          <-
0	// HALT/RET
--------------------------------
отладка с выводом - принципиально должен был быть вывод но пока я оставил так
//PUSH val 4
//PUSH val 5
//MUL
//PUSH val 5
//ADD
STORE 25 in index 0
//PUSH val 0
STORE 0 in index 1
LOAD 1
//PUSH val 10
CMP_LT: 0 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 25
LOAD 1
//PUSH val 1
//ADD
STORE 1 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 1 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 26
LOAD 1
//PUSH val 1
//ADD
STORE 2 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 2 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 27
LOAD 1
//PUSH val 1
//ADD
STORE 3 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 3 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 28
LOAD 1
//PUSH val 1
//ADD
STORE 4 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 4 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 29
LOAD 1
//PUSH val 1
//ADD
STORE 5 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 5 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 30
LOAD 1
//PUSH val 1
//ADD
STORE 6 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 6 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 31
LOAD 1
//PUSH val 1
//ADD
STORE 7 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 7 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 32
LOAD 1
//PUSH val 1
//ADD
STORE 8 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 8 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 33
LOAD 1
//PUSH val 1
//ADD
STORE 9 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 9 < 10 result 1
JUMP_IF_FALSE to 36 if 1 is false?
LOAD 1
LOAD 0
//ADD
PRINT 34
LOAD 1
//PUSH val 1
//ADD
STORE 10 in index 1
JUMP to 14
LOAD 1
//PUSH val 10
CMP_LT: 10 < 10 result 0
JUMP_IF_FALSE to 36 if 0 is false?
//PUSH val 0
//HALT 
---------------------------------------
конец
Байт-код сохранен в 

вот кароче, else пока нету ) вобщем это интересная штука советую)

вся эта суета пока интересна пока она на ansi C, с генерацией ассемблера сложнее, но я думаю об этом уже, ну и байткод перенос есть уже ) вообще классно работает, вдохновило это bytecode

Почему C++17 и что мы будем использовать

Таки почему 17, а не 23?

Старт на С++ 17. Но всегда можно будет повысить версию.

Сорри, я вас нечайно минуснул, пальцем промахнулся. Отвечал с телефона.

вроде обещали constexpr, а по всему коду магические значений разбросаны. всякие 0xB8000 и 80/25 размазаны по всему коду. Для читателей этой статьи-то понятно конечно что одно это указатель на VGA буффер и соотвественно размеры, но дял читателя кода это ни разу не очевидно.

noexcept кстати тоже ни разу не появился нигде кроме new/delete.

ASFLAGS = --32

я кстати правильно понимаю ,что 64-битная архитектура пока не поддерживается совсем

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

Пока 32 бит, но со временем добавлю и 64 бит. Просто сейчас я пока не знаю как сделать. Но обязательно добавлю.

эдак вы на UE5 что-то сделаете, обзовёте "ОС".

HAL - это не заполнение буфера экранной памяти ))))

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

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

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

Вот и вы и сами говорите один из способов вывода, вот в статье показано как абстрагировать один из способов вывода.

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

Сам Линус говорил, что его первая ос была 11 строк кода:))

Вот и вы и сами говорите один из способов вывода

и? это к ядру ОС как относится? вы можете и без наличия ОС выводить информацию куда угодно. INT 13H например (если это часть БИОС VGA).

например command.com это уже надстройка над ОС, а не HAL.

В защиту могу сказать, что это только первый этап. Я тоже начинал написание ядра с вывода в UART, просто потому что иногда так проще отлаживать, чем через отладчик.

Это первый этап. Самое начало. Ядро же выводит информацию, что значит как относится? Самым прямым образом.

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

Все этапы: прерывания, память и тд. включая линию A20 уже описаны "программистом из Латвии" в 2012 году https://habr.com/ru/articles/160427/

Чем же всё закончилось? Науке это неизвестно, но его сайт пока жив!
Внимательно следим.

Кстати, самый полезный комментарий звучит так:

Надо начинать всегда с эмулятора терминала, тогда точно взлетит.

эмуляция терминала подразумевает линкер ld-<worldOrHost?>.so как и ядро так и user-space имеют линкер, тоесть хост асм-С надо проделать тогда станет ясно, что такое создание ОС как я понимать начинаю

щас я возможно кого-то отрезвлю 1 момент пример из мира линукса буквально вчера разбирался и хотел писать из линукса реализацию С на ассемблере насм и вот к чему я пришел, когда стал нужен -pie

nasm -felf64 main.asm -Iinclude/ -o main.o && 
nasm -felf64 asm_lib.asm -Iinclude/ -o asm_lib.o && 
ld -dynamic-linker=/lib64/ld-linux-x86-64.so.2 -pie main.o asm_lib.o -o my_program

ld-linux-x86-64... как я понял имеет ключевую ситуацию в виде библиотеки скомпанованая в виде ld чтоли

для более глубокого понимания что такое С можно посмотреть мейкфайл наверно самого tinyC наверно, ведь gcc под ос это что-то там такое, которое реально несёт смысл на какой ОС запуск

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

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

alignas(HAL::Console) uint8_t _consoleBuffer[sizeof(HAL::Console)];

перемудрили на ровном месте

зачем, если с таким кодом смена типа, который реализует IConsole, все равно требует переписывание нескольких мест в том же классе?

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

  • если собираетесь использовать std, то брали бы сразу unique_ptr (с кастомным делитером)

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

Вы правы, думаю следует добавить функции для создания, которые уже возвращают инициализированный объект. В конструкторе nullptr, в методе Initialize уже проверяем и вызываем _console = Hal::CreateConsole();

Тогда в приват части простото бъявленпе указателя. Вы в частности и об этом говорили?

та вариантов может быть миллион, вопрос, какая задача решается

  • а нужно ли вообще тут абстрагирование от типа?

  • можно задать тип консоли через шаблон

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

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

тогда у тебя вариативность в compile time
значит в console.h описываешь интерфейс класса
дальше идут реализации console_x86.cpp, console_ARM.cpp и так далее
полиморфизм тут вообще не нужен

Нет, названия для хоста и x86 одинаковы, я подменяю только пути инклюдов и исходных файлов при сборке. И оно собирается без горы ifdef

Пишем ядро ОС на modern C++ без макросов

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

Архитектурные решения и почему они важны

Это же не архитектурные решения, а просто технические моменты. А вот про архитектуру ОС у вас почти ни слова, кроме абстрактной фразы про "будет писать монолитное ядро с HAL". Хотелось бы подробностей. Почему монолитное ядро, например?

мы своими руками разберёмся в основах, поймём, как устроены операционные системы изнутри

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

Я знаю С++ и люблю его использовать. И как раз его уровень абстракция/оптимизация позволяет его использовать в ядре. Это больше мой личный выбор.

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

Это первая статья, в следующих статьях буду раскрывать архитектуру, прерывания, память, физическую, страничную, виртуальную, напишем алокатор. В самом ядре перегрузим глобально new и delete. И буду использовать STL контейнеры.

Я только в самом начале, информации много, она не простая. Двигаюсь к цели небольшими шагами, потому, что мне так проще понимать и обучаться. Посмотрел информацию, применил, реализовал, протестировал, работает и для закрепления материала написал статью продолжение.

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

Sign up to leave a comment.

Articles