Skip to content

Commit 6c6d54b

Browse files
committed
sys/process: send SIGCHLD to parent on exit
1 parent 25fac09 commit 6c6d54b

4 files changed

Lines changed: 75 additions & 4 deletions

File tree

include/kernel/process.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,14 @@ typedef enum thread_state {
8686
struct process {
8787
char name[PROCESS_NAME_MAX_LEN]; /*!< The thread's name */
8888
pid_t pid; /*!< Process' unique ID */
89+
unsigned int flags;
8990

9091
struct address_space *as; /*!< The process's address space */
9192

93+
struct process *parent;
9294
llist_t threads; /*!< Linked list of the process' active threads */
9395
llist_t children; /*!< Linked list of the process' active children */
96+
9497
node_t this; /*!< Node inside the parent's list of children */
9598
node_t this_global; /*!< Used by the global list of alive processes */
9699

@@ -121,6 +124,16 @@ struct process {
121124
spinlock_t lock;
122125
};
123126

127+
/*
128+
* Possible values for (struct process)->flags.
129+
*/
130+
enum process_flags {
131+
PROC_SA_NOCLDWAIT = BIT(0), /* Do not generate SIGCHLD when children stop */
132+
};
133+
134+
/* Union of all the flags that should be inherited when forking. */
135+
#define PROC_FLAGS_INHERITED (PROC_SA_NOCLDWAIT)
136+
124137
/**
125138
* @brief A single thread.
126139
*

kernel/sys/process.c

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ static void process_collect_zombie(struct process *zombie)
198198
static void process_make_zombie(struct process *process)
199199
{
200200
struct process *child;
201+
struct process *parent;
202+
siginfo_t sigchld;
201203

202204
log_info("freeing process: %s", process->name);
203205

@@ -223,19 +225,53 @@ static void process_make_zombie(struct process *process)
223225
signal_queue_flush(&process->sig_pending);
224226
address_space_destroy(process->as);
225227

226-
/* Attach all orphans to the init process. */
228+
/*
229+
* Attach all orphans to the init process.
230+
*/
227231
FOREACH_LLIST_ENTRY(child, &process->children, this)
228232
{
229233
locked_scope (&child->lock) {
230234
llist_add(&init_process->children, &child->this);
235+
child->parent = init_process;
231236
}
232237
}
233238

234-
spinlock_release(&process->lock);
239+
/*
240+
* We cannot hold a reference to the process' parents so it might be in
241+
* the process of being made into a zombie itself. Detect this case and
242+
* move ourselves into the init process' children before sending SIGCHLD.
243+
*/
244+
parent = process->parent;
245+
if (READ_ONCE(parent->state) == SCHED_ZOMBIE) {
246+
llist_add(&init_process->children, &process->this);
247+
parent = init_process;
248+
}
235249

236250
/*
237-
* TODO: a SIGCHLD shall be sent to the parent process.
251+
* If the parent process' ignores the SIGCHLD signal the process' lifetime
252+
* must end immediately. This avoids dangling references to uncollectable
253+
* zombie processes.
254+
*
255+
* FIXME: We should delay this a bit to make sure no one's holding
256+
* the lock anymore. This may the source of use after free errors.
257+
* But ... that's for another day, this will work for now.
238258
*/
259+
if (parent->flags & PROC_SA_NOCLDWAIT) {
260+
process_collect_zombie(process);
261+
return;
262+
}
263+
264+
/*
265+
* TODO: Use our own signal.h header since newlib defines almost no macro...
266+
* We should be forwarding additional informations, such as the exit
267+
* code and CLD_* values...
268+
*/
269+
memset(&sigchld, 0, sizeof(sigchld));
270+
sigchld.si_signo = SIGCHLD;
271+
signal_process(parent, &sigchld);
272+
273+
spinlock_release(&process->lock);
274+
239275
}
240276

241277
static struct process *process_get(struct process *process)
@@ -387,6 +423,8 @@ void process_init_kernel_process(void)
387423

388424
thread_set_user_stack(&kernel_process_initial_thread, ustack);
389425

426+
kernel_process.flags = 0;
427+
390428
kernel_process.sig_set = NULL;
391429
signal_queue_init(&kernel_process.sig_pending);
392430
signal_queue_init(&kernel_process_initial_thread.sig_pending);
@@ -698,6 +736,7 @@ thread_fork(struct thread *thread, thread_entry_t entrypoint, void *arg)
698736
PROCESS_NAME_MAX_LEN);
699737
address_space_copy_current(new_process->as);
700738
new_process->creds = creds_get(current->process->creds);
739+
new_process->flags = current->process->flags & PROC_FLAGS_INHERITED;
701740
new_process->sig_set = signal_set_clone(current->process->sig_set);
702741
if (new_process->sig_set == NULL) {
703742
err = E_NOMEM;
@@ -719,6 +758,7 @@ thread_fork(struct thread *thread, thread_entry_t entrypoint, void *arg)
719758
goto process_destroy;
720759
}
721760

761+
new_process->parent = current->process;
722762
locked_scope (&current->process->lock)
723763
llist_add(&current->process->children, &new_process->this);
724764

@@ -784,7 +824,7 @@ pid_t sys_getpid(void)
784824
}
785825

786826
/*
787-
* TODO: waitpid(): hande signals
827+
*
788828
*/
789829
pid_t sys_waitpid(pid_t pid, int *stat_loc, int options)
790830
{

kernel/sys/signal.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ static void signal_set_init(struct signal_set *set)
122122
{
123123
for (int signo = SIGNAL_MIN; signo <= SIGNAL_MAX; ++signo)
124124
set->sig_actions[signo].sa_action.sa_handler = SIG_DFL;
125+
current->process->flags |= PROC_SA_NOCLDWAIT;
125126
}
126127

127128
/*
@@ -657,6 +658,21 @@ static error_t signal_action_configure(struct signal_set *set, int signo,
657658
*orig = *sig_action;
658659
}
659660

661+
/*
662+
* Pre-compute whether children should issue a SIGCHLD signal when exiting.
663+
* According to the spec the signal should still be generated if sa_handler
664+
* is set to SI_IGN and SA_NOCLDWAIT is not set, but this things easier to
665+
* understand and who cares I guess.
666+
*/
667+
if (signo == SIGCHLD) {
668+
if (sa_action->sa_flags & SA_NOCLDWAIT ||
669+
sa_action->sa_handler == SIG_IGN ||
670+
sa_action->sa_handler == SIG_DFL) /* SIGCHLD: SIG_DFL == ignore */
671+
current->process->flags |= SA_NOCLDWAIT;
672+
else
673+
current->process->flags &= ~SA_NOCLDWAIT;
674+
}
675+
660676
return E_SUCCESS;
661677
}
662678

root/usr/lib/dailyrun/include/sys/signal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#include_next <sys/signal.h>
22

3+
#define SA_NOCLDWAIT 0x20 /* Children dont' generate SIGCHLD in _exit(). */
4+
35
/*
46
* Signature of both signal handler functions inside struct sigaction.
57
*/

0 commit comments

Comments
 (0)