feat(kernel): add preemptive round-robin thread scheduler
This commit is contained in:
1 parent
ad8ae808e6
commit
455e81de5e
2 files changed
+228
No files matched your search
@@ -0,0 +1,45 @@
|
|||||||
|
#ifndef FROSTBYTE_SCHED_H
|
||||||
|
#define FROSTBYTE_SCHED_H
|
||||||
|
|
||||||
|
#include "types.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
threadUnused = 0,
|
||||||
|
threadRunnable = 1,
|
||||||
|
threadRunning = 2,
|
||||||
|
threadSleeping = 3,
|
||||||
|
threadDead = 4
|
||||||
|
} ThreadState;
|
||||||
|
|
||||||
|
typedef void (*ThreadEntry)(void);
|
||||||
|
|
||||||
|
typedef struct Thread {
|
||||||
|
uint64 rsp;
|
||||||
|
uint64 stackBase;
|
||||||
|
uint64 kernelStackTop;
|
||||||
|
uint64 sleepUntil;
|
||||||
|
uint64 brk;
|
||||||
|
uint32 id;
|
||||||
|
ThreadState state;
|
||||||
|
const char* name;
|
||||||
|
FileDescriptor fds[maxOpenFiles];
|
||||||
|
} Thread;
|
||||||
|
|
||||||
|
void schedInit(void);
|
||||||
|
uint32 threadCreate(const char* name, ThreadEntry entry);
|
||||||
|
uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop);
|
||||||
|
uint64 schedule(uint64 currentRsp);
|
||||||
|
void schedStart(void);
|
||||||
|
|
||||||
|
void threadExit(uint64 code);
|
||||||
|
void threadSleep(uint64 milliseconds);
|
||||||
|
void threadSetBrk(uint32 id, uint64 brk);
|
||||||
|
uint64 currentThreadSbrk(int64 increment);
|
||||||
|
|
||||||
|
uint32 threadCount(void);
|
||||||
|
Thread* threadAt(uint32 index);
|
||||||
|
Thread* currentThread(void);
|
||||||
|
uint32 currentThreadId(void);
|
||||||
|
|
||||||
|
#endif
|
||||||
+183
@@ -0,0 +1,183 @@
|
|||||||
|
#include "sched.h"
|
||||||
|
#include "isr.h"
|
||||||
|
#include "heap.h"
|
||||||
|
#include "string.h"
|
||||||
|
#include "ports.h"
|
||||||
|
#include "gdt.h"
|
||||||
|
#include "tss.h"
|
||||||
|
#include "pit.h"
|
||||||
|
#include "console.h"
|
||||||
|
#include "paging.h"
|
||||||
|
#include "vfs.h"
|
||||||
|
|
||||||
|
#define maxThreads 16
|
||||||
|
#define threadStackSize 16384
|
||||||
|
|
||||||
|
static Thread threads[maxThreads];
|
||||||
|
static uint32 threadTotal;
|
||||||
|
static uint32 currentIndex;
|
||||||
|
static bool schedulerActive;
|
||||||
|
|
||||||
|
void schedInit(void)
|
||||||
|
{
|
||||||
|
threadTotal = 1;
|
||||||
|
currentIndex = 0;
|
||||||
|
|
||||||
|
threads[0].id = 0;
|
||||||
|
threads[0].name = "idle";
|
||||||
|
threads[0].state = threadRunning;
|
||||||
|
threads[0].rsp = 0;
|
||||||
|
threads[0].stackBase = 0;
|
||||||
|
threads[0].kernelStackTop = 0;
|
||||||
|
threads[0].sleepUntil = 0;
|
||||||
|
threads[0].brk = 0;
|
||||||
|
vfsFdInit(threads[0].fds);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 threadCreate(const char* name, ThreadEntry entry)
|
||||||
|
{
|
||||||
|
uint32 slot = threadTotal;
|
||||||
|
threadTotal++;
|
||||||
|
|
||||||
|
uint64 stackBase = (uint64)kmalloc(threadStackSize);
|
||||||
|
uint64 stackTop = (stackBase + threadStackSize) & ~15ull;
|
||||||
|
|
||||||
|
Registers* frame = (Registers*)(stackTop - sizeof(Registers));
|
||||||
|
memset(frame, 0, sizeof(Registers));
|
||||||
|
frame->rip = (uint64)entry;
|
||||||
|
frame->cs = kernelCodeSelector;
|
||||||
|
frame->rflags = 0x202;
|
||||||
|
frame->rsp = stackTop;
|
||||||
|
frame->ss = kernelDataSelector;
|
||||||
|
|
||||||
|
threads[slot].id = slot;
|
||||||
|
threads[slot].name = name;
|
||||||
|
threads[slot].state = threadRunnable;
|
||||||
|
threads[slot].stackBase = stackBase;
|
||||||
|
threads[slot].kernelStackTop = stackTop;
|
||||||
|
threads[slot].rsp = (uint64)frame;
|
||||||
|
threads[slot].sleepUntil = 0;
|
||||||
|
threads[slot].brk = 0;
|
||||||
|
vfsFdInit(threads[slot].fds);
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 threadCreateUser(const char* name, uint64 entry, uint64 userStackTop)
|
||||||
|
{
|
||||||
|
uint32 slot = threadTotal;
|
||||||
|
threadTotal++;
|
||||||
|
|
||||||
|
uint64 stackBase = (uint64)kmalloc(threadStackSize);
|
||||||
|
uint64 kernelStackTop = (stackBase + threadStackSize) & ~15ull;
|
||||||
|
|
||||||
|
Registers* frame = (Registers*)(kernelStackTop - sizeof(Registers));
|
||||||
|
memset(frame, 0, sizeof(Registers));
|
||||||
|
frame->rip = entry;
|
||||||
|
frame->cs = userCodeSelector | 3;
|
||||||
|
frame->rflags = 0x202;
|
||||||
|
frame->rsp = userStackTop;
|
||||||
|
frame->ss = userDataSelector | 3;
|
||||||
|
|
||||||
|
threads[slot].id = slot;
|
||||||
|
threads[slot].name = name;
|
||||||
|
threads[slot].state = threadRunnable;
|
||||||
|
threads[slot].stackBase = stackBase;
|
||||||
|
threads[slot].kernelStackTop = kernelStackTop;
|
||||||
|
threads[slot].rsp = (uint64)frame;
|
||||||
|
threads[slot].sleepUntil = 0;
|
||||||
|
threads[slot].brk = 0;
|
||||||
|
vfsFdInit(threads[slot].fds);
|
||||||
|
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 schedule(uint64 currentRsp)
|
||||||
|
{
|
||||||
|
if (!schedulerActive)
|
||||||
|
return currentRsp;
|
||||||
|
|
||||||
|
threads[currentIndex].rsp = currentRsp;
|
||||||
|
if (threads[currentIndex].state == threadRunning)
|
||||||
|
threads[currentIndex].state = threadRunnable;
|
||||||
|
|
||||||
|
uint64 now = pitTicks();
|
||||||
|
for (uint32 i = 0; i < threadTotal; i++)
|
||||||
|
if (threads[i].state == threadSleeping && now >= threads[i].sleepUntil)
|
||||||
|
threads[i].state = threadRunnable;
|
||||||
|
|
||||||
|
uint32 next = 0;
|
||||||
|
for (uint32 step = 1; step <= threadTotal; step++) {
|
||||||
|
uint32 candidate = (currentIndex + step) % threadTotal;
|
||||||
|
if (threads[candidate].state == threadRunnable) {
|
||||||
|
next = candidate;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
currentIndex = next;
|
||||||
|
threads[next].state = threadRunning;
|
||||||
|
tssSetRsp0(threads[next].kernelStackTop);
|
||||||
|
return threads[next].rsp;
|
||||||
|
}
|
||||||
|
|
||||||
|
void schedStart(void)
|
||||||
|
{
|
||||||
|
schedulerActive = true;
|
||||||
|
enableInterrupts();
|
||||||
|
for (;;)
|
||||||
|
halt();
|
||||||
|
}
|
||||||
|
|
||||||
|
void threadExit(uint64 code)
|
||||||
|
{
|
||||||
|
Thread* thread = &threads[currentIndex];
|
||||||
|
thread->state = threadDead;
|
||||||
|
kprintf("[user] pid=%u '%s' exited with code %lu\n", thread->id, thread->name, code);
|
||||||
|
}
|
||||||
|
|
||||||
|
void threadSleep(uint64 milliseconds)
|
||||||
|
{
|
||||||
|
uint64 ticks = milliseconds * pitFrequency() / 1000;
|
||||||
|
if (ticks == 0)
|
||||||
|
ticks = 1;
|
||||||
|
threads[currentIndex].sleepUntil = pitTicks() + ticks;
|
||||||
|
threads[currentIndex].state = threadSleeping;
|
||||||
|
}
|
||||||
|
|
||||||
|
void threadSetBrk(uint32 id, uint64 brk)
|
||||||
|
{
|
||||||
|
threads[id].brk = brk;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64 currentThreadSbrk(int64 increment)
|
||||||
|
{
|
||||||
|
Thread* thread = &threads[currentIndex];
|
||||||
|
uint64 previous = thread->brk;
|
||||||
|
|
||||||
|
if (increment > 0)
|
||||||
|
pagingMapUserRange(previous, (uint64)increment);
|
||||||
|
|
||||||
|
thread->brk = previous + (uint64)increment;
|
||||||
|
return previous;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 threadCount(void)
|
||||||
|
{
|
||||||
|
return threadTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* threadAt(uint32 index)
|
||||||
|
{
|
||||||
|
return &threads[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
Thread* currentThread(void)
|
||||||
|
{
|
||||||
|
return &threads[currentIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 currentThreadId(void)
|
||||||
|
{
|
||||||
|
return threads[currentIndex].id;
|
||||||
|
}
|
||||||
Reference in new issue
Block a user