В связи с отрицательными отзывами пробной статьи «Разработка микроядерной Unix подобной OC — планировщик» я решил перезапустить серию статей с учетом некоторых замечаний. Теперь, осознав свою целевую аудиторию, я смог сместить фокус с подобных себе на тех кому это действительно нужно.
Замечание 1. Слишком простой планировщик.
За время с момента издания предыдущей статьи код сильно изменился. Образовалось полноценное ядро, которое перестало быть микроядерным. Появилась поддержка initial ram disk (файловой системы ядра), поддержка виртуальной памяти (MMU). Для ядра стало возможным писать пользовательские программы. Появились системные вызовы и библиотека clib (stdio, string). Так, оболочка /initrd/sh.elf является отдельной программой, которая парсится загрузчиком elf и запускается в виде процесса.
Список команд оболочки: exec <file.elf>, ps, kill , exit. При этом оболочку можно запускать из самой оболочки (демонстрируя многозадачность).
Замечание 2. Слишком много магии за кадром в видеоуроках.
Я решил подсвечивать текст, который в данный момент обьясняю. Также взял курс на новичков, которые нуждаются в разьяснении и самых простых вещей. Честно говоря я удивился что новички не имеющие опыта в программировании интересуются такой сложной темой, хотя чего греха таить, сам с того и начинал. Сместил уклон с обьяснения моей ОС на обьяснение как написать Вашу OC.
Замечание 3. Где ссылка на гитхаб?
Теперь она есть. В описании видео на ютубе (да, хочу чтобы вы не прошли мимо моих видеоуроков, хотябы одним глазком гляньте).
Замечание 4. Надо бы со сборки сначала начинать.
Именно так мы и поступим.
Замечание 5. Ты ничего не пишешь а просто комментируешь.
Да, это было ошибкой, рассказывать сразу о чем-то большом. Теперь мы будем двигаться шаг за шагом, постепенно, начиная с самого простого Hello world ядра. Видеоуроки позволят сформировать целостную картину мира, а исходный код на гитхабе погрузит вас в детали.
1. Система сборки (make, gcc, gas). Первоначальная загрузка (multiboot). Запуск (qemu). Библиотека C (strcpy, memcpy, strext).
2. Библиотека C (sprintf, strcpy, strcmp, strtok, va_list ...). Сборка библиотеки в режиме ядра и в режиме пользовательского приложения.
3. Системный журнал ядра. Видеопамять. Вывод на терминал (kprintf, kpanic, kassert).
4. Динамическая память, куча (kmalloc, kfree).
6. Организация памяти и обработка прерываний (GDT, IDT, PIC, syscall). Исключения.
5. Виртуальная память (каталог страниц и таблица страниц).
6. Процесс. Планировщик. Многозадачность. Системные вызовы (kill, exit, ps).
7. Файловая система ядра (initrd), elf и его внутренности. Системные вызовы (exec).
8. Драйверы символьных устройств. Системные вызовы (ioctl, fopen, fread, fwrite). Библиотека C (fopen, fclose, fprintf, fscanf).
9. Оболочка как полноценная программа для ядра.
10. Пользовательский режим защиты (ring3). Сегмент состояния задачи (tss).
В статье я буду перечислять лишь ключевые шаги. За подробным обьяснением отсылаю к видеоуроку для этой статьи.
Тебе потребуется Linux. Собирать будем старым добрым make. Для того чтобы собрать исполняемый файл ядра, нужны следующие флаги:
В качестве эмулятора железа поставь себе на Linux эмулятор qemu. Запускать наше ядро будем так:
Потребуется и небольшой скрипт для линкера. Он нужен для того чтобы загрузить секции по правильному адресу и в правильном порядке. Также в нем мы укажем точку входа:
Поскольку ядро будет загружено по спецификации multiboot в начале секции кода потребуется заголовок:
Рекомендуется сразу переходить на собственный стек и настаривать глобальную таблицу дескрипторов. Определим наш собственный стек:
Напишем точку входа. Тебе может показаться непривычным синтаксис ассемблера gnu. Я когда-то тоже предпочитал синтаксис Intel, то распробовав вкус, копаясь в исходниках Linux, всецело предпочел для себя синтаксис AT & T. Главное помни что операнды у них располагаются наоборот. Остальное будет интуитивно понятно.
На этом шаблонный код закончен. Начинается самое интересное. Теперь мы можем писать код на С. И первым делом определим сообщение приветствия.
Далее напишем саму точку входа на которую будет передавать управление ассемблерный код:
Тут мы просто выводим сообщение на экран. В принципе ты можешь полностью скопировать код первого урока, поскольку он шаблонный и изменяться никогда не будет. Для того чтобы вывести что-то на экран тебе достаточно записать это напрямую в видеопамять, дополняя каждый символ символом аттрибута. Для этого тебе понадобится своя библиотека языка C, которую мы будем писать сами под наши нужды. Так будет проще контролировать процесс. Так например сегодня у нас в распоряжении 2 всем знакомые функции (strcpy, memcpy) и одна наша собственная strext для того чтобы вставить байт аттрибута после каждого символа.
На сегодня все. Смотри видеоурок и попытайся самостоятельно проделать тоже самое. Если не получится можешь подглядывать в исходники к уроку на гитхабе. Ссылка на гитхаб в описании видеоурока:
1. James Molloy. Roll your own toy UNIX-clone OS.
2. Зубков. Ассемблер для DOS, Windows, Unix
3. Калашников. Ассемблер — это просто!
4. Таненбаум. Операционные системы. Реализация и разработка.
5. Роберт Лав. Ядро Linux. Описание процесса разработки.
Ответы на замечания к предыдущей статье.
Замечание 1. Слишком простой планировщик.
За время с момента издания предыдущей статьи код сильно изменился. Образовалось полноценное ядро, которое перестало быть микроядерным. Появилась поддержка initial ram disk (файловой системы ядра), поддержка виртуальной памяти (MMU). Для ядра стало возможным писать пользовательские программы. Появились системные вызовы и библиотека clib (stdio, string). Так, оболочка /initrd/sh.elf является отдельной программой, которая парсится загрузчиком elf и запускается в виде процесса.
Список команд оболочки: exec <file.elf>, ps, kill , exit. При этом оболочку можно запускать из самой оболочки (демонстрируя многозадачность).
Замечание 2. Слишком много магии за кадром в видеоуроках.
Я решил подсвечивать текст, который в данный момент обьясняю. Также взял курс на новичков, которые нуждаются в разьяснении и самых простых вещей. Честно говоря я удивился что новички не имеющие опыта в программировании интересуются такой сложной темой, хотя чего греха таить, сам с того и начинал. Сместил уклон с обьяснения моей ОС на обьяснение как написать Вашу OC.
Замечание 3. Где ссылка на гитхаб?
Теперь она есть. В описании видео на ютубе (да, хочу чтобы вы не прошли мимо моих видеоуроков, хотябы одним глазком гляньте).
Замечание 4. Надо бы со сборки сначала начинать.
Именно так мы и поступим.
Замечание 5. Ты ничего не пишешь а просто комментируешь.
Да, это было ошибкой, рассказывать сразу о чем-то большом. Теперь мы будем двигаться шаг за шагом, постепенно, начиная с самого простого Hello world ядра. Видеоуроки позволят сформировать целостную картину мира, а исходный код на гитхабе погрузит вас в детали.
Оглавление.
1. Система сборки (make, gcc, gas). Первоначальная загрузка (multiboot). Запуск (qemu). Библиотека C (strcpy, memcpy, strext).
2. Библиотека C (sprintf, strcpy, strcmp, strtok, va_list ...). Сборка библиотеки в режиме ядра и в режиме пользовательского приложения.
3. Системный журнал ядра. Видеопамять. Вывод на терминал (kprintf, kpanic, kassert).
4. Динамическая память, куча (kmalloc, kfree).
6. Организация памяти и обработка прерываний (GDT, IDT, PIC, syscall). Исключения.
5. Виртуальная память (каталог страниц и таблица страниц).
6. Процесс. Планировщик. Многозадачность. Системные вызовы (kill, exit, ps).
7. Файловая система ядра (initrd), elf и его внутренности. Системные вызовы (exec).
8. Драйверы символьных устройств. Системные вызовы (ioctl, fopen, fread, fwrite). Библиотека C (fopen, fclose, fprintf, fscanf).
9. Оболочка как полноценная программа для ядра.
10. Пользовательский режим защиты (ring3). Сегмент состояния задачи (tss).
Поехали. Часть 1. Система сборки и запуск
В статье я буду перечислять лишь ключевые шаги. За подробным обьяснением отсылаю к видеоуроку для этой статьи.
Тебе потребуется Linux. Собирать будем старым добрым make. Для того чтобы собрать исполняемый файл ядра, нужны следующие флаги:
CC_FLAGS=-g -m32 -isystem $(IDIR) -I include -DKERNEL=1 -fno-stack-protector -Wall -Werror -fno-pie
AS_FLAGS=-g --32
LD_FLAGS=-m elf_i386
В качестве эмулятора железа поставь себе на Linux эмулятор qemu. Запускать наше ядро будем так:
qemu-system-i386 -kernel ./bin/kernel.elf
Потребуется и небольшой скрипт для линкера. Он нужен для того чтобы загрузить секции по правильному адресу и в правильном порядке. Также в нем мы укажем точку входа:
OUTPUT_FORMAT(elf32-i386)
ENTRY(start)
SECTIONS
{
. = 0x100000;
.text : { *(.text) }
.data : { *(.data) }
.bss : { *(.bss) }
}
Поскольку ядро будет загружено по спецификации multiboot в начале секции кода потребуется заголовок:
.code32
.text
# multiboot spec
.align 4
multiboot:
.long 0x1BADB002 # magic
.long 0x00 # flags
.long -(0x1BADB002 + 0x00) # checksum. m+f+c should be zero
Рекомендуется сразу переходить на собственный стек и настаривать глобальную таблицу дескрипторов. Определим наш собственный стек:
.bss
.fill 8192,1 # 8Kb
stack:
Напишем точку входа. Тебе может показаться непривычным синтаксис ассемблера gnu. Я когда-то тоже предпочитал синтаксис Intel, то распробовав вкус, копаясь в исходниках Linux, всецело предпочел для себя синтаксис AT & T. Главное помни что операнды у них располагаются наоборот. Остальное будет интуитивно понятно.
start:
cli
movl $stack,%esp
push %esp
push %ebx /* address of struct multiboot_t */
call kernel_start /* should never return */
hlt
На этом шаблонный код закончен. Начинается самое интересное. Теперь мы можем писать код на С. И первым делом определим сообщение приветствия.
char *hello = "Hello world!";
int screen_size = 80 * 25;
Далее напишем саму точку входа на которую будет передавать управление ассемблерный код:
/*
* Api - Kernel entry point
*/
extern void kernel_start(struct multiboot_t* multiboot, void* kstack)
{
char *video = (char*)0xB8000;
char buff[screen_size + 1];
video[screen_size] = '\0';
memset(buff, ' ', screen_size);
strext(video, buff, 0x7);
strext(video, hello, 0x7);
}
Тут мы просто выводим сообщение на экран. В принципе ты можешь полностью скопировать код первого урока, поскольку он шаблонный и изменяться никогда не будет. Для того чтобы вывести что-то на экран тебе достаточно записать это напрямую в видеопамять, дополняя каждый символ символом аттрибута. Для этого тебе понадобится своя библиотека языка C, которую мы будем писать сами под наши нужды. Так будет проще контролировать процесс. Так например сегодня у нас в распоряжении 2 всем знакомые функции (strcpy, memcpy) и одна наша собственная strext для того чтобы вставить байт аттрибута после каждого символа.
Заключение
На сегодня все. Смотри видеоурок и попытайся самостоятельно проделать тоже самое. Если не получится можешь подглядывать в исходники к уроку на гитхабе. Ссылка на гитхаб в описании видеоурока:
Литература
1. James Molloy. Roll your own toy UNIX-clone OS.
2. Зубков. Ассемблер для DOS, Windows, Unix
3. Калашников. Ассемблер — это просто!
4. Таненбаум. Операционные системы. Реализация и разработка.
5. Роберт Лав. Ядро Linux. Описание процесса разработки.
Комментарии (5)
anikavoi
06.09.2019 02:51Парень на видео внешне похож на молодого Линуса… чета мне не по себе, камрады :)
shiotiny
06.09.2019 03:42Автор молодец!
Наверное каждый второй писал "свою ос".
На самое интересное, что у всех и всегда получался unix:)
Не останавливайся, автор. Это главное.
odysset
09.09.2019 11:09Если бы каждый читал не туторы, как написать ос, а спецификации железа и своей головой придумывал, как это железо заставить работать, был бы иной результат
vdem
Пишите ещё! Я тоже (как и любой нормальный девелопер) пытался свою ОС замутить еще в детстве, но руки до этого дошли только год назад, а месяц назад случилась беда — винт сдох, а на гитхаб я свои потуги еще не залил. Придется начинать заново. Только я чисто ассемблер использовал. Отдельное спасибо за список литературы. И, вот это Вам знакомо? — https://wiki.osdev.org/Main_Page