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.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..."
|
||||||
|
|
|
||||||
|
|
@ -22,7 +22,7 @@
|
||||||
- [x] Физ. менеджер памяти
|
- [x] Физ. менеджер памяти
|
||||||
- [x] Вирт. менеджер памяти
|
- [x] Вирт. менеджер памяти
|
||||||
- [x] Куча ядра
|
- [x] Куча ядра
|
||||||
- [ ] Потоки ядра
|
- [x] Потоки ядра
|
||||||
- [ ] **VFS**
|
- [ ] **VFS**
|
||||||
- [ ] DebugFS
|
- [ ] DebugFS
|
||||||
- [ ] Драйвер cpio ramdisk
|
- [ ] Драйвер cpio ramdisk
|
||||||
|
|
|
||||||
|
|
@ -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"
|
||||||
|
|
||||||
// Глобальный счетчик тиков системы
|
// Глобальный счетчик тиков системы
|
||||||
|
|
|
||||||
|
|
@ -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
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