diff --git a/src/drivers/nulldrv/main.s b/src/drivers/nulldrv/main.s index ddc6974..a0175bd 100644 --- a/src/drivers/nulldrv/main.s +++ b/src/drivers/nulldrv/main.s @@ -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 diff --git a/src/kernel/process.cpp b/src/kernel/process.cpp new file mode 100644 index 0000000..d8b8ac9 --- /dev/null +++ b/src/kernel/process.cpp @@ -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; +} + diff --git a/src/kernel/process.h b/src/kernel/process.h new file mode 100644 index 0000000..4fd1cbd --- /dev/null +++ b/src/kernel/process.h @@ -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; +using process_node = process_list::item_type; diff --git a/src/kernel/scheduler.cpp b/src/kernel/scheduler.cpp index 159cc2c..a818ffe 100644 --- a/src/kernel/scheduler.cpp +++ b/src/kernel/scheduler.cpp @@ -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(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; +} diff --git a/src/kernel/scheduler.h b/src/kernel/scheduler.h index 99b08c5..b686754 100644 --- a/src/kernel/scheduler.h +++ b/src/kernel/scheduler.h @@ -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; -using process_node = process_list::item_type; -using process_slab = kutil::slab_allocator; /// 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_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; }; diff --git a/src/kernel/syscall.cpp b/src/kernel/syscall.cpp index ac780cf..c839fc0 100644 --- a/src/kernel/syscall.cpp +++ b/src/kernel/syscall.cpp @@ -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; diff --git a/src/kernel/syscall.h b/src/kernel/syscall.h index bbb1570..ebbf440 100644 --- a/src/kernel/syscall.h +++ b/src/kernel/syscall.h @@ -11,6 +11,7 @@ enum class syscall : uint64_t debug, message, pause, + sleep, last_syscall };