From bc1ab6776c4a2a3ec6831284ccc2b09d402f62a3 Mon Sep 17 00:00:00 2001 From: Mark Date: Sun, 5 Jan 2020 14:44:07 +0200 Subject: [PATCH] Add waitpid syscall --- include/sys/syscall.h | 1 + include/sys/thread.h | 5 +++ sys/amd64/sys/sched.c | 70 +++++++++++++++++++++++++-------- sys/amd64/syscall.c | 43 ++++++++++++++++++++ usr/hello.c | 1 + usr/init.c | 6 ++- usr/libc/include/bits/syscall.h | 1 + usr/libc/syscall.c | 4 ++ 8 files changed, 113 insertions(+), 18 deletions(-) diff --git a/include/sys/syscall.h b/include/sys/syscall.h index c3dac81..35e1107 100644 --- a/include/sys/syscall.h +++ b/include/sys/syscall.h @@ -24,3 +24,4 @@ #define SYSCALL_NRX_OPENPTY 118 #define SYSCALL_NRX_SIGRET 119 +#define SYSCALL_NRX_WAITPID 247 diff --git a/include/sys/thread.h b/include/sys/thread.h index 5f40417..ae20bc0 100644 --- a/include/sys/thread.h +++ b/include/sys/thread.h @@ -4,6 +4,8 @@ #define THREAD_KERNEL (1 << 31) #define THREAD_CTX_ONLY (1 << 30) +// Thread is stopped, wait()ed by its parent and ready for reaping +#define THREAD_DONE_WAITING (1 << 5) // Well, this is actually the opposite - not a child waiting to be // wait()ed by a parent (as I have no wait() yet), but rather a // parent awaiting child process tree termination. @@ -34,6 +36,8 @@ struct thread { uint64_t flags; uint32_t pid; + int exit_code; + struct vfs_ioctx ioctx; struct ofile fds[4]; @@ -66,5 +70,6 @@ int thread_init(struct thread *t, uint32_t flags, void *arg); void thread_cleanup(struct thread *t); +void thread_terminate(struct thread *t); void thread_signal(struct thread *t, int signum); diff --git a/sys/amd64/sys/sched.c b/sys/amd64/sys/sched.c index b1816fb..bc0ae03 100644 --- a/sys/amd64/sys/sched.c +++ b/sys/amd64/sys/sched.c @@ -294,6 +294,25 @@ static void thread_unchild(struct thread *thr) { } } +// Called after parent is done waiting +void thread_terminate(struct thread *thr) { + _assert(thr->flags & THREAD_STOPPED); + _assert(thr->flags & THREAD_DONE_WAITING); + + if (!thr->child) { + // Remove from parent's "child" list + thread_unchild(thr); + + kdebug("Thread cleanup: %d\n", thr->pid); + thread_cleanup(thr); + } else { + // TODO: add to some kind of "zombie" queue + // mark children as "zombie-children" to notify parent task + kdebug("Couldn't terminate thread %d: has children\n", thr->pid); + thr->flags |= THREAD_ZOMBIE; + } +} + void sched_remove_from(int cpu, struct thread *thr) { struct thread *prev = thr->prev; struct thread *next = thr->next; @@ -316,23 +335,9 @@ void sched_remove_from(int cpu, struct thread *thr) { thr->next = NULL; thr->prev = NULL; - if (!thr->child) { - // Remove from parent's "child" list - thread_unchild(thr); - - // Parents have to wait for their children to terminate - if (cpu == (int) get_cpu()->processor_id) { - // No need for IPI - just cleanup - kdebug("Thread cleanup: %d on cpu%d\n", thr->pid, cpu); - thread_cleanup(thr); - } else { - // TODO: make other cpus schedule a new thread if they're running a stopped one - } - } else { - // TODO: add to some kind of "zombie" queue - // mark children as "zombie-children" to notify parent task - kdebug("Couldn't terminate thread %d: has children\n", thr->pid); - thr->flags |= THREAD_ZOMBIE; + // TODO: child process sends SIGCHLD to its parent (ignored by default) + if (thr->pid == 1) { + thread_terminate(thr); } } @@ -362,10 +367,41 @@ void sched_reboot(unsigned int cmd) { static uint64_t debug_last_tick = 0; +static void debug_thread_tree(struct thread *thr, size_t depth) { + for (size_t i = 0; i < depth; ++i) { + debugs(DEBUG_DEFAULT, " "); + } + debugf(DEBUG_DEFAULT, "Thread %d", thr->pid); + + if (thr->flags & THREAD_ZOMBIE) { + debugc(DEBUG_DEFAULT, 'z'); + } + if (thr->flags & THREAD_STOPPED) { + debugc(DEBUG_DEFAULT, 's'); + } + + if (thr->child) { + debugs(DEBUG_DEFAULT, " {\n"); + for (struct thread *ch = thr->child; ch; ch = ch->next_child) { + debug_thread_tree(ch, depth + 1); + } + for (size_t i = 0; i < depth; ++i) { + debugs(DEBUG_DEFAULT, " "); + } + debugc(DEBUG_DEFAULT, '}'); + } + debugc(DEBUG_DEFAULT, '\n'); +} + static void debug_stats(void) { kdebug("--- STATS ---\n"); kdebug("syscalls/s: %lu\n", syscall_count); + if (t_user_init) { + kdebug("init [1] tree:\n"); + debug_thread_tree(t_user_init, 0); + } + kdebug("CPU queues:\n"); for (size_t i = 0; i < sched_ncpus; ++i) { debugf(DEBUG_DEFAULT, " cpu%d [ ", i); diff --git a/sys/amd64/syscall.c b/sys/amd64/syscall.c index 22ea5b1..9caeee2 100644 --- a/sys/amd64/syscall.c +++ b/sys/amd64/syscall.c @@ -35,6 +35,7 @@ static int sys_nanosleep(const struct timespec *req, struct timespec *rem); static int sys_gettimeofday(struct timeval *tv, struct timezone *tz); static int sys_reboot(int magic1, int magic2, unsigned int cmd, void *arg); static int sys_access(const char *path, int mode); +static int sys_waitpid(int pid, int *status); // TODO: const struct termios *termp, const struct winsize *winp static int sys_openpty(int *master, int *slave); @@ -102,6 +103,8 @@ intptr_t amd64_syscall(uintptr_t rdi, uintptr_t rsi, uintptr_t rdx, uintptr_t rc return 0; case SYSCALL_NRX_OPENPTY: return sys_openpty((int *) rdi, (int *) rsi); + case SYSCALL_NRX_WAITPID: + return sys_waitpid((int) rdi, (int *) rsi); default: kerror("unknown syscall: %u\n", rax); @@ -367,6 +370,7 @@ static void sys_exit(int status) { struct thread *thr = get_cpu()->thread; _assert(thr); kdebug("%u exited with code %d\n", thr->pid, status); + thr->exit_code = status; thr->flags |= THREAD_STOPPED; } @@ -451,3 +455,42 @@ static void sys_sigret(void) { panic("Fuck\n"); } + +static int sys_waitpid(int pid, int *status) { + struct thread *thr = get_cpu()->thread; + _assert(thr); + // Find child with such PID + struct thread *waited = NULL; + int wait_good = 0; + + for (struct thread *child = thr->child; child; child = child->next_child) { + if (!(child->flags & THREAD_KERNEL) && (int) child->pid == pid) { + waited = child; + break; + } + } + + if (!waited) { + return -ESRCH; + } + + while (1) { + if (waited->flags & THREAD_STOPPED) { + kdebug("%d is done waiting for %d\n", thr->pid, waited->pid); + wait_good = 1; + break; + } + asm volatile ("sti; hlt; cli"); + } + + if (wait_good) { + if (status) { + *status = waited->exit_code; + } + waited->flags |= THREAD_DONE_WAITING; + + thread_terminate(waited); + } + + return 0; +} diff --git a/usr/hello.c b/usr/hello.c index 46b9da6..bed55c8 100644 --- a/usr/hello.c +++ b/usr/hello.c @@ -43,6 +43,7 @@ int main(int argc, char **argv) { // TODO: first argument is used for the program name itself if (argc != 1) { printf("wrong\n"); + usleep(1000000); return -1; } diff --git a/usr/init.c b/usr/init.c index adb0012..6d64fcd 100644 --- a/usr/init.c +++ b/usr/init.c @@ -366,6 +366,8 @@ static int cmd_exec(const char *line) { }; int pid = fork(); + int res; + int status; switch (pid) { case -1: @@ -377,7 +379,9 @@ static int cmd_exec(const char *line) { } exit(-1); default: - // TODO: waitpid + if (waitpid(pid, &status) != 0) { + perror("waitpid()"); + } return 0; } } diff --git a/usr/libc/include/bits/syscall.h b/usr/libc/include/bits/syscall.h index ceef155..33d9f72 100644 --- a/usr/libc/include/bits/syscall.h +++ b/usr/libc/include/bits/syscall.h @@ -25,5 +25,6 @@ int openpty(int *amaster, int *aslave); int gettimeofday(struct timeval *tv, struct timezone *tz); int reboot(int magic1, int magic2, unsigned int cmd, void *arg); int access(const char *filename, int mode); +int waitpid(int pid, int *status); __attribute__((noreturn)) void exit(int code); diff --git a/usr/libc/syscall.c b/usr/libc/syscall.c index 500a355..9d0dfbd 100644 --- a/usr/libc/syscall.c +++ b/usr/libc/syscall.c @@ -159,6 +159,10 @@ int access(const char *filename, int mode) { return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NR_ACCESS, filename, mode)); } +int waitpid(int pid, int *status) { + return SET_ERRNO(int, ASM_SYSCALL2(SYSCALL_NRX_WAITPID, pid, status)); +} + // Although sbrk() is implemented in userspace, I guess it should also be here void *sbrk(intptr_t inc) { if (inc == 0) {