using select() with pipe - this is what I am doing and now I need to catch SIGTERM
on that. how can I do it? Do I have to do it when select()
returns error ( < 0 ) ?
3 Answers
First, SIGTERM
will kill your process if not caught, and select()
will not return. Thus, you must install a signal handler for SIGTERM
. Do that using sigaction()
.
However, the SIGTERM
signal can arrive at a moment where your thread is not blocked at select()
. It would be a rare condition, if your process is mostly sleeping on the file descriptors, but it can otherwise happen. This means that either your signal handler must do something to inform the main routine of the interruption, namely, setting some flag variable (of type sig_atomic_t
), or you must guarantee that SIGTERM
is only delivered when the process is sleeping on select()
.
I'll go with the latter approach, since it's simpler, albeit less flexible (see end of the post).
So, you block SIGTERM
just before calling select()
, and reblock it right away after the function returns, so that your process only receives the signal while sleeping inside select()
. But note that this actually creates a race condition. If the signal arrives just after the unblock, but just before select()
is called, the system call will not have been called yet and thus it will not return -1
. If the signal arrives just after select()
returns successfully, but just before the re-block, you have also lost the signal.
Thus, you must use pselect()
for that. It does the blocking/unblocking around select()
atomically.
First, block SIGTERM
using sigprocmask()
before entering the pselect()
loop. After that, just call pselect()
with the original mask returned by sigprocmask()
. This way you guarantee your process will only be interrupted while sleeping on select()
.
In summary:
- Install a handler for
SIGTERM
(that does nothing); - Before entering the
pselect()
loop, blockSIGTERM
usingsigprocmask()
; - Call
pselect()
with the old signal mask returned bysigprocmask()
; - Inside the
pselect()
loop, now you can check safely whetherpselect()
returned-1
anderrno
isEINTR
.
Please note that if, after pselect()
returns successfully, you do a lot of work, you may experience bigger latency when responding to SIGTERM
(since the process must do all processing and return to pselect()
before actually processing the signal). If this is a problem, you must use a flag variable inside the signal handler, so that you can check for this variable in a number of specific points in your code. Using a flag variable does not eliminate the race condition and does not eliminate the need for pselect()
, though.
Remember: whenever you need to wait on some file descriptors or for the delivery of a signal, you must use pselect()
(or ppoll()
, for the systems that support it).
Edit: nothing better than a code example to illustrate the usage.
#define _POSIX_C_SOURCE 200809L
#include <errno.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/select.h>
#include <unistd.h>
// Signal handler to catch SIGTERM.
void sigterm(int signo) {
(void)signo;
}
int main(void) {
// Install the signal handler for SIGTERM.
struct sigaction s;
s.sa_handler = sigterm;
sigemptyset(&s.sa_mask);
s.sa_flags = 0;
sigaction(SIGTERM, &s, NULL);
// Block SIGTERM.
sigset_t sigset, oldset;
sigemptyset(&sigset);
sigaddset(&sigset, SIGTERM);
sigprocmask(SIG_BLOCK, &sigset, &oldset);
// Enter the pselect() loop, using the original mask as argument.
fd_set set;
FD_ZERO(&set);
FD_SET(0, &set);
while (pselect(1, &set, NULL, NULL, NULL, &oldset) >= 0) {
// Do some processing. Note that the process will not be
// interrupted while inside this loop.
sleep(5);
}
// See why pselect() has failed.
if (errno == EINTR)
puts("Interrupted by SIGTERM.");
else
perror("pselect()");
return EXIT_SUCCESS;
}

- 4,882
- 2
- 29
- 55
-
@Akek: Firstly, thanks for the thorough explanation. I have a few questions though: The SIGTERM signal can arrive at a moment where your thread is not blocked at select() -- How to handle that SIGTERM? seeking input from the fds is one of the things (not the only thing) that my process does. – hari Aug 05 '11 at 22:34
-
@hari: If you do the above, the signal will _never_ be delivered on that condition. If it arrives outside `pselect()`, the signal is deferred and delivered only in the moment you call `pselect()`. Also, there is nothing bad in deferring the signal for later processing. If you want to handle the signal as instantly as possible, you will have to use the flag approach I've described briefly. And instead of just setting a flag, you actually can do more processing in the signal handler, but it's dangerous: you're limited to call async-safe functions only, and the global state might be inconsistent. – alecov Aug 05 '11 at 22:50
-
Thanks. BTW, at some point in time, I do need to `SIG_UNBLOCK` the blocked signals too, right? – hari Aug 05 '11 at 22:59
-
The above example is complete (in regards to `SIGTERM`). If you unblock `SIGTERM` somewhere, you risk receiving the signal outside `pselect()`. If you use the flag approach, `SIGTERM` will remain _unblocked_, and will be blocked only right before checking for the flag and calling `pselect()`, and will be unblocked after `pselect()` returns. All other signals are untouched. – alecov Aug 05 '11 at 23:28
-
Thanks for bearing with me. In my program, there is a function which does all the `read` on `pipe`. So where should I put all this code? In `main()` or in that function which does read on pipe? – hari Aug 05 '11 at 23:42
-
That's up to how your program is structured. If it is small or simple enough, I'd follow the template on the example: put the signal management calls and the `pselect()` loop right in `main()`, and call a do-all-work function inside the loop (replacing the `sleep()` call); in your case, the function that `read()`s. This function will never be interrupted by `SIGTERM`. – alecov Aug 06 '11 at 00:55
-
@hari let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/2175/discussion-between-alek-and-hari) – alecov Aug 06 '11 at 00:55
The answer is partly in one of the comment in the Q&A you point to;
> Interrupt will cause select() to return a -1 with errno set to EINTR
That is; for any interrupt(signal) caught the select will return, and the errno will be set to EINTR.
Now if you specifically want to catch SIGTERM, then you need to set that up with a call to signal
, like this;
signal(SIGTERM,yourcatchfunction);
where your catch function should be defined something like
void yourcatchfunction(int signaleNumber) { .... }
So in summary, you have setup a signal handler yourcatchfunction
and your program is currently in a select()
call waiting for IO -- when a signal arrives, your catchfunction will be called and when you return from that the select call will return with the errno set to EINTR.
However be aware that the SIGTERM can occur at any time so you may not be in the select call when it occur, in which case you will never see the EINTR but only a regular call of the yourcatchfunction
Hence the select() returning with err and errno EINTR is just so you can take non-blocking action -- it is not what will catch the signal.

- 14,402
- 4
- 41
- 67
-
Good point, Soren -- On re-reading, the OP is asking how to catch a signal. The signal catching has to be set up prior to the signal arriving at the process, prior to the select() call. – Heath Hunnicutt Aug 05 '11 at 20:41
-
@Soren: Thank you. Where in the code should I put `signal(SIGTERM,yourcatchfunction);` ?? – hari Aug 05 '11 at 20:43
-
In most cases you should put the call to `signal()` as part of you initialization of the process. – Soren Aug 05 '11 at 21:10
You can call select()
in a loop. This is known as restarting the system call. Here is some pseudo-C.
int retval = -1;
int select_errno = 0;
do {
retval = select(...);
if (retval < 0)
{
/* Cache the value of errno in case a system call is later
* added prior to the loop guard (i.e., the while expression). */
select_errno = errno;
}
/* Other system calls might be added here. These could change the
* value of errno, losing track of the error during the select(),
* again this is the reason we cached the value. (E.g, you might call
* a log method which calls gettimeofday().) */
/* Automatically restart the system call if it was interrupted by
* a signal -- with a while loop. */
} while ((retval < 0) && (select_errno == EINTR));
if (retval < 0) {
/* Handle other errors here. See select man page. */
} else {
/* Successful invocation of select(). */
}

- 18,667
- 3
- 39
- 62
-
Thanks for the explanation. I have a question: what do you mean by the comment "Automatically restart the ...." – hari Aug 05 '11 at 20:41
-
The signal ostensibly interrupted the call to ``select()`` before it had a chance to return back to the caller (you). In this case (if ``errno`` is set to ``EINTR``) you just go back into the loop and call ``select`` again thereby "restarting" the call. – Eugene S Aug 05 '11 at 20:48
-
@unluddite: Thanks, I am trying to understand the `do while()` condition combination: when both the sub-conditions are true, that means, `select()` returned because of a signal, correct? but if `retval is not < 0` and `select_errno == EINTR` that means some other call (other than `select()`) got interrupted? – hari Aug 05 '11 at 20:57
-
No, if ``select_errno == EINTR`` then ``retval`` **must** be ``< 0`` (-1, in fact). A -1 as the return means that "something other than the norm" happened. We check the value of ``errno`` and if its set to ``EINTR`` then that "something" was a signal that interrupted the call. – Eugene S Aug 05 '11 at 21:05
-
@hari - unluddite is correct because I initialized ```select_errno``` to ```0```, which does not equal ```EINTR```. If I had not initialized the variable, there would be a small probability of accidentally having that value. Otherwise, ```select_errno``` is never assigned another value than ```0``` except when ```retval < 0```. – Heath Hunnicutt Aug 05 '11 at 21:14
-
1May I suggest you not call this technique "restarting the system call"? That phrase is easily (mis)understood as the semantics of SA_RESTART and some historical `signal()` implementations, both quite germane to a discussion of signals and system calls. – pilcrow Aug 09 '11 at 18:17