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

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.

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.

Does POSIX specify that only one signal can interrupt pselect?

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.

Why my sig_int() function can't prevent my function from exit in c?

The codes is as below, and is the same as the one in book apue3e:
#include "apue.h"
#include "sys/wait.h"
static void sig_int(int);
int
main(int argc, char *argv[]) {
pid_t pid;
char buf[MAXLINE];
int status;
if (signal(SIGINT, sig_int) == SIG_ERR) {
err_sys("signal error");
}
printf("%% ");
while (fgets(buf, MAXLINE, stdin) != NULL) {
if (buf[strlen(buf)-1] == '\n') {
buf[strlen(buf)-1] = '\0';
}
if ((pid = fork()) < 0) {
err_sys("fork error");
} else if (pid == 0) {
execlp(buf, buf, (char *)NULL);
err_ret("couldn't execlvp: %s\n", buf);
exit(127);
}
if ((pid = waitpid(pid, &status, 0)) < 0) {
err_sys("waitpid_error");
}
printf("%% ");
}
exit(0);
}
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
So, my question is why this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
[W]hy this signal handler doesn't handle the SIGINT signal and exit immediately after pressing the Ctrl+c which i was testing on archlinux.
Given
static void
sig_int(int signo/* arguments */) {
/* code */
printf("Interrupted\n%%3 ");
}
and
signal(SIGINT, sig_int)
Your process doesn't exit when you press CTRL-C for the simple reason your signal handler doesn't cause the process to exit.
You replaced the default SIGINT handler with your own, so the default action of exiting the process no longer happens.
Since you're running on Linux, I'll refer to the GNU glibc documentation on termination signals:
24.2.2 Termination Signals
These signals are all used to tell a process to terminate, in one way
or another. They have different names because they’re used for
slightly different purposes, and programs might want to handle them
differently.
The reason for handling these signals is usually so your program can
tidy up as appropriate before actually terminating. For example, you
might want to save state information, delete temporary files, or
restore the previous terminal modes. Such a handler should end by
specifying the default action for the signal that happened and then
reraising it; this will cause the program to terminate with that
signal, as if it had not had a handler. (See Termination in
Handler.)
The (obvious) default action for all of these signals is to cause the
process to terminate.
...
Macro: int SIGINT
The SIGINT (“program interrupt”) signal is sent when the user types
the INTR character (normally C-c).
The Termination in Handler glibc documentation states:
24.4.2 Handlers That Terminate the Process
Handler functions that terminate the program are typically used to
cause orderly cleanup or recovery from program error signals and
interactive interrupts.
The cleanest way for a handler to terminate the process is to raise
the same signal that ran the handler in the first place. Here is how
to do this:
volatile sig_atomic_t fatal_error_in_progress = 0;
void
fatal_error_signal (int sig)
{
/* Since this handler is established for more than one kind of signal,
it might still get invoked recursively by delivery of some other kind
of signal. Use a static variable to keep track of that. */
if (fatal_error_in_progress)
raise (sig);
fatal_error_in_progress = 1;
/* Now do the clean up actions:
- reset terminal modes
- kill child processes
- remove lock files */
…
/* Now reraise the signal. We reactivate the signal’s
default handling, which is to terminate the process.
We could just call exit or abort,
but reraising the signal sets the return status
from the process correctly. */
signal (sig, SIG_DFL);
raise (sig);
}
Also, note that there can be significant differences between signal() and sigaction(). See What is the difference between sigaction and signal?
Finally, calling printf() from with a signal handler is undefined behavior. Only async-signal-safe functions can be safely called from within a signal handler. See POSIX 2.4 Signal Concepts for the gory details.

How to block signals in C?

I'm trying to create a program that blocks the signal SIGUSR1 and the it unblocks the signal.
In the middle I want to see that the signal is blocked using sigpending. But it always says that the signal isn't blocked, and I can use the signal when it's supposed to be blocked.
This is the code that I have.
#include <stdlib.h>
#include <stdio.h>
#include <signal.h>
static void signals(int signaln)
{
switch (signaln) {
case SIGUSR1:
printf("Signal SIGUSR1\n"); break;
}
return;
}
main()
{
sigset_t set,set2;
struct sigaction sigs;
sigs.sa_handler = signals;
sigemptyset(&sigs.sa_mask);
sigs.sa_flags=SA_ONESHOT;
sigaction(SIGUSR1, &sigs,0);
sigemptyset(&set);
sigemptyset(&set2);
sigaddset(&set,SIGUSR1);
if(sigprocmask(SIG_BLOCK, &set, NULL)==0){
printf("Blocking SISGUSR1...\n");
}
sigpending(&set2);
if (sigismember(&set2,SIGUSR1)==1)
{
printf("The signal is blocked\n"); //it should print this
}
wait(2);
kill(getpid(),SIGUSR1); //the signal shouldn't work
wait(2);
if(sigprocmask(SIG_UNBLOCK, &set, NULL)==0){
printf("Unblocking SIGUSR1\n");
}
}
Could anyone help me?
sigpending doesn't tell you whether a signal is blocked. It tells you whether a signal is waiting to be delivered. (i.e., the signal is blocked and one has been sent.)
Also, blocked doesn't meean that the signal won't be delivered; it means that the signal won't be delivered now. So you can send the signal, and it will be delivered as soon as the signal is unblocked; probably after the call to sigprocmask(SIGUNBLOCKED...) but before the call to printf, so you'll probably see the signal received message before you see the "unblocking" message.

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