summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authordzwdz2023-01-08 00:28:24 +0100
committerdzwdz2023-01-08 00:28:24 +0100
commit50723d19f972b7a50e3165620e57cb261c28056f (patch)
tree2d4ee4f6d9c5a09382697cafea419dc94de06a60
parenta1a4ab33cd75f0bc1d5e71989b01ba3446c998ae (diff)
kernel/proc: don't kill children when parent dies
-rw-r--r--src/kernel/proc.c66
-rw-r--r--src/kernel/proc.h11
-rw-r--r--src/kernel/syscalls.c4
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
}
}