Add waitpid syscall
This commit is contained in:
@@ -24,3 +24,4 @@
|
||||
|
||||
#define SYSCALL_NRX_OPENPTY 118
|
||||
#define SYSCALL_NRX_SIGRET 119
|
||||
#define SYSCALL_NRX_WAITPID 247
|
||||
|
||||
@@ -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);
|
||||
|
||||
+53
-17
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+5
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user