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 = %d\n", 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?
CodePudding user response:
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=%d\n", 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.