mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
Initial priority-based scheduler
- Scheduler now has multiple linked_lists of processes at different priorities - Process structure improvements - scheduler::tick() and scheduler::schedule() separation
This commit is contained in:
@@ -13,6 +13,11 @@ dest = "screenfont.psf"
|
|||||||
source = "assets/fonts/tamsyn8x16r.psf"
|
source = "assets/fonts/tamsyn8x16r.psf"
|
||||||
|
|
||||||
[[files]]
|
[[files]]
|
||||||
dest = "nulldrv"
|
dest = "nulldrv1"
|
||||||
|
source = "build/kernel/src/drivers/nulldrv/nulldrv"
|
||||||
|
executable = true
|
||||||
|
|
||||||
|
[[files]]
|
||||||
|
dest = "nulldrv2"
|
||||||
source = "build/kernel/src/drivers/nulldrv/nulldrv"
|
source = "build/kernel/src/drivers/nulldrv/nulldrv"
|
||||||
executable = true
|
executable = true
|
||||||
|
|||||||
@@ -3,7 +3,8 @@ _start:
|
|||||||
xor rbp, rbp ; Sentinel rbp
|
xor rbp, rbp ; Sentinel rbp
|
||||||
|
|
||||||
.loop:
|
.loop:
|
||||||
mov rax, 0 ; DEBUG syscall
|
;mov rax, 1 ; DEBUG syscall
|
||||||
|
mov rax, 0 ; NOOP syscall
|
||||||
syscall
|
syscall
|
||||||
|
|
||||||
jmp .loop
|
jmp .loop
|
||||||
|
|||||||
@@ -306,6 +306,9 @@ syscall_handler(addr_t return_rsp, cpu_state regs)
|
|||||||
syscall call = static_cast<syscall>(regs.rax);
|
syscall call = static_cast<syscall>(regs.rax);
|
||||||
|
|
||||||
switch (call) {
|
switch (call) {
|
||||||
|
case syscall::noop:
|
||||||
|
break;
|
||||||
|
|
||||||
case syscall::debug:
|
case syscall::debug:
|
||||||
cons->set_color(11);
|
cons->set_color(11);
|
||||||
cons->printf("\nReceived DEBUG syscall\n");
|
cons->printf("\nReceived DEBUG syscall\n");
|
||||||
@@ -314,6 +317,9 @@ syscall_handler(addr_t return_rsp, cpu_state regs)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case syscall::message:
|
case syscall::message:
|
||||||
|
cons->set_color(11);
|
||||||
|
cons->printf("\nReceived MESSAGE syscall\n");
|
||||||
|
cons->set_color();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
|||||||
@@ -8,8 +8,14 @@ ramdisk_process_loader:
|
|||||||
; create_process already pushed a cpu_state onto the stack for us, this
|
; create_process already pushed a cpu_state onto the stack for us, this
|
||||||
; acts both as the cpu_state parameter to load_process, and the saved
|
; acts both as the cpu_state parameter to load_process, and the saved
|
||||||
; state for the following iretq
|
; state for the following iretq
|
||||||
|
;
|
||||||
|
; Additional parameters:
|
||||||
|
; rax - the address of the program image
|
||||||
|
; rbx - the size of the program image
|
||||||
|
; rcx - the address of this process' process structure
|
||||||
mov rdi, rax
|
mov rdi, rax
|
||||||
mov rsi, rbx
|
mov rsi, rbx
|
||||||
|
mov rdx, rcx
|
||||||
call load_process
|
call load_process
|
||||||
|
|
||||||
pop_all_and_segments
|
pop_all_and_segments
|
||||||
|
|||||||
@@ -20,20 +20,33 @@ const uint64_t rflags_int = 0x202;
|
|||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
void ramdisk_process_loader();
|
void ramdisk_process_loader();
|
||||||
void load_process(const void *image_start, size_t butes, cpu_state state);
|
void load_process(const void *image_start, size_t bytes, process *proc, cpu_state state);
|
||||||
};
|
};
|
||||||
|
|
||||||
scheduler::scheduler(lapic *apic) :
|
scheduler::scheduler(lapic *apic) :
|
||||||
m_apic(apic),
|
m_apic(apic),
|
||||||
m_current(0),
|
m_next_pid(1)
|
||||||
m_next_pid(0)
|
|
||||||
{
|
{
|
||||||
m_processes.ensure_capacity(50);
|
auto *idle = m_process_allocator.pop();
|
||||||
m_processes.append({m_next_pid++, 0, page_manager::get_pml4()}); // The kernel idle task, also the thread we're in now
|
|
||||||
|
uint8_t last_pri = num_priorities - 1;
|
||||||
|
|
||||||
|
// The kernel idle task, also the thread we're in now
|
||||||
|
idle->pid = 0;
|
||||||
|
idle->ppid = 0;
|
||||||
|
idle->priority = last_pri;
|
||||||
|
idle->rsp = 0; // This will get set when we switch away
|
||||||
|
idle->pml4 = page_manager::get_pml4();
|
||||||
|
idle->flags =
|
||||||
|
process_flags::ready |
|
||||||
|
process_flags::const_pri;
|
||||||
|
|
||||||
|
m_runlists[last_pri].push_back(idle);
|
||||||
|
m_current = idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
load_process(const void *image_start, size_t bytes, cpu_state state)
|
load_process(const void *image_start, size_t bytes, process *proc, cpu_state state)
|
||||||
{
|
{
|
||||||
// We're now in the process space for this process, allocate memory for the
|
// We're now in the process space for this process, allocate memory for the
|
||||||
// process code and load it
|
// process code and load it
|
||||||
@@ -85,6 +98,7 @@ load_process(const void *image_start, size_t bytes, cpu_state state)
|
|||||||
}
|
}
|
||||||
|
|
||||||
state.rip = image.entrypoint();
|
state.rip = image.entrypoint();
|
||||||
|
proc->flags &= ~process_flags::loading;
|
||||||
|
|
||||||
log::debug(logs::task, "Loaded! New process state:");
|
log::debug(logs::task, "Loaded! New process state:");
|
||||||
log::debug(logs::task, " CS: %d [%d]", state.cs >> 3, state.cs & 0x07);
|
log::debug(logs::task, " CS: %d [%d]", state.cs >> 3, state.cs & 0x07);
|
||||||
@@ -137,14 +151,28 @@ scheduler::create_process(const char *name, const void *data, size_t size)
|
|||||||
loader_state->rbx = size;
|
loader_state->rbx = size;
|
||||||
|
|
||||||
uint16_t pid = m_next_pid++;
|
uint16_t pid = m_next_pid++;
|
||||||
|
auto *proc = m_process_allocator.pop();
|
||||||
|
|
||||||
|
proc->pid = pid;
|
||||||
|
proc->ppid = 0; // TODO
|
||||||
|
proc->priority = default_priority;
|
||||||
|
proc->rsp = reinterpret_cast<addr_t>(loader_state);
|
||||||
|
proc->pml4 = pml4;
|
||||||
|
proc->flags =
|
||||||
|
process_flags::ready |
|
||||||
|
process_flags::loading;
|
||||||
|
|
||||||
|
m_runlists[default_priority].push_back(proc);
|
||||||
|
|
||||||
|
loader_state->rcx = reinterpret_cast<uint64_t>(proc);
|
||||||
|
|
||||||
log::debug(logs::task, "Creating process %s:", name);
|
log::debug(logs::task, "Creating process %s:", name);
|
||||||
log::debug(logs::task, " PID %d", pid);
|
log::debug(logs::task, " PID %d", pid);
|
||||||
|
log::debug(logs::task, " Pri %d", pid);
|
||||||
log::debug(logs::task, " RSP0 %016lx", state);
|
log::debug(logs::task, " RSP0 %016lx", state);
|
||||||
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
|
log::debug(logs::task, " RSP3 %016lx", state->user_rsp);
|
||||||
log::debug(logs::task, " PML4 %016lx", pml4);
|
log::debug(logs::task, " PML4 %016lx", pml4);
|
||||||
log::debug(logs::task, " Loading %016lx [%d]", loader_state->rax, loader_state->rbx);
|
log::debug(logs::task, " Loading %016lx [%d]", loader_state->rax, loader_state->rbx);
|
||||||
|
|
||||||
m_processes.append({pid, reinterpret_cast<addr_t>(loader_state), pml4});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -155,21 +183,41 @@ scheduler::start()
|
|||||||
}
|
}
|
||||||
|
|
||||||
addr_t
|
addr_t
|
||||||
scheduler::tick(addr_t rsp0)
|
scheduler::schedule(addr_t rsp0)
|
||||||
{
|
{
|
||||||
log::debug(logs::task, "Scheduler tick.");
|
m_current->rsp = rsp0;
|
||||||
|
m_runlists[m_current->priority].remove(m_current);
|
||||||
|
m_runlists[m_current->priority].push_back(m_current);
|
||||||
|
|
||||||
m_processes[m_current].rsp = rsp0;
|
uint8_t pri = 0;
|
||||||
m_current = (m_current + 1) % m_processes.count();
|
while (m_runlists[pri].empty()) {
|
||||||
rsp0 = m_processes[m_current].rsp;
|
++pri;
|
||||||
|
kassert(pri < num_priorities, "All runlists are empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
m_current = m_runlists[pri].pop_front();
|
||||||
|
rsp0 = m_current->rsp;
|
||||||
|
|
||||||
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
||||||
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
||||||
|
|
||||||
// Swap page tables
|
// Swap page tables
|
||||||
page_table *pml4 = m_processes[m_current].pml4;
|
page_table *pml4 = m_current->pml4;
|
||||||
page_manager::set_pml4(pml4);
|
page_manager::set_pml4(pml4);
|
||||||
|
|
||||||
|
bool loading = bitfield_has(m_current->flags, process_flags::loading);
|
||||||
|
log::debug(logs::task, "Scheduler switched to process %d, priority %d%s.",
|
||||||
|
m_current->pid, m_current->priority, loading ? " (loading)" : "");
|
||||||
|
|
||||||
|
return rsp0;
|
||||||
|
}
|
||||||
|
|
||||||
|
addr_t
|
||||||
|
scheduler::tick(addr_t rsp0)
|
||||||
|
{
|
||||||
|
// TODO: action based on the task using the whole quantum
|
||||||
|
|
||||||
|
rsp0 = schedule(rsp0);
|
||||||
m_apic->reset_timer(quantum);
|
m_apic->reset_timer(quantum);
|
||||||
return rsp0;
|
return rsp0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,57 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
/// \file scheduler.h
|
/// \file scheduler.h
|
||||||
/// The task scheduler and related definitions
|
/// The task scheduler and related definitions
|
||||||
|
#include "kutil/enum_bitfields.h"
|
||||||
|
#include "kutil/linked_list.h"
|
||||||
#include "kutil/memory.h"
|
#include "kutil/memory.h"
|
||||||
|
#include "kutil/slab_allocator.h"
|
||||||
#include "kutil/vector.h"
|
#include "kutil/vector.h"
|
||||||
|
|
||||||
class lapic;
|
class lapic;
|
||||||
struct page_table;
|
struct page_table;
|
||||||
|
struct cpu_state;
|
||||||
|
|
||||||
|
extern "C" addr_t isr_handler(addr_t, cpu_state);
|
||||||
|
|
||||||
|
enum class process_flags : uint32_t
|
||||||
|
{
|
||||||
|
running = 0x00000001,
|
||||||
|
ready = 0x00000002,
|
||||||
|
loading = 0x00000004,
|
||||||
|
|
||||||
|
const_pri = 0x80000000,
|
||||||
|
|
||||||
|
none = 0x00000000
|
||||||
|
};
|
||||||
|
IS_BITFIELD(process_flags);
|
||||||
|
|
||||||
struct process
|
struct process
|
||||||
{
|
{
|
||||||
uint16_t pid;
|
uint32_t pid;
|
||||||
|
uint32_t ppid;
|
||||||
|
|
||||||
|
uint8_t priority;
|
||||||
|
|
||||||
|
uint8_t reserved0;
|
||||||
|
uint16_t reserved;
|
||||||
|
|
||||||
|
process_flags flags;
|
||||||
|
|
||||||
addr_t rsp;
|
addr_t rsp;
|
||||||
page_table *pml4;
|
page_table *pml4;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
using process_list = kutil::linked_list<process>;
|
||||||
|
using process_node = process_list::item_type;
|
||||||
|
using process_slab = kutil::slab_allocator<process>;
|
||||||
|
|
||||||
/// The task scheduler
|
/// The task scheduler
|
||||||
class scheduler
|
class scheduler
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static const uint8_t num_priorities = 8;
|
||||||
|
static const uint8_t default_priority = num_priorities / 2;
|
||||||
|
|
||||||
/// Constructor.
|
/// Constructor.
|
||||||
/// \arg apic Pointer to the local APIC object
|
/// \arg apic Pointer to the local APIC object
|
||||||
scheduler(lapic *apic);
|
scheduler(lapic *apic);
|
||||||
@@ -34,20 +66,34 @@ public:
|
|||||||
/// timer interrupts or other preemption methods.
|
/// timer interrupts or other preemption methods.
|
||||||
void start();
|
void start();
|
||||||
|
|
||||||
/// Handle a timer tick
|
/// Run the scheduler, possibly switching to a new task
|
||||||
/// \arg rsp0 The stack pointer of the current interrupt handler
|
/// \arg rsp0 The stack pointer of the current interrupt handler
|
||||||
/// \returns The stack pointer to handler to switch to
|
/// \returns The stack pointer to switch to
|
||||||
addr_t tick(addr_t rsp0);
|
addr_t schedule(addr_t rsp0);
|
||||||
|
|
||||||
|
/// Get the current process.
|
||||||
|
/// \returns A pointer to the current process' process struct
|
||||||
|
inline process * current() { return m_current; }
|
||||||
|
|
||||||
/// Get a reference to the system scheduler
|
/// Get a reference to the system scheduler
|
||||||
/// \returns A reference to the global system scheduler
|
/// \returns A reference to the global system scheduler
|
||||||
static scheduler & get() { return s_instance; }
|
static scheduler & get() { return s_instance; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend addr_t isr_handler(addr_t, cpu_state);
|
||||||
|
|
||||||
|
/// Handle a timer tick
|
||||||
|
/// \arg rsp0 The stack pointer of the current interrupt handler
|
||||||
|
/// \returns The stack pointer to switch to
|
||||||
|
addr_t tick(addr_t rsp0);
|
||||||
|
|
||||||
lapic *m_apic;
|
lapic *m_apic;
|
||||||
kutil::vector<process> m_processes;
|
|
||||||
uint16_t m_current;
|
uint32_t m_next_pid;
|
||||||
uint16_t m_next_pid;
|
|
||||||
|
process_node *m_current;
|
||||||
|
process_slab m_process_allocator;
|
||||||
|
process_list m_runlists[num_priorities];
|
||||||
|
|
||||||
static scheduler s_instance;
|
static scheduler s_instance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
enum class syscall : uint64_t
|
enum class syscall : uint64_t
|
||||||
{
|
{
|
||||||
|
noop,
|
||||||
debug,
|
debug,
|
||||||
message,
|
message,
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user