mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
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:
@@ -2,6 +2,7 @@ global _start
|
|||||||
_start:
|
_start:
|
||||||
xor rbp, rbp ; Sentinel rbp
|
xor rbp, rbp ; Sentinel rbp
|
||||||
mov r11, 0 ; counter
|
mov r11, 0 ; counter
|
||||||
|
mov rbx, 20 ; sleep timeout
|
||||||
|
|
||||||
.loop:
|
.loop:
|
||||||
mov rax, 1 ; DEBUG syscall
|
mov rax, 1 ; DEBUG syscall
|
||||||
@@ -10,13 +11,15 @@ _start:
|
|||||||
int 0xee
|
int 0xee
|
||||||
|
|
||||||
inc r11
|
inc r11
|
||||||
cmp r11, 3
|
cmp r11, 2
|
||||||
|
|
||||||
jle .loop
|
jle .loop
|
||||||
|
|
||||||
mov rax, 3 ; PAUSE syscall
|
mov rax, 4 ; SLEEP syscall
|
||||||
; syscall
|
; syscall
|
||||||
int 0xee
|
int 0xee
|
||||||
|
|
||||||
|
add rbx, 20
|
||||||
|
|
||||||
mov r11, 0
|
mov r11, 0
|
||||||
jmp .loop
|
jmp .loop
|
||||||
|
|||||||
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->rsp = 0; // This will get set when we switch away
|
||||||
idle->pml4 = page_manager::get_pml4();
|
idle->pml4 = page_manager::get_pml4();
|
||||||
idle->flags =
|
idle->flags =
|
||||||
|
process_flags::running |
|
||||||
process_flags::ready |
|
process_flags::ready |
|
||||||
process_flags::const_pri;
|
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->rsp = reinterpret_cast<addr_t>(loader_state);
|
||||||
proc->pml4 = pml4;
|
proc->pml4 = pml4;
|
||||||
proc->flags =
|
proc->flags =
|
||||||
|
process_flags::running |
|
||||||
process_flags::ready |
|
process_flags::ready |
|
||||||
process_flags::loading;
|
process_flags::loading;
|
||||||
|
|
||||||
@@ -175,9 +177,65 @@ scheduler::start()
|
|||||||
m_apic->enable_timer(isr::isrTimer, 128, quantum, false);
|
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
|
addr_t
|
||||||
scheduler::schedule(addr_t rsp0)
|
scheduler::schedule(addr_t rsp0)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// TODO: lol a real clock
|
||||||
|
static uint64_t now = 0;
|
||||||
|
prune(++now);
|
||||||
|
|
||||||
m_current->rsp = rsp0;
|
m_current->rsp = rsp0;
|
||||||
m_runlists[m_current->priority].remove(m_current);
|
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
|
// 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));
|
||||||
wrmsr(msr::ia32_kernel_gs_base, rsp0);
|
wrmsr(msr::ia32_kernel_gs_base, rsp0);
|
||||||
log::debug(logs::task, "Scheduler set kernel_gs_base to %016lx", rsp0);
|
|
||||||
|
|
||||||
// Swap page tables
|
// Swap page tables
|
||||||
page_table *pml4 = m_current->pml4;
|
page_table *pml4 = m_current->pml4;
|
||||||
@@ -220,3 +277,24 @@ scheduler::tick(addr_t rsp0)
|
|||||||
m_apic->reset_timer(quantum);
|
m_apic->reset_timer(quantum);
|
||||||
return rsp0;
|
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
|
#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/slab_allocator.h"
|
||||||
#include "kutil/vector.h"
|
#include "process.h"
|
||||||
|
|
||||||
class lapic;
|
class lapic;
|
||||||
struct page_table;
|
struct page_table;
|
||||||
@@ -13,42 +11,6 @@ struct cpu_state;
|
|||||||
|
|
||||||
extern "C" addr_t isr_handler(addr_t, 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
|
/// The task scheduler
|
||||||
class scheduler
|
class scheduler
|
||||||
@@ -80,10 +42,17 @@ public:
|
|||||||
/// \returns A pointer to the current process' process struct
|
/// \returns A pointer to the current process' process struct
|
||||||
inline process * current() { return m_current; }
|
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
|
/// 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:
|
||||||
|
friend addr_t syscall_dispatch(addr_t, const cpu_state &);
|
||||||
friend addr_t isr_handler(addr_t, cpu_state);
|
friend addr_t isr_handler(addr_t, cpu_state);
|
||||||
|
|
||||||
/// Handle a timer tick
|
/// Handle a timer tick
|
||||||
@@ -91,15 +60,19 @@ public:
|
|||||||
/// \returns The stack pointer to switch to
|
/// \returns The stack pointer to switch to
|
||||||
addr_t tick(addr_t rsp0);
|
addr_t tick(addr_t rsp0);
|
||||||
|
|
||||||
private:
|
void prune(uint64_t now);
|
||||||
|
|
||||||
lapic *m_apic;
|
lapic *m_apic;
|
||||||
|
|
||||||
uint32_t m_next_pid;
|
uint32_t m_next_pid;
|
||||||
|
|
||||||
process_node *m_current;
|
using process_slab = kutil::slab_allocator<process>;
|
||||||
process_slab m_process_allocator;
|
process_slab m_process_allocator;
|
||||||
|
|
||||||
|
process_node *m_current;
|
||||||
process_list m_runlists[num_priorities];
|
process_list m_runlists[num_priorities];
|
||||||
process_list m_blocked;
|
process_list m_blocked;
|
||||||
|
process_list m_exited;
|
||||||
|
|
||||||
static scheduler s_instance;
|
static scheduler s_instance;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include "cpu.h"
|
#include "cpu.h"
|
||||||
#include "debug.h"
|
#include "debug.h"
|
||||||
#include "msr.h"
|
#include "msr.h"
|
||||||
|
#include "process.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
#include "syscall.h"
|
#include "syscall.h"
|
||||||
|
|
||||||
@@ -62,12 +63,22 @@ syscall_dispatch(addr_t return_rsp, const cpu_state ®s)
|
|||||||
|
|
||||||
auto &s = scheduler::get();
|
auto &s = scheduler::get();
|
||||||
auto *p = s.current();
|
auto *p = s.current();
|
||||||
p->flags -= process_flags::ready;
|
p->wait_on_signal(-1ull);
|
||||||
//log::debug(logs::task, "Pausing process %d, flags: %08x", p->pid, p->flags);
|
|
||||||
cons->printf("\nReceived PAUSE syscall\n");
|
cons->printf("\nReceived PAUSE syscall\n");
|
||||||
return_rsp = s.tick(return_rsp);
|
return_rsp = s.tick(return_rsp);
|
||||||
//log::debug(logs::task, "Switching to stack %016lx", return_rsp);
|
cons->set_color();
|
||||||
cons->printf("\nDONE WITH PAUSE syscall\n");
|
}
|
||||||
|
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();
|
cons->set_color();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ enum class syscall : uint64_t
|
|||||||
debug,
|
debug,
|
||||||
message,
|
message,
|
||||||
pause,
|
pause,
|
||||||
|
sleep,
|
||||||
|
|
||||||
last_syscall
|
last_syscall
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user