I have this code:
#include <signal.h> #include <unistd.h> #include <stdio.h> #include <sys/wait.h> #include <stdlib.h> int cpt = 0; void handler (int sig) { cpt++ ; } int main() { int i; signal(SIGCHLD, handler); for (i = 0; i < 5; i++) { if (fork() == 0) { exit(0); } } while (wait(NULL) != -1) ; printf("cpt = %dn", cpt); return 0; }
this program to my understanding should always print cpt = 5
but when i run it on my machine it returns different values (3,4,5) why is that?
Advertisement
Answer
The SIGCHLD
signal is a little funny and doesn’t work like you’d expect: we think we should get one signal per child death, but that’s not it.
Instead, it’s a kind of level-triggered thing where at some unknown intervals it sends the signal if there are any un-waited-for children.
In the loop you provided that burns through the wait()
, this loop is consuming multiple children before the signal handler gets around to it, hence less trips through the handler.
Others have pointed out that you should be using a volatile sig_atomic_t
variable, and though this is a good idea, it’s not why you’re seeing this behavior.
I believe the only way to get a guaranteed one-signal-per-child is to actually wait for the child in the signal handler – this makes it appear more like an edge-triggered signal.
Of course, you’re pretty limited to what you can do in the signal handler, so if your application already has a good regimen for waiting for child processes, you likely don’t need a SIGCHLD
handler.
#include <signal.h> #include <unistd.h> #include <stdio.h> #include <sys/wait.h> #include <stdlib.h> static volatile sig_atomic_t cpt = 0; static void handler(int sig) { cpt++; wait(NULL); // ADD ME } int main() { int i; signal(SIGCHLD, handler); for (i = 0; i < 5; i++) { if (fork() == 0) { exit(0); } } while (wait(NULL) != -1) ; printf("cpt=%dn", cpt); return 0; }
As an alternative, if the while()
loop were not so tight and had other processing (or even an explicit delay), there would not be a race condition and you’d see all five SIGCHLD
delivered.