Reputation: 501
While reading up and learning about signals, I found a program, that uses signals in a specific way. I tried understand it, but I am not sure, how all the parts of the code interact with another.
Below is the above mentioned code and I added comments, where I have difficulties:
#include <dirent.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#define CP 5
static volatile int curprocs =0; ;
static void die() {
exit(EXIT_FAILURE);
}
static void chldhandler() {
int e = errno;
// Why do we use waitpid here? What does it do?
while(waitpid(-1, NULL, WNOHANG) > 0) {
curprocs--;
}
errno = e;
}
void do_work() {
time_t t;
srand((unsigned) time(&t));
sleep(5+ rand() % 4);
}
int main() {
struct sigaction sa = {
.sa_handler = chldhandler,
.sa_flags = SA_RESTART,
};
sigemptyset(&sa.sa_mask);
if (sigaction(SIGCHLD, &sa, NULL) == -1) {
exit(-1);
}
while(1) {
sigset_t chld, empty;
sigemptyset(&empty);
sigemptyset(&chld);
sigaddset(&chld, SIGCHLD);
// What do the following lines of code do??
sigprocmask(SIG_BLOCK, &chld, NULL);
while (curprocs >= CP) { // cap for the number of child processes
sigsuspend(&empty);
}
curprocs++;
sigprocmask(SIG_UNBLOCK, &chld, NULL);
pid_t p = fork();
if (p == -1) {
return -1;
}
if (p == 0) {
// code for the child processes to execute
do_work();
die();
} else {
// Parent process does nothing
}
}
return 0;
}
Obviously above program is intended to have a max amount of 42 child processes doing work. Whenever we want to have a new child process, we use fork, and adjust curprocs
.
Whenever a child process finishes, chldhandler() is called and curprocs
is adjusted as well.
However I don't understand the interplay of the two sigproc_mask, sigsuspend, waitpid
and our two signalsets chld, empty
.
Can someone explain what these lines do or why they are used the way they are?
Upvotes: 1
Views: 67
Reputation: 60056
sigprocmask(SIG_BLOCK, &chld, NULL);
blocks SIGCHLD
so that you can be sure that while you do while (curprocs >= 42)
the SIGCHLD
handler won't interrupt the code, changing curprocs
in the middle of the check.
sigsuspends
atomically unblocks it and waits for a SIGCHLD
(any signal really, since your passing an empty mask), atomically reblocking it when it returns.
The waitpid(-1,/*...*/)
in the handler reaps the status of any (that's what the -1 means) child that has a status change (typically termination notification) pending so that the data the kernel associates with the status change can be freed. The second argument would be where the status change info would go but since you passed NULL
, the info will simply be dropped. WNOHANG
means don't wait if there aren't any more status change notifications.
Since the handler is run in response to SIGCHLD
, there should be at least one status change notification, but there could be more because SIGCHLD
s can coalesce (it's also possible there isn't any — if you called waitpid
from normal context while SIGCHLD
was blocked). That's why the handler loops. The WNOHANG
is important because without it, after all the status change notifications have been reaped, the waitpid
call would block your process until a new notification arrived.
Upvotes: 2