From 60f028db645fc4bf397624c24bef6271a4b814f7 Mon Sep 17 00:00:00 2001 From: Leonmmcoset Date: Thu, 9 Apr 2026 22:35:11 +0800 Subject: [PATCH] Stage 4 --- Makefile | 1 + clks/include/clks/scheduler.h | 20 +++++ clks/include/clks/task.h | 25 ++++++ clks/kernel/interrupts.c | 1 + clks/kernel/kmain.c | 17 ++++- clks/kernel/scheduler.c | 138 ++++++++++++++++++++++++++++++++++ docs/stage4.md | 35 +++++++++ 7 files changed, 236 insertions(+), 1 deletion(-) create mode 100644 clks/include/clks/scheduler.h create mode 100644 clks/include/clks/task.h create mode 100644 clks/kernel/scheduler.c create mode 100644 docs/stage4.md diff --git a/Makefile b/Makefile index 7995a3c..870af5b 100644 --- a/Makefile +++ b/Makefile @@ -71,6 +71,7 @@ C_SOURCES := \ clks/kernel/pmm.c \ clks/kernel/heap.c \ clks/kernel/interrupts.c \ + clks/kernel/scheduler.c \ clks/lib/string.c \ clks/drivers/serial/serial.c \ clks/drivers/video/framebuffer.c \ diff --git a/clks/include/clks/scheduler.h b/clks/include/clks/scheduler.h new file mode 100644 index 0000000..9a615f9 --- /dev/null +++ b/clks/include/clks/scheduler.h @@ -0,0 +1,20 @@ +#ifndef CLKS_SCHEDULER_H +#define CLKS_SCHEDULER_H + +#include +#include + +struct clks_scheduler_stats { + u32 task_count; + u32 current_task_id; + u64 total_timer_ticks; + u64 context_switch_count; +}; + +void clks_scheduler_init(void); +clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks); +void clks_scheduler_on_timer_tick(u64 tick); +struct clks_scheduler_stats clks_scheduler_get_stats(void); +const struct clks_task_descriptor *clks_scheduler_get_task(u32 task_id); + +#endif \ No newline at end of file diff --git a/clks/include/clks/task.h b/clks/include/clks/task.h new file mode 100644 index 0000000..f316c42 --- /dev/null +++ b/clks/include/clks/task.h @@ -0,0 +1,25 @@ +#ifndef CLKS_TASK_H +#define CLKS_TASK_H + +#include + +#define CLKS_TASK_NAME_MAX 32U + +enum clks_task_state { + CLKS_TASK_UNUSED = 0, + CLKS_TASK_READY = 1, + CLKS_TASK_RUNNING = 2, + CLKS_TASK_BLOCKED = 3 +}; + +struct clks_task_descriptor { + u32 id; + char name[CLKS_TASK_NAME_MAX]; + enum clks_task_state state; + u32 time_slice_ticks; + u32 remaining_ticks; + u64 total_ticks; + u64 switch_count; +}; + +#endif \ No newline at end of file diff --git a/clks/kernel/interrupts.c b/clks/kernel/interrupts.c index 4ebfc29..a7a56e2 100644 --- a/clks/kernel/interrupts.c +++ b/clks/kernel/interrupts.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #define CLKS_IDT_ENTRY_COUNT 256U diff --git a/clks/kernel/kmain.c b/clks/kernel/kmain.c index 9add0d4..1d5b352 100644 --- a/clks/kernel/kmain.c +++ b/clks/kernel/kmain.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -15,6 +16,7 @@ void clks_kernel_main(void) { const struct limine_memmap_response *boot_memmap; struct clks_pmm_stats pmm_stats; struct clks_heap_stats heap_stats; + struct clks_scheduler_stats sched_stats; void *heap_probe = CLKS_NULL; clks_serial_init(); @@ -31,7 +33,7 @@ void clks_kernel_main(void) { clks_tty_init(); } - clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE3 START"); + clks_log(CLKS_LOG_INFO, "BOOT", "CLEONOS STAGE4 START"); if (boot_fb == CLKS_NULL) { clks_log(CLKS_LOG_WARN, "VIDEO", "NO FRAMEBUFFER FROM LIMINE"); @@ -78,6 +80,19 @@ void clks_kernel_main(void) { clks_kfree(heap_probe); } + clks_scheduler_init(); + + if (clks_scheduler_add_kernel_task("klogd", 4U) == CLKS_FALSE) { + clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KLOGD TASK"); + } + + if (clks_scheduler_add_kernel_task("kworker", 3U) == CLKS_FALSE) { + clks_log(CLKS_LOG_WARN, "SCHED", "FAILED TO ADD KWORKER TASK"); + } + + sched_stats = clks_scheduler_get_stats(); + clks_log_hex(CLKS_LOG_INFO, "SCHED", "TASK_COUNT", sched_stats.task_count); + clks_interrupts_init(); clks_log(CLKS_LOG_INFO, "INT", "IDT + PIC INITIALIZED"); diff --git a/clks/kernel/scheduler.c b/clks/kernel/scheduler.c new file mode 100644 index 0000000..3575714 --- /dev/null +++ b/clks/kernel/scheduler.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include + +#define CLKS_SCHED_MAX_TASKS 16U +#define CLKS_SCHED_MIN_SLICE 1U + +static struct clks_task_descriptor clks_tasks[CLKS_SCHED_MAX_TASKS]; +static u32 clks_task_count = 0; +static u32 clks_current_task = 0; +static u64 clks_total_timer_ticks = 0; +static u64 clks_context_switch_count = 0; + +static void clks_sched_copy_name(char *dst, const char *src) { + u32 i = 0; + + while (i < (CLKS_TASK_NAME_MAX - 1U) && src[i] != '\0') { + dst[i] = src[i]; + i++; + } + + dst[i] = '\0'; +} + +static u32 clks_sched_next_ready_task(u32 from) { + u32 i; + + for (i = 1U; i <= clks_task_count; i++) { + u32 idx = (from + i) % clks_task_count; + + if (clks_tasks[idx].state == CLKS_TASK_READY || clks_tasks[idx].state == CLKS_TASK_RUNNING) { + return idx; + } + } + + return from; +} + +void clks_scheduler_init(void) { + clks_memset(clks_tasks, 0, sizeof(clks_tasks)); + clks_task_count = 0; + clks_current_task = 0; + clks_total_timer_ticks = 0; + clks_context_switch_count = 0; + + clks_scheduler_add_kernel_task("idle", 1U); + + clks_tasks[0].state = CLKS_TASK_RUNNING; + clks_tasks[0].remaining_ticks = clks_tasks[0].time_slice_ticks; + + clks_log(CLKS_LOG_INFO, "SCHED", "ROUND-ROBIN ONLINE"); +} + +clks_bool clks_scheduler_add_kernel_task(const char *name, u32 time_slice_ticks) { + struct clks_task_descriptor *task; + + if (name == CLKS_NULL) { + return CLKS_FALSE; + } + + if (clks_task_count >= CLKS_SCHED_MAX_TASKS) { + return CLKS_FALSE; + } + + if (time_slice_ticks < CLKS_SCHED_MIN_SLICE) { + time_slice_ticks = CLKS_SCHED_MIN_SLICE; + } + + task = &clks_tasks[clks_task_count]; + task->id = clks_task_count; + clks_sched_copy_name(task->name, name); + task->state = CLKS_TASK_READY; + task->time_slice_ticks = time_slice_ticks; + task->remaining_ticks = time_slice_ticks; + task->total_ticks = 0; + task->switch_count = 0; + + clks_task_count++; + return CLKS_TRUE; +} + +void clks_scheduler_on_timer_tick(u64 tick) { + struct clks_task_descriptor *current; + + if (clks_task_count == 0U) { + return; + } + + clks_total_timer_ticks = tick; + + current = &clks_tasks[clks_current_task]; + + if (current->state == CLKS_TASK_RUNNING || current->state == CLKS_TASK_READY) { + current->total_ticks++; + + if (current->remaining_ticks > 0U) { + current->remaining_ticks--; + } + } + + if (current->remaining_ticks == 0U) { + u32 next = clks_sched_next_ready_task(clks_current_task); + + current->remaining_ticks = current->time_slice_ticks; + + if (next != clks_current_task) { + if (current->state == CLKS_TASK_RUNNING) { + current->state = CLKS_TASK_READY; + } + + clks_current_task = next; + clks_tasks[clks_current_task].state = CLKS_TASK_RUNNING; + clks_tasks[clks_current_task].switch_count++; + clks_tasks[clks_current_task].remaining_ticks = clks_tasks[clks_current_task].time_slice_ticks; + clks_context_switch_count++; + } + } +} + +struct clks_scheduler_stats clks_scheduler_get_stats(void) { + struct clks_scheduler_stats stats; + + stats.task_count = clks_task_count; + stats.current_task_id = clks_current_task; + stats.total_timer_ticks = clks_total_timer_ticks; + stats.context_switch_count = clks_context_switch_count; + + return stats; +} + +const struct clks_task_descriptor *clks_scheduler_get_task(u32 task_id) { + if (task_id >= clks_task_count) { + return CLKS_NULL; + } + + return &clks_tasks[task_id]; +} \ No newline at end of file diff --git a/docs/stage4.md b/docs/stage4.md new file mode 100644 index 0000000..34dbd5e --- /dev/null +++ b/docs/stage4.md @@ -0,0 +1,35 @@ +# CLeonOS Stage4 + +## Stage Goal +- Add process/task scheduling foundation in CLKS. +- Introduce task control blocks and kernel task registry. +- Implement timer-driven round-robin time-slice scheduling logic. +- Connect scheduler tick updates to IRQ0 interrupt path. + +## Acceptance Criteria +- Kernel boots and prints `CLEONOS STAGE4 START`. +- Scheduler initializes and logs task count. +- At least 3 kernel tasks exist (`idle`, `klogd`, `kworker`). +- Timer IRQ invokes scheduler tick handler without panic/reboot. +- System stays in idle loop with interrupts and scheduler active. + +## Build Targets +- `make setup` +- `make iso` +- `make run` +- `make debug` + +## QEMU Command +- `qemu-system-x86_64 -M q35 -m 1024M -cdrom build/CLeonOS-x86_64.iso -serial stdio` + +## Common Bugs and Debugging +- Immediate reboot after enabling interrupts: + - Verify IDT selector uses runtime `CS` and ISR stubs are linked. +- Scheduler task count is 0: + - Confirm `clks_scheduler_init()` is called before idle loop. +- Build failure on ISR symbols: + - Ensure `interrupt_stubs.S` is included and `.S` build rule exists. +- Build failure on scheduler symbols: + - Ensure `scheduler.c` is in `C_SOURCES` and `scheduler.h` is included where needed. +- No periodic scheduling activity: + - Verify IRQ0 is unmasked and timer vector 32 path calls scheduler tick handler. \ No newline at end of file