Files
Sylva/kernel/scheduler/scheduler.cpp
T
2026-06-06 10:31:20 +08:00

226 lines
5.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include <scheduler.h>
#include <idt.h>
#include <pic.h>
#include <memory/heap.h>
#include <memory/pmm.h>
#include <common.h>
#include <serial.h>
// 汇编函数:context_switch(UINT64* old_rsp, UINT64 new_rsp)
extern "C" void context_switch(UINT64* old_rsp, UINT64 new_rsp);
static task_t g_tasks[TASK_MAX];
static UINT32 g_task_count = 0;
static task_t* g_current = NULL;
static task_t* g_task_list = NULL; // 循环链表头
// 跳板函数:新任务在 context_switch 后首先执行的函数
static void (*g_task_entries[TASK_MAX])(void);
extern "C" void task_entry_trampoline() {
ASM("sti");
task_t* cur = scheduler_current();
if (cur && g_task_entries[cur->id]) {
g_task_entries[cur->id]();
}
task_exit();
}
task_t* task_create(const char* name, void (*entry)(void)) {
if (g_task_count >= TASK_MAX) {
serial_write("SCHED: task limit reached\n");
return NULL;
}
UINT32 id = g_task_count++;
task_t* task = &g_tasks[id];
g_task_entries[id] = entry;
// 分配内核栈
UINTN stack_pages = TASK_STACK_SIZE / PAGE_SIZE;
void* stack = pmm_alloc_pages(stack_pages);
if (!stack) {
serial_write("SCHED: stack alloc failed for task ");
serial_write(name);
serial_write("\n");
return NULL;
}
task->id = id;
task->state = TASK_STATE_READY;
task->stack_base = stack;
task->time_slice = TIME_SLICE_DEFAULT;
// 复制任务名称
str_copy(task->name, name, TASK_NAME_LEN);
// 设置首次 context_switch 时的初始栈
// 栈向下增长。context_switch 会弹出 6 个寄存器然后 ret。
//
// 布局(高地址 → 低地址):
// [stack + TASK_STACK_SIZE] <- 栈顶
// 返回地址 = task_entry_trampolineret 跳转到这里)
// rbx = 0
// rbp = 0
// r12 = 0
// r13 = 0
// r14 = 0
// r15 = 0 <- RSP 初始指向这里
//
UINT64* sp = (UINT64*)((UINT8*)stack + TASK_STACK_SIZE);
*--sp = (UINT64)task_entry_trampoline;
*--sp = 0; // rbx
*--sp = 0; // rbp
*--sp = 0; // r12
*--sp = 0; // r13
*--sp = 0; // r14
*--sp = 0; // r15
task->rsp = (UINT64)sp;
// 插入循环链表
if (g_task_list == NULL) {
task->next = task;
g_task_list = task;
} else {
task->next = g_task_list->next;
g_task_list->next = task;
g_task_list = task;
}
serial_write("SCHED: created task '");
serial_write(task->name);
serial_write("' id=");
serial_write_hex(id);
serial_write("\n");
return task;
}
// 在循环链表中查找下一个就绪任务
static task_t* find_next_ready(void) {
if (g_current == NULL || g_task_list == NULL) return NULL;
task_t* next = g_current->next;
task_t* start = next;
do {
if (next->state == TASK_STATE_READY) {
return next;
}
next = next->next;
} while (next != start);
return NULL; // 没有就绪任务
}
void yield(void) {
if (g_current == NULL || g_task_list == NULL) return;
task_t* cur = g_current;
task_t* next = find_next_ready();
if (next == NULL || next == cur) return;
if (cur->state != TASK_STATE_TERMINATED)
cur->state = TASK_STATE_READY;
next->state = TASK_STATE_RUNNING;
next->time_slice = TIME_SLICE_DEFAULT;
g_current = next;
context_switch(&cur->rsp, next->rsp);
}
// 定时器 tick 处理 — 由 PIT IRQ 0 调用
void scheduler_tick(void) {
if (g_current == NULL) return;
// 递减时间片
if (g_current->time_slice > 0) {
g_current->time_slice--;
}
// 时间片用完则抢占
if (g_current->time_slice == 0) {
task_t* cur = g_current;
task_t* next = find_next_ready();
if (next == NULL || next == cur) {
// 没有其他就绪任务,或仅此一个 — 重置时间片
cur->time_slice = TIME_SLICE_DEFAULT;
return;
}
// 抢占
cur->state = TASK_STATE_READY;
cur->time_slice = TIME_SLICE_DEFAULT;
next->state = TASK_STATE_RUNNING;
next->time_slice = TIME_SLICE_DEFAULT;
g_current = next;
context_switch(&cur->rsp, next->rsp);
}
}
void scheduler_run(void) {
if (g_task_list == NULL) {
serial_write("SCHED: no tasks to run\n");
return;
}
// 查找第一个就绪任务
task_t* start = g_task_list->next;
task_t* t = start;
do {
if (t->state == TASK_STATE_READY) {
break;
}
t = t->next;
} while (t != start);
if (t->state != TASK_STATE_READY) {
serial_write("SCHED: no READY tasks\n");
return;
}
g_current = t;
t->state = TASK_STATE_RUNNING;
t->time_slice = TIME_SLICE_DEFAULT;
serial_write("SCHED: starting task '");
serial_write(t->name);
serial_write("'\n");
// 首次上下文切换 — 切换到任务栈
// 此后不会返回(直到所有任务终止)
UINT64 dummy_rsp;
context_switch(&dummy_rsp, t->rsp);
// 只有所有任务终止后才会返回到这里
serial_write("SCHED: all tasks finished\n");
while (1) ASM ("hlt");
}
void task_exit(void) {
if (g_current == NULL) return;
serial_write("SCHED: task '");
serial_write(g_current->name);
serial_write("' exited\n");
g_current->state = TASK_STATE_TERMINATED;
// 让出 CPU 给下一个任务 — 不会回来
yield();
// 不应到达此处
while (1) ASM ("hlt");
}
task_t* scheduler_current(void) {
return g_current;
}