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:
Justin C. Miller
2018-09-16 12:22:52 -07:00
parent f4e7eaeb40
commit 482b9f50fc
7 changed files with 259 additions and 48 deletions

View File

@@ -2,6 +2,7 @@ global _start
_start:
xor rbp, rbp ; Sentinel rbp
mov r11, 0 ; counter
mov rbx, 20 ; sleep timeout
.loop:
mov rax, 1 ; DEBUG syscall
@@ -10,13 +11,15 @@ _start:
int 0xee
inc r11
cmp r11, 3
cmp r11, 2
jle .loop
mov rax, 3 ; PAUSE syscall
mov rax, 4 ; SLEEP syscall
; syscall
int 0xee
add rbx, 20
mov r11, 0
jmp .loop

64
src/kernel/process.cpp Normal file
View 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
View 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;

View File

@@ -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;
}

View File

@@ -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;
};

View File

@@ -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 &regs)
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;

View File

@@ -11,6 +11,7 @@ enum class syscall : uint64_t
debug,
message,
pause,
sleep,
last_syscall
};