После того, как вы прочитали базовые шаги по написанию Hello World ядра из цикла имеющихся на Хабре статей, самое время приступить к серьезной разработке самых базовых инструментов: аллокатора кучи и планировщика.
Важно:
1. Эта серия уроков для новичков. Цель — сформировать целостную картину мира. Очень долго у меня в голове была теория Таненбаума и я не мог ее связать с практикой. Тем у кого то же самое — посвящаю эту статью.
2. Код самый простой и тупой, но максимально понятный. Моя цель дать вам понимание чтобы вы смогли написать свое ядро, гораздо более крутое чем это.
3. Репо опубликую как только посчитаю его готовым для широкого круга. Я пишу небольшую часть, отлаживаю, и снимаю видеоурок. У меня нет готового продукта.
Честно говоря я долго думал стоит ли начинать писать статьи и делать видеоуроки на столь изьезженную тему. Но страсть к системному программированию и отсутствие структурированной информации на русском языке все же подтолкнули меня на этот эксперимент. Посему, если труд мой окажется востребованным, статьи планирую выпускать не реже чем раз в месяц и не чаще чем раз в неделю.
Свою ОС я мечтал написать еще в десятом классе. Книга Таненбаума обладает удивительным свойством. Всяк кто к ней прикоснется рано или поздно начинает мечтать написать свою операционную систему. Вот только я тогда ничего не написал, после того как понял что на мастдае невозможно нормально скомпилить и слинковать чистый бинарник с сишным кодом. Пришлось изучать Linux, поступать в универ, идти на работу. Все бы ничего. Да только мечта осуществилась лишь через десять лет. Сейчас, верстая скучные формочки на React, я оглядываюсь назад, вспоминаю, с какой страстью я просиживал часами за дизассемблером и отладчиком, снимал упаковщики, ломал крякмисы, на ночь вместо школьных романов зачитывался статьями мыщьха… и понимаю что моя жизнь могла бы сложиться иначе. Совсем иначе. Если бы только у меня был человек, который смог бы мне помочь в самом начале. Но время ушло. Программы ломать стало трудно. Ядро Linux разрослось до неимоверных размеров. Появились гиппервизоры. Ассемблер Intel стал настолько большим что взглянув на один список команд в мануале пропадает всякий энтузиазм что либо о нем изучать. Хлеб пришлось зарабатывать в вебе. Да. Мир системного программирования пережил свои золотые годы.
Посему всем школьникам, чей энтузиазм еще не иссяк, я посвящаю подробный туториал на тему как взять и с нуля написать простейшее многозадачное микроядро ядро со своей оболочкой. А всех, у кого он еще не появился, хочу предостеречь, что дело это неблагодарное и малополезное, хотя и жутко интересное. А у молодости, как вы знаете, свои заботы.
Поехали!
Данная статья содержит видеоурок снятый в моей домашней студии. Он посвещен непосредственно коду.
Приступим к разработке менеджера кучи.
extern void *kmalloc(size_t size);
extern void kfree(void *addr);
struct slist_head_t
{
struct slist_head_t *prev;
struct slist_head_t *next;
bool is_valid;
void *data;
} attribute(packed);
struct slist_definition_t
{
size_t base;
u_int slots;
size_t slot_size;
struct slist_head_t *head;
struct slist_head_t *tail;
};
struct kheap_entry_t
{
struct slist_head_t list_head; /* static (array placed) list */
size_t addr; /* physical address */
size_t size; /* memory block size */
bool is_buzy; /* whether block used */
} attribute(packed);
struct kheap_entry_t kheap_blocks[KHEAP_MAX_ENTRIES];
struct slist_definition_t kheap_list = {
.head = null,
.tail = null,
.slot_size = sizeof(struct kheap_entry_t),
.slots = KHEAP_MAX_ENTRIES,
.base = (size_t)kheap_blocks
};
struct task_t
{
struct clist_head_t list_head; /* cyclic list */
u_short tid; /* task id */
struct gp_registers_t gp_registers; /* general purpose registers */
struct op_registers_t op_registers; /* other purpose registers */
struct flags_t flags; /* processor flags */
u_int time; /* time of task execution */
bool reschedule; /* whether task need to be rescheduled */
u_short status; /* task status */
int msg_count_in; /* count of incomming messages */
struct message_t msg_buff[TASK_MSG_BUFF_SIZE]; /* task message buffer */
void *kstack; /* kernel stack top */
void *ustack; /* user stack top */
} attribute(packed);
struct clist_definition_t task_list = {
.head = null,
.slot_size = sizeof(struct task_t)
};
/*
* Handle IRQ0
* void asm_ih_timer(unsigned long *addr)
*/
asm_ih_timer:
cli
pushal
mov %esp,%ebp
mov %ebp,%ebx
pushl %ebx # ®
add $32,%ebx
pushl %ebx # &ret addr
call ih_timer
mov %ebp,%esp
popal
sti
iretl
/* save task state */
current_task->op_registers.eip = *ret_addr;
current_task->op_registers.cs = *(u16 *)((size_t)ret_addr + 4);
*(u32 *)(¤t_task->flags) = *(u32 *)((size_t)ret_addr + 6) | 0x200;
current_task->op_registers.u_esp = (size_t)ret_addr + 12;
current_task->gp_registers.esp = current_task->op_registers.u_esp;
memcpy(¤t_task->gp_registers, (void *)reg_addr, sizeof(struct gp_registers_t));
/* prepare context for the next task */
next_task->op_registers.u_esp -= 4;
*(u32 *)(next_task->op_registers.u_esp) = (*(u16 *)(&next_task->flags)) | 0x200;
next_task->op_registers.u_esp -= 4;
*(u32 *)(next_task->op_registers.u_esp) = next_task->op_registers.cs;
next_task->op_registers.u_esp -= 4;
*(u32 *)(next_task->op_registers.u_esp) = next_task->op_registers.eip;
next_task->gp_registers.esp = next_task->op_registers.u_esp;
next_task->op_registers.u_esp -= sizeof(struct gp_registers_t);
memcpy((void *)next_task->op_registers.u_esp, (void *)&next_task->gp_registers, sizeof(struct gp_registers_t));
/* update current task pointer */
current_task = next_task;
/* run next task */
asm_switch_context(next_task->op_registers.u_esp);
#
# Switch context
# void asm_switch_context(u32 esp)
#
asm_switch_context:
mov 4(%esp),%ebp # ebp = esp
mov %ebp,%esp
popal
sti
iretl
К сожалению, не доступен сервер mySQL