Bladeren bron

feat(syscall): impl. exit and wait syscall

greatbridf 2 jaren geleden
bovenliggende
commit
b2586e331c

+ 1 - 0
CMakeLists.txt

@@ -72,6 +72,7 @@ set(KERNEL_MAIN_SOURCES src/fs/fat.cpp
                         include/asm/sys.h
                         include/fs/fat.hpp
                         include/kernel/event/event.h
+                        include/kernel/event/evtqueue.hpp
                         include/kernel/errno.h
                         include/kernel/tty.h
                         include/kernel/interrupt.h

+ 1 - 0
include/kernel/errno.h

@@ -21,3 +21,4 @@ extern uint32_t* _get_errno(void);
 #define EISDIR (1 << 4)
 #define ENOTDIR (1 << 5)
 #define ENOTFOUND (1 << 6)
+#define ECHILD (1 << 7)

+ 45 - 0
include/kernel/event/evtqueue.hpp

@@ -0,0 +1,45 @@
+#pragma once
+
+#include <types/list.hpp>
+#include <types/lock.hpp>
+
+// declaration in kernel/process.hpp
+struct thread;
+
+namespace kernel {
+
+struct evt {
+    thread* emitter;
+    void* data1;
+    void* data2;
+    void* data3;
+};
+
+class evtqueue {
+public:
+    // TODO: use small object allocator
+    using evt_list_type = types::list<evt>;
+    using subscriber_list_type = types::list<thread*>;
+
+private:
+    types::mutex m_mtx;
+    evt_list_type m_evts;
+    subscriber_list_type m_subscribers;
+
+public:
+    evtqueue(void) = default;
+    evtqueue(const evtqueue&) = delete;
+    evtqueue(evtqueue&&);
+
+    void push(evt&& event);
+    evt&& front();
+    const evt* peek(void) const;
+
+    bool empty(void) const;
+    void notify(void);
+
+    void subscribe(thread* thd);
+    void unsubscribe(thread* thd);
+};
+
+} // namespace kernel

+ 6 - 1
include/kernel/process.hpp

@@ -1,5 +1,6 @@
 #pragma once
 
+#include <kernel/event/evtqueue.hpp>
 #include <kernel/interrupt.h>
 #include <kernel/mm.hpp>
 #include <kernel/task.h>
@@ -15,6 +16,7 @@ struct thread;
 
 struct process_attr {
     uint16_t system : 1;
+    uint16_t zombie : 1 = 0;
 };
 
 struct thread_attr {
@@ -73,7 +75,6 @@ public:
     {
         if (kstack)
             free_n_raw_pages(to_page(kstack), 2);
-        memset(this, 0x00, sizeof(thread));
     }
 };
 
@@ -81,6 +82,7 @@ class process {
 public:
     mutable kernel::mm_list mms;
     types::list<thread> thds;
+    kernel::evtqueue wait_lst;
     process_attr attr;
     pid_t pid;
     pid_t ppid;
@@ -94,6 +96,8 @@ public:
     explicit process(void);
     explicit process(void (*func_in_kernel_space)(void), pid_t ppid);
 
+    ~process();
+
 private:
     static inline pid_t max_pid;
 
@@ -118,6 +122,7 @@ extern "C" void NORETURN init_scheduler();
 void schedule(void);
 
 pid_t add_to_process_list(process&& proc);
+void remove_from_process_list(pid_t pid);
 
 void add_to_ready_list(thread* thd);
 void remove_from_ready_list(thread* thd);

+ 58 - 0
src/kernel/event/event.cpp

@@ -1,10 +1,15 @@
 #include <asm/port_io.h>
 #include <kernel/event/event.h>
+#include <kernel/event/evtqueue.hpp>
 #include <kernel/input/input_event.h>
+#include <kernel/process.hpp>
 #include <kernel/stdio.h>
 #include <kernel/tty.h>
 #include <types/allocator.hpp>
+#include <types/assert.h>
+#include <types/cplusplus.hpp>
 #include <types/list.hpp>
+#include <types/lock.hpp>
 
 static ::types::list<::input_event>* _input_event_queue;
 
@@ -40,3 +45,56 @@ void dispatch_event(void)
         }
     }
 }
+
+kernel::evtqueue::evtqueue(evtqueue&& q)
+    : m_evts { types::move(q.m_evts) }
+    , m_subscribers { types::move(q.m_subscribers) }
+{
+}
+
+void kernel::evtqueue::push(kernel::evt&& event)
+{
+    types::lock_guard lck(m_mtx);
+    m_evts.push_back(types::move(event));
+    this->notify();
+}
+
+kernel::evt&& kernel::evtqueue::front()
+{
+    assert(!this->empty());
+    types::lock_guard lck(m_mtx);
+
+    auto iter = m_evts.begin();
+    evt e = types::move(*iter);
+    m_evts.erase(iter);
+    return types::move(e);
+}
+
+const kernel::evt* kernel::evtqueue::peek(void) const
+{
+    return m_evts.begin().ptr();
+}
+
+bool kernel::evtqueue::empty(void) const
+{
+    return m_evts.empty();
+}
+
+void kernel::evtqueue::notify(void)
+{
+    for (auto* sub : m_subscribers) {
+        sub->attr.ready = 1;
+        sub->attr.wait = 0;
+        add_to_ready_list(sub);
+    }
+}
+
+void kernel::evtqueue::subscribe(thread* thd)
+{
+    m_subscribers.push_back(thd);
+}
+
+void kernel::evtqueue::unsubscribe(thread* thd)
+{
+    m_subscribers.erase(m_subscribers.find(thd));
+}

+ 22 - 2
src/kernel/process.cpp

@@ -16,6 +16,7 @@
 #include <types/hash_map.hpp>
 #include <types/list.hpp>
 #include <types/lock.hpp>
+#include <types/size.h>
 #include <types/status.h>
 #include <types/stdint.h>
 #include <types/types.h>
@@ -50,6 +51,7 @@ struct no_irq_guard {
 process::process(process&& val)
     : mms(types::move(val.mms))
     , thds(types::move(val.thds))
+    , wait_lst(types::move(val.wait_lst))
     , pid(val.pid)
     , ppid(val.ppid)
 {
@@ -83,7 +85,6 @@ process::process(const process& val, const thread& main_thd)
 
 process::process(void)
     : mms(*kernel_mms)
-    , thds {}
     , attr { .system = 1 }
     , pid { process::alloc_pid() }
     , ppid { 1 }
@@ -95,7 +96,6 @@ process::process(void)
 
 process::process(void (*func)(void), pid_t _ppid)
     : mms(*kernel_mms)
-    , thds {}
     , attr { .system = 1 }
     , pid { process::alloc_pid() }
     , ppid { _ppid }
@@ -120,6 +120,12 @@ process::process(void (*func)(void), pid_t _ppid)
     push_stack(esp, 0x200);
 }
 
+process::~process()
+{
+    for (auto iter = thds.begin(); iter != thds.end(); ++iter)
+        remove_from_ready_list(iter.ptr());
+}
+
 inline void NORETURN _noreturn_crash(void)
 {
     for (;;)
@@ -245,6 +251,20 @@ pid_t add_to_process_list(process&& proc)
     return iter->pid;
 }
 
+void remove_from_process_list(pid_t pid)
+{
+    auto proc_iter = idx_processes->find(pid);
+    auto ppid = proc_iter->value->ppid;
+
+    auto& parent_children = idx_child_processes->find(ppid)->value;
+
+    auto i = parent_children.find(pid);
+    parent_children.erase(i);
+
+    processes->erase(proc_iter->value);
+    idx_processes->remove(proc_iter);
+}
+
 void add_to_ready_list(thread* thd)
 {
     ready_thds->push_back(thd);

+ 60 - 5
src/kernel/syscall.cpp

@@ -1,5 +1,6 @@
 #include <asm/port_io.h>
 #include <asm/sys.h>
+#include <kernel/errno.h>
 #include <kernel/interrupt.h>
 #include <kernel/mem.h>
 #include <kernel/mm.hpp>
@@ -122,6 +123,8 @@ void _syscall_exec(interrupt_stack* data)
 void _syscall_exit(interrupt_stack* data)
 {
     uint32_t exit_code = data->s_regs.edi;
+    pid_t pid = current_process->pid;
+    pid_t ppid = current_process->ppid;
 
     // TODO: terminating a thread only
     if (current_thread->owner->thds.size() != 1) {
@@ -140,20 +143,72 @@ void _syscall_exit(interrupt_stack* data)
     current_process->mms.clear_user();
 
     // make child processes orphans (children of init)
-    auto children = idx_child_processes->find(current_process->pid);
+    auto children = idx_child_processes->find(pid);
     if (children) {
-        for (auto iter = children->value.begin(); iter != children->value.end(); ++iter)
+        auto init_children = idx_child_processes->find(1);
+        for (auto iter = children->value.begin(); iter != children->value.end(); ++iter) {
+            init_children->value.push_back(*iter);
             findproc(*iter)->ppid = 1;
+        }
         idx_child_processes->remove(children);
     }
 
-    // TODO: notify parent process and init
+    current_process->attr.zombie = 1;
+
+    // notify parent process and init
+    auto* proc = findproc(pid);
+    auto* parent = findproc(ppid);
+    auto* init = findproc(1);
+    while (!proc->wait_lst.empty()) {
+        init->wait_lst.push(proc->wait_lst.front());
+    }
+    parent->wait_lst.push({ current_thread, (void*)pid, (void*)exit_code, nullptr });
 
     // switch to new process and continue
     schedule();
 
     // we should not return to here
-    MAKE_BREAK_POINT();
+    assert(false);
+}
+
+// @param address of exit code: int*
+// @return pid of the exited process
+void _syscall_wait(interrupt_stack* data)
+{
+    auto* arg1 = reinterpret_cast<int*>(data->s_regs.edi);
+
+    if (arg1 < (int*)0x40000000) {
+        SYSCALL_SET_RETURN_VAL(-1, EINVAL);
+        return;
+    }
+
+    auto& waitlst = current_process->wait_lst;
+    if (waitlst.empty() && !idx_child_processes->find(current_process->pid)) {
+        SYSCALL_SET_RETURN_VAL(-1, ECHILD);
+        return;
+    }
+
+    while (waitlst.empty()) {
+        current_thread->attr.ready = 0;
+        current_thread->attr.wait = 1;
+        waitlst.subscribe(current_thread);
+
+        schedule();
+
+        if (!waitlst.empty()) {
+            waitlst.unsubscribe(current_thread);
+            break;
+        }
+    }
+
+    auto evt = waitlst.front();
+
+    pid_t pid = (pid_t)evt.data1;
+    // TODO: copy_to_user check privilege
+    *arg1 = (int)evt.data2;
+
+    remove_from_process_list(pid);
+    SYSCALL_SET_RETURN_VAL(pid, 0);
 }
 
 void init_syscall(void)
@@ -164,6 +219,6 @@ void init_syscall(void)
     syscall_handlers[3] = _syscall_crash;
     syscall_handlers[4] = _syscall_exec;
     syscall_handlers[5] = _syscall_exit;
-    syscall_handlers[6] = _syscall_not_impl;
+    syscall_handlers[6] = _syscall_wait;
     syscall_handlers[7] = _syscall_not_impl;
 }