[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:
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 "objects/thread.h"
|
||||
#include "objects/process.h"
|
||||
#include "scheduler.h"
|
||||
|
||||
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),
|
||||
m_parent(parent),
|
||||
m_state(state::loading),
|
||||
m_wait_type(wait_type::none),
|
||||
m_wait_data(0),
|
||||
m_wait_obj(0)
|
||||
{
|
||||
TCB *tcbp = tcb();
|
||||
tcbp->pml4 = pml4;
|
||||
tcbp->pml4 = parent.pml4();
|
||||
tcbp->priority = pri;
|
||||
tcbp->thread_data = this;
|
||||
set_state(state::ready);
|
||||
@@ -74,8 +76,6 @@ thread::wake_on_result(kobject *obj, j6_status_t result)
|
||||
void
|
||||
thread::exit(uint32_t code)
|
||||
{
|
||||
// TODO: check if the process containing this thread
|
||||
// needs to exit and clean up.
|
||||
m_return_code = code;
|
||||
set_state(state::exited);
|
||||
clear_state(state::ready);
|
||||
|
||||
@@ -5,9 +5,8 @@
|
||||
#include "kutil/linked_list.h"
|
||||
#include "objects/kobject.h"
|
||||
|
||||
using priority_t = uint8_t;
|
||||
|
||||
struct page_table;
|
||||
class process;
|
||||
|
||||
struct TCB
|
||||
{
|
||||
@@ -18,7 +17,7 @@ struct TCB
|
||||
uintptr_t rsp3;
|
||||
page_table *pml4;
|
||||
|
||||
priority_t priority;
|
||||
uint8_t priority;
|
||||
// note: 3 bytes padding
|
||||
|
||||
// TODO: move state into TCB?
|
||||
@@ -48,11 +47,6 @@ public:
|
||||
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.
|
||||
/// \returns True if the thread is ready to execute.
|
||||
inline bool ready() const { return has_state(state::ready); }
|
||||
@@ -66,11 +60,11 @@ public:
|
||||
inline bool constant() const { return has_state(state::constant); }
|
||||
|
||||
/// 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.
|
||||
/// \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.
|
||||
/// \arg obj Object to wait on
|
||||
@@ -110,6 +104,7 @@ public:
|
||||
}
|
||||
|
||||
inline tcb_node * tcb() { return &m_tcb; }
|
||||
inline process & parent() { return m_parent; }
|
||||
|
||||
/// Terminate this thread.
|
||||
/// \arg code The return code to exit with.
|
||||
@@ -119,6 +114,14 @@ private:
|
||||
thread() = 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;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user