When I receive a SIGCHLD
blocking system calls like read
doesn’t return (with EINTR
). If I get another signal they do.
It doesn’t matter, whether the signal handler is set to a handler or to SIG_DFL
. SA_RESTART
isn’t set, explicitly unsetting it with siginterrupt (SIGCHLD, TRUE)
doesn’t do anything.
Why is there special behaviour for SIGCHLD
?
How can I configure, that syscalls should be interrupted by SIGCHILD
?
I could not reproduce the behavior with this test program on Linux (6.11.10-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.11.10-1 (2024-11-23)):
#include <signal.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
static sig_atomic_t child_caught = 0;
static void child_handler(int sig)
{
child_caught++;
}
int main(void)
{
int pipefd[2];
int err;
static const struct sigaction chld_action = {
.sa_handler = child_handler,
};
pid_t child_pid;
err = pipe(pipefd);
if (err == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
err = sigaction(SIGCHLD, &chld_action, NULL);
if (err == -1) {
perror("sigaction");
exit(EXIT_FAILURE);
}
child_pid = fork();
if (child_pid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (child_pid == 0) {
/* child */
sleep(2);
exit(EXIT_SUCCESS);
} else {
char buf[100];
ssize_t rret;
do {
rret = read(pipefd[0], buf, sizeof(buf));
if (rret == -1) {
perror("read");
} else {
printf("read returned %zdn", rret);
}
} while (rret > 0);
printf("child_caught: %dn", (int)child_caught);
exit(rret ? EXIT_FAILURE : EXIT_SUCCESS);
}
}
After 2 seconds, the program produced the following output, indicating that a SIGCHLD
signal was caught and read
failed with error EINTR
(“Interrupted system call”):
read: Interrupted system call
child_caught: 1
As Ian Abbott showed, system calls do in fact get interrupted by SIGCHLD
.
However the system call I wanted to interrupt wasn’t in the main thread, but system calls seam to be only interrupted in the thread, that receives the signal. Blocking the signal in the main thread fixed the problem:
sigset_t signal_set;
sigemptyset(&signal_set);
sigaddset(&signal_set, SIGCHLD);
sigprocmask(SIG_BLOCK, &signal_set, NULL);
What makes SIGCHLD
special is, that when the handler is set to SIG_DFL
, it is actually ignored, so there must be signal handler, even if it doesn’t do anything:
static void noop_handler (int signal) {
return;
}
struct sigaction handler = {0};
handler.sa_handler = noop_handler;
sigaction (SIGCHLD, &handler, NULL))
That maked me think, that SIGCHLD
doesn’t work, but combining both it does.
However, I wonder, if there is a different way to achieve that signal disposition, then specifying ǹoop_handler
.
7