KERNEL: добавлены потоки ядра

This commit is contained in:
msh356 2026-07-03 17:31:52 +03:00
parent e3dfa1f016
commit 507c247f8e
7 changed files with 178 additions and 2 deletions

View file

@ -25,6 +25,8 @@ OBJ = boot/boot.o \
kernel/vmm.o \ kernel/vmm.o \
kernel/vmm_asm.o \ kernel/vmm_asm.o \
kernel/heap.o \ kernel/heap.o \
kernel/thread.o \
kernel/switch.o \
drivers/serial.o \ drivers/serial.o \
drivers/vga.o \ drivers/vga.o \
drivers/speaker.o \ drivers/speaker.o \
@ -51,6 +53,11 @@ kernel/gdt_asm.o: kernel/gdt.asm
@echo "[+] Компилируем gdt.asm..." @echo "[+] Компилируем gdt.asm..."
$(AS) $(ASFLAGS) kernel/gdt.asm -o kernel/gdt_asm.o $(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 # Компиляция ассемблера IDT
kernel/interrupt.o: kernel/interrupt.asm kernel/interrupt.o: kernel/interrupt.asm
@echo "[+] Компилируем interrupt.asm..." @echo "[+] Компилируем interrupt.asm..."

View file

@ -22,7 +22,7 @@
- [x] Физ. менеджер памяти - [x] Физ. менеджер памяти
- [x] Вирт. менеджер памяти - [x] Вирт. менеджер памяти
- [x] Куча ядра - [x] Куча ядра
- [ ] Потоки ядра - [x] Потоки ядра
- [ ] **VFS** - [ ] **VFS**
- [ ] DebugFS - [ ] DebugFS
- [ ] Драйвер cpio ramdisk - [ ] Драйвер cpio ramdisk

View file

@ -1,5 +1,6 @@
#include "timer.h" #include "timer.h"
#include "../kernel/io.h" #include "../kernel/io.h"
#include "../kernel/thread.h"
#include "../kernel/logger.h" #include "../kernel/logger.h"
// Глобальный счетчик тиков системы // Глобальный счетчик тиков системы

View file

@ -10,12 +10,20 @@
#include "vmm.h" #include "vmm.h"
#include "pic.h" #include "pic.h"
#include "logger.h" #include "logger.h"
#include "thread.h"
#include "string.h" #include "string.h"
#include "multiboot.h" #include "multiboot.h"
extern uint32_t end; // Символ 'end' прописан в конце любого стандартного linker.ld extern uint32_t end; // Символ 'end' прописан в конце любого стандартного linker.ld
uint32_t kernel_end = (uint32_t)&end; 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) { void kernel_main(uint32_t magic, uint32_t mboot_addr) {
log_info(LOG_NORMAL, "Initializing serial..."); log_info(LOG_NORMAL, "Initializing serial...");
serial_init(); serial_init();
@ -158,6 +166,10 @@ void kernel_main(uint32_t magic, uint32_t mboot_addr) {
kfree(arr2); kfree(arr2);
kprint_serial("Kfree done. It don't died twice!\n"); 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..."); log_info(LOG_NORMAL, "Enabling IRQ...");
__asm__ __volatile__("sti"); __asm__ __volatile__("sti");
vga_set_color(VGA_COLOR_LIGHT_GREEN, VGA_COLOR_BLACK); 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_init();
vga_set_color(VGA_COLOR_LIGHT_GRAY, VGA_COLOR_BLACK); 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();
};
} }

33
kernel/switch.asm Normal file
View file

@ -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

87
kernel/thread.c Normal file
View file

@ -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);
}

33
kernel/thread.h Normal file
View file

@ -0,0 +1,33 @@
#ifndef THREAD_H
#define THREAD_H
#include <stdint.h>
#include <stddef.h>
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