Reputation: 53
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<signal.h>
#include<unistd.h>
void handler1(int sig) {
int pid;
if ((pid = waitpid(-1, NULL, 0)) < 0)
printf("waitpid error");
printf("Handler reaped child %d\n", pid);
sleep(1);
}
int main() {
int i, n;
if (signal(SIGCHLD, handler1) == SIG_ERR) {
printf("signal error\n");
}
for (i = 0; i < 3; ++i) {
if (fork() == 0) {
printf("Hello from child %d\n", getpid());
exit(0);
}
}
// sleep(1);
printf("Father love you!\n");
while (1);
exit(0);
}
When I run it, it shows this :
Father love you!
Hello from child 7843
Hello from child 7844
Hello from child 7842
Handler reaped child 7842
But I think it should be
Father love you!
Hello from child 7843
Hello from child 7844
Hello from child 7842
Handler reaped child 7842
Handler reaped child 7843
There a repetition of Handler reaped child xx
.
If I uncomment sleep(1);
, it will show what i want :
Hello from child 7858
Handler reaped child 7858
Hello from child 7859
Hello from child 7860
Handler reaped child 7859
Father love you!
I dont know why the first one has only one Handler reaped child. Please help me, thank you in advence.
Upvotes: 1
Views: 202
Reputation: 140659
The operating system is allowed to deliver only one SIGCHLD
signal to tell you that several child processes have exited. That means you need to loop calling waitpid
inside the signal handler, something like this:
void sigchld_handler(int unused)
{
pid_t pid;
int status;
for (;;) {
pid = waitpid(-1, &status, WNOHANG);
if (pid == -1) {
if (errno != ECHILD)
printf("waitpid failure: %s\n", strerror(errno);
return;
}
printf("child %d exit status %d\n", (int)pid, status);
}
}
You need to use WNOHANG
so that waitpid
will fail and set errno
to ECHILD
when there are no more processes to wait for, instead of blocking (possibly forever).
Also, in order to make it safe to call printf
from inside the signal handler, you need to use sigprocmask
and sigsuspend
in your main function, so that the signal can only be delivered when normal execution is blocked on sigsuspend
. Also also, never use signal
, only sigaction
; the specification of signal
doesn't cover several important details that will bite you.
int main(void)
{
sigset_t sigchld_set;
sigemptyset(&sigchld_set);
sigaddset(&sigchild_set, SIGCHLD);
sigset_t unblock_sigchld_set;
if (sigprocmask(SIG_BLOCK, &sigchld_set, &unblock_sigchld_set)) {
perror("sigprocmask");
return 1;
}
sigdelset(SIGCHLD, &unblock_sigchld_set);
struct sigaction sa;
sigfillset(&sa.sa_mask);
sa.sa_handler = sigchld_handler;
sa.sa_flags = SA_RESTART;
if (sigaction(SIGCHLD, &sa, 0)) {
perror("sigaction");
return 1;
}
/* fork loop goes here */
for (;;)
sigsuspend(&unblock_sigchld_set);
return 0;
}
Making main
exit when all child processes have been waited for is left as an exercise. (Hint: you need C11 atomic types.)
Upvotes: 3