refactor(kernel): centralize selftests and split scheduler code

This commit is contained in:
Microindole 2026-05-10 12:04:10 +08:00
parent 20cdf179d7
commit 7db47d0e38
No known key found for this signature in database
GPG Key ID: 22FB34CD2133ACA1
17 changed files with 534 additions and 392 deletions

View File

@ -31,7 +31,7 @@ include kernel/Makefile
all: $(BOOT_EFI) $(KERNEL_ELF)
dirs:
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/arch/mm $(BUILD_DIR)/kernel $(BUILD_DIR)/kernel/locking $(BUILD_DIR)/mm $(EFI_DIR)
mkdir -p $(BUILD_DIR)/arch/boot $(BUILD_DIR)/arch/kernel $(BUILD_DIR)/arch/mm $(BUILD_DIR)/kernel $(BUILD_DIR)/kernel/locking $(BUILD_DIR)/kernel/sched $(BUILD_DIR)/kernel/selftest $(BUILD_DIR)/kernel/time $(BUILD_DIR)/mm $(EFI_DIR)
$(BUILD_DIR)/arch/boot/%.obj: $(ARCH_DIR)/boot/%.c $(ARCH_DIR)/include/efi.h $(ARCH_DIR)/boot/debug_log.h include/tianole/boot_info.h include/tianole/elf.h | dirs
$(CC) $(CFLAGS) -c $< -o $@

View File

@ -34,7 +34,9 @@ KERNEL_CFLAGS := \
-Wall \
-Wextra \
-Werror \
-I. \
-Iinclude \
-Ikernel \
-I$(ARCH_DIR)/include
KERNEL_ASFLAGS := \

View File

@ -4,11 +4,12 @@
#include <tianole/early_log.h>
#include <tianole/mm.h>
#include "page_table.h"
#define ENTRY_COUNT 512
#define PAGE_MASK 0x000ffffffffff000ull
#define PAGE_SIZE_FLAG (1ull << 7)
#define MAX_RESERVED_TABLE_PAGES 4096
#define TEST_VIRTUAL_PAGE 0xffffff0000000000ull
static phys_addr_t reserved_table_pages[MAX_RESERVED_TABLE_PAGES];
static uint64_t reserved_table_page_count;
@ -141,7 +142,7 @@ static uint64_t make_table_entry(phys_addr_t table)
return table | PAGE_PRESENT | PAGE_WRITABLE;
}
static void page_tables_init(void)
void page_tables_init(void)
{
uint64_t index;
uint64_t *firmware_pml4;
@ -268,38 +269,3 @@ int virt_to_phys(virt_addr_t virt, phys_addr_t *phys)
*phys = (*entry & PAGE_MASK) | (virt & (PAGE_SIZE - 1));
return 0;
}
void page_table_selftest(void)
{
phys_addr_t page = alloc_page();
phys_addr_t resolved;
volatile uint64_t *mapped =
(volatile uint64_t *)(uintptr_t)TEST_VIRTUAL_PAGE;
if (page == 0) {
panic("page table selftest allocation failed");
}
page_tables_init();
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
panic("page table selftest map failed");
}
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
resolved != page) {
panic("page table selftest resolve failed");
}
*mapped = 0x54494f4c45504d4dull;
if (*mapped != 0x54494f4c45504d4dull) {
panic("page table selftest access failed");
}
if (unmap_page(TEST_VIRTUAL_PAGE) != 0) {
panic("page table selftest unmap failed");
}
free_page(page);
early_log_puts("page table selftest ok\n");
}

8
arch/x86/mm/page_table.h Normal file
View File

@ -0,0 +1,8 @@
#ifndef ARCH_X86_MM_PAGE_TABLE_H
#define ARCH_X86_MM_PAGE_TABLE_H
#include <tianole/mm.h>
void page_tables_init(void);
#endif

View File

@ -67,6 +67,8 @@
- 可独立执行的检查放在 `scripts/checks/`
- 多个检查共享的函数放在 `scripts/lib/`
- 不把所有检查逻辑持续堆进 `scripts/check.sh`
- 项目结构规则放在 `scripts/checks/structure.sh`,优先用宿主 Linux/LLVM 工具实现底层扫描,把 Tianole 自己的目录和 include 约束固化在脚本里。
- 启动阶段内核自测集中放在 `kernel/selftest/`,不要散落在具体实现目录里。
## 验证

View File

@ -12,7 +12,10 @@
## 建议边界
- `mm/`:架构无关内存管理。
- `arch/x86/mm/`页表格式、地址空间切换、TLB 操作。
- `arch/x86/mm/page_table.c`页表格式、地址空间切换、TLB 操作和 map/unmap/query 主路径。
- `arch/x86/mm/fault.c`page fault 诊断。
- `arch/x86/mm/page_table.h`x86 页表子目录私有接口。
- `kernel/selftest/page_table.c`:页表 map/unmap/query 启动自测。
- `include/tianole/`:通用内存接口。
## 实现内容
@ -55,6 +58,7 @@
- 已切换到内核自有 PML4不再直接修改固件页表。
- 已提供最小 `map_page()`、`unmap_page()` 和 `virt_to_phys()` 接口。
- 已加入页表 map/unmap/query selftest。
- 已把 x86 页表 selftest 从 `page_table.c` 移到 `kernel/selftest/page_table.c`,避免页表主路径和启动验证逻辑混在同一目录边界。
- 已拆出 x86 page fault 诊断路径,能输出 fault address、错误码、访问类型和权限来源。
- 已建立最小内核堆,提供 `kmalloc()``kfree()`,底层通过页表按需映射物理页。
- 已加入内核堆分配、写入、释放、复用 selftest。

View File

@ -12,7 +12,12 @@
## 建议边界
- `kernel/sched/`:线程、调度器、等待队列。
- `kernel/sched/core.c`run queue、调度选择、tick 和 IRQ exit 调度边界。
- `kernel/sched/thread.c`:线程对象创建、初始栈、退出和 DEAD 线程回收。
- `kernel/sched/wait.c`:等待队列。
- `kernel/sched/idle.c`idle thread。
- `kernel/sched/sched.h`:调度子系统私有接口,不向通用内核层公开内部状态。
- `kernel/selftest/sched.c`:当前阶段的调度自测和启动演示线程。
- `kernel/time/`:通用时间与 timer 抽象。
- `arch/x86/`:具体 timer、上下文切换。
@ -66,6 +71,7 @@
- 已把 `kernel_thread_create()` 中的线程 id 分配和 run queue 入队纳入 interrupt-safe lock 保护。
- 已建立 `sched_irq_exit()`timer IRQ 只设置 `need_resched`trap 的 IRQ 返回边界统一消费调度请求。
- 已建立最小 DEAD 线程回收路径,调度前会释放非当前 DEAD 线程的内核栈和线程对象。
- 已把调度代码按职责拆分为 `core.c`、`thread.c`、`wait.c`、`idle.c` 和私有 `sched.h`,并把当前阶段自测/演示线程移到 `kernel/selftest/sched.c`
- `scripts/check.sh` 已验证 `timer initialized`、`timer tick=1/2/3`、`scheduler initialized`、`kernel thread selftest ok`、timer 驱动线程轮转、`sched_sleep()` 和 wait queue wakeup。
后续扩展:

View File

@ -3,7 +3,12 @@ KERNEL_SRCS := \
kernel/boot_report.c \
kernel/early_log.c \
kernel/locking/spinlock.c \
kernel/sched/core.c \
kernel/sched/idle.c \
kernel/sched/thread.c \
kernel/sched/wait.c \
kernel/selftest/page_table.c \
kernel/selftest/sched.c \
kernel/time/timer.c
KERNEL_OBJS := \

171
kernel/sched/core.c Normal file
View File

@ -0,0 +1,171 @@
#include <stdint.h>
#include <arch/switch.h>
#include <tianole/early_log.h>
#include <tianole/sched.h>
#include <tianole/timer.h>
#include "sched.h"
struct thread *run_queue_head;
struct thread *run_queue_tail;
struct thread *current_thread;
uintptr_t boot_stack_pointer;
uint64_t next_thread_id = 1;
int scheduler_ready;
int schedule_locked;
int need_resched;
struct thread *idle_thread;
struct spinlock scheduler_lock = SPINLOCK_INITIALIZER;
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;
}
static struct thread *next_runnable_thread(void)
{
struct thread *start;
struct thread *thread;
if (current_thread == 0 || current_thread->next == 0) {
start = run_queue_head;
} else {
start = current_thread->next;
}
thread = start;
while (thread != 0) {
if (thread->state == THREAD_READY && thread != idle_thread) {
return thread;
}
thread = thread->next;
}
for (thread = run_queue_head; thread != start; thread = thread->next) {
if (thread->state == THREAD_READY && thread != idle_thread) {
return thread;
}
}
if (idle_thread != 0 && idle_thread->state == THREAD_READY) {
return idle_thread;
}
return 0;
}
static void wake_sleeping_threads(uint64_t tick)
{
struct thread *thread;
for (thread = run_queue_head; thread != 0; thread = thread->next) {
if (thread->state == THREAD_SLEEPING &&
thread->wake_tick <= tick) {
thread->wake_tick = 0;
thread->state = THREAD_READY;
}
}
}
void sched_yield(void)
{
struct thread *prev;
struct thread *next;
if (schedule_locked != 0) {
return;
}
sched_reap_dead_threads();
prev = current_thread;
next = next_runnable_thread();
if (next == 0 || next == prev) {
return;
}
schedule_locked = 1;
if (prev != 0 && prev->state == THREAD_RUNNING) {
prev->state = THREAD_READY;
}
next->state = THREAD_RUNNING;
current_thread = next;
schedule_locked = 0;
if (prev == 0) {
arch_context_switch(&boot_stack_pointer, next->stack_pointer);
return;
}
arch_context_switch(&prev->stack_pointer, next->stack_pointer);
}
void sched_tick(uint64_t tick)
{
wake_sleeping_threads(tick);
if (current_thread != 0 && current_thread->state == THREAD_RUNNING) {
need_resched = 1;
}
}
void sched_irq_exit(void)
{
if (need_resched == 0 || current_thread == 0 || schedule_locked != 0) {
return;
}
need_resched = 0;
sched_yield();
}
void sched_sleep(uint64_t ticks)
{
uint64_t now;
if (current_thread == 0 || ticks == 0) {
return;
}
now = timer_ticks();
current_thread->wake_tick = now + ticks;
current_thread->state = THREAD_SLEEPING;
sched_yield();
}
void sched_init(void)
{
if (scheduler_ready != 0) {
return;
}
run_queue_head = 0;
run_queue_tail = 0;
idle_thread = 0;
scheduler_ready = 1;
early_log_puts("scheduler initialized\n");
sched_selftest();
}
void sched_start(void)
{
if (sched_idle_create() != 0) {
panic("idle thread creation failed");
}
sched_demo_start();
}

22
kernel/sched/idle.c Normal file
View File

@ -0,0 +1,22 @@
#include <tianole/sched.h>
#include "sched.h"
static void idle_thread_entry(void *arg)
{
(void)arg;
for (;;) {
__asm__ volatile("hlt");
}
}
int sched_idle_create(void)
{
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
if (idle_thread == 0) {
return -1;
}
return 0;
}

26
kernel/sched/sched.h Normal file
View File

@ -0,0 +1,26 @@
#ifndef KERNEL_SCHED_SCHED_H
#define KERNEL_SCHED_SCHED_H
#include <stdint.h>
#include <tianole/sched.h>
#include <tianole/spinlock.h>
extern struct thread *run_queue_head;
extern struct thread *run_queue_tail;
extern struct thread *current_thread;
extern uintptr_t boot_stack_pointer;
extern uint64_t next_thread_id;
extern int scheduler_ready;
extern int schedule_locked;
extern int need_resched;
extern struct thread *idle_thread;
extern struct spinlock scheduler_lock;
void enqueue_thread(struct thread *thread);
void sched_reap_dead_threads(void);
void sched_selftest(void);
int sched_idle_create(void);
void sched_demo_start(void) __attribute__((noreturn));
#endif

View File

@ -1,28 +1,16 @@
#include <stddef.h>
#include <stdint.h>
#include <arch/switch.h>
#include <tianole/early_log.h>
#include <tianole/mm.h>
#include <tianole/sched.h>
#include <tianole/spinlock.h>
#include <tianole/timer.h>
#include "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 struct thread *current_thread;
static uintptr_t boot_stack_pointer;
static uint64_t next_thread_id = 1;
static int scheduler_ready;
static int schedule_locked;
static int need_resched;
static struct thread *idle_thread;
static struct spinlock scheduler_lock = SPINLOCK_INITIALIZER;
static void thread_trampoline(void) __attribute__((noreturn));
static uintptr_t align_down_uintptr(uintptr_t value, uintptr_t alignment)
@ -49,53 +37,6 @@ static void copy_thread_name(char *dest, size_t dest_size, const char *src)
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;
}
static void release_thread(struct thread *thread)
{
kfree(thread->stack_base);
kfree(thread);
}
static void reap_dead_threads(void)
{
struct thread *prev = 0;
struct thread *thread = run_queue_head;
while (thread != 0) {
struct thread *next = thread->next;
if (thread->state == THREAD_DEAD && thread != current_thread) {
if (prev != 0) {
prev->next = next;
} else {
run_queue_head = next;
}
if (run_queue_tail == thread) {
run_queue_tail = prev;
}
release_thread(thread);
} else {
prev = thread;
}
thread = next;
}
}
static uintptr_t prepare_initial_stack(uintptr_t stack_top)
{
uintptr_t *stack = (uintptr_t *)stack_top;
@ -155,178 +96,37 @@ struct thread *kernel_thread_create(
return thread;
}
static struct thread *next_runnable_thread(void)
static void release_thread(struct thread *thread)
{
struct thread *start;
struct thread *thread;
if (current_thread == 0 || current_thread->next == 0) {
start = run_queue_head;
} else {
start = current_thread->next;
kfree(thread->stack_base);
kfree(thread);
}
thread = start;
void sched_reap_dead_threads(void)
{
struct thread *prev = 0;
struct thread *thread = run_queue_head;
while (thread != 0) {
if (thread->state == THREAD_READY && thread != idle_thread) {
return thread;
}
thread = thread->next;
}
struct thread *next = thread->next;
for (thread = run_queue_head; thread != start; thread = thread->next) {
if (thread->state == THREAD_READY && thread != idle_thread) {
return thread;
}
}
if (idle_thread != 0 && idle_thread->state == THREAD_READY) {
return idle_thread;
}
return 0;
}
static void wake_sleeping_threads(uint64_t tick)
{
struct thread *thread;
for (thread = run_queue_head; thread != 0; thread = thread->next) {
if (thread->state == THREAD_SLEEPING &&
thread->wake_tick <= tick) {
thread->wake_tick = 0;
thread->state = THREAD_READY;
}
}
}
void sched_yield(void)
{
struct thread *prev;
struct thread *next;
if (schedule_locked != 0) {
return;
}
reap_dead_threads();
prev = current_thread;
next = next_runnable_thread();
if (next == 0 || next == prev) {
return;
}
schedule_locked = 1;
if (prev != 0 && prev->state == THREAD_RUNNING) {
prev->state = THREAD_READY;
}
next->state = THREAD_RUNNING;
current_thread = next;
schedule_locked = 0;
if (prev == 0) {
arch_context_switch(&boot_stack_pointer, next->stack_pointer);
return;
}
arch_context_switch(&prev->stack_pointer, next->stack_pointer);
}
void sched_tick(uint64_t tick)
{
wake_sleeping_threads(tick);
if (current_thread != 0 && current_thread->state == THREAD_RUNNING) {
need_resched = 1;
}
}
void sched_irq_exit(void)
{
if (need_resched == 0 || current_thread == 0 || schedule_locked != 0) {
return;
}
need_resched = 0;
sched_yield();
}
void sched_sleep(uint64_t ticks)
{
uint64_t now;
if (current_thread == 0 || ticks == 0) {
return;
}
now = timer_ticks();
current_thread->wake_tick = now + ticks;
current_thread->state = THREAD_SLEEPING;
sched_yield();
}
void wait_queue_init(struct wait_queue *queue)
{
if (queue == 0) {
return;
}
queue->head = 0;
queue->tail = 0;
}
static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread)
{
thread->wait_next = 0;
if (queue->tail != 0) {
queue->tail->wait_next = thread;
if (thread->state == THREAD_DEAD && thread != current_thread) {
if (prev != 0) {
prev->next = next;
} else {
queue->head = thread;
run_queue_head = next;
}
queue->tail = thread;
if (run_queue_tail == thread) {
run_queue_tail = prev;
}
void wait_queue_sleep(struct wait_queue *queue)
{
if (queue == 0 || current_thread == 0) {
return;
release_thread(thread);
} else {
prev = thread;
}
wait_queue_enqueue(queue, current_thread);
current_thread->state = THREAD_WAITING;
sched_yield();
}
void wait_queue_wake_one(struct wait_queue *queue)
{
struct thread *thread;
if (queue == 0 || queue->head == 0) {
return;
}
thread = queue->head;
queue->head = thread->wait_next;
if (queue->head == 0) {
queue->tail = 0;
}
thread->wait_next = 0;
if (thread->state == THREAD_WAITING) {
thread->state = THREAD_READY;
}
}
void wait_queue_wake_all(struct wait_queue *queue)
{
while (queue != 0 && queue->head != 0) {
wait_queue_wake_one(queue);
thread = next;
}
}
@ -345,128 +145,3 @@ static void thread_trampoline(void)
sched_yield();
}
}
static void thread_selftest_entry(void *arg)
{
(void)arg;
}
static void scheduler_selftest(void)
{
struct spinlock test_lock;
struct thread *first =
kernel_thread_create("worker-a", thread_selftest_entry, 0);
struct thread *second =
kernel_thread_create("worker-b", thread_selftest_entry, 0);
uint64_t flags;
test_lock.locked = 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");
}
spin_lock_irqsave(&test_lock, &flags);
spin_unlock_irqrestore(&test_lock, flags);
early_log_puts("kernel thread selftest ok\n");
}
static void scheduler_demo_entry(void *arg)
{
uint64_t id = (uint64_t)(uintptr_t)arg;
uint64_t step;
for (step = 1; step <= 3; step++) {
early_log_puts("preempt thread ");
early_log_u64_decimal(id);
early_log_puts(" step=");
early_log_u64_decimal(step);
early_log_puts("\n");
sched_sleep(2);
}
}
static struct wait_queue demo_wait_queue;
static void wait_queue_demo_waiter(void *arg)
{
(void)arg;
early_log_puts("waiter sleeping\n");
wait_queue_sleep(&demo_wait_queue);
early_log_puts("waiter woke\n");
}
static void wait_queue_demo_waker(void *arg)
{
(void)arg;
early_log_puts("waker sleeping\n");
sched_sleep(4);
early_log_puts("waker wake_one\n");
wait_queue_wake_one(&demo_wait_queue);
}
static void idle_thread_entry(void *arg)
{
(void)arg;
for (;;) {
__asm__ volatile("hlt");
}
}
void sched_init(void)
{
if (scheduler_ready != 0) {
return;
}
run_queue_head = 0;
run_queue_tail = 0;
idle_thread = 0;
scheduler_ready = 1;
early_log_puts("scheduler initialized\n");
scheduler_selftest();
}
void sched_start(void)
{
struct thread *first = kernel_thread_create(
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
struct thread *second = kernel_thread_create(
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
struct thread *waiter =
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
struct thread *waker =
kernel_thread_create("waker", wait_queue_demo_waker, 0);
idle_thread = kernel_thread_create("idle", idle_thread_entry, 0);
if (first == 0 || second == 0 || waiter == 0 || waker == 0 ||
idle_thread == 0) {
panic("scheduler demo thread creation failed");
}
wait_queue_init(&demo_wait_queue);
early_log_puts("scheduler starting\n");
sched_yield();
panic("scheduler returned to boot context");
}

64
kernel/sched/wait.c Normal file
View File

@ -0,0 +1,64 @@
#include <tianole/sched.h>
#include "sched.h"
void wait_queue_init(struct wait_queue *queue)
{
if (queue == 0) {
return;
}
queue->head = 0;
queue->tail = 0;
}
static void wait_queue_enqueue(struct wait_queue *queue, struct thread *thread)
{
thread->wait_next = 0;
if (queue->tail != 0) {
queue->tail->wait_next = thread;
} else {
queue->head = thread;
}
queue->tail = thread;
}
void wait_queue_sleep(struct wait_queue *queue)
{
if (queue == 0 || current_thread == 0) {
return;
}
wait_queue_enqueue(queue, current_thread);
current_thread->state = THREAD_WAITING;
sched_yield();
}
void wait_queue_wake_one(struct wait_queue *queue)
{
struct thread *thread;
if (queue == 0 || queue->head == 0) {
return;
}
thread = queue->head;
queue->head = thread->wait_next;
if (queue->head == 0) {
queue->tail = 0;
}
thread->wait_next = 0;
if (thread->state == THREAD_WAITING) {
thread->state = THREAD_READY;
}
}
void wait_queue_wake_all(struct wait_queue *queue)
{
while (queue != 0 && queue->head != 0) {
wait_queue_wake_one(queue);
}
}

View File

@ -0,0 +1,43 @@
#include <stdint.h>
#include <tianole/early_log.h>
#include <tianole/mm.h>
#include "arch/x86/mm/page_table.h"
#define TEST_VIRTUAL_PAGE 0xffffff0000000000ull
void page_table_selftest(void)
{
phys_addr_t page = alloc_page();
phys_addr_t resolved;
volatile uint64_t *mapped =
(volatile uint64_t *)(uintptr_t)TEST_VIRTUAL_PAGE;
if (page == 0) {
panic("page table selftest allocation failed");
}
page_tables_init();
if (map_page(TEST_VIRTUAL_PAGE, page, PAGE_WRITABLE) != 0) {
panic("page table selftest map failed");
}
if (virt_to_phys(TEST_VIRTUAL_PAGE, &resolved) != 0 ||
resolved != page) {
panic("page table selftest resolve failed");
}
*mapped = 0x54494f4c45504d4dull;
if (*mapped != 0x54494f4c45504d4dull) {
panic("page table selftest access failed");
}
if (unmap_page(TEST_VIRTUAL_PAGE) != 0) {
panic("page table selftest unmap failed");
}
free_page(page);
early_log_puts("page table selftest ok\n");
}

108
kernel/selftest/sched.c Normal file
View File

@ -0,0 +1,108 @@
#include <stdint.h>
#include <tianole/early_log.h>
#include <tianole/sched.h>
#include <tianole/spinlock.h>
#include "sched/sched.h"
#define STACK_ALIGNMENT 16u
static void thread_selftest_entry(void *arg)
{
(void)arg;
}
void sched_selftest(void)
{
struct spinlock test_lock;
struct thread *first =
kernel_thread_create("worker-a", thread_selftest_entry, 0);
struct thread *second =
kernel_thread_create("worker-b", thread_selftest_entry, 0);
uint64_t flags;
test_lock.locked = 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");
}
spin_lock_irqsave(&test_lock, &flags);
spin_unlock_irqrestore(&test_lock, flags);
early_log_puts("kernel thread selftest ok\n");
}
static void scheduler_demo_entry(void *arg)
{
uint64_t id = (uint64_t)(uintptr_t)arg;
uint64_t step;
for (step = 1; step <= 3; step++) {
early_log_puts("preempt thread ");
early_log_u64_decimal(id);
early_log_puts(" step=");
early_log_u64_decimal(step);
early_log_puts("\n");
sched_sleep(2);
}
}
static struct wait_queue demo_wait_queue;
static void wait_queue_demo_waiter(void *arg)
{
(void)arg;
early_log_puts("waiter sleeping\n");
wait_queue_sleep(&demo_wait_queue);
early_log_puts("waiter woke\n");
}
static void wait_queue_demo_waker(void *arg)
{
(void)arg;
early_log_puts("waker sleeping\n");
sched_sleep(4);
early_log_puts("waker wake_one\n");
wait_queue_wake_one(&demo_wait_queue);
}
void sched_demo_start(void)
{
struct thread *first = kernel_thread_create(
"round-robin-a", scheduler_demo_entry, (void *)(uintptr_t)1);
struct thread *second = kernel_thread_create(
"round-robin-b", scheduler_demo_entry, (void *)(uintptr_t)2);
struct thread *waiter =
kernel_thread_create("waiter", wait_queue_demo_waiter, 0);
struct thread *waker =
kernel_thread_create("waker", wait_queue_demo_waker, 0);
if (first == 0 || second == 0 || waiter == 0 || waker == 0) {
panic("scheduler demo thread creation failed");
}
wait_queue_init(&demo_wait_queue);
early_log_puts("scheduler starting\n");
sched_yield();
panic("scheduler returned to boot context");
}

View File

@ -4,6 +4,7 @@ set -euo pipefail
cd "$(dirname "$0")/.."
./scripts/checks/format.sh
./scripts/checks/structure.sh
./scripts/checks/boot.sh
./scripts/checks/trap.sh
./scripts/checks/page-fault.sh

39
scripts/checks/structure.sh Executable file
View File

@ -0,0 +1,39 @@
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/../.."
fail()
{
echo "structure check failed: $*" >&2
exit 1
}
check_no_relative_parent_includes()
{
local matches
matches=$(find arch include kernel mm -name '*.c' -o -name '*.h' |
xargs grep -nE '^[[:space:]]*#include[[:space:]]+[<"]\.\./' || true)
if [ -n "$matches" ]; then
echo "$matches" >&2
fail "do not use ../ includes; promote shared headers or add a private include root"
fi
}
check_selftests_are_centralized()
{
local matches
matches=$(find arch kernel mm -name '*selftest*.c' |
grep -v '^kernel/selftest/' || true)
if [ -n "$matches" ]; then
echo "$matches" >&2
fail "kernel selftests belong under kernel/selftest/"
fi
}
check_no_relative_parent_includes
check_selftests_are_centralized