From 507c247f8e51346600e709ed8aaa976dee9f728a Mon Sep 17 00:00:00 2001 From: msh356 Date: Fri, 3 Jul 2026 17:31:52 +0300 Subject: [PATCH] =?UTF-8?q?KERNEL:=20=D0=B4=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D1=8B=20=D0=BF=D0=BE=D1=82=D0=BE=D0=BA=D0=B8=20?= =?UTF-8?q?=D1=8F=D0=B4=D1=80=D0=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Makefile | 7 ++++ README.md | 2 +- drivers/timer.c | 1 + kernel/kmain.c | 17 ++++++++- kernel/switch.asm | 33 ++++++++++++++++++ kernel/thread.c | 87 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/thread.h | 33 ++++++++++++++++++ 7 files changed, 178 insertions(+), 2 deletions(-) create mode 100644 kernel/switch.asm create mode 100644 kernel/thread.c create mode 100644 kernel/thread.h diff --git a/Makefile b/Makefile index 0451037..0b06963 100644 --- a/Makefile +++ b/Makefile @@ -25,6 +25,8 @@ OBJ = boot/boot.o \ kernel/vmm.o \ kernel/vmm_asm.o \ kernel/heap.o \ + kernel/thread.o \ + kernel/switch.o \ drivers/serial.o \ drivers/vga.o \ drivers/speaker.o \ @@ -51,6 +53,11 @@ kernel/gdt_asm.o: kernel/gdt.asm @echo "[+] Компилируем gdt.asm..." $(AS) $(ASFLAGS) kernel/gdt.asm -o kernel/gdt_asm.o +kernel/switch.o: kernel/switch.asm + @echo "[+] Компилируем switch.asm..." + $(AS) $(ASFLAGS) kernel/switch.asm -o kernel/switch.o + + # Компиляция ассемблера IDT kernel/interrupt.o: kernel/interrupt.asm @echo "[+] Компилируем interrupt.asm..." diff --git a/README.md b/README.md index 4c861a3..211ad8e 100644 --- a/README.md +++ b/README.md @@ -22,7 +22,7 @@ - [x] Физ. менеджер памяти - [x] Вирт. менеджер памяти - [x] Куча ядра -- [ ] Потоки ядра +- [x] Потоки ядра - [ ] **VFS** - [ ] DebugFS - [ ] Драйвер cpio ramdisk diff --git a/drivers/timer.c b/drivers/timer.c index 0b2680a..6efff9d 100644 --- a/drivers/timer.c +++ b/drivers/timer.c @@ -1,5 +1,6 @@ #include "timer.h" #include "../kernel/io.h" +#include "../kernel/thread.h" #include "../kernel/logger.h" // Глобальный счетчик тиков системы diff --git a/kernel/kmain.c b/kernel/kmain.c index b0b275d..2a1e384 100644 --- a/kernel/kmain.c +++ b/kernel/kmain.c @@ -10,12 +10,20 @@ #include "vmm.h" #include "pic.h" #include "logger.h" +#include "thread.h" #include "string.h" #include "multiboot.h" extern uint32_t end; // Символ 'end' прописан в конце любого стандартного linker.ld uint32_t kernel_end = (uint32_t)&end; +void test_thread() { + while(1) { + kprint_serial("YOOO! I am thread 2!\n"); + kthread_yield(); + } +} + void kernel_main(uint32_t magic, uint32_t mboot_addr) { log_info(LOG_NORMAL, "Initializing serial..."); serial_init(); @@ -158,6 +166,10 @@ void kernel_main(uint32_t magic, uint32_t mboot_addr) { kfree(arr2); kprint_serial("Kfree done. It don't died twice!\n"); + log_info(LOG_NORMAL, "Initializing threading..."); + kthread_init(); + log_info(LOG_NORMAL, "Creating test thread..."); + kthread_create(test_thread); log_info(LOG_NORMAL, "Enabling IRQ..."); __asm__ __volatile__("sti"); vga_set_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK); @@ -186,5 +198,8 @@ void kernel_main(uint32_t magic, uint32_t mboot_addr) { vga_init(); vga_set_color(VGA_COLOR_LIGHT_GRAY, VGA_COLOR_BLACK); - while (1); + while (1) { + kprint_serial("HELLO! I am main thread 1!\n"); + kthread_yield(); + }; } diff --git a/kernel/switch.asm b/kernel/switch.asm new file mode 100644 index 0000000..a745e51 --- /dev/null +++ b/kernel/switch.asm @@ -0,0 +1,33 @@ +global asm_switch_context + +asm_switch_context: + ; 1. Сохраняем регистры текущей задачи (то, что не сохраняет C) + push ebx + push ebp + push esi + push edi + + ; После 4-х пушей структура стека выглядит так: + ; [esp + 0] -> edi + ; [esp + 4] -> esi + ; [esp + 8] -> ebp + ; [esp + 12] -> ebx + ; [esp + 16] -> адрес возврата (куда возвращаться в kthread_yield) + ; [esp + 20] -> первый аргумент: uint32_t** old_esp + ; [esp + 24] -> второй аргумент: uint32_t* new_esp + + ; 2. Забираем аргументы из старого стека + mov eax, [esp + 20] ; eax = old_esp + mov edx, [esp + 24] ; edx = new_esp + + ; 3. Меняем стеки + mov [eax], esp ; Сохраняем текущий ESP потока + mov esp, edx ; ПЕРЕПРЫГИВАЕМ НА НОВЫЙ СТЕК + + ; 4. Восстанавливаем регистры новой задачи + pop edi + pop esi + pop ebp + pop ebx + + ret ; Прыгаем либо в kthread_yield другого потока, либо в entry_point diff --git a/kernel/thread.c b/kernel/thread.c new file mode 100644 index 0000000..7767eb9 --- /dev/null +++ b/kernel/thread.c @@ -0,0 +1,87 @@ +#include "thread.h" +#include "heap.h" +#include "../drivers/serial.h" // Подставь свой путь к ком-порту + +#define STACK_SIZE 1024 + +static thread_t* current_thread = NULL; +static thread_t* main_thread = NULL; +static uint32_t next_thread_id = 1; + +// Объявляем функцию, которая скомпилирована в switch.asm +extern void asm_switch_context(uint32_t** old_esp, uint32_t* new_esp); + +void kthread_init(void) { + kprint_serial("THREADS: Initializing Round-Robin Scheduler...\n"); + + main_thread = (thread_t*)kmalloc(sizeof(thread_t)); + main_thread->id = 0; + main_thread->state = THREAD_RUNNING; + main_thread->stack_limit = NULL; + main_thread->esp = NULL; + main_thread->next = main_thread; // Зацикливаем на себя + + current_thread = main_thread; + kprint_serial("THREADS: Main kernel thread (kmain) registered.\n"); +} + +thread_t* kthread_create(void (*entry_point)(void)) { + thread_t* new_thread = (thread_t*)kmalloc(sizeof(thread_t)); + void* stack_mem = kmalloc(STACK_SIZE); + + if (!new_thread || !stack_mem) { + kprint_serial("THREADS ERROR: NO MEMORY FOR NEW THREAD!\n"); + return NULL; // или вешай здесь cpu_halt() + } + + new_thread->id = next_thread_id++; + new_thread->stack_limit = stack_mem; + new_thread->state = THREAD_READY; + + // Стек растет вниз, встаем на самый конец выделенного куска + uint32_t* stack_top = (uint32_t*)((uint32_t)stack_mem + STACK_SIZE); + + // Когда поток вызовет `ret` внутри asm_switch_context, + // он должен извлечь из стека адрес точки входа. + stack_top--; + *stack_top = (uint32_t)entry_point; // Адрес возврата для ret + + // Теперь имитируем push четырех регистров, которые сохраняет asm_switch_context: + // edi, esi, ebp, ebx + stack_top--; *stack_top = 0; // edi + stack_top--; *stack_top = 0; // esi + stack_top--; *stack_top = 0; // ebp + stack_top--; *stack_top = 0; // ebx + + new_thread->esp = stack_top; + + new_thread->esp = stack_top; + + // Круглый список: вставляем новый поток сразу после текущего + new_thread->next = current_thread->next; + current_thread->next = new_thread; + + kprint_serial("THREADS: New thread created successfully.\n"); + return new_thread; +} + +void kthread_yield(void) { + thread_t* old_thread = current_thread; + thread_t* new_thread = current_thread->next; + + // Ищем живой поток по кругу + while (new_thread->state != THREAD_READY && new_thread->state != THREAD_RUNNING) { + new_thread = new_thread->next; + } + + if (new_thread == old_thread) return; + + current_thread = new_thread; + if (old_thread->state == THREAD_RUNNING) { + old_thread->state = THREAD_READY; + } + new_thread->state = THREAD_RUNNING; + + // Сама магия смены контекста + asm_switch_context(&(old_thread->esp), new_thread->esp); +} diff --git a/kernel/thread.h b/kernel/thread.h new file mode 100644 index 0000000..7691146 --- /dev/null +++ b/kernel/thread.h @@ -0,0 +1,33 @@ +#ifndef THREAD_H +#define THREAD_H + +#include +#include + +typedef enum { + THREAD_READY, + THREAD_RUNNING, + THREAD_ZOMBIE +} thread_state_t; + +// То, что пушится на стек при переключении +typedef struct { + uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; + uint32_t eip; + uint32_t cs; + uint32_t eflags; +} cpu_context_t; + +typedef struct thread { + uint32_t id; + uint32_t* esp; // Указатель на текущий топ стека + void* stack_limit; // Чтобы потом делать kfree + thread_state_t state; + struct thread* next; +} thread_t; + +void kthread_init(void); +thread_t* kthread_create(void (*entry_point)(void)); +void kthread_yield(void); + +#endif