Reputation: 861
In the following code, what I am expecting is the console prints ten SIGCHLD caught
. I've already queued up the SIGCHLD by setting sa_flags
to SA_SIGINFO
and using sa_sigaction
instead of sa_handler
. However, it seems some of the SIGCHLD are lost. Why?
I'm thinking fork()
might be interrupted by SIGCHLD
so I use SA_RESTART
to restart the fork()
. I run the same piece of code on different computers. On my MacBook, it says [1] 24481 illegal hardware instruction
. On the other Linux computer, less than 10 SIGCHLD caught
are printed.
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define CHECK(syscall, msg) do { \
if ((syscall) == -1) { \
perror(msg); \
} \
} while(0)
void catch(int signo, siginfo_t *info, void *context) {
if (signo == SIGCHLD) {
printf("SIGCHLD caught\n");
}
}
int main () {
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGCHLD);
struct sigaction act;
act.sa_sigaction = catch;
act.sa_mask = new_set;
act.sa_flags = SA_SIGINFO | SA_RESTART;
CHECK(sigaction(SIGCHLD, &act, NULL), "sigaction error");
int pid, i;
for (i = 0; i < 10; i++) {
pid = fork();
if (!pid) return;
}
while (1);
}
Upvotes: 2
Views: 1709
Reputation: 136208
SIGCHLD
is a standard signal, which means multiple occurrences of it get collapsed into one. Linux kernel maintains a bitset for standard signals, one bit per signal and supports queuing exactly one associated siginfo_t
.
Fix:
void catch(int signo, siginfo_t*, void*) {
int status;
pid_t pid;
if(signo == SIGCHLD) {
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
printf("child %u terminated.\n", (unsigned)pid);
}
}
Also note, that you do not need to explicitly block the signal you handle because it is automatically blocked for you, unless SA_NODEFER
flag is used.
And, pedantically, only a limited number of async-signal safe functions (see man signal-safety
) can be used in a signal handler, printf
is not one of those.
Upvotes: 4