K.Miao
K.Miao

Reputation: 861

Can not receive all the SIGCHLD

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

Answers (1)

Maxim Egorushkin
Maxim Egorushkin

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

Related Questions