Should child processes also be unblocking blocked SIGCHLD signals? - c

I'm trying to understand how blocking and unblocking signals work and I'm trying to understand the following piece of code. Specifically I am looking at line 28 (commented in the code): int a = sigprocmask(SIG_UNBLOCK, &mask, NULL);, aka where the signal is unblocked in the child.
The textbook I got the code from says that the code uses signal blocking in order to ensure that the program performs its add function (simplified to printf("adding %d\n", pid);) before its delete function (simplified to printf("deleting %d\n", pid);). This makes sense to me; by blocking the SIGCHLD signal, then unblocking it after we perform the add function, we ensure that handler isn't called until we perform the add function. However, why would we unblock the signal in the child? Doesn't that just eliminate the whole point of blocking by immediately unblocking it, allowing the child to delete before the parent adds?
However, the output (described after the code) is identical whether or not I have the line commented out or not, meaning that is clearly not what happens. The textbook states:
"Notice that children inherit the blocked set of their parents, so we must be careful to unblock the SIGCHLD signal in the child before calling execve."
But that still seems to me like the unblocking would result in the handler being called. What exactly does this line do?
void handler(int sig) {
pid_t pid;
printf("here\n");
while ((pid = waitpid(-1, NULL, 0)) > 0); /* Reap a zombie child */
printf("deleting %d\n", pid); /* Delete the child from the job list */
}
int main(int argc, char **argv) {
int pid;
sigset_t mask;
signal(SIGCHLD, handler);
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, NULL); /* Block SIGCHLD */
pid = fork();
if (pid == 0) {
printf("in child\n");
int a = sigprocmask(SIG_UNBLOCK, &mask, NULL); // LINE 28
printf("a is %d\n",a);
execve("/bin/date", argv, NULL);
exit(0);
}
printf("adding %d\n", pid);/* Add the child to the job list */
sleep(5);
printf("awake\n");
int b = sigprocmask(SIG_UNBLOCK, &mask, NULL);
printf("b is %d\n", b);
sleep(3);
exit(0);
}
Outputs:
adding 652
in child
a is 0
Wed Apr 24 20:18:04 UTC 2019
awake
here
deleting -1
b is 0

However, why would we unblock the signal in the child? Doesn't that
just eliminate the whole point of blocking by immediately unblocking
it, allowing the child to delete before the parent adds?
No. Each process has its own signal mask. A new process inherits its parent's signal mask, but only in the same sense that it inherits the contents of the parent's memory -- the child gets what amounts to an independent copy. Its modifications to that copy are not reflected in the parent's copy, nor vise versa after the child starts. If this were not the case, then all processes in the system would share a single signal mask.
It is only the parent that must not receive SIGCLD too soon, so only the parent needs to have that signal blocked.
[...] The textbook states:
"Notice that children inherit the blocked set of their parents, so we must be careful to unblock the SIGCHLD signal in the child before
calling execve."
But that still seems to me like the unblocking would result in the
handler being called.
Again, "inherit" in the sense of inheriting a copy, not in the sense of sharing the same mask.
What exactly does this line do?
It unblocks SIGCLD in the child -- again, having no effect on the parent -- in case it being blocked would interfere with the behavior of /bin/date, which the child is about to exec.

Related

How can waitpid() reap more than one child?

In this example from the CSAPP book chap.8:
\#include "csapp.h"
/* WARNING: This code is buggy! \*/
void handler1(int sig)
{
int olderrno = errno;
if ((waitpid(-1, NULL, 0)) < 0)
sio_error("waitpid error");
Sio_puts("Handler reaped child\n");
Sleep(1);
errno = olderrno;
}
int main()
{
int i, n;
char buf[MAXBUF];
if (signal(SIGCHLD, handler1) == SIG_ERR)
unix_error("signal error");
/* Parent creates children */
for (i = 0; i < 3; i++) {
if (Fork() == 0) {
printf("Hello from child %d\n", (int)getpid());
exit(0);
}
}
/* Parent waits for terminal input and then processes it */
if ((n = read(STDIN_FILENO, buf, sizeof(buf))) < 0)
unix_error("read");
printf("Parent processing input\n");
while (1)
;
exit(0);
}
It generates the following output:
......
Hello from child 14073
Hello from child 14074
Hello from child 14075
Handler reaped child
Handler reaped child //more than one child reaped
......
The if block used for waitpid() is used to generate a mistake that waitpid() is not able to reap all children. While I understand that waitpid() is to be put in a while() loop to ensure reaping all children, what I don't understand is that why only one waitpid() call is made, yet was able to reap more than one children(Note in the output more than one child is reaped by handler)? According to this answer: Why does waitpid in a signal handler need to loop?
waitpid() is only able to reap one child.
Thanks!
update:
this is irrelevant, but the handler is corrected in the following way(also taken from the CSAPP book):
void handler2(int sig)
{
int olderrno = errno;
while (waitpid(-1, NULL, 0) > 0) {
Sio_puts("Handler reaped child\n");
}
if (errno != ECHILD)
Sio_error("waitpid error");
Sleep(1);
errno = olderrno;
}
Running this code on my linux computer.
The signal handler you designated runs every time the signal you assigned to it (SIGCHLD in this case) is received. While it is true that waitpid is only executed once per signal receival, the handler still executes it multiple times because it gets called every time a child terminates.
Child n terminates (SIGCHLD), the handler springs into action and uses waitpid to "reap" the just exited child.
Child n+1 terminates and its behaviour follows the same as Child n. This goes on for every child there is.
There is no need to loop it as it gets called only when needed in the first place.
Edit: As pointed out below, the reason as to why the book later corrects it with the intended loop is because if multiple children send their termination signal at the same time, the handler may only end up getting one of them.
signal(7):
Standard signals do not queue. If multiple instances of a
standard signal are generated while that signal is blocked, then
only one instance of the signal is marked as pending (and the
signal will be delivered just once when it is unblocked).
Looping waitpid assures the reaping of all exited children and not just one of them as is the case right now.
Why is looping solving the issue of multiple signals?
Picture this: you are currently inside the handler, handling a SIGCHLD signal you have received and whilst you are doing that, you receive more signals from other children that have terminated in the meantime. These signals cannot queue up. By constantly looping waitpid, you are making sure that even if the handler itself can't deal with the multiple signals being sent, waitpid still picks them up as it's constantly running, rather than only running when the handler activates, which can or can't work as intended depending on whether signals have been merged or not.
waitpid still exits correctly once there are no more children to reap. It is important to understand that the loop is only there to catch signals that are sent when you are already in the signal handler and not during normal code execution as in that case the signal handler will take care of it as normal.
If you are still in doubt, try reading these two answers to your question.
How to make sure that `waitpid(-1, &stat, WNOHANG)` collect all children processes
Why does waitpid in a signal handler need to loop? (first two paragraphs)
The first one uses flags such as WNOHANG, but this only makes waitpid return immediately instead of waiting, if there is no child process ready to be reaped.

Different signal handlers for parent and child

I have a program with a signal handler:
signal(SIGINT, signalhandler);
Then the program forks and the child needs a different signal handler so:
pid = fork();
/* What happens here? */
if(pid==0)
{
signal(SIGINT, signalhandler_for_child);
}
So what happens if a SIGINT is called right after the fork but before the new sign handler is assigned?
Can this happen or there is no possibility to be interrupted before the child gets the new signal handler.
If it is possible. How could I queue the signal to the child so it gets time to get the new handler?
I know that the probabilities, if they exist, must be almost 0, but I want to make sure the application is robust in this aspect.
So what happens if a SIGINT is called right after the fork but before the new sign handler is assigned?
The signal handler installed in the parent will be called. Child process inherits it.
Can this happen or there is no possibility to be interrupted before the child gets the new signal handler.
Cetainly can happen.
If it is possible. How could I queue the signal to the child so it gets time to get the new handler?
To ensure, you need to block SIGINT before calling fork() and then reinstall a different for SIGINT
in the child process and then unblock SGINT.
/* block SIGINT here. */
pid = fork();
if (pid == 0) {
/* Install a new SIGINT handler here. */
/* Unblock SIGINT. */
...
} else if (pid > 0) {
/* The SIGINT handler is already in place. So just unblock SIGINT. */
...
} else {
/* error */
}
Look at sigprocmask() and pthread_sigmask() for blocking and unblocking signals.
You may also find the GNU documentation on signal blocking useful.

Why does waitpid in a signal handler need to loop?

I read in an ebook that waitpid(-1, &status, WNOHANG) should be put under a while loop so that if multiple child process exits simultaniously , they are all get reaped.
I tried this concept by creating and terminating 2 child processes at the same time and reaping it by waitpid WITHOUT using loop. And the are all been reaped .
Question is , is it very necessary to put waitpid under a loop ?
#include<stdio.h>
#include<sys/wait.h>
#include<signal.h>
int func(int pid)
{
if(pid < 0)
return 0;
func(pid - 1);
}
void sighand(int sig)
{
int i=45;
int stat, pid;
printf("Signal caught\n");
//while( (
pid = waitpid(-1, &stat, WNOHANG);
//) > 0){
printf("Reaped process %d----%d\n", pid, stat);
func(pid);
}
int main()
{
int i;
signal(SIGCHLD, sighand);
pid_t child_id;
if( (child_id=fork()) == 0 ) //child process
{
printf("Child ID %d\n",getpid());
printf("child exiting ...\n");
}
else
{
if( (child_id=fork()) == 0 ) //child process
{
printf("Child ID %d\n",getpid());
printf("child exiting ...\n");
}
else
{
printf("------------Parent with ID %d \n",getpid());
printf("parent exiting ....\n");
sleep(10);
sleep(10);
}
}
}
Yes.
Okay, I'll elaborate.
Each call to waitpid reaps one, and only one, child. Since you put the call inside the signal handler, there is no guarantee that the second child will exit before you finish executing the first signal handler. For two processes that is okay (the pending signal will be handled when you finish), but for more, it might be that two children will finish while you're still handling another one. Since signals are not queued, you will miss a notification.
If that happens, you will not reap all children. To avoid that problem, the loop recommendation was introduced. If you want to see it happen, try running your test with more children. The more you run, the more likely you'll see the problem.
With that out of the way, let's talk about some other issues.
First, your signal handler calls printf. That is a major no-no. Very few functions are signal handler safe, and printf definitely isn't one. You can try and make your signal handler safer, but a much saner approach is to put in a signal handler that merely sets a flag, and then doing the actual wait call in your main program's flow.
Since your main flow is, typically, to call select/epoll, make sure to look up pselect and epoll_pwait, and to understand what they do and why they are needed.
Even better (but Linux specific), look up signalfd. You might not need the signal handler at all.
Edited to add:
The loop does not change the fact that two signal deliveries are merged into one handler call. What it does do is that this one call handles all pending events.
Of course, once that's the case, you must use WNOHANG. The same artifacts that cause signals to be merged might also cause you to handle an event for which a signal is yet to be delivered.
If that happens, then once your first signal handler exists, it will get called again. This time, however, there will be no pending events (as the events were already extracted by the loop). If you do not specify WNOHANG, your wait block, and the program will be stuck indefinitely.

Detecting SIGTTIN when a child background process runs "cat"

I have the following program where I set the parent's process group and the child's process group, as well as giving the terminal control to the parent. Then, I run "cat" in the "background" child, which is supposed to generate SIGTTIN. However, the printf line in sighandler is not printed. Any ideas how to properly detect SIGTTIN in this case?
void sighandler(int signo){
printf("SIGTTIN detected\n");
}
int main() {
int status;
pid_t pid;
pid = fork ();
setpgid(0,0);
tcsetpgrp (STDIN_FILENO, 0);
signal(SIGTTIN, sighandler);
if (pid == 0)
{
setpgid(0,0);
execl ("cat", NULL);
_exit (EXIT_FAILURE);
}
else{
int status;
setpgid(pid,pid);
waitpid(-1, &status, 0);
}
return status;
}
Mariska,
For Parent Processes
As explained in the Stack Overflow post titled, "Catch Ctrl-C in C,":
The behavior of signal() varies across UNIX versions, and has also
varied historically across different versions of Linux. Avoid its use:
use sigaction(2) instead.
As described in the Linux Programmer's Manual, you should use sigaction():
The sigaction() system call is used to change the action taken by a
process on receipt of a specific signal.
Try this:
#include<stdio.h>
#include <signal.h>
static void handler(int signum)
{
/* Take appropriate actions for signal delivery */
printf("SIGTTIN detected\n");
}
int main()
{
struct sigaction sa;
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; /* Restart functions if
interrupted by handler */
if (sigaction(SIGINT, &sa, NULL) == -1)
/* Handle error */;
/* Further code */
}
For Child Processes
There are a couple of points you should know when dealing with signal handlers for the child processes:
A forked child inherits the signal handlers from the parent
Because of the above, you need to implement some sort of signal handler for the parent and then change the signal handler before and after executing a child.
As explained in the Linux Programmer's Manual:
All process attributes are preserved during an execve(), except the following:
a. The set of pending signals is cleared (sigpending(2)).
b. The dispositions of any signals that are being caught are
reset to being ignored.
c. Any alternate signal stack is not preserved (sigaltstack(2)).
Thus, the exec() functions do not preserve signal handlers.
From the above, I am trying to show you that pressing Ctrl-C sends the signal to the parent process (unless you use exec()), and then the signals are automatically propagated to children. This is why we need to change the signal handler. Even when the child is currently "active", the parent will still receive signals before the child will.
Please let me know if you have any questions!

How can I be sure I'm not losing signals when using pause()?

I'm writing a program that uses fork to create child processes and count them when they're done.
How can I be sure I'm not losing signals?
what will happen if a child sends the signal while the main program still handles the previous signal? is the signal "lost"? how can I avoid this situation?
void my_prog()
{
for(i = 0; i<numberOfDirectChildrenGlobal; ++i) {
pid = fork();
if(pid > 0)//parent
//do parent thing
else if(0 == pid) //child
//do child thing
else
//exit with error
}
while(numberOfDirectChildrenGlobal > 0) {
pause(); //waiting for signal as many times as number of direct children
}
kill(getppid(),SIGUSR1);
exit(0);
}
void sigUsrHandler(int signum)
{
//re-register to SIGUSR1
signal(SIGUSR1, sigUsrHandler);
//update number of children that finished
--numberOfDirectChildrenGlobal;
}
It's recommended to use sigaction instead of signal, but in both cases it won't provide what you need. If a child sends a signal while the previous signal is still being handled, it will become a pending signal, but if more signals are sent they will be discarded (on systems that are not blocking incoming signals, the signals can be delivered before reestablishment of the handler and again resulting in missing signals). There is no workaround for this.
What one usually does is to assume that some signals are missing, and lets the handler take care of exiting children.
In your case, instead of sending a signal from your children, just let the children terminate. Once they terminate, the parent's SIGCHLD handler should be used to reap them. Using waitpid with WNOHANG option ensures that the parent will catch all the children even if they all terminate at the same time.
For example, a SIGCHLD handler that counts the number of exited children can be :
pid_t pid;
while((pid = waitpid(-1, NULL, WNOHANG)) > 0) {
nrOfChildrenHandled++;
}
To avoid this situation you can use the posix real-time signals.
Use sigaction instead of signal to register your handlers, and the delivery of the signals is assured.

Resources