Does POSIX specify that only one signal can interrupt pselect? - c

The POSIX pselect function take a signal mask argument. The signal mask is "atomically" set as the current mask before execution of the function begins, and is restored as the function returns.
This allows an otherwise masked signal to be unmasked while the function executes, and masked again when the function returns. It's guaranteed* that if a signal unmasked in this way is caught, the pselect function will be interrupted by the signal and (unless the signal action is specified with the SA_RESTART flag) will return an EINTR error.
(*: or is it? the language in the document linked above would seem to allow that a signal being received between when pselect unblocked due to seeing a file readiness or timeout and when it replaced the signal mask with the original would not necessarily cause EINTR, since EINTR is required if "The function was interrupted while blocked ..." - however, that ultimately doesn't affect this question).
My question is: supposing that two separate signals are temporarily unmasked during pselect execution, is it possible that both signals will be caught before the pselect function returns and the previous signal mask is restored - or is there some kind of guarantee that only one signal will be caught in this case (leaving the other one pending)? (For purposes of the question, suppose that SA_RESTART is not set for the signal action, and that all signals were specified to be masked during execution of the signal handler when it was established via sigaction).
I can find nothing which suggests that only one signal may be processed, but I may have missed something, and I am writing some code for which this would be a very useful guarantee. I'd be interested to know if POSIX itself makes any guarantee, and also if different OSes provide such a guarantee independently.

No, but it also doesn’t specify that multiple signals can or must. Since it is unspecified, it is best to follow the general rule, which allows all pending unmasked signals to be processed. If you attempt to strictly depend upon this, you are likely on a bad path because the timing of asynchronous events is difficult to predict.
In general, it would be very difficult to make an implementation that imposed an ‘only one' restriction because the os runtime would have to leave one or more signals pending but unmasked until some unspecified point. Remember that the signal handler which runs when pselect is interrupted could do a siglongjmp rather than returning, so the kernel would have to keep a complicated, possibly unbounded data structure to track which signal mask to enforce.
Below is a modified version of your test program. In this one, each event emits a string via write() so there are no buffering problems. The program sets its “main” environment to mask SIGUSR1, SIGUSR2; but while pselect is running, it permits SIGUSR1, SIGUSR2, SIGTERM.
The program forks, with the parent (default:) sitting in a loop invoking pselect(), then outputting ‘.’ after it completes.
The child sits in a loop, delivering SIGUSR1, SIGUSR2 to the parent, then sleeping for a bit. It outputs ‘^’ after delivering the signals.
The handler emits a prefix “(1” or “(2” for SIGUSR1, SIGUSR2 resp; then sleeps for a bit, and outputs “)” to indicate the sleep has completed.
The output I see on macos (10.12.6, but I doubt it matters much) is:
^(2)(1).^(2)(1).^(2)(1).^(2)(1).Terminated: 15
which indicates that the signal handler for each of SIGUSR1 and SIGUSR2 are being run for every invocation of pselect(). This is what I would expect; as it is designed to not admit a window of uncertainty as would be the case with bracketting select() with sigprocmasks().
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
#include <unistd.h>
void handle(int signo)
{
char s[2];
s[0] = '(';
s[1] = signo == SIGUSR1? '1' : '2';
write(1, s, 2);
sleep(1);
write(1, ")", 1);
}
int main(int argc, char **argv)
{
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGUSR1);
sigaddset(&mask, SIGUSR2);
sigprocmask(SIG_SETMASK, &mask, NULL);
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigdelset(&mask, SIGUSR2);
sigdelset(&mask, SIGTERM);
signal(SIGUSR1, handle);
signal(SIGUSR2, handle);
pid_t t = fork();
switch (t) {
default:
while (1) {
/* no USR1, USR2 */
pselect(0, NULL, NULL, NULL, NULL, &mask);
/* no USR1, USR2 */
write(1, ".", 1);
}
break;
case 0:
t = getppid();
for (int i = 0; i < 4; i++) {
kill(t, SIGUSR1);
kill(t, SIGUSR2);
write(1, "^", 1);
sleep(5);
}
kill(t, SIGTERM);
break;
case -1:
perror("fork\n");
}
return 0;
}

I've continued searching and found no additional information, so I can only conclude that there are no guarantees in POSIX generally.
Under Linux, if I understand the code below correctly, only one signal can be handled (assuming that the signal handler itself doesn't unmask signals): the relevant code and a revealing comment is in fs/select.c, in the do_pselect function:
ret = core_sys_select(n, inp, outp, exp, to);
ret = poll_select_copy_remaining(&end_time, tsp, 0, ret);
if (ret == -ERESTARTNOHAND) {
/*
* Don't restore the signal mask yet. Let do_signal() deliver
* the signal on the way back to userspace, before the signal
* mask is restored.
*/
if (sigmask) {
memcpy(&current->saved_sigmask, &sigsaved,
sizeof(sigsaved));
set_restore_sigmask();
}
} else ...
It essentially returns from the system call, allowing the signal handler to execute, after which the original signal mask will immediately be restored (from current->saved_sigmask, because set_restore_sigmask() sets a flag indicating that this should occur).
The following test program verifies this:
#include <stdio.h>
#include <signal.h>
#include <sys/select.h>
volatile sig_atomic_t got_usr1 = 0;
volatile sig_atomic_t got_usr2 = 0;
void handle_usr1(int signo, siginfo_t *info, void *v)
{
got_usr1 = 1;
}
void handle_usr2(int signo, siginfo_t *info, void *v)
{
got_usr2 = 1;
}
int main(int argc, char **argv)
{
// mask SIGUSR1 and SIGUSR2:
sigset_t curmask;
sigemptyset(&curmask);
sigaddset(&curmask, SIGUSR1);
sigaddset(&curmask, SIGUSR2);
sigprocmask(SIG_SETMASK, &curmask, NULL);
// Create a mask for all but SIGUSR1 and SIGUSR2:
sigset_t mask;
sigfillset(&mask);
sigdelset(&mask, SIGUSR1);
sigdelset(&mask, SIGUSR2);
// Set up signal handlers:
struct sigaction action;
action.sa_sigaction = handle_usr1;
sigfillset(&action.sa_mask);
action.sa_flags = SA_SIGINFO;
sigaction(SIGUSR1, &action, NULL);
action.sa_sigaction = handle_usr2;
sigaction(SIGUSR2, &action, NULL);
// Make signals pending:
raise(SIGUSR1);
raise(SIGUSR2);
// pselect with no file descriptors and no timeout:
pselect(0, NULL, NULL, NULL, NULL, &mask);
int count = got_usr1 + got_usr2;
printf("Handled %d signals while in pselect.\n", count);
return 0;
}
On Linux, the output of the above is consistently:
Handled 1 signals while in pselect.
This also seems to be the case on FreeBSD; however, I'm not willing to count on this being the case on all other platforms. The solution I have found to ensuring that only one signal can be handled is to use siglongjmp to jump out of the signal handler as well as out of the pselect call while also restoring the signal mask so that no further signals can be processed.
Essentially, that code looks like this:
jmp_buf jbuf; // signal handlers have access to this
if (sigsetjmp(jbuf, 1) != 0) {
// We received a signal while in pselect ...
}
int r = pselect(nfds, &read_set_c, &write_set_c, &err_set, wait_ts, &sigmask);
The signal handlers must execute a siglongjmp:
void signal_handler(int signo, siginfo_t *siginfo, void *v)
{
siglongjmp(jbuf, 1);
}
This feels crufty, but seems to work on all platforms that I've tested it on (Linux, MacOS and FreeBSD) - furthermore it seems to be supported by POSIX generally.

Related

Linux - Why is a blocked and ignored signal pending?

When sending a signal to a process that both blocks and ignores it, the kernel still keeps this signal in the pending list (my terminology here). In this case the kernel behaves like the signal is only blocked, although it should also be ignored. I can't understand this behavior. Here is a C code for example with SIGUSR1 (which has the index 10):
#define _GNU_SOURCE
#include <signal.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <stdlib.h>
void handler(int sig)
{
printf("Received signal %d\n", sig);
}
int main(int argc, char *argv[])
{
printf("PID is %ld\n", (long) getpid());
int sig = SIGUSR1;
//creating the sigaction struct and setting the handler
struct sigaction act;
act.sa_handler = handler;
sigemptyset(&act.sa_mask);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Blocking the signal
sigset_t blockedSignals;
sigemptyset(&blockedSignals);
sigaddset(&blockedSignals, sig);
printf("Blocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
//Ignoring the signal
act.sa_handler = SIG_IGN;
printf("Ignoring signal %d\n", sig);
if(sigaction(sig, &act, NULL) == -1)
{
printf("Error: sigaction\n");
exit(1);
}
//Sleeping for a while in order to give the user a chance to send the signal to this process
printf("Sleeping for 20 sec. Please send the signal.\n");
sleep(20);
//Unblocking the signal
/*sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}*/
//Let's check the pending list
sigset_t pendingSignals;
sigemptyset(&pendingSignals);
if(sigpending(&pendingSignals) == -1)
{
printf("Error: sigpending\n");
exit(1);
}
if(sigismember(&pendingSignals, sig) == 1)
{
printf("Signal %d is pending.\n", sig);
}
else
{
printf("Signal %d isn't pending.\n", sig);
}
exit(0);
}
SIGUSR1 is both blocked and ignored. While this process sleeps, if I send a SIGUSR1 to it (from shell: kill -s SIGUSR1 PID), and then checks the pending list, I get this printing:
Signal 10 is pending.
If I uncomment the commented block of code, which unblocks the signal:
sigemptyset(&blockedSignals);
printf("Unblocking signal %d\n", sig);
if(sigprocmask(SIG_SETMASK, &blockedSignals, NULL) == -1)
{
printf("Error: sigprocmask\n");
exit(1);
}
and repeat the experiment, I see the following printing:
Signal 10 isn't pending.
It's like the kernel gives priority to the 'blocking' over the 'ignoring'.
Is it really the case?
Update: As far as I understand, when the process ignores a signal, it means that the kernel won't send it to the process. This also means that it won't keep it in the pending list. For example, if a signal is only blocked by the process, and exists in the pending list, and then we call 'sigaction' in order to ignore it, the kernel will remove this signal from the pending list. So the question is, why if we block+ignore in ahead, the kernel inserts the signal to its pending list?
Blocking a signal and ignoring it are two separate and independent things.
Ignoring a signal by setting its disposition to SIG_IGN instructs that when the signal is delivered the resulting action should be to do nothing.
Blocking a signal (by setting a signal mask that includes that signal) has the effect of preventing that signal from being delivered at all. If it is received, then it will remain pending until unblocked or the process terminates. Signal disposition does not matter until the signal is actually delivered. So,
It's like the kernel gives priority to the 'blocking' over the 'ignoring'. Is it really the case?
Yes. The effect of ignoring a signal cannot be realized while that signal is blocked.
With regard to the update to the question:
As far as I understand, when the process ignores a signal, it means
that the kernel won't send it to the process.
No, that's incorrect. SIG_IGN is a signal disposition. That's what the process does in response to a signal. It can't respond if the kernel doesn't send the signal in the first place.
Note that another option for signal disposition is for the process to run a custom signal handler function. It should be clearer that this is something that the process must do, not something that the kernel does for it.
This also means that it
won't keep it in the pending list.
It would mean that ignored signals never became pending, but your understanding of the semantics is incorrect.
So the question is, why if we
block+ignore from ahead, the kernel inserts the signal to its pending
list?
Because that's what the kernel does with signals. You can characterize it as what the kernel does with all signals, but those that aren't blocked don't stay pending very long.

master error when multiple signal are sent

I got this issue:
I made a program in c, where the main process creates some child process, and these, after a while, are able to send a signal to the main process:
the signal is sent with this code:
kill(getppid(), SIGUSR1);
and the main process, in the while loop is waiting the SIGUSR1 message...
everything is fine, but if I increase the child number and automatically the possibility to have more signals in the same time, the program crash printing the message:
User defined signal 1
the main code is like this:
void signalHandler(int sig, siginfo_t* info, void* vp) {
if (sig == SIGUSR1) {
printf("SIGUSR1 has arrived\n");
} else if (sig == SIGUSR2) {
printf("SIGUSR2 has arrived\n");
}
}
int main(int argc, char const *argv[]) {
struct sigaction action, old_action;
memset(&action, 0, sizeof(struct sigaction));
action.sa_sigaction = signalHandler;
sigemptyset(&action.sa_mask);
action.sa_flags = SA_RESTART | SA_NODEFER;
while (1) {
sigaction(SIGUSR1, &action, &old_action);
sigaction(SIGUSR2, &action, &old_action);
}
}
I think the problem is that the signal is sent when the master is still working on the previous signal...but how can I do to fix this thing
thank you very much
It means that the child is sending the signal before the parent process was able to call sigaction() to configure the signal handler. When this happens, the default signal reaction to SIGUSR1 terminates the program:
SIGUSR1 P1990 Term User-defined signal 1
https://man7.org/linux/man-pages/man7/signal.7.html
However, there are many problems with your code. printf() is not safe to be called inside a signal handler (it's AS-Unsafe as defined by POSIX):
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_03
Also, using SA_NODEFER may create nested signals (another signal handler is called while some signal handler is running) but your program does not protect against a flood. Given enough children this will generate a stack overflow. Finally, the main program keeps running a non-stop infinite loop reconfiguring the signals, while it should have configured them only once outside the loop and blocked inside the loop (for example sigwait() or pselect()):
https://man7.org/linux/man-pages/man2/select.2.html
Finally, if you expect to run a large number of children that might flood the parent with signals, then it would be better to use the real time signal generation function (sigqueue()) rather than kill(). The difference is that with sigqueue(), all signals are queued and SA_NODEFER is not necessary to avoid discarding signals while some other signal handler is running:
https://pubs.opengroup.org/onlinepubs/9699919799.2018edition/functions/V2_chap02.html#tag_15_04_02
Final conclusion: the code should be completely rewritten.

What are the pitfalls of using `pause()` in signal handler?

I want to suspend the thread and resume it. There are few methods listed here. But I thought of using pause() library function from unistd.h.
What are the pitfalls of using pausing in signal handler?
One I noticed is, when I send 0 to pause thread and send 0 again then my signal is queued. I need to send 1 twice to resume the thread.
I guess there may be many more cases like this. How to handle such conditions if I want to use pause() or sleep() in signal handler.
#include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <signal.h>
#include <stdbool.h>
static bool thread_ready = false;
static void cb_sig(int signal)
{
if (signal == SIGUSR1)
pause();
else if (signal == SIGUSR2)
;
}
static void *thread_job(void *ignore)
{
int i = 0;
struct sigaction act;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
act.sa_handler = cb_sig;
if (sigaction(SIGUSR1, &act, NULL) == -1)
printf("unable to handle siguser1\n");
if (sigaction(SIGUSR2, &act, NULL) == -1)
printf("unable to handle siguser2\n");
thread_ready = true;
while (1) {
printf("thread counter: %d\n", i++);
sleep(1);
}
return NULL;
}
int main()
{
bool running;
int user_input;
pthread_t thread;
if (pthread_create(&thread, NULL, thread_job, NULL))
return -1;
while (!thread_ready);
for (running = true; running; ) {
printf("0: pause thread, 1: resume thread, -1: exit\n");
scanf("%d", &user_input);
switch(user_input) {
case -1:
running = false;
break;
case 0:
pthread_kill(thread, SIGUSR1);
break;
case 1:
pthread_kill(thread, SIGUSR2);
break;
}
}
pthread_kill(thread, SIGKILL);
return 0;
}
A signal handler should not sleep(), and probably should not pause(), even though technically, both of these functions are async-signal-safe. Signal handlers should run quickly and minimize or (preferrably) completely avoid blocking.
As for specific pitfalls, you already noted one: by default, a signal is automatically blocked while its handler is running. It is possible to install the handler in a way that avoids that, but here that wouldn't help you: if you kept sending signals that get handled by your particular handler, then you would always have at least one thread blocked in that handler. If you kept sending them to the same thread then that thread would never unblock.
More generally, there are any number of poor interactions that might happen between signal masks, signaling, and signal handlers blocking on signal receipt.
Moreover, pause() is rather non-specific unless you combine it with setting a rather restrictive signal mask (in which case sigsuspend() is probably a better choice). But if you set a restrictive signal mask then you potentially interfere with other uses of signaling.
Do not "handle" such issues other than by avoiding use of pause() and sleep() in your signal handlers.

Signal handler for all signal

How can I register a signal handler for ALL signal, available on the running OS, using signal(3)?
My code looks like this:
void sig_handler(int signum)
{
printf("Received signal %d\n", signum);
}
int main()
{
signal(ALL_SIGNALS_??, sig_handler);
while (1) {
sleep(1);
};
return 0;
}
Most systems have a macro NSIG or _NSIG (the former would not be available in standards-conformance mode since it violates the namespace) defined in signal.h such that a loop for (i=1; i<_NSIG; i++) will walk all signals. Also, on POSIX systems that have signal masks, CHAR_BIT*sizeof(sigset_t) is an upper bound on the number of signals which you could use as a fallback if neither NSIG nor _NSIG is defined.
Signal handlers have to deal with reentrancy concerns and other problems. In practice, it's often more convenient to mask signals and then retrieve them from time to time. You can mask all signals (except SIGSTOP and SIGKILL, which you can't handle anyway) with this:
sigset_t all_signals;
sigfillset(&all_signals);
sigprocmask(SIG_BLOCK, &all_signals, NULL);
The code is slightly different if you're using pthreads. Call this in every thread, or (preferably) in the main thread before you create any others:
sigset_t all_signals;
sigfillset(&all_signals);
pthread_sigmask(SIG_BLOCK, &all_signals, NULL);
Once you've done that, you should periodically call sigtimedwait(2) like this:
struct timespec no_time = {0, 0};
siginfo_t result;
int rc = sigtimedwait(&all_signals, &result, &no_time);
If there is a signal pending, information about it will be placed in result and rc will be the signal number; if not, rc will be -1 and errno will be EAGAIN. If you're already calling select(2)/poll(2) (e.g. as part of some event-driven system), you may want to create a signalfd(2) instead and attach it to your event loop. In this case, you still need to mask the signals as shown above.

Non-blocking check for signals in a loop

I have a thread in an application that has a loop like this:
...
while (1)
{
checkDatabase();
checkChildren();
sleep(3);
}
...
checkDatabase() is self-explanatory; checkChildren() simply calls waitpid(-1, &status, WNOHANG) to deal with child processes that have either exited or received a signal.
The application works fairly well, but it has default signal handling. The problem is that this parent process has a number of threads (don't worry about child processes for now) and I don't have any experience with synchronous signals, let alone in a POSIX threads application. I have used signal() before but apparently it's non-portable and it doesn't do what I need anyway. I have no experience at all with sigaction methods, and I can't find good documentation on how to fill in the structs and so on.
What I need to do is to synchronously catch terminating signals like SIGINT, SIGTERM and SIGQUIT in the above loop (and I need to ignore SIGPIPE altogether so that I can catch the EPIPE error from IO methods), so it would look like this:
...
while (1)
{
checkDatabase();
checkChildren();
checkForSignals();
sleep(3);
}
...
All other threads should not have anything to do with the signal; only the thread that executes this loop should be aware of it. And, obviously, it needs to be a non-blocking check so the loop doesn't block during its first iteration. The method called if a signal is found will sort out the other threads and destroy mutexes, and all that.
Could anyone please give me a heads-up? Many thanks.
(Following the question's comments, and for completeness, this solution tries to avoid signal handlers.)
It is possible to block signals from being raised through sigprocmask() (or, rather, pthread_sigmask() since you're using threads). From there on, the signals that were raised but blocked are available through sigpending().
Therefore, you could do something like (error checking omitted for brevity):
sigset_t blocked;
sigemptyset(&blocked);
sigaddset(&blocked, SIGINT);
sigaddset(&blocked, SIGTERM);
sigaddset(&blocked, SIGQUIT);
pthread_sigmask(SIG_BLOCK, &blocked, NULL); // Block SIGINT, SIGTERM and SIGQUIT.
signal(SIGPIPE, SIG_IGN); // Ignore SIGPIPE.
Then, later:
void checkForSignals(void)
{
sigset_t pending;
sigpending(&pending);
if (sigismember(&pending, SIGINT)) {
// Handle SIGINT...
}
if (sigismember(&pending, SIGTERM)) {
// Handle SIGTERM...
}
if (sigismember(&pending, SIGQUIT)) {
// Handle SIGQUIT...
}
}
Since sigpending() does not block, this seems to match your requirements.
Create a signal handler for SIGINT, SIGTERM and SIGQUIT, using the same function. In that signal function just set a flag that can be polled in your loop.
Something like this:
/* Global variable, will be set to non-zero if SIGINT, SIGTERM or SIGQUIT is caught */
int term_signal_set = 0;
void my_signal_handler(int)
{
term_signal_set = 1;
}
/* ... */
signal(SIGINT, my_signal_handler);
signal(SIGTERM, my_signal_handler);
signal(SIGQUIT, my_signal_handler);
signal(SIGPIPE, SIG_IGN); /* So functions return EPIPE */
while (1)
{
/* ... */
if (term_signal_set > 0)
break; /* Or do something else */
sleep(3);
}
In a multithreaded application receiving a signal, there is no predetermination, which thread receives the signal. Typical workaraounds include setting a global variable in the signal handler and checking it from a dedicated thread.
So in your case the signal handler (called from whatever thread) would just set something like a global variable for the signal received, and in CheckForSignals() you would test it.
sigaction is the way to go. man sigaction should help you. Here is an example from the web
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
struct sigaction act;
void sighandler(int signum, siginfo_t *info, void *ptr)
{
printf("Received signal %d\n", signum);
printf("Signal originates from process %lu\n",
(unsigned long)info->si_pid);
}
int main()
{
printf("I am %lu\n", (unsigned long)getpid());
memset(&act, 0, sizeof(act));
act.sa_sigaction = sighandler;
act.sa_flags = SA_SIGINFO;
sigaction(SIGTERM, &act, NULL);
// Waiting for CTRL+C...
sleep(100);
return 0;
}

Resources