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:
parent
e6c6a1677a
commit
1c12cf016e
15
kernel/include/thread/Scheduler.h
Normal file
15
kernel/include/thread/Scheduler.h
Normal 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);
|
||||
}
|
16
kernel/include/thread/Task.h
Normal file
16
kernel/include/thread/Task.h
Normal 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);
|
@ -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;
|
||||
}
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
@ -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
|
145
kernel/src/thread/Scheduler.cpp
Normal file
145
kernel/src/thread/Scheduler.cpp
Normal 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;
|
||||
}
|
12
kernel/src/thread/Task.cpp
Normal file
12
kernel/src/thread/Task.cpp
Normal 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));
|
||||
}
|
Loading…
Reference in New Issue
Block a user