feat(sched): add kernel thread foundation
This commit is contained in:
parent
9e610bdbaa
commit
76a9363e3c
@ -51,16 +51,18 @@
|
|||||||
- 已建立通用 `timer_tick()` 入口和 `timer_ticks()` 计数接口。
|
- 已建立通用 `timer_tick()` 入口和 `timer_ticks()` 计数接口。
|
||||||
- 已在 trap dispatch 中区分 CPU exception 和外部 IRQ。
|
- 已在 trap dispatch 中区分 CPU exception 和外部 IRQ。
|
||||||
- 已对 timer IRQ0 发送 EOI,避免中断只触发一次。
|
- 已对 timer IRQ0 发送 EOI,避免中断只触发一次。
|
||||||
- `scripts/check.sh` 已验证 `timer initialized` 和 `timer tick=1/2/3`。
|
- 已建立最小 kernel thread 对象,包含 id、状态、入口、参数、内核栈和 run queue 链接。
|
||||||
|
- 已建立动态 run queue,不固定写死线程数量。
|
||||||
|
- 已能通过 `kernel_thread_create()` 动态分配线程对象和内核栈。
|
||||||
|
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized` 和 `kernel thread selftest ok`。
|
||||||
|
|
||||||
后续扩展:
|
后续扩展:
|
||||||
|
|
||||||
- 把 IRQ 分发扩展为可注册 handler 的表,而不是只处理 timer。
|
- 把 IRQ 分发扩展为可注册 handler 的表,而不是只处理 timer。
|
||||||
- 建立 kernel thread 对象和内核栈。
|
|
||||||
- 建立上下文切换入口。
|
- 建立上下文切换入口。
|
||||||
- 建立 run queue 和最小 round-robin scheduler。
|
- 建立 run queue 和最小 round-robin scheduler。
|
||||||
- 建立 `yield()`、`sleep()`、timer wakeup 和 wait queue。
|
- 建立 `yield()`、`sleep()`、timer wakeup 和 wait queue。
|
||||||
|
|
||||||
下一阶段:
|
下一阶段:
|
||||||
|
|
||||||
- 继续在 `04-time-scheduler.md` 内推进最小 kernel thread。
|
- 继续在 `04-time-scheduler.md` 内推进上下文切换入口和最小 round-robin scheduler。
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
| `01-early-debug.md` | 基础完成 | 已有 early log 前端、QEMU debug port、COM1 串口和最小 `panic()`。 |
|
| `01-early-debug.md` | 基础完成 | 已有 early log 前端、QEMU debug port、COM1 串口和最小 `panic()`。 |
|
||||||
| `02-cpu-interrupts.md` | 基础完成 | 已有 GDT/TSS/IDT、exception vector 0-31、`trap_frame` 和 invalid opcode 回归测试。 |
|
| `02-cpu-interrupts.md` | 基础完成 | 已有 GDT/TSS/IDT、exception vector 0-31、`trap_frame` 和 invalid opcode 回归测试。 |
|
||||||
| `03-memory.md` | 基础完成 | 已有最小物理页 allocator、页表 map/unmap/query、page fault 诊断和内核堆。 |
|
| `03-memory.md` | 基础完成 | 已有最小物理页 allocator、页表 map/unmap/query、page fault 诊断和内核堆。 |
|
||||||
| `04-time-scheduler.md` | 进行中 | 已有 PIT timer interrupt 和通用 tick 计数;kernel thread、调度和等待队列未完成。 |
|
| `04-time-scheduler.md` | 进行中 | 已有 PIT timer interrupt、通用 tick 计数、kernel thread 对象、内核栈和 run queue 基础;上下文切换、调度和等待队列未完成。 |
|
||||||
| `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 |
|
| `05-input-events.md` | 未开始 | 需要输入事件模型和键盘接入。 |
|
||||||
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
|
| `06-storage-vfs.md` | 未开始 | 需要块层、缓存、VFS 和基础文件系统。 |
|
||||||
| `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 |
|
| `07-user-mode.md` | 未开始 | 需要 syscall、进程、用户地址空间和 ELF 加载。 |
|
||||||
@ -36,7 +36,7 @@
|
|||||||
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
|
| `09-driver-expansion.md` | 未开始 | 需要设备模型、PCI/ACPI、存储、网络等驱动扩展。 |
|
||||||
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
|
| `10-real-machine.md` | 未开始 | 需要 U 盘真机启动验证和硬件差异记录。 |
|
||||||
|
|
||||||
当前最合适的下一步仍在 `04-time-scheduler.md` 内:引入最小 kernel thread 和内核栈,再做调度循环。
|
当前最合适的下一步仍在 `04-time-scheduler.md` 内:引入上下文切换入口,再做最小 round-robin 调度循环。
|
||||||
|
|
||||||
## Linux 级能力缺口路由
|
## Linux 级能力缺口路由
|
||||||
|
|
||||||
|
|||||||
@ -24,6 +24,7 @@
|
|||||||
- page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。
|
- page fault 基础:已能输出 fault address、错误码、访问类型和权限来源。
|
||||||
- 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。
|
- 内核堆基础:已提供 `kmalloc/kfree`,并接入启动 selftest。
|
||||||
- timer 基础:已接入 x86 PIC/PIT,timer IRQ 能进入通用 tick 入口。
|
- timer 基础:已接入 x86 PIC/PIT,timer IRQ 能进入通用 tick 入口。
|
||||||
|
- kernel thread 基础:已建立线程对象、内核栈和 run queue。
|
||||||
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
- 构建系统已拆成根 Makefile、`arch/x86/Makefile` 和目录 Makefile。
|
||||||
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
- `scripts/check.sh` 已验证启动日志、串口日志和 invalid opcode 异常路径,GitHub Actions 已接入。
|
||||||
- `.clang-format` 已用于强制当前 C 代码风格。
|
- `.clang-format` 已用于强制当前 C 代码风格。
|
||||||
@ -43,7 +44,7 @@
|
|||||||
|
|
||||||
目标:
|
目标:
|
||||||
|
|
||||||
- 建立最小 kernel thread 和调度循环。
|
- 建立上下文切换入口和最小 round-robin 调度循环。
|
||||||
|
|
||||||
## 任务路由
|
## 任务路由
|
||||||
|
|
||||||
|
|||||||
32
include/tianole/sched.h
Normal file
32
include/tianole/sched.h
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
#ifndef TIANOLE_SCHED_H
|
||||||
|
#define TIANOLE_SCHED_H
|
||||||
|
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
typedef void (*kernel_thread_entry_t)(void *arg);
|
||||||
|
|
||||||
|
enum thread_state {
|
||||||
|
THREAD_READY,
|
||||||
|
THREAD_RUNNING,
|
||||||
|
THREAD_SLEEPING,
|
||||||
|
THREAD_DEAD,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct thread {
|
||||||
|
uint64_t id;
|
||||||
|
enum thread_state state;
|
||||||
|
kernel_thread_entry_t entry;
|
||||||
|
void *arg;
|
||||||
|
void *stack_base;
|
||||||
|
uintptr_t stack_top;
|
||||||
|
size_t stack_size;
|
||||||
|
struct thread *next;
|
||||||
|
char name[32];
|
||||||
|
};
|
||||||
|
|
||||||
|
void sched_init(void);
|
||||||
|
struct thread *kernel_thread_create(
|
||||||
|
const char *name, kernel_thread_entry_t entry, void *arg);
|
||||||
|
|
||||||
|
#endif
|
||||||
@ -2,6 +2,7 @@ KERNEL_SRCS := \
|
|||||||
kernel/main.c \
|
kernel/main.c \
|
||||||
kernel/boot_report.c \
|
kernel/boot_report.c \
|
||||||
kernel/early_log.c \
|
kernel/early_log.c \
|
||||||
|
kernel/sched/thread.c \
|
||||||
kernel/time/timer.c
|
kernel/time/timer.c
|
||||||
|
|
||||||
KERNEL_OBJS := \
|
KERNEL_OBJS := \
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
#include <tianole/early_log.h>
|
#include <tianole/early_log.h>
|
||||||
#include <tianole/kernel_init.h>
|
#include <tianole/kernel_init.h>
|
||||||
#include <tianole/mm.h>
|
#include <tianole/mm.h>
|
||||||
|
#include <tianole/sched.h>
|
||||||
|
|
||||||
void kernel_main(const boot_info_t *boot_info)
|
void kernel_main(const boot_info_t *boot_info)
|
||||||
{
|
{
|
||||||
@ -12,6 +13,7 @@ void kernel_main(const boot_info_t *boot_info)
|
|||||||
arch_traps_init();
|
arch_traps_init();
|
||||||
kernel_report_boot_state(boot_info);
|
kernel_report_boot_state(boot_info);
|
||||||
mm_init(boot_info);
|
mm_init(boot_info);
|
||||||
|
sched_init();
|
||||||
arch_timer_init();
|
arch_timer_init();
|
||||||
|
|
||||||
#if KERNEL_TEST_TRAP
|
#if KERNEL_TEST_TRAP
|
||||||
|
|||||||
136
kernel/sched/thread.c
Normal file
136
kernel/sched/thread.c
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
#include <tianole/early_log.h>
|
||||||
|
#include <tianole/mm.h>
|
||||||
|
#include <tianole/sched.h>
|
||||||
|
|
||||||
|
#define KERNEL_STACK_SIZE (PAGE_SIZE * 4u)
|
||||||
|
#define STACK_ALIGNMENT 16u
|
||||||
|
|
||||||
|
static struct thread *run_queue_head;
|
||||||
|
static struct thread *run_queue_tail;
|
||||||
|
static uint64_t next_thread_id = 1;
|
||||||
|
static int scheduler_ready;
|
||||||
|
|
||||||
|
static uintptr_t align_down_uintptr(uintptr_t value, uintptr_t alignment)
|
||||||
|
{
|
||||||
|
return value & ~(alignment - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void copy_thread_name(char *dest, size_t dest_size, const char *src)
|
||||||
|
{
|
||||||
|
size_t index;
|
||||||
|
|
||||||
|
if (dest_size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (src == 0) {
|
||||||
|
src = "thread";
|
||||||
|
}
|
||||||
|
|
||||||
|
for (index = 0; index + 1 < dest_size && src[index] != '\0'; index++) {
|
||||||
|
dest[index] = src[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[index] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
static void enqueue_thread(struct thread *thread)
|
||||||
|
{
|
||||||
|
thread->next = 0;
|
||||||
|
|
||||||
|
if (run_queue_tail != 0) {
|
||||||
|
run_queue_tail->next = thread;
|
||||||
|
} else {
|
||||||
|
run_queue_head = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_queue_tail = thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct thread *kernel_thread_create(
|
||||||
|
const char *name, kernel_thread_entry_t entry, void *arg)
|
||||||
|
{
|
||||||
|
struct thread *thread;
|
||||||
|
uintptr_t stack_top;
|
||||||
|
|
||||||
|
if (entry == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread = kmalloc(sizeof(*thread));
|
||||||
|
if (thread == 0) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
thread->stack_base = kmalloc(KERNEL_STACK_SIZE);
|
||||||
|
if (thread->stack_base == 0) {
|
||||||
|
kfree(thread);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
stack_top = (uintptr_t)thread->stack_base + KERNEL_STACK_SIZE;
|
||||||
|
|
||||||
|
thread->id = next_thread_id++;
|
||||||
|
thread->state = THREAD_READY;
|
||||||
|
thread->entry = entry;
|
||||||
|
thread->arg = arg;
|
||||||
|
thread->stack_top = align_down_uintptr(stack_top, STACK_ALIGNMENT);
|
||||||
|
thread->stack_size = KERNEL_STACK_SIZE;
|
||||||
|
thread->next = 0;
|
||||||
|
copy_thread_name(thread->name, sizeof(thread->name), name);
|
||||||
|
|
||||||
|
enqueue_thread(thread);
|
||||||
|
|
||||||
|
return thread;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void thread_selftest_entry(void *arg)
|
||||||
|
{
|
||||||
|
(void)arg;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void scheduler_selftest(void)
|
||||||
|
{
|
||||||
|
struct thread *first =
|
||||||
|
kernel_thread_create("worker-a", thread_selftest_entry, 0);
|
||||||
|
struct thread *second =
|
||||||
|
kernel_thread_create("worker-b", thread_selftest_entry, 0);
|
||||||
|
|
||||||
|
if (first == 0 || second == 0 || first == second) {
|
||||||
|
panic("kernel thread selftest allocation failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (first->id == second->id || first->state != THREAD_READY ||
|
||||||
|
second->state != THREAD_READY) {
|
||||||
|
panic("kernel thread selftest state failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((first->stack_top & (STACK_ALIGNMENT - 1)) != 0 ||
|
||||||
|
(second->stack_top & (STACK_ALIGNMENT - 1)) != 0) {
|
||||||
|
panic("kernel thread selftest stack alignment failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (run_queue_head != first || first->next != second ||
|
||||||
|
run_queue_tail != second) {
|
||||||
|
panic("kernel thread selftest run queue failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
early_log_puts("kernel thread selftest ok\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
void sched_init(void)
|
||||||
|
{
|
||||||
|
if (scheduler_ready != 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
run_queue_head = 0;
|
||||||
|
run_queue_tail = 0;
|
||||||
|
scheduler_ready = 1;
|
||||||
|
|
||||||
|
early_log_puts("scheduler initialized\n");
|
||||||
|
scheduler_selftest();
|
||||||
|
}
|
||||||
@ -23,6 +23,8 @@ check_lines build/debug.log \
|
|||||||
"page table selftest ok" \
|
"page table selftest ok" \
|
||||||
"kernel heap initialized" \
|
"kernel heap initialized" \
|
||||||
"kernel heap selftest ok" \
|
"kernel heap selftest ok" \
|
||||||
|
"scheduler initialized" \
|
||||||
|
"kernel thread selftest ok" \
|
||||||
"timer initialized" \
|
"timer initialized" \
|
||||||
"timer tick=1" \
|
"timer tick=1" \
|
||||||
"timer tick=2" \
|
"timer tick=2" \
|
||||||
@ -41,6 +43,8 @@ check_lines build/serial.log \
|
|||||||
"page table selftest ok" \
|
"page table selftest ok" \
|
||||||
"kernel heap initialized" \
|
"kernel heap initialized" \
|
||||||
"kernel heap selftest ok" \
|
"kernel heap selftest ok" \
|
||||||
|
"scheduler initialized" \
|
||||||
|
"kernel thread selftest ok" \
|
||||||
"timer initialized" \
|
"timer initialized" \
|
||||||
"timer tick=1" \
|
"timer tick=1" \
|
||||||
"timer tick=2" \
|
"timer tick=2" \
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user