mirror of
https://github.com/justinian/jsix.git
synced 2025-12-10 00:14:32 -08:00
[kernel] Create process kernel object
Re-implent the concept of processes as separate from threads, and as a kobject API object. Also improve scheduler::prune which was doing some unnecessary iterations.
This commit is contained in:
@@ -40,6 +40,7 @@ modules:
|
|||||||
- src/kernel/objects/handle.cpp
|
- src/kernel/objects/handle.cpp
|
||||||
- src/kernel/objects/kobject.cpp
|
- src/kernel/objects/kobject.cpp
|
||||||
- src/kernel/objects/thread.cpp
|
- src/kernel/objects/thread.cpp
|
||||||
|
- src/kernel/objects/process.cpp
|
||||||
- src/kernel/page_manager.cpp
|
- src/kernel/page_manager.cpp
|
||||||
- src/kernel/pci.cpp
|
- src/kernel/pci.cpp
|
||||||
- src/kernel/scheduler.cpp
|
- src/kernel/scheduler.cpp
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#define j6_signal_no_handles (1ull << 0)
|
#define j6_signal_no_handles (1ull << 0)
|
||||||
|
|
||||||
// Signals 16-47 are defined per-object-type
|
// Signals 16-47 are defined per-object-type
|
||||||
|
#define j6_signal_process_exit (1ull << 16)
|
||||||
|
|
||||||
// Signals 48-63 are user-defined signals
|
// Signals 48-63 are user-defined signals
|
||||||
#define j6_signal_user0 (1ull << 48)
|
#define j6_signal_user0 (1ull << 48)
|
||||||
|
|||||||
78
src/kernel/objects/process.cpp
Normal file
78
src/kernel/objects/process.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
#include "j6/signals.h"
|
||||||
|
#include "kutil/assert.h"
|
||||||
|
#include "objects/process.h"
|
||||||
|
#include "objects/thread.h"
|
||||||
|
#include "page_manager.h"
|
||||||
|
|
||||||
|
kutil::vector<process*> process::s_processes;
|
||||||
|
|
||||||
|
process::process(page_table *pml4) :
|
||||||
|
kobject(kobject::type::process),
|
||||||
|
m_pml4(pml4)
|
||||||
|
{
|
||||||
|
s_processes.append(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
process::~process()
|
||||||
|
{
|
||||||
|
s_processes.remove_swap(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
process::exit(unsigned code)
|
||||||
|
{
|
||||||
|
for (auto *thread : m_threads) {
|
||||||
|
thread->exit(code);
|
||||||
|
}
|
||||||
|
m_return_code = code;
|
||||||
|
page_manager::get()->delete_process_map(m_pml4);
|
||||||
|
assert_signal(j6_signal_process_exit);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
process::update()
|
||||||
|
{
|
||||||
|
kassert(m_threads.count() > 0, "process::update with zero threads!");
|
||||||
|
|
||||||
|
size_t i = 0;
|
||||||
|
uint32_t status = 0;
|
||||||
|
while (i < m_threads.count()) {
|
||||||
|
thread *th = m_threads[i];
|
||||||
|
if (th->has_state(thread::state::exited)) {
|
||||||
|
status = th->m_return_code;
|
||||||
|
m_threads.remove_swap_at(i);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_threads.count() == 0) {
|
||||||
|
// TODO: What really is the return code in this case?
|
||||||
|
exit(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
thread *
|
||||||
|
process::create_thread(uint8_t priority)
|
||||||
|
{
|
||||||
|
thread *th = new thread(*this, priority);
|
||||||
|
kassert(th, "Failed to create thread!");
|
||||||
|
m_threads.append(th);
|
||||||
|
return th;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
process::thread_exited(thread *th)
|
||||||
|
{
|
||||||
|
kassert(&th->m_parent == this, "Process got thread_exited for non-child!");
|
||||||
|
uint32_t status = th->m_return_code;
|
||||||
|
m_threads.remove_swap(th);
|
||||||
|
delete th;
|
||||||
|
|
||||||
|
if (m_threads.count() == 0) {
|
||||||
|
exit(status);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
46
src/kernel/objects/process.h
Normal file
46
src/kernel/objects/process.h
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#pragma once
|
||||||
|
/// \file process.h
|
||||||
|
/// Definition of process kobject types
|
||||||
|
|
||||||
|
#include "objects/kobject.h"
|
||||||
|
#include "page_table.h"
|
||||||
|
|
||||||
|
class process :
|
||||||
|
public kobject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Constructor.
|
||||||
|
/// \args pml4 Root of the process' page tables
|
||||||
|
process(page_table *pml4);
|
||||||
|
|
||||||
|
/// Destructor.
|
||||||
|
virtual ~process();
|
||||||
|
|
||||||
|
/// Terminate this process.
|
||||||
|
/// \arg code The return code to exit with.
|
||||||
|
void exit(unsigned code);
|
||||||
|
|
||||||
|
/// Update internal bookkeeping about threads.
|
||||||
|
void update();
|
||||||
|
|
||||||
|
/// Get the process' page table root
|
||||||
|
page_table * pml4() { return m_pml4; }
|
||||||
|
|
||||||
|
/// Create a new thread in this process
|
||||||
|
/// \args priority The new thread's scheduling priority
|
||||||
|
/// \returns The newly created thread object
|
||||||
|
thread * create_thread(uint8_t priorty);
|
||||||
|
|
||||||
|
/// Inform the process of an exited thread
|
||||||
|
/// \args th The thread which has exited
|
||||||
|
/// \returns True if this thread ending has ended the process
|
||||||
|
bool thread_exited(thread *th);
|
||||||
|
|
||||||
|
private:
|
||||||
|
uint32_t m_return_code;
|
||||||
|
|
||||||
|
page_table *m_pml4;
|
||||||
|
kutil::vector<thread*> m_threads;
|
||||||
|
|
||||||
|
static kutil::vector<process*> s_processes;
|
||||||
|
};
|
||||||
@@ -1,18 +1,20 @@
|
|||||||
#include "j6/signals.h"
|
#include "j6/signals.h"
|
||||||
#include "objects/thread.h"
|
#include "objects/thread.h"
|
||||||
|
#include "objects/process.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
static constexpr j6_signal_t thread_default_signals = 0;
|
static constexpr j6_signal_t thread_default_signals = 0;
|
||||||
|
|
||||||
thread::thread(page_table *pml4, priority_t pri) :
|
thread::thread(process &parent, uint8_t pri) :
|
||||||
kobject(kobject::type::thread, thread_default_signals),
|
kobject(kobject::type::thread, thread_default_signals),
|
||||||
|
m_parent(parent),
|
||||||
m_state(state::loading),
|
m_state(state::loading),
|
||||||
m_wait_type(wait_type::none),
|
m_wait_type(wait_type::none),
|
||||||
m_wait_data(0),
|
m_wait_data(0),
|
||||||
m_wait_obj(0)
|
m_wait_obj(0)
|
||||||
{
|
{
|
||||||
TCB *tcbp = tcb();
|
TCB *tcbp = tcb();
|
||||||
tcbp->pml4 = pml4;
|
tcbp->pml4 = parent.pml4();
|
||||||
tcbp->priority = pri;
|
tcbp->priority = pri;
|
||||||
tcbp->thread_data = this;
|
tcbp->thread_data = this;
|
||||||
set_state(state::ready);
|
set_state(state::ready);
|
||||||
@@ -74,8 +76,6 @@ thread::wake_on_result(kobject *obj, j6_status_t result)
|
|||||||
void
|
void
|
||||||
thread::exit(uint32_t code)
|
thread::exit(uint32_t code)
|
||||||
{
|
{
|
||||||
// TODO: check if the process containing this thread
|
|
||||||
// needs to exit and clean up.
|
|
||||||
m_return_code = code;
|
m_return_code = code;
|
||||||
set_state(state::exited);
|
set_state(state::exited);
|
||||||
clear_state(state::ready);
|
clear_state(state::ready);
|
||||||
|
|||||||
@@ -5,9 +5,8 @@
|
|||||||
#include "kutil/linked_list.h"
|
#include "kutil/linked_list.h"
|
||||||
#include "objects/kobject.h"
|
#include "objects/kobject.h"
|
||||||
|
|
||||||
using priority_t = uint8_t;
|
|
||||||
|
|
||||||
struct page_table;
|
struct page_table;
|
||||||
|
class process;
|
||||||
|
|
||||||
struct TCB
|
struct TCB
|
||||||
{
|
{
|
||||||
@@ -18,7 +17,7 @@ struct TCB
|
|||||||
uintptr_t rsp3;
|
uintptr_t rsp3;
|
||||||
page_table *pml4;
|
page_table *pml4;
|
||||||
|
|
||||||
priority_t priority;
|
uint8_t priority;
|
||||||
// note: 3 bytes padding
|
// note: 3 bytes padding
|
||||||
|
|
||||||
// TODO: move state into TCB?
|
// TODO: move state into TCB?
|
||||||
@@ -48,11 +47,6 @@ public:
|
|||||||
none = 0x00
|
none = 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Constructor.
|
|
||||||
/// \arg pml4 Root page table for the thread's owning process
|
|
||||||
/// \arg pri Initial priority level of this thread
|
|
||||||
thread(page_table *pml4, priority_t pri);
|
|
||||||
|
|
||||||
/// Get the `ready` state of the thread.
|
/// Get the `ready` state of the thread.
|
||||||
/// \returns True if the thread is ready to execute.
|
/// \returns True if the thread is ready to execute.
|
||||||
inline bool ready() const { return has_state(state::ready); }
|
inline bool ready() const { return has_state(state::ready); }
|
||||||
@@ -66,11 +60,11 @@ public:
|
|||||||
inline bool constant() const { return has_state(state::constant); }
|
inline bool constant() const { return has_state(state::constant); }
|
||||||
|
|
||||||
/// Get the thread priority.
|
/// Get the thread priority.
|
||||||
inline priority_t priority() const { return m_tcb.priority; }
|
inline uint8_t priority() const { return m_tcb.priority; }
|
||||||
|
|
||||||
/// Set the thread priority.
|
/// Set the thread priority.
|
||||||
/// \arg p The new thread priority
|
/// \arg p The new thread priority
|
||||||
inline void set_priority(priority_t p) { if (!constant()) m_tcb.priority = p; }
|
inline void set_priority(uint8_t p) { if (!constant()) m_tcb.priority = p; }
|
||||||
|
|
||||||
/// Block the thread, waiting on the given object's signals.
|
/// Block the thread, waiting on the given object's signals.
|
||||||
/// \arg obj Object to wait on
|
/// \arg obj Object to wait on
|
||||||
@@ -110,6 +104,7 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
inline tcb_node * tcb() { return &m_tcb; }
|
inline tcb_node * tcb() { return &m_tcb; }
|
||||||
|
inline process & parent() { return m_parent; }
|
||||||
|
|
||||||
/// Terminate this thread.
|
/// Terminate this thread.
|
||||||
/// \arg code The return code to exit with.
|
/// \arg code The return code to exit with.
|
||||||
@@ -119,6 +114,14 @@ private:
|
|||||||
thread() = delete;
|
thread() = delete;
|
||||||
thread(const thread &other) = delete;
|
thread(const thread &other) = delete;
|
||||||
thread(const thread &&other) = delete;
|
thread(const thread &&other) = delete;
|
||||||
|
friend class process;
|
||||||
|
|
||||||
|
/// Constructor.
|
||||||
|
/// \arg p The process which owns this thread
|
||||||
|
/// \arg pri Initial priority level of this thread
|
||||||
|
thread(process &parent, uint8_t pri);
|
||||||
|
|
||||||
|
process &m_parent;
|
||||||
|
|
||||||
tcb_node m_tcb;
|
tcb_node m_tcb;
|
||||||
|
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "kernel_memory.h"
|
#include "kernel_memory.h"
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
#include "msr.h"
|
#include "msr.h"
|
||||||
|
#include "objects/process.h"
|
||||||
#include "page_manager.h"
|
#include "page_manager.h"
|
||||||
#include "scheduler.h"
|
#include "scheduler.h"
|
||||||
|
|
||||||
@@ -83,10 +84,12 @@ add_fake_task_return(TCB *tcb, uintptr_t rip)
|
|||||||
scheduler::scheduler(lapic *apic) :
|
scheduler::scheduler(lapic *apic) :
|
||||||
m_apic(apic),
|
m_apic(apic),
|
||||||
m_next_pid(1),
|
m_next_pid(1),
|
||||||
m_clock(0)
|
m_clock(0),
|
||||||
|
m_last_promotion(0)
|
||||||
{
|
{
|
||||||
page_table *pml4 = page_manager::get_pml4();
|
page_table *pml4 = page_manager::get_pml4();
|
||||||
thread *idle = new thread(pml4, max_priority);
|
m_kernel_process = new process(pml4);
|
||||||
|
thread *idle = m_kernel_process->create_thread(max_priority);
|
||||||
|
|
||||||
auto *tcb = idle->tcb();
|
auto *tcb = idle->tcb();
|
||||||
|
|
||||||
@@ -170,7 +173,8 @@ load_process_image(const void *image_start, size_t bytes, TCB *tcb)
|
|||||||
thread *
|
thread *
|
||||||
scheduler::create_process(page_table *pml4)
|
scheduler::create_process(page_table *pml4)
|
||||||
{
|
{
|
||||||
thread *th = new thread(pml4, default_priority);
|
process *p = new process(pml4);
|
||||||
|
thread *th = p->create_thread(default_priority);
|
||||||
auto *tcb = th->tcb();
|
auto *tcb = th->tcb();
|
||||||
|
|
||||||
tcb->time_left = quantum(default_priority) + startup_bonus;
|
tcb->time_left = quantum(default_priority) + startup_bonus;
|
||||||
@@ -271,81 +275,68 @@ scheduler::start()
|
|||||||
|
|
||||||
void scheduler::prune(uint64_t now)
|
void scheduler::prune(uint64_t now)
|
||||||
{
|
{
|
||||||
// Find processes that aren't ready or aren't running and
|
// Find processes that are ready or have exited and
|
||||||
// move them to the appropriate lists.
|
// move them to the appropriate lists.
|
||||||
for (auto &pri_list : m_runlists) {
|
|
||||||
auto *tcb = pri_list.front();
|
|
||||||
while (tcb) {
|
|
||||||
thread *th = tcb->thread_data;
|
|
||||||
uint64_t age = now - tcb->last_ran;
|
|
||||||
uint8_t priority = tcb->priority;
|
|
||||||
|
|
||||||
bool ready = th->has_state(thread::state::ready);
|
|
||||||
bool constant = th->has_state(thread::state::constant);
|
|
||||||
|
|
||||||
bool stale = age > quantum(priority) * 2 &&
|
|
||||||
tcb->priority > promote_limit &&
|
|
||||||
!constant;
|
|
||||||
|
|
||||||
if (ready) {
|
|
||||||
auto *remove = tcb;
|
|
||||||
tcb = tcb->next();
|
|
||||||
|
|
||||||
if (stale) {
|
|
||||||
m_runlists[remove->priority].remove(remove);
|
|
||||||
remove->priority -= 1;
|
|
||||||
remove->time_left = quantum(remove->priority);
|
|
||||||
m_runlists[remove->priority].push_back(remove);
|
|
||||||
log::debug(logs::task, "Scheduler promoting thread %llx, priority %d",
|
|
||||||
th->koid(), remove->priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto *remove = tcb;
|
|
||||||
tcb = tcb->next();
|
|
||||||
pri_list.remove(remove);
|
|
||||||
|
|
||||||
bool exited = th->has_state(thread::state::exited);
|
|
||||||
|
|
||||||
if (exited) {
|
|
||||||
// TODO: Alert continaing process thread exitied,
|
|
||||||
// and exit process if it was the last thread.
|
|
||||||
/*
|
|
||||||
auto *parent = get_process_by_id(remove->ppid);
|
|
||||||
if (parent && parent->wake_on_child(remove)) {
|
|
||||||
m_blocked.remove(parent);
|
|
||||||
m_runlists[parent->priority].push_back(parent);
|
|
||||||
delete remove;
|
|
||||||
} else {
|
|
||||||
m_exited.push_back(remove);
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
m_exited.push_back(remove);
|
|
||||||
} else {
|
|
||||||
log::debug(logs::task, "Prune: moving blocked thread %llx", th->koid());
|
|
||||||
m_blocked.push_back(remove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find blocked processes that are ready (possibly after waking wating
|
|
||||||
// ones) and move them to the appropriate runlist.
|
|
||||||
auto *tcb = m_blocked.front();
|
auto *tcb = m_blocked.front();
|
||||||
while (tcb) {
|
while (tcb) {
|
||||||
thread *th = tcb->thread_data;
|
thread *th = tcb->thread_data;
|
||||||
|
uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
bool ready = th->has_state(thread::state::ready);
|
bool ready = th->has_state(thread::state::ready);
|
||||||
|
bool exited = th->has_state(thread::state::exited);
|
||||||
|
bool constant = th->has_state(thread::state::constant);
|
||||||
|
|
||||||
ready |= th->wake_on_time(now);
|
ready |= th->wake_on_time(now);
|
||||||
|
|
||||||
auto *remove = tcb;
|
auto *remove = tcb;
|
||||||
tcb = tcb->next();
|
tcb = tcb->next();
|
||||||
if (!ready) continue;
|
if (!exited && !ready)
|
||||||
|
continue;
|
||||||
|
|
||||||
m_blocked.remove(remove);
|
m_blocked.remove(remove);
|
||||||
m_runlists[remove->priority].push_front(remove);
|
|
||||||
|
if (exited) {
|
||||||
|
process &p = th->parent();
|
||||||
|
if(p.thread_exited(th))
|
||||||
|
delete &p;
|
||||||
|
} else {
|
||||||
|
log::debug(logs::task, "Prune: readying unblocked thread %llx", th->koid());
|
||||||
|
m_runlists[remove->priority].push_back(remove);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
scheduler::check_promotions(uint64_t now)
|
||||||
|
{
|
||||||
|
for (auto &pri_list : m_runlists) {
|
||||||
|
for (auto *tcb : pri_list) {
|
||||||
|
const thread *th = m_current->thread_data;
|
||||||
|
const bool constant = th->has_state(thread::state::constant);
|
||||||
|
if (constant)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const uint64_t age = now - tcb->last_ran;
|
||||||
|
const uint8_t priority = tcb->priority;
|
||||||
|
|
||||||
|
bool stale =
|
||||||
|
age > quantum(priority) * 2 &&
|
||||||
|
tcb->priority > promote_limit &&
|
||||||
|
!constant;
|
||||||
|
|
||||||
|
if (stale) {
|
||||||
|
// If the thread is stale, promote it
|
||||||
|
m_runlists[priority].remove(tcb);
|
||||||
|
tcb->priority -= 1;
|
||||||
|
tcb->time_left = quantum(tcb->priority);
|
||||||
|
m_runlists[tcb->priority].push_back(tcb);
|
||||||
|
log::debug(logs::task, "Scheduler promoting thread %llx, priority %d",
|
||||||
|
th->koid(), tcb->priority);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
m_last_promotion = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -381,6 +372,8 @@ scheduler::schedule()
|
|||||||
|
|
||||||
clock::get().update();
|
clock::get().update();
|
||||||
prune(++m_clock);
|
prune(++m_clock);
|
||||||
|
if (m_clock - m_last_promotion > promote_frequency)
|
||||||
|
check_promotions(m_clock);
|
||||||
|
|
||||||
priority = 0;
|
priority = 0;
|
||||||
while (m_runlists[priority].empty()) {
|
while (m_runlists[priority].empty()) {
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "objects/thread.h"
|
#include "objects/thread.h"
|
||||||
|
|
||||||
class lapic;
|
class lapic;
|
||||||
|
class process;
|
||||||
struct page_table;
|
struct page_table;
|
||||||
struct cpu_state;
|
struct cpu_state;
|
||||||
|
|
||||||
@@ -86,6 +87,8 @@ private:
|
|||||||
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
friend uintptr_t syscall_dispatch(uintptr_t, cpu_state &);
|
||||||
friend class process;
|
friend class process;
|
||||||
|
|
||||||
|
static constexpr uint64_t promote_frequency = 10;
|
||||||
|
|
||||||
/// Create a new process object. This process will have its pid
|
/// Create a new process object. This process will have its pid
|
||||||
/// set but nothing else.
|
/// set but nothing else.
|
||||||
/// \arg pml4 The root page table of the process
|
/// \arg pml4 The root page table of the process
|
||||||
@@ -93,19 +96,21 @@ private:
|
|||||||
thread * create_process(page_table *pml4);
|
thread * create_process(page_table *pml4);
|
||||||
|
|
||||||
void prune(uint64_t now);
|
void prune(uint64_t now);
|
||||||
|
void check_promotions(uint64_t now);
|
||||||
|
|
||||||
lapic *m_apic;
|
lapic *m_apic;
|
||||||
|
|
||||||
uint32_t m_next_pid;
|
uint32_t m_next_pid;
|
||||||
uint32_t m_tick_count;
|
uint32_t m_tick_count;
|
||||||
|
|
||||||
|
process *m_kernel_process;
|
||||||
tcb_node *m_current;
|
tcb_node *m_current;
|
||||||
tcb_list m_runlists[num_priorities];
|
tcb_list m_runlists[num_priorities];
|
||||||
tcb_list m_blocked;
|
tcb_list m_blocked;
|
||||||
tcb_list m_exited;
|
|
||||||
|
|
||||||
// TODO: lol a real clock
|
// TODO: lol a real clock
|
||||||
uint64_t m_clock = 0;
|
uint64_t m_clock = 0;
|
||||||
|
uint64_t m_last_promotion;
|
||||||
|
|
||||||
static scheduler s_instance;
|
static scheduler s_instance;
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user