Add a Scheduler.

Finally.

Just Round Robin with sleeping, but it's still awesome. I think this can finish v0.3, with a few adjustments.
This commit is contained in:
apio 2022-09-20 19:58:04 +02:00
parent e6c6a1677a
commit 1c12cf016e
8 changed files with 232 additions and 16 deletions

View File

@ -0,0 +1,15 @@
#pragma once
#include "thread/Task.h"
namespace Scheduler
{
void init();
void yield();
void sleep(unsigned long ms);
void add_kernel_task(void (*task)(void));
Task* current_task();
void task_yield(Context* context);
void task_tick(Context* context);
}

View File

@ -0,0 +1,16 @@
#pragma once
#include "interrupts/Context.h"
struct Task
{
uint64_t id;
Context regs;
int64_t task_sleep = 0;
int64_t task_time = 0;
Task* next_task = nullptr;
};
void set_context_from_task(Task& task, Context* ctx);
void get_context_to_task(Task& task, Context* ctx);

View File

@ -7,6 +7,7 @@
#include "log/Log.h"
#include "panic/hang.h"
#include "std/stdio.h"
#include "thread/Scheduler.h"
#include "trace/StackTracer.h"
extern "C" void common_handler(Context* context)
@ -47,6 +48,7 @@ extern "C" void common_handler(Context* context)
tracer.trace();
hang();
}
if (context->number == 48) { Scheduler::task_yield(context); }
if (context->number == 256) { kwarnln("Unused interrupt"); }
return;
}

View File

@ -7,12 +7,16 @@
#include "rand/Init.h"
#include "scheduling/PIT.h"
#include "std/stdio.h"
#include "thread/Scheduler.h"
void IRQ::interrupt_handler(Context* context)
{
switch (context->irq_number)
{
case 0: PIT::tick(); break;
case 0:
PIT::tick();
Scheduler::task_tick(context);
break;
case 1: {
[[maybe_unused]] volatile unsigned char scancode = IO::inb(0x60);
kdbgln("Keyboard key pressed/released, seconds since boot: %ld.%ld", PIT::ms_since_boot / 1000,

View File

@ -11,6 +11,7 @@
#include "interrupts/Install.h"
#include "interrupts/Interrupts.h"
#include "io/PIC.h"
#include "io/Serial.h"
#include "log/Log.h"
#include "memory/KernelMemoryManager.h"
#include "memory/Memory.h"
@ -25,6 +26,7 @@
#include "std/stdio.h"
#include "std/stdlib.h"
#include "std/string.h"
#include "thread/Scheduler.h"
extern "C" void _start()
{
@ -63,9 +65,34 @@ extern "C" void _start()
kinfoln("Prepared PIT");
Interrupts::enable();
Scheduler::init();
kinfoln("Interrupts enabled");
kinfoln("Prepared scheduler");
Scheduler::add_kernel_task([]() {
while (1)
{
sleep(1000);
for (int i = 0; i < 5; i++) { Serial::println("woo!"); }
}
});
Scheduler::add_kernel_task([]() {
sleep(500);
while (1)
{
sleep(2000);
for (int i = 0; i < 6; i++) { Serial::println("boo!"); }
}
});
Scheduler::add_kernel_task([]() {
sleep(20000);
kinfoln("Here i am, the hard-to-get thread!");
while (1) Scheduler::yield();
});
kinfoln("Prepared scheduler tasks");
ACPI::SDTHeader* rootSDT = ACPI::GetRSDTOrXSDT();
bool isXSDT = ACPI::IsXSDT();
@ -73,8 +100,10 @@ extern "C" void _start()
framebuffer0.clear(Color::Cyan);
sleep(2500);
reboot();
Interrupts::enable();
while (1) halt();
kinfoln("Interrupts enabled");
sleep(20000);
reboot();
}

View File

@ -1,5 +1,5 @@
#include "panic/hang.h"
#include "scheduling/PIT.h"
#include "thread/Scheduler.h"
#include <stdlib.h>
static void strrev(char* arr, int start, int end)
@ -145,14 +145,7 @@ char* ultoa(uint64_t number, char* arr, int base)
return arr;
}
#pragma GCC push_options
#pragma GCC optimize("O0")
void sleep(uint64_t ms)
{
volatile uint64_t start = PIT::ms_since_boot;
volatile uint64_t end = (uint64_t)(start + ms);
while (PIT::ms_since_boot < end) { asm volatile("hlt"); }
Scheduler::sleep(ms);
}
#pragma GCC pop_options

View File

@ -0,0 +1,145 @@
#define MODULE "sched"
#include "thread/Scheduler.h"
#include "interrupts/Interrupts.h"
#include "log/Log.h"
#include "memory/KernelMemoryManager.h"
#include "panic/hang.h"
#include "scheduling/PIT.h"
#include "std/string.h"
#include "thread/Task.h"
static Task tasks[32];
static uint64_t task_num = 0;
static Task idle_task;
static uint64_t free_tid = 0;
static Task* sched_current_task;
static Task* base_task;
static Task* end_task;
static void idle_task_function()
{
Interrupts::enable();
while (1) halt();
}
static uint64_t frequency;
void Scheduler::init()
{
memset(&idle_task, 0, sizeof(Task));
idle_task.id = free_tid++;
idle_task.regs.rip = (uint64_t)&idle_task_function;
idle_task.regs.rsp = (uint64_t)KernelMemoryManager::get_page();
idle_task.regs.cs = 0x08;
idle_task.regs.ds = 0x10;
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(idle_task.regs.rflags)::"%rax");
idle_task.regs.rflags |= 0x200;
idle_task.task_sleep = 1000;
base_task = &tasks[task_num++];
memset(base_task, 0, sizeof(Task));
end_task = base_task;
sched_current_task = base_task;
sched_current_task->id = free_tid++;
sched_current_task->task_time = 30; // gets 30 ms of cpu time before next switch
sched_current_task->next_task = nullptr;
// the other registers will be saved next task switch
frequency = 1000 / PIT::frequency();
}
void Scheduler::add_kernel_task(void (*task)(void))
{
if (task_num == 32) return; // FIXME: allow for dynamically allocated linked list instead of a fixed array of Tasks
Task* new_task = &tasks[task_num++];
memset(new_task, 0, sizeof(Task));
new_task->id = free_tid++;
new_task->regs.rip = (uint64_t)task;
new_task->regs.rsp = (uint64_t)KernelMemoryManager::get_pages(2); // 8 KB is enough for everyone, right?
new_task->regs.cs = 0x08;
new_task->regs.ds = 0x10;
asm volatile("pushfq; movq (%%rsp), %%rax; movq %%rax, %0; popfq;" : "=m"(new_task->regs.rflags)::"%rax");
new_task->regs.rflags |= 0x200;
new_task->task_sleep = 0;
new_task->task_time = 0;
end_task->next_task = new_task;
end_task = new_task;
kinfoln("Adding task: starts at %lx, tid %ld, stack at %lx, total tasks: %ld", new_task->regs.rip, new_task->id,
new_task->regs.rsp, task_num);
}
static void sched_decrement_sleep_times()
{
Task* task = base_task;
while (task)
{
if (task->task_sleep > 0)
{
task->task_sleep -= frequency;
if (task->task_sleep < 0) task->task_sleep = 0;
}
task = task->next_task;
}
}
void Scheduler::task_tick(Context* context)
{
sched_decrement_sleep_times();
sched_current_task->task_time -= frequency;
if (sched_current_task->task_time < 0)
{
sched_current_task->task_time = 0;
task_yield(context);
}
}
void Scheduler::task_yield(Context* context)
{
get_context_to_task(*sched_current_task, context);
bool was_idle = false;
if (sched_current_task->id == 0) // idle task
{
sched_current_task = base_task;
was_idle = true;
}
Task* original_task = sched_current_task;
do {
sched_current_task = sched_current_task->next_task;
if (!sched_current_task) { sched_current_task = base_task; }
if (sched_current_task->task_sleep == 0)
{
sched_current_task->task_time = 30;
set_context_from_task(*sched_current_task, context);
return;
}
} while (sched_current_task != original_task);
if (original_task->task_sleep > 0)
{
sched_current_task = &idle_task;
if (!was_idle) { set_context_from_task(*sched_current_task, context); }
return;
}
kinfoln("Resuming current task %ld", original_task->id);
original_task->task_time = 30; // grant 30 more ms, there is no other task available
return;
}
void Scheduler::yield()
{
asm volatile("int $48");
}
void Scheduler::sleep(unsigned long ms)
{
current_task()->task_sleep = ms;
yield();
}
Task* Scheduler::current_task()
{
return sched_current_task;
}

View File

@ -0,0 +1,12 @@
#include "thread/Task.h"
#include "std/string.h"
void set_context_from_task(Task& task, Context* ctx)
{
memcpy(ctx, &task.regs, sizeof(Context));
}
void get_context_to_task(Task& task, Context* ctx)
{
memcpy(&task.regs, ctx, sizeof(Context));
}