Initial process waiting/waking
Processes can now wait on signals/children/time. There is no clock currently so "time" is just a monotonically increating tick count. Added a SLEEP syscall to test this waiting/waking.
This commit is contained in:
64
src/kernel/process.cpp
Normal file
64
src/kernel/process.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include "process.h"
|
||||
|
||||
|
||||
void
|
||||
process::wait_on_signal(uint64_t sigmask)
|
||||
{
|
||||
waiting = process_wait::signal;
|
||||
waiting_info = sigmask;
|
||||
flags -= process_flags::ready;
|
||||
}
|
||||
|
||||
void
|
||||
process::wait_on_child(uint32_t pid)
|
||||
{
|
||||
waiting = process_wait::child;
|
||||
waiting_info = pid;
|
||||
flags -= process_flags::ready;
|
||||
}
|
||||
|
||||
void
|
||||
process::wait_on_time(uint64_t time)
|
||||
{
|
||||
waiting = process_wait::time;
|
||||
waiting_info = time;
|
||||
flags -= process_flags::ready;
|
||||
}
|
||||
|
||||
bool
|
||||
process::wake_on_signal(int signal)
|
||||
{
|
||||
if (waiting != process_wait::signal ||
|
||||
(waiting_info & (1 << signal)) == 0)
|
||||
return false;
|
||||
|
||||
waiting = process_wait::none;
|
||||
flags += process_flags::ready;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
process::wake_on_child(process *child)
|
||||
{
|
||||
if (waiting != process_wait::child ||
|
||||
(waiting_info && waiting_info != child->pid))
|
||||
return false;
|
||||
|
||||
waiting = process_wait::none;
|
||||
flags += process_flags::ready;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
process::wake_on_time(uint64_t now)
|
||||
{
|
||||
if (waiting != process_wait::time ||
|
||||
waiting_info > now)
|
||||
return false;
|
||||
|
||||
waiting = process_wait::none;
|
||||
flags += process_flags::ready;
|
||||
return true;
|
||||
}
|
||||
|
||||
81
src/kernel/process.h
Normal file
81
src/kernel/process.h
Normal file
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
/// \file process.h
|
||||
/// The processes and related definitions
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/linked_list.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "page_manager.h"
|
||||
|
||||
|
||||
enum class process_flags : uint32_t
|
||||
{
|
||||
running = 0x00000001,
|
||||
ready = 0x00000002,
|
||||
loading = 0x00000004,
|
||||
|
||||
const_pri = 0x80000000,
|
||||
|
||||
none = 0x00000000
|
||||
};
|
||||
IS_BITFIELD(process_flags);
|
||||
|
||||
enum class process_wait : uint8_t
|
||||
{
|
||||
none,
|
||||
signal,
|
||||
child,
|
||||
time
|
||||
};
|
||||
|
||||
/// A process
|
||||
struct process
|
||||
{
|
||||
uint32_t pid;
|
||||
uint32_t ppid;
|
||||
|
||||
process_flags flags;
|
||||
|
||||
uint16_t reserved0;
|
||||
|
||||
uint8_t priority;
|
||||
|
||||
process_wait waiting;
|
||||
uint64_t waiting_info;
|
||||
|
||||
uint32_t return_code;
|
||||
|
||||
uint32_t reserved1;
|
||||
|
||||
addr_t rsp;
|
||||
page_table *pml4;
|
||||
|
||||
/// Unready this process until it gets a signal
|
||||
/// \arg sigmask A bitfield of signals to wake on
|
||||
void wait_on_signal(uint64_t sigmask);
|
||||
|
||||
/// Unready this process until a child exits
|
||||
/// \arg pid PID of the child to wait for, or 0 for any
|
||||
void wait_on_child(uint32_t pid);
|
||||
|
||||
/// Unready this process until after the given time
|
||||
/// \arg time The time after which to wake
|
||||
void wait_on_time(uint64_t time);
|
||||
|
||||
/// If this process is waiting on the given signal, wake it
|
||||
/// \argument signal The signal sent to the process
|
||||
/// \returns True if this wake was handled
|
||||
bool wake_on_signal(int signal);
|
||||
|
||||
/// If this process is waiting on the given child, wake it
|
||||
/// \argument child The process that exited
|
||||
/// \returns True if this wake was handled
|
||||
bool wake_on_child(process *child);
|
||||
|
||||
/// If this process is waiting on a time, check it
|
||||
/// \argument now The current time
|
||||
/// \returns True if this wake was handled
|
||||
bool wake_on_time(uint64_t now);
|
||||
};
|
||||
|
||||
using process_list = kutil::linked_list<process>;
|
||||
using process_node = process_list::item_type;
|
||||
@@ -40,6 +40,7 @@ scheduler::scheduler(lapic *apic) :
|
||||
idle->rsp = 0; // This will get set when we switch away
|
||||
idle->pml4 = page_manager::get_pml4();
|
||||
idle->flags =
|
||||
process_flags::running |
|
||||
process_flags::ready |
|
||||
process_flags::const_pri;
|
||||
|
||||
@@ -156,6 +157,7 @@ scheduler::create_process(const char *name, const void *data, size_t size)
|
||||
proc->rsp = reinterpret_cast<addr_t>(loader_state);
|
||||
proc->pml4 = pml4;
|
||||
proc->flags =
|
||||
process_flags::running |
|
||||
process_flags::ready |
|
||||
process_flags::loading;
|
||||
|
||||
@@ -175,9 +177,65 @@ scheduler::start()
|
||||
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
|
||||
}
|
||||
|
||||
void scheduler::prune(uint64_t now)
|
||||
{
|
||||
// Find processes that aren't ready or aren't running and
|
||||
// move them to the appropriate lists.
|
||||
for (auto &pri_list : m_runlists) {
|
||||
auto *proc = pri_list.front();
|
||||
while (proc) {
|
||||
bool running = proc->flags && process_flags::running;
|
||||
bool ready = proc->flags && process_flags::ready;
|
||||
if (running && ready) {
|
||||
proc = proc->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *remove = proc;
|
||||
proc = proc->next();
|
||||
pri_list.remove(remove);
|
||||
|
||||
if (!(remove->flags && process_flags::running)) {
|
||||
auto *parent = get_process_by_id(remove->ppid);
|
||||
if (parent && parent->wake_on_child(remove)) {
|
||||
m_blocked.remove(parent);
|
||||
m_runlists[parent->priority].push_front(parent);
|
||||
m_process_allocator.push(remove);
|
||||
} else {
|
||||
m_exited.push_back(remove);
|
||||
}
|
||||
} else {
|
||||
m_blocked.push_back(remove);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Find blocked processes that are ready (possibly after waking wating
|
||||
// ones) and move them to the appropriate runlist.
|
||||
auto *proc = m_blocked.front();
|
||||
while (proc) {
|
||||
bool ready = proc->flags && process_flags::ready;
|
||||
ready |= proc->wake_on_time(now);
|
||||
if (!ready) {
|
||||
proc = proc->next();
|
||||
continue;
|
||||
}
|
||||
|
||||
auto *remove = proc;
|
||||
proc = proc->next();
|
||||
m_blocked.remove(remove);
|
||||
m_runlists[remove->priority].push_front(remove);
|
||||
}
|
||||
}
|
||||
|
||||
addr_t
|
||||
scheduler::schedule(addr_t rsp0)
|
||||
{
|
||||
|
||||
// TODO: lol a real clock
|
||||
static uint64_t now = 0;
|
||||
prune(++now);
|
||||
|
||||
m_current->rsp = rsp0;
|
||||
m_runlists[m_current->priority].remove(m_current);
|
||||
|
||||
@@ -198,7 +256,6 @@ scheduler::schedule(addr_t rsp0)
|
||||
// Set rsp0 to after the end of the about-to-be-popped cpu state
|
||||
tss_set_stack(0, rsp0 + sizeof(cpu_state));
|
||||
wrmsr(msr::ia32_kernel_gs_base, rsp0);
|
||||
log::debug(logs::task, "Scheduler set kernel_gs_base to %016lx", rsp0);
|
||||
|
||||
// Swap page tables
|
||||
page_table *pml4 = m_current->pml4;
|
||||
@@ -220,3 +277,24 @@ scheduler::tick(addr_t rsp0)
|
||||
m_apic->reset_timer(quantum);
|
||||
return rsp0;
|
||||
}
|
||||
|
||||
process_node *
|
||||
scheduler::get_process_by_id(uint32_t pid)
|
||||
{
|
||||
// TODO: this needs to be a hash map
|
||||
for (auto *proc : m_blocked) {
|
||||
if (proc->pid == pid) return proc;
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_priorities; ++i) {
|
||||
for (auto *proc : m_runlists[i]) {
|
||||
if (proc->pid == pid) return proc;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto *proc : m_exited) {
|
||||
if (proc->pid == pid) return proc;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,9 @@
|
||||
#pragma once
|
||||
/// \file scheduler.h
|
||||
/// The task scheduler and related definitions
|
||||
#include "kutil/enum_bitfields.h"
|
||||
#include "kutil/linked_list.h"
|
||||
#include "kutil/memory.h"
|
||||
#include "kutil/slab_allocator.h"
|
||||
#include "kutil/vector.h"
|
||||
#include "process.h"
|
||||
|
||||
class lapic;
|
||||
struct page_table;
|
||||
@@ -13,42 +11,6 @@ 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);
|
||||
|
||||
/// A process
|
||||
struct process
|
||||
{
|
||||
uint32_t pid;
|
||||
uint32_t ppid;
|
||||
|
||||
uint8_t priority;
|
||||
|
||||
uint8_t reserved0;
|
||||
uint16_t reserved;
|
||||
|
||||
process_flags flags;
|
||||
|
||||
addr_t rsp;
|
||||
page_table *pml4;
|
||||
|
||||
/// Helper to check if this process is ready
|
||||
/// \returns true if the process has the ready flag
|
||||
inline bool ready() { return bitfield_has(flags, process_flags::ready); }
|
||||
};
|
||||
|
||||
using process_list = kutil::linked_list<process>;
|
||||
using process_node = process_list::item_type;
|
||||
using process_slab = kutil::slab_allocator<process>;
|
||||
|
||||
/// The task scheduler
|
||||
class scheduler
|
||||
@@ -80,10 +42,17 @@ public:
|
||||
/// \returns A pointer to the current process' process struct
|
||||
inline process * current() { return m_current; }
|
||||
|
||||
/// Look up a process by its PID
|
||||
/// \arg pid The requested PID
|
||||
/// \returns The process matching that PID, or nullptr
|
||||
process_node * get_process_by_id(uint32_t pid);
|
||||
|
||||
/// Get a reference to the system scheduler
|
||||
/// \returns A reference to the global system scheduler
|
||||
static scheduler & get() { return s_instance; }
|
||||
|
||||
private:
|
||||
friend addr_t syscall_dispatch(addr_t, const cpu_state &);
|
||||
friend addr_t isr_handler(addr_t, cpu_state);
|
||||
|
||||
/// Handle a timer tick
|
||||
@@ -91,15 +60,19 @@ public:
|
||||
/// \returns The stack pointer to switch to
|
||||
addr_t tick(addr_t rsp0);
|
||||
|
||||
private:
|
||||
void prune(uint64_t now);
|
||||
|
||||
lapic *m_apic;
|
||||
|
||||
uint32_t m_next_pid;
|
||||
|
||||
process_node *m_current;
|
||||
using process_slab = kutil::slab_allocator<process>;
|
||||
process_slab m_process_allocator;
|
||||
|
||||
process_node *m_current;
|
||||
process_list m_runlists[num_priorities];
|
||||
process_list m_blocked;
|
||||
process_list m_exited;
|
||||
|
||||
static scheduler s_instance;
|
||||
};
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#include "cpu.h"
|
||||
#include "debug.h"
|
||||
#include "msr.h"
|
||||
#include "process.h"
|
||||
#include "scheduler.h"
|
||||
#include "syscall.h"
|
||||
|
||||
@@ -62,12 +63,22 @@ syscall_dispatch(addr_t return_rsp, const cpu_state ®s)
|
||||
|
||||
auto &s = scheduler::get();
|
||||
auto *p = s.current();
|
||||
p->flags -= process_flags::ready;
|
||||
//log::debug(logs::task, "Pausing process %d, flags: %08x", p->pid, p->flags);
|
||||
p->wait_on_signal(-1ull);
|
||||
cons->printf("\nReceived PAUSE syscall\n");
|
||||
return_rsp = s.tick(return_rsp);
|
||||
//log::debug(logs::task, "Switching to stack %016lx", return_rsp);
|
||||
cons->printf("\nDONE WITH PAUSE syscall\n");
|
||||
cons->set_color();
|
||||
}
|
||||
break;
|
||||
|
||||
case syscall::sleep:
|
||||
{
|
||||
cons->set_color(11);
|
||||
|
||||
auto &s = scheduler::get();
|
||||
auto *p = s.current();
|
||||
p->wait_on_time(regs.rbx);
|
||||
cons->printf("\nReceived SLEEP syscall\n");
|
||||
return_rsp = s.tick(return_rsp);
|
||||
cons->set_color();
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -11,6 +11,7 @@ enum class syscall : uint64_t
|
||||
debug,
|
||||
message,
|
||||
pause,
|
||||
sleep,
|
||||
|
||||
last_syscall
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user