diff options
author | dzwdz | 2023-01-08 00:28:24 +0100 |
---|---|---|
committer | dzwdz | 2023-01-08 00:28:24 +0100 |
commit | 50723d19f972b7a50e3165620e57cb261c28056f (patch) | |
tree | 2d4ee4f6d9c5a09382697cafea419dc94de06a60 /src/kernel | |
parent | a1a4ab33cd75f0bc1d5e71989b01ba3446c998ae (diff) |
kernel/proc: don't kill children when parent dies
Diffstat (limited to 'src/kernel')
-rw-r--r-- | src/kernel/proc.c | 66 | ||||
-rw-r--r-- | src/kernel/proc.h | 11 | ||||
-rw-r--r-- | src/kernel/syscalls.c | 4 |
3 files changed, 52 insertions, 29 deletions
diff --git a/src/kernel/proc.c b/src/kernel/proc.c index 3f3aba5..374a031 100644 --- a/src/kernel/proc.c +++ b/src/kernel/proc.c @@ -120,7 +120,7 @@ static bool unref(uint64_t *refcount) { } void process_kill(struct process *p, int ret) { - if (p->state != PS_DEAD) { + if (proc_alive(p)) { if (p->controlled) { // TODO vfs_backend_user_handlerdown assert(p->controlled->potential_handlers > 0); @@ -174,7 +174,7 @@ void process_kill(struct process *p, int ret) { vfs_mount_remref(p->mount); p->mount = NULL; - process_transition(p, PS_DEAD); + process_transition(p, PS_TOREAP); p->death_msg = ret; if (p->execbuf.buf) { @@ -185,38 +185,53 @@ void process_kill(struct process *p, int ret) { if (unref(p->pages_refcount)) { pagedir_free(p->pages); } - - // TODO VULN unbounded recursion - struct process *c2; - for (struct process *c = p->child; c; c = c2) { - c2 = c->sibling; - process_kill(c, -1); - } } - assert(!p->child); - process_try2collect(p); + assert(!proc_alive(p)); + process_tryreap(p); - if (p == process_first) shutdown(); + if (p == process_first) { + shutdown(); + } } -void process_try2collect(struct process *dead) { - assert(dead && dead->state == PS_DEAD); - assert(!dead->child); +void process_tryreap(struct process *dead) { + struct process *parent; + assert(dead && !proc_alive(dead)); + parent = dead->parent; + + if (dead->state == PS_TOREAP) { + if (!dead->noreap && parent && proc_alive(parent)) { /* reapable? */ + if (parent->state != PS_WAITS4CHILDDEATH) { + return; /* don't reap yet */ + } + regs_savereturn(&parent->regs, dead->death_msg); + process_transition(parent, PS_RUNNING); + } + /* can't be reaped anymore */ + process_transition(dead, PS_TOMBSTONE); + } + + assert(dead->state == PS_TOMBSTONE); + for (struct process *p = dead->child; p; p = p->sibling) { + assert(p->state != PS_TOREAP); + } - struct process *parent = dead->parent; - if (!dead->noreap && parent && parent->state != PS_DEAD) { /* reapable? */ - if (parent->state != PS_WAITS4CHILDDEATH) - return; /* don't reap yet */ - regs_savereturn(&parent->regs, dead->death_msg); - process_transition(parent, PS_RUNNING); + if (dead->child) { + return; /* keep the tombstone */ } handle_close(dead->specialh.procfs); assert(dead->refcount == 0); - if (dead != process_first) { + if (parent) { + /* not applicable to init */ process_forget(dead); kfree(dead); + + // TODO force gcc to optimize the tail call here + if (!proc_alive(parent)) { + process_tryreap(parent); + } } } @@ -365,8 +380,11 @@ handle_t process_handle_put(struct process *p, struct handle *h) { } void process_transition(struct process *p, enum process_state state) { - assert(p->state != PS_DEAD); - if (state != PS_RUNNING && state != PS_DEAD) + assert(p->state != PS_TOMBSTONE); + if (state == PS_TOMBSTONE) { + assert(p->state == PS_TOREAP); + } else if (state != PS_RUNNING && state != PS_TOREAP) { assert(p->state == PS_RUNNING); + } p->state = state; } diff --git a/src/kernel/proc.h b/src/kernel/proc.h index 07cba28..135baad 100644 --- a/src/kernel/proc.h +++ b/src/kernel/proc.h @@ -6,9 +6,12 @@ struct vfs_mount; #define HANDLE_MAX 16 +/* legal transitions described by process_transition */ enum process_state { PS_RUNNING, - PS_DEAD, // return message not collected + PS_TOREAP, /* return message not collected */ + PS_TOMBSTONE, /* fully dead, supports alive children */ + PS_WAITS4CHILDDEATH, PS_WAITS4FS, PS_WAITS4REQUEST, @@ -18,6 +21,8 @@ enum process_state { PS_LAST, }; +#define proc_alive(p) (p && p->state != PS_TOREAP && p->state != PS_TOMBSTONE) + struct process { struct pagedir *pages; /* if NULL, refcount == 1. kmalloc'd */ @@ -80,8 +85,8 @@ struct process *process_seed(void *data, size_t datalen); struct process *process_fork(struct process *parent, int flags); void process_kill(struct process *proc, int ret); -/** Tries to free a process / collect its return value. */ -void process_try2collect(struct process *dead); +/** Tries to reap a dead process / free a tombstone. */ +void process_tryreap(struct process *dead); /** Switches execution to any running process. */ _Noreturn void process_switch_any(void); diff --git a/src/kernel/syscalls.c b/src/kernel/syscalls.c index f1abe30..8e42f8e 100644 --- a/src/kernel/syscalls.c +++ b/src/kernel/syscalls.c @@ -32,8 +32,8 @@ long _syscall_await(void) { { if (iter->noreap) continue; has_children = true; - if (iter->state == PS_DEAD) { - process_try2collect(iter); + if (iter->state == PS_TOREAP) { + process_tryreap(iter); return 0; // dummy } } |