Handling multiple SIGCHLD - linux

In a system running Linux 2.6.35+ my program creates many child processes and monitors them. If a child process dies I do some clean-up and spawn the process again. I use signalfd() to get the SIGCHLD signal in my process. signalfd is used asynchronously using libevent.
When using signal handlers for non-real time signals, while the signal handler is running for a particular signal further occurrence of the same signal has to be blocked to avoid getting into recursive handlers. If multiple signals arrive at that time then kernel invokes the handler only once (when the signal is unblocked).
Is it the same behavior when using signalfd() as well? Since signalfd based handling doesn't have the typical problems associated with the asynchronous execution of the normal signal handlers I was thinking kernel can queue all the further occurrences of SIGCHLD?
Can anyone clarify the Linux behavior in this case ...

On Linux, multiple children terminating before you read a SIGCHLD with signalfd() will be compressed into a single SIGCHLD. This means that when you read the SIGCHLD signal, you have to clean up after all children that have terminated:
// Do this after you've read() a SIGCHLD from the signalfd file descriptor:
while (1) {
int status;
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0) {
break;
}
// something happened with child 'pid', do something about it...
// Details are in 'status', see waitpid() manpage
}
I should note that I have in fact seen this signal compression when two child processed terminated at the same time. If I did only a single waitpid(), one of the children that terminated was not handled; and the above loop fixed it.
Corresponding documentation:
http://man7.org/linux/man-pages/man7/signal.7.html "By contrast, if multiple instances of a standard signal are delivered while that signal is currently blocked, then only one instance is queued"
http://man7.org/linux/man-pages/man3/sigwait.3p.html "If prior to the call to sigwait() there are multiple pending instances of a single signal number, it is implementation-defined whether upon successful return there are any remaining pending signals for that signal number."

Actually the hassle-free way would be the waitfd functionally that would allow you to add a specific pid to poll()/epoll(). Unfortunately, it wasn't accepted to Linux years ago when it was proposed.

Related

What happens if the call to `kill` causes the signal to be generated for the calling process and the signal is not blocked?

Advanced Programming Unix Environment says:
#include <signal.h>
int kill(pid_t pid, int signo);
...
If the call to kill causes the signal to be generated for the
calling process and if the signal is not blocked, either signo or
some other pending, unblocked signal is delivered to the process
before kill returns. (Additional conditions occur with threads;
see Section 12.8 for more information.)
I have some trouble understanding the above paragraph.
What does the description of the case mean
"the call to kill causes the signal to be generated for the calling process"
"the signal is not blocked"?
What does the result mean?
Would someone rephrase and/or give some example?
All this is saying is that if the process is using kill to send a signal to itself, then that signal will be delivered before kill returns. With some caveats.
One of the uses of kill is to send s signal to a program telling that program to end - that where the name comes from. However, nowadays, there are several other signals you can send a program.
Basically, when one program calls kill with the pid (process id) of a different program, it is telling the kernel to pass that signal to the other program.
That doesn't always mean that the signal will be sent to the other program. For instead, if the program calling kill runs with a different user than the program bring signaled, the kernel will not pass the signal, and block it instead.

pause() system call and receiving a SIGINT signal

I'm a beginner in Linux and Process signal handling.
Let's say we have a process A and it execute pause() function, we know that puts the current process to sleep until a signal is received by the process.
But when we type ctrl-c, kernel also sends a SIGINT to process A and when A receives the signal, it execute the SIGINT's default handler which is terminating the current process. So my question is:
Does the process A resume first or handler get executed first?
For simplicity, let's assume process A has only a single thread, which is blocking in a pause() call, and exactly one signal gets sent to the process.
Does the process A resume first or handler get executed first?
The signal handler gets executed first, then the pause() call returns.
What if there are multiple signals?
Standard signals are not queued, so if you send say two INT signals to the process very quickly in succession, only one of them is delivered.
If there are multiple signals, the order is unspecified.
What about POSIX realtime signals? (SIGRTMIN+0 to SIGRTMAX-0)
They are just like standard named signals, except they are queued (to a limit), and if more than one of them is pending, they get delivered in increasing numerical order.
If there are both standard and realtime signals pending, it is unspecified which ones get delivered first; although in practice, in Linux and many other systems, the standard signals get delivered first, then the realtime ones.
What if there are multiple threads in the process?
The kernel will pick one thread among those that do not have the signal masked (via sigprocmask() or pthread_sigmask()), and use that thread to deliver the signal to the signal handler.
If there are more than one thread blocking in a pause() call, one of them gets woken up. If there are more than one pending signal, it is unspecified whether the one woken thread handles them all, or if more than one thread is woken up.
In general, I warmly recommend reading the man 7 signal, man 7 signal-safety, man 2 sigaction, man 2 sigqueue, and man 2 sigwaitinfo man pages. (While the links go to the Linux man pages project, each of the pages includes a Conforming To section naming the related standards, and Linux-specific behaviour is clearly marked.)

When several signals arrive at a process, what is the order between the process handling the signals?

When several signals arrives at a process, what is the order between the process handling the signals?
What data structure is used to store the signals which have arrived at a process but not yet been delivered?
For example, from APUE
Since the process group is orphaned when the parentterminates, POSIX.1 requires that every process in the newly orphaned process group that is stopped (as our child is) be sent the hang-up signal (SIGHUP) followed by the continue signal (SIGCONT)
This causes the child to be continued, after processing the hang-up signal. The
default action for the hang-up signal is to terminate the process, so we have to
provide a signal handler to catch the signal. We therefore expect the printf in
the sig_hup function to appear before the printf in the pr_ids function.
As order of SIGCONT and SIGHUP sent to orphaned linux process group says:
The SIGHUP cannot be delivered until the child's execution is resumed.
When a process is stopped, all signal delivery is suspended except for
SIGCONT and SIGKILL.
So, the SIGHUP does arrive first, but it cannot be processed until the
SIGCONT awakens the process execution.
SIGHUP arrives before SIGCONT at a stopped process. SIGHUP can't be delievered while SIGCONT can be.
Is SIGCONT handled before or after SIGHUP? The first quote seems to say "after", while the second quote seems to say "before" by "until".
If "before":
How can SIGCONT be arranged to jump ahead of SIGHUP to be delivered?
How can SIGHUP not be discarded when SIGCONF jumps before it to be delievered?
Are the above implemented based on some data structures such as FIFO queue or FILO stack?
Thanks.
The situation is probably confused by different implementations and by the introduction of POSIX real time signals. signal(7) says that real-time signals are distinguished from old style signals by
Real-time signals are delivered in a guaranteed order. Multiple
real-time signals of the same type are delivered in the order
they were sent. If different real-time signals are sent to a
process, they are delivered starting with the lowest-numbered
signal. (I.e., low-numbered signals have highest priority.) By
contrast, if multiple standard signals are pending for a process,
the order in which they are delivered is unspecified.
As of the old style signals described in "The Design of the Unix Operating System" by Bach (before the introduction of POSIX real-time signals).
To send a signal to a process, the kernel sets a bit in the signal field of the process table entry, corresponding to the type of signal received. ... the kernel checks for receipt of a signal when the process returns from kernel mode to user mode and when it leaves the sleep state at a suitably low signalling priority.
You can see some of the current linux data structures at sched.h. Looking at this I suspect that the old-style bitmap has gone, and a combination of bitmap and linked list is used to handle both old style and POSIX real time signals, but I have not gone through enough of the code to be sure of this.
To add to mcdowella's response:
1) The specifics of "signal handling" can vary from platform to platform
2) In the specific case of Linux:
http://man7.org/linux/man-pages/man7/signal.7.html
Linux supports both POSIX reliable signals (hereinafter "standard
signals") and POSIX real-time signals.
3) See also:
About the delivery of standard signals
Why Linux decides that standard signals have higher priority than rt-signals?
The Linux Kernel - Signals
SIGCONT has special semantics.
Regardless of whether SIGCONT is caught, is ignored, or has default disposition, its generation will clear all pending stop signals and resume execution of a stopped process. [IEEE Std 1003.1-2017] Again, this resumption happens before any other signals are delivered, and even before SIGCONT's handler (if any) is invoked.
(This special “dispositionless” semantic makes sense. In order for a process to execute a signal handler, the process must itself be executing.)
POSIX is clearer than APUE here, saying that "[t]he default action for SIGCONT is to resume execution at the point where the process was stopped, after first handling any pending unblocked signals."
As others have mentioned, the actual order in which pending signals are delivered is implementation-specific. Linux, at least, delivers basic UNIX signals in ascending numeric order.
To demonstrate all this, consider the following code. It STOPs a process, then sends it several signals, then CONTinues it, having installed handlers for all catchable signals so we can see what is handled when:
#define _POSIX_SOURCE
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
static int signals[] = { SIGSTOP, SIGURG, SIGUSR1, SIGHUP, SIGCONT, 0 };
static void
handler(int signo) {
// XXX not async-signal-safe
printf("<signal %d>\n", signo);
}
int
main(int argc, char **argv) {
int *sig = signals;
struct sigaction sa = { .sa_flags = 0, .sa_handler = handler };
sigfillset(&sa.sa_mask);
sig++; // can't catch SIGSTOP
while (*sig) {
sigaction(*sig, &sa, NULL); // XXX error check
sig++;
}
if (fork() == 0) { // XXX error check
sleep(2); // faux synchronization - let parent pause()
sig = signals;
while (*sig) {
printf("sending signal %d\n", *sig);
kill(getppid(), *sig);
sig++;
}
exit(0);
}
pause();
return 0;
}
For me, this prints
sending signal 19
sending signal 23
sending signal 10
sending signal 1
sending signal 18
<signal 1>
<signal 10>
<signal 18>
<signal 23>

What happens when a signal is received while already in a signal handler?

I have a parent process spawning several child processes. I want to know when any child process exits by registering a SIGCHLD signal handler.
The question is, what happens if another SIGCHLD (or any other signal) is received, while the parent process is already in a signal handler?
I can think of the following outcomes:
The signal is ignored
The signal is queued, and will be processed as soon as the current handler returns
The current handler is in turn interrupted, just like the main program
Which one is correct?
In your concrete example (the same signal being received), the signal is delivered after the signal handler has finished (so bullet point #2 is correct). Note, however, that you may "lose" signals.
The reason for that is that while a signal is being inside its handler, it is blocked. Blocked signals are set to pending, but not queued. The term "pending" means that the operating system remembers that there is a signal waiting to be delivered at the next opportunity, and "not queued" means that it does this by setting a flag somewhere, but not by keeping an exact record of how many signals have arrived.
Thus, you may receive 2 or 3 (or 10) more SIGCHLD while in your handler, but only see one (so in some cases, bullet point #1 can be correct, too).
Note that several flags that you can pass to sigaction can affect the default behaviour, such as SA_NODEFER (prevents blocking signal) and SA_NOCLDWAIT (may not generate signal at all on some systems).
Now of course, if you receive a different type of signal, there's no guarantee that it won't interrupt your handler. For that reason, one preferrably doesn't use non signal safe functions.

Terminating a process -- Transition from allproc list to zombieproc list

How does a process get terminated? Lets say a process has three threads A,B & C. Now when we send a SIG_KILL signal to the process. All is fine so far, Now each process has exit status field in its structure! So, when a process is sent a kill signal, my understanding is that it is sent to all the threads. A thread gets killed either when it traps into kernel. If it is alreading in the kernel, it quits when it exits from the kernel. If a thread is sleeping it exits when it wakes up. Is my understanding right or am i misunderstading/missing something?
If my understanding is correct, when is a process put into zombie list? when all the threads exited or as soon as it receives a kill signal?
Lets say a process has three threads A,B & C.
Ok. I assume modern Linux, with kernel supporting threads (Linux 2.6 + glibc > 2.3)
Then the process (or thread group) consists of 3 threads (or, there is a 3 threads with different tids and same tgid=PID)
Now when we send a SIG_KILL signal to the process.
So, you use a tgid (PID) here. Ok.
Now each process has exit status field in its structure!
Wwwhat? Yes, but killing and exiting from thread group have a special code to get right exit code to waiter. For killing, the exit status is get from signal; for exiting (syscall sys_group_exit) it is the argument of syscall.
So, when a process is sent a kill signal,... it is sent to all the threads.
No.
Basically there can be two kinds of signals:
process-wide - it will be delivered to ANY thread in the process
thread (Can't name it correctly) - which is delivered by tid to some thread and not another.
So, SIGKILL is process-wide, it will kill entire process. It is delivered to some thread.
When kernel will deliver this signal - it will call do_group_exit() function ( http://lxr.linux.no/linux+v2.6.28/kernel/exit.c#L1156 called from http://lxr.linux.no/linux+v2.6.28/kernel/signal.c#L1870) to kill all threads in thread group (in process).
There is a zap_other_threads() function to iterate over all threads and kill them (with resending a thread-delivered SIGKILL) http://lxr.linux.no/linux+v2.6.28/kernel/signal.c#L966
when is a process put into zombie list?
After do_exit() kernel function call. It has a tsk->state = TASK_DEAD; line at the end.
when all the threads exited or as soon as it receives a kill signal?
The moment, when task get its state to TASK_DEAD is after receiving SIGKILL. This signal is already redelivered to all threads of the process at this moment. Can't find the actual exit time of threads, but all threads have a flag of pending fatal signal, so they will be killed at any resched.
UPDATE: all threads of process must be killed (must receive the KILL signal and do a cleanup), as they have some accounting information to be accumulated in the first thread (here first mention not the first-started, but the thread which got an original process-wide SIGKILL; or the thread, which called an exit_group syscall). First thread must wait all another threads; and it will change status only after that.
In FreeBSD, a zombie process cannot execute any code. Therefore, everything that needs the moribund process to do something is performed before that point. If you see a process in this state in ps(1) (usually only if it gets stuck), it has a usual state such as D, S, R or I, with E (trying to exit) appended to it.
A signal is delivered to one thread (either a particular thread or any thread, depending on how the signal was generated). The act of terminating the process (default action of various signals) has a process-global effect. One of the things that happens is that the thread that was chosen to deliver the signal (or that called _exit(2)) requests all other threads to exit.
A thread does not have an exit status at the kernel level; the value available via pthread_join() is a userland feature.

Resources