From 180cdd79930b0677ff7a6902edd9579686de1d04 Mon Sep 17 00:00:00 2001 From: Stepan Usatiuk Date: Sun, 14 Apr 2024 13:08:08 +0200 Subject: [PATCH] Waitpid seems to be working --- .../newlib/libc/sys/ficus/sys/syscalls.h | 2 +- .../newlib/libc/sys/ficus/syscalls.c | 6 + src/arch/x86/syscalls.cpp | 24 ++-- src/arch/x86/task.cpp | 127 ++++++++++++++++-- src/arch/x86/task.hpp | 29 +++- src/test/hello2.c | 41 ++++++ src/test/init.c | 5 + 7 files changed, 206 insertions(+), 28 deletions(-) diff --git a/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/sys/syscalls.h b/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/sys/syscalls.h index 40cd7362f..6f8b954cb 100644 --- a/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/sys/syscalls.h +++ b/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/sys/syscalls.h @@ -30,9 +30,9 @@ extern "C" { #define SYSCALL_EXECVE_ID 50 #define SYSCALL_FORK_ID 51 +#define SYSCALL_WAITPID_ID 52 #define SYSCALL_SBRK_ID 100 - #define SYSCALL_PRINT_MEM 1000 #define SYSCALL_PRINT_TASKS 1001 diff --git a/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/syscalls.c b/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/syscalls.c index 653055e9a..a6aadc04e 100644 --- a/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/syscalls.c +++ b/ficus-toolchain/newlib/newlib-4.4.0.20231231/newlib/libc/sys/ficus/syscalls.c @@ -8,6 +8,7 @@ #include #include #include +#include uint64_t _do_syscall(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_rdx, uint64_t a3_rcx) { register uint64_t res asm("rax"); @@ -92,6 +93,11 @@ int _unlink(char *name) { } int _wait(int *status) { + return waitpid(-1, &status, 0); +} + +pid_t waitpid(pid_t pid, int *status, int options) { + return _do_syscall(SYSCALL_WAITPID_ID, (uint64_t) pid, (uint64_t) status, (uint64_t) options); } int _write(int file, char *ptr, int len) { diff --git a/src/arch/x86/syscalls.cpp b/src/arch/x86/syscalls.cpp index 6df523ce5..f03781587 100644 --- a/src/arch/x86/syscalls.cpp +++ b/src/arch/x86/syscalls.cpp @@ -110,15 +110,15 @@ uint64_t syscall_lseek(uint64_t fd, uint64_t off, uint64_t whence) { } uint64_t syscall_print_tasks() { - static SkipListMap> last_times = Scheduler::getTaskTimePerPid(); - static std::atomic last_print_time = micros; + static SkipListMap> last_times = Scheduler::getTaskTimePerPid(); + static std::atomic last_print_time = micros; - uint64_t prev_print_time = last_print_time; - last_print_time = micros; - SkipListMap> prev_times = std::move(last_times); - last_times = Scheduler::getTaskTimePerPid(); + uint64_t prev_print_time = last_print_time; + last_print_time = micros; + SkipListMap> prev_times = std::move(last_times); + last_times = Scheduler::getTaskTimePerPid(); - uint64_t slice = last_print_time - prev_print_time; + uint64_t slice = last_print_time - prev_print_time; if (slice == 0) return 0; for (const auto &t: prev_times) { @@ -127,7 +127,7 @@ uint64_t syscall_print_tasks() { assert(f->second.second >= t.second.second); String buf; buf += "PID: "; - buf += t.first; + buf += (unsigned long) t.first; buf += " "; buf += t.second.first; buf += " usage: "; @@ -137,7 +137,7 @@ uint64_t syscall_print_tasks() { } else { String buf; buf += "PID: "; - buf += t.first; + buf += (unsigned long) t.first; buf += " "; buf += t.second.first; buf += " dead \n"; @@ -218,6 +218,10 @@ char *syscall_sbrk(int brk) { return ret; } +pid_t syscall_waitpid(pid_t pid, int *status, int options) { + return Scheduler::waitpid(pid, status, options); +} + int64_t syscall_getdents(int fd, struct dirent *dp, int count) { auto f = FDT::current()->get(fd); if (!f) return -1; @@ -282,6 +286,8 @@ extern "C" uint64_t syscall_impl(uint64_t id_rdi, uint64_t a1_rsi, uint64_t a2_r return syscall_fork(); case SYSCALL_EXECVE_ID: return syscall_execve(reinterpret_cast(a1_rsi), reinterpret_cast(a2_rdx), reinterpret_cast(a3_rcx)); + case SYSCALL_WAITPID_ID: + return syscall_waitpid(static_cast(a1_rsi), reinterpret_cast(a2_rdx), static_cast(a3_rcx)); case SYSCALL_SBRK_ID: return reinterpret_cast(syscall_sbrk(static_cast(a1_rsi))); case SYSCALL_OPENDIR_ID: diff --git a/src/arch/x86/task.cpp b/src/arch/x86/task.cpp index c5194d47f..642a2d890 100644 --- a/src/arch/x86/task.cpp +++ b/src/arch/x86/task.cpp @@ -58,26 +58,130 @@ Mutex WaitingTasks_mlock; CV WaitingTasks_cv; SkipListMap::Node *>> WaitingTasks; -static std::atomic initialized = false; +static std::atomic initialized = false; -// -void Scheduler::remove_self() { - assert(RunningTask != nullptr); + +void Scheduler::dispose_self() { { LockGuard l(TasksToFree_lock); // TasksToFree is expected to do nothing with TS_RUNNING tasks TasksToFree.emplace_front(RunningTask); + // This might not cause freeing of this task, as it might be preempted + // and still be running and task freer won't delete it + // But eventually it will get cleaned + TasksToFree_cv.notify_one(); } - // This might not cause freeing of this task, as it might be preempted - // and still be running and task freer won't delete it - // But eventually it will get cleaned - TasksToFree_cv.notify_one(); - Scheduler::self_block(); + assert(false); +} +// + +void Scheduler::zombify_self() { + NO_INT( + { + { + SpinlockLockNoInt l(NextTasks_lock); + RunningTask->val->_state = Task::TaskState::TS_ZOMBIE; + RunningTask->val->_waitpid_node = extract_running_task_node(); + AllTasks_lock.unlock_nolock(); + } + Scheduler::yield_self(); + }) +} + +void Scheduler::remove_self() { + assert(RunningTask != nullptr); + if (cur_task()->_parent == -1) { + dispose_self(); + } else { + AllTasks_lock.lock(); + if (auto foundp = AllTasks.find(cur_task()->_parent); foundp != AllTasks.end()) { + if (foundp->second->state() == Task::TaskState::TS_WAITPID_BLOCKED && + (foundp->second->_woken_pid == cur_task()->_pid || foundp->second->_woken_pid == -1)) { + // Parent found and waiting, notify and wake + SpinlockLockNoInt l(NextTasks_lock); + foundp->second->_woken_pid = cur_task()->_pid; + foundp->second->_state = Task::TaskState::TS_RUNNING; + List::Node *node = nullptr; + std::swap(foundp->second->_waitpid_node, node); + NextTasks.emplace_front(node); + } + zombify_self(); + } else { + // Parent not found! + AllTasks_lock.unlock(); + dispose_self(); + } + } + assert2(0, "should be removed!"); } +void Scheduler::waitpid_block() { + NO_INT( + { + { + SpinlockLockNoInt l(NextTasks_lock); + RunningTask->val->_state = Task::TaskState::TS_WAITPID_BLOCKED; + RunningTask->val->_waitpid_node = extract_running_task_node(); + AllTasks_lock.unlock_nolock(); + } + Scheduler::yield_self(); + }) + AllTasks_lock.lock(); +} + + +void Scheduler::dispose_zombie(Task *zombie) { + LockGuard l(TasksToFree_lock); + zombie->_state = Task::TaskState::TS_BLOCKED; + List::Node *node = nullptr; + std::swap(zombie->_waitpid_node, node); + TasksToFree.emplace_front(node); + TasksToFree_cv.notify_one(); +} + +pid_t Scheduler::waitpid(pid_t pid, int *status, int options) { + AllTasks_lock.lock(); + if (pid != -1) { + // Wait for specific thing + if (auto found = AllTasks.find(pid); found != AllTasks.end()) { + if (found->second->_parent != cur_task()->_pid) return -1; // Task not our child + if (found->second->state() != Task::TaskState::TS_ZOMBIE) { // Already dead + cur_task()->_woken_pid = pid; + waitpid_block(); + assert(cur_task()->_woken_pid == pid); + } + dispose_zombie(found->second.get()); + AllTasks_lock.unlock(); + return pid; + } else { + AllTasks_lock.unlock(); + return -1; // Task not found + } + } else { + // Wait for anything + pid_t found_zombie = -1; + for (const auto &t: AllTasks) { // Check if we have anything already dead, FIXME: it's slow + if (t.second->_parent == cur_task()->_pid && t.second->state() == Task::TaskState::TS_ZOMBIE) { + found_zombie = t.first; + break; + } + } + if (found_zombie == -1) { + cur_task()->_woken_pid = -1; + waitpid_block(); + found_zombie = cur_task()->_woken_pid; + } + auto found = AllTasks.find(found_zombie); + AllTasks_lock.unlock(); + dispose_zombie(found->second.get()); + return found_zombie; + } + assert(false); +} + static void trampoline(void *rdi, void (*rsi_entrypoint)()) { rsi_entrypoint(); Scheduler::remove_self(); @@ -157,8 +261,8 @@ Task::~Task() { assert(_state != TaskState::TS_RUNNING); } -SkipListMap> Scheduler::getTaskTimePerPid() { - SkipListMap> ret; +SkipListMap> Scheduler::getTaskTimePerPid() { + SkipListMap> ret; { LockGuard l(AllTasks_lock); for (const auto &t: AllTasks) { @@ -291,6 +395,7 @@ Task *Task::clone() { ret->_frame.ss = Arch::GDT::gdt_data.selector(); ret->_frame.cs = Arch::GDT::gdt_code.selector(); ret->_frame.sp = ret->_entry_ksp_val; + ret->_parent = Scheduler::cur_task()->_pid; return ret; } diff --git a/src/arch/x86/task.hpp b/src/arch/x86/task.hpp index cf989bc56..d66b917ba 100644 --- a/src/arch/x86/task.hpp +++ b/src/arch/x86/task.hpp @@ -12,6 +12,8 @@ #include "idt.hpp" #include "task_arch.hpp" +#include + #define TASK_SS 65536 class Mutex; @@ -21,8 +23,6 @@ class Spinlock; class Task { public: - using TaskPID = uint64_t; - enum class TaskMode { TASKMODE_KERN, TASKMODE_USER @@ -30,9 +30,17 @@ public: enum class TaskState { TS_RUNNING, - TS_BLOCKED + TS_BLOCKED, + TS_ZOMBIE, + TS_WAITPID_BLOCKED }; + /// PID of processor that we're waiting for + /// or -1, wait for anything, then it's set by the zombified process to its PID + pid_t _woken_pid = -1; + /// Place to put list node when waiting for pid/being zombified + List::Node *_waitpid_node; + Task(TaskMode mode, void (*entrypoint)(), const char *name); Task(const Task &) = delete; @@ -43,7 +51,7 @@ public: void start(); [[nodiscard]] const String &name() const { return _name; } - [[nodiscard]] TaskPID pid() const { return _pid; } + [[nodiscard]] pid_t pid() const { return _pid; } [[nodiscard]] uint64_t used_time() const { return _used_time; } [[nodiscard]] TaskState state() const { return _state; } @@ -62,7 +70,7 @@ public: uint64_t _entry_ksp_val; Arch::TaskFrame _frame; - TaskPID _pid; + pid_t _pid; std::atomic _used_time; // Note that address space must be destroyed after VMA! @@ -77,6 +85,7 @@ public: TaskMode _mode; uint64_t _sleep_until; TaskState _state; + pid_t _parent = -1; }; @@ -97,6 +106,9 @@ namespace Scheduler { void sleep_self(uint64_t diff); void remove_self(); + void dispose_self(); + void zombify_self(); + void dispose_zombie(Task *zombie); void self_block(); void self_block(Spinlock &to_unlock); @@ -106,12 +118,15 @@ namespace Scheduler { void unblock(List::Node *what); void unblock_nolock(List::Node *what); + void waitpid_block(); + pid_t waitpid(pid_t pid, int *status, int options); + extern "C" void switch_task(Arch::TaskFrame *cur_frame); // TODO: that's quite inefficient! - SkipListMap> getTaskTimePerPid(); + SkipListMap> getTaskTimePerPid(); - void yield_self(); + void yield_self(); } // namespace Scheduler // Expects the caller to save interrupt state diff --git a/src/test/hello2.c b/src/test/hello2.c index df81ed06f..8a0ce4bb7 100644 --- a/src/test/hello2.c +++ b/src/test/hello2.c @@ -1,12 +1,53 @@ #include #include +#include int main() { printf("Hi!\n"); if (fork() == 0) { printf("I'm a fork!\n"); + sleep(100); + printf("I'm exiting!\n"); + return 1; } else { printf("Forked!\n"); + wait(NULL); + printf("Fork exited!\n"); + } + + if (fork() == 0) { + printf("I'm a fork!\n"); + printf("I'm exiting!\n"); + return 1; + } else { + printf("Forked!\n"); + sleep(100); + wait(NULL); + printf("Fork exited!\n"); + } + + pid_t forkpid = fork(); + if (forkpid == 0) { + printf("I'm a fork!\n"); + printf("I'm exiting!\n"); + return 1; + } else { + printf("Forked!\n"); + sleep(100); + waitpid(forkpid, NULL, 0); + printf("Fork exited!\n"); + } + + forkpid = fork(); + if (forkpid == 0) { + printf("I'm a fork!\n"); + sleep(100); + printf("I'm exiting!\n"); + return 1; + } else { + printf("Forked!\n"); + waitpid(forkpid, NULL, 0); + printf("Fork exited!\n"); } } \ No newline at end of file diff --git a/src/test/init.c b/src/test/init.c index a7e45fde1..3b6561234 100644 --- a/src/test/init.c +++ b/src/test/init.c @@ -32,6 +32,11 @@ int main() { close(test123); printf("\n %s \n", buf); } + while (1) { + execve("hello2", 0, 0); + print_mem(); + sleep(500); + } while (1) { printf("\n> ");