KERNEL: добавлены потоки ядра
This commit is contained in:
parent
e3dfa1f016
commit
507c247f8e
7 changed files with 178 additions and 2 deletions
7
Makefile
7
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..."
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
- [x] Физ. менеджер памяти
|
||||
- [x] Вирт. менеджер памяти
|
||||
- [x] Куча ядра
|
||||
- [ ] Потоки ядра
|
||||
- [x] Потоки ядра
|
||||
- [ ] **VFS**
|
||||
- [ ] DebugFS
|
||||
- [ ] Драйвер cpio ramdisk
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "timer.h"
|
||||
#include "../kernel/io.h"
|
||||
#include "../kernel/thread.h"
|
||||
#include "../kernel/logger.h"
|
||||
|
||||
// Глобальный счетчик тиков системы
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
33
kernel/switch.asm
Normal file
33
kernel/switch.asm
Normal 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
87
kernel/thread.c
Normal 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
33
kernel/thread.h
Normal 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
|
||||
Loading…
Reference in a new issue