If I send a bunch of SIGIO signals to a process and that process block SIGIO signals and doing something else. When I unblock the signal, will there be only one SIGIO signal or multiple SIGIO signals in sequence?
The answer is ... it depends.
First the signal that is being sent is 'handled' by a handler method. There are two parts to a handler: top and bottom. The top should be quick, it should get the signal and set some flag and return. The bottom part will check that flag and then respond. Now some Linux/UNIX system reset the handler to the default when a signal happens (so you have to reset the sigaction for that signal in your handler).
Signals will queue up, but only a few (depending upon the implementation). If you are sending alog of signals you could loose all those that occur after the queue fills.
Look at signal and sigaction in man pages.
Here is an AIX/Linux solution. First setting the handler.
sigset_t mask;
sigemptyset(&mask);
#ifdef AIX
exitaction.sa_handler = C_signalExit;
exitaction.sa_mask = mask;
exitaction.sa_flags = SA_OLDSTYLE;
#else // LINUX
sigaddset(&mask, SIGHUP);
sigaddset(&mask, SIGQUIT);
sigaddset(&mask, SIGTERM);
exitaction.sa_sigaction = C_signalActionExit;
exitaction.sa_mask = mask;
exitaction.sa_flags = SA_SIGINFO;
#endif
Now the handler code (top) - Note here I had to 'inject a workaround' to handle signals on Linux (but still keep the code compatible with AIX)
SystemOS is the class that handles signals and other OS related activities.
#ifdef AIX
void C_signalExit(int signal) { sys->signalExit(signal,0,NULL); }
void SystemOS::signalExit(int signal, int code, struct sigcontext *sigcon)
#else // LINUX
void C_signalActionExit(int signal, siginfo_t* siginfo, void *data)
{ sys->actionExit(signal,siginfo,data); }
void SystemOS::actionExit(int signal, siginfo_t* siginfo, void* data)
#endif
{
switch(signal)
{
case SIGINT : // interrupt from keyboard (^C ??)
case SIGKILL : // can't be caught or ignored // 080209 can't be blocked with sigblock() fields set si_pid,si_uid - see sigqueue(3)
case SIGTSTP : // ^Z
case SIGTTIN : // background read
case SIGTTOU : // background rite
#ifdef AIX
case SIGDANGER: // disk space
case SIGPRE : // program exception
case SIGSAK : // secure attention
#endif
default :
exec.kill(signal,SIGNAL_ERROR); // exec is the 'main program'
}
}
The exec.kill would be the bottom half - it takes the signal and value and will kill the application. You'd have some other function there (its not a standard method - but part of my app framework.
I hope this helps.
Related
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(¤t->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.
I want to simulate a game server that should continuously send and receive signals with its parent. The scenario is as follows:
Parent sends signal to game.
Game catches the signal and sends a signal to the parent.
Parent catches the signal and sends again a signal to game.
and so on...
The problem is that the stops receiving or sending after the first lap:
static int game_s;
void game()
{
printf("game\n");
signal(SIGUSR1,game);
sleep(1);
kill(getppid(),SIGUSR1);
pause();
}
void parent()
{
printf("parent\n");
signal(SIGUSR1,parent);
sleep(1);
kill(game_s,SIGUSR1);
pause();
}
void main()
{
game_s = fork();
if(game_s>0)
{
signal(SIGUSR1,parent);
sleep(1);
kill(game_s,SIGUSR1);
pause();
}
else
{
signal(SIGUSR1,game);
pause();
}
}
The output is the following:
game
parent
Why it stopped here? Shouldn't the game server catch parent's signal and print "game" again...
By default the reception of a specific signal is blocked from the moment a process received this specific signal until the related signal handler had been left.
From man 3 signal:
void (*signal(int sig, void (*func)(int)))(int);
[...]
When a signal occurs, and func points to a function, it is implementation-defined whether the equivalent of a:
signal(sig, SIG_DFL);
is executed or the implementation prevents some implementation-defined set of signals (at least including sig) from occurring until the current signal handling has completed.
To change this behaviour establish the signal handling via sigaction() instead of signal() (which one should do any ways for portability reasons).
sigaction() takes a struct sigaction. The member sa_flags of the latter should have SA_NODEFER set.
From Linux' man 2 sigaction:
SA_NODEFER
Do not prevent the signal from being received from within its own signal handler. This flag is meaningful only when establishing a signal handler.
POSIX words this differently:
SA_NODEFER
If set and sig is caught, sig shall not be added to the
thread's signal mask on entry to the signal handler
unless it is included in sa_mask. Otherwise, sig shall
always be added to the thread's signal mask on entry to
the signal handler.
Be aware that each signal handler gets it's own stack allocated each time it gets invoked, so sooner or later this recursive ping-pong ends up in an out-of-memory condition.
Use message queues, or shared memory to do this. As stated above, this will eventually run out of memory and it will crash.
I have a parent process that manages a child (fork, execve). I created a handler in the parent to catch SIGCHLD signals from the child in order to call waitpid() and take appropriate action such as restarting the child.
I understood from the manual page for sigaction() that, while inside a signal handler, further signals of the same type would be blocked by default. I definitely wish for this behaviour so I decided to test it.
I put a sleep (my own implementation using clock_nanosleep() in a loop which resumes when interrupted) at the end of the signal handler and sent a SIGINT to the child. This duly made it quit and sent SIGCHLD to the parent. I logged the fact and started my sleep for 10 seconds. Now, I sent another SIGINT to the new child (sighandler restarted it first time) and was surprised to see another log and sleep happen.
How can this be? When I attached using a debugger to the parent it clearly showed two different threads interrupted to call my signal handler, both now sat in sleep. If that keeps up I will run out of threads!
I understand putting long sleeps into a signal handler is a daft thing to do but it does illustrate the point; I expected to see the second signal marked as pending in /proc/[PID]/status but instead it's delivered.
Here's the relevant bits of my code:
Set up the SIGCHLD handler:
typedef struct SigActType {
struct sigaction act;
int retval;
void (*func)(int);
}SigActType;
static SigActType sigActList[64];
public void setChildHandler(void (*func)(int)) {
SigActType *sat = &sigActList[SIGCHLD];
sat->act.sa_sigaction = sigchldHandler;
sigemptyset(&sat->act.sa_mask);
sigaddset (&sat->act.sa_mask, SIGTERM);
sigaddset (&sat->act.sa_mask, SIGINT);
sigaddset (&sat->act.sa_mask, SIGCHLD);
sat->act.sa_flags = SA_SIGINFO;
sat->retval = 0;
sat->func = func;
sigaction(SIGCHLD, &sat->act, NULL);
}
static void sigchldHandler(int sig, siginfo_t *si, void *thing) {
SigActType *sat = &sigActList[SIGCHLD];
if (sat->func) {
sat->func(si->si_pid);
}
}
and using this:
int main(int argc, char **argv) {
setChildHandler(manageChildSignals);
...
}
static void manageChildSignals(int d) {
if ((pid = waitpid(-1, &stat, WAIT_MYPGRP)) > 0) {
... restart child if appropriate
}
printf("start of pause...\n");
mySleep(10);
printf("end of pause...\n");
}
Stdout clearly shows:
(when I type kill -2 [PID]
start of pause
(when the new child is started and I type kill -2 [NEWPID]
start of pause
...10 seconds slide past...
end of pause
end of pause
I am puzzled as to why this happens. As you can see I even added SIGCHLD to the block mask for sigaction() to try to encourage it to do the right thing.
Any pointers most welcome!
signals of the same type would be blocked by default.
Yes, but only for the thread sigaction() is called from.
From man sigaction (bold emphasis by me):
sa_mask specifies a mask of signals which should be blocked (i.e.,
added to the signal mask of the thread in which the signal handler is
invoked) during execution of the signal handler.
As signal dispostion is per process any other thread not blocking the signal in question might receive it, that is get interupted and process it.
If this behaviour is not what you want you should perhaps modify the design of the way your program handles signals in such a way that per default all signals are blocked for each thread, and only one specifiy thread has signal reception unblocked.
Update:
Signals masks are inherited from the parent thread by the child thread.
If signal handling shall be done by one specific thread only, have the main thread block all signals prior to creating any other thread. Then create one specfic thread to do the signal handling, and have this thread unblock the signals to be handled. This concept also allows models like one thread per signal.
In a mutlithreaded environment use pthread_sigmask() to mask signals on a per thread base.
Please note that the behaviour of sigprocmask() in a multithreaded process is unspecified, use pthread_sigmask() then.
I was doing a little reading about sigaction() (sources are from my course notes) and I'm not sure I understand this text:
The signal mask is calculated and installed only for the duration of
the signal handler.
By default, the signal “sig” is also blocked when the signal occurs.
Once an action is installed for a specific signal using sigaction,
it remains installed until another action is explicitly requested.
Does this mean that the default signal mask is restored after returning form the signal handler?
Also, do I have to re-install the handler after using it, as if I was using signal()?
Also, there's this piece of code:
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
void termination_handler(int signum) {
exit(7);
}
int main (void) {
struct sigaction new_action,old_action;
new_action.sa_handler = termination_handler;
sigemptyset(&new_action.sa_mask);
sigaddset(&new_action.sa_mask, SIGTERM);
new_action.sa_flags = 0;
sigaction(SIGINT, NULL, &old_action);
if (old_action.sa_handler != SIG_IGN) {
sigaction(SIGINT,&new_action,NULL);
}
sleep(10);
return 0;
}
So - how exactly will SIGTERM be handled? I can see that the installed handler is termination handler(), but then SIGTERM was added to the signal mask with no use of sigprocmask(). What does this mean? Thanks!
P.s. one last question: why the if statement in main()?
Let's try to understand what's happening with a modified version of your code :
#include <signal.h>
#include <stdio.h>
void termination_handler(int signum)
{
printf("Hello from handler\n");
sleep(1);
}
int main (void)
{
//Structs that will describe the old action and the new action
//associated to the SIGINT signal (Ctrl+c from keyboard).
struct sigaction new_action, old_action;
//Set the handler in the new_action struct
new_action.sa_handler = termination_handler;
//Set to empty the sa_mask. It means that no signal is blocked
// while the handler run.
sigemptyset(&new_action.sa_mask);
//Block the SEGTERM signal.
// It means that while the handler run, the SIGTERM signal is ignored
sigaddset(&new_action.sa_mask, SIGTERM);
//Remove any flag from sa_flag. See documentation for flags allowed
new_action.sa_flags = 0;
//Read the old signal associated to SIGINT (keyboard, see signal(7))
sigaction(SIGINT, NULL, &old_action);
//If the old handler wasn't SIG_IGN (it's a handler that just
// "ignore" the signal)
if (old_action.sa_handler != SIG_IGN)
{
//Replace the signal handler of SIGINT with the one described by new_action
sigaction(SIGINT,&new_action,NULL);
}
while(1)
{
printf("In the loop\n");
sleep(100);
}
return 0;
}
So, if you compile it and launch it, and press Ctrl+C, then you'll have the handler message executed, and then you get back immediately out of the main's sleep. You can do it as many time as you want, and the handler message and the inloop message are still displayed.
So, you give a function, and sigaction does everything needed to hook the signal with your handler.
Now, what about sigterm? If you increase the sleep time in termination_handler, you can type something like "pkill --signal SIGTERM ./a.out" after pressing Ctrl+C. Then, what happens? Nothing! The SIGTERM signal is blocked while termination_handler is running. But once you are back in the main, now the SIGTERM will kill the application.
(Remember, while you are testing this code, you can still kill applications by sending a SIGKILL signal.)
If you want to know more, and have more fun with signals, you have the signal manual and the sigaction manual which tell a lot more. Notice that you also have the detailed description of the sigaction structure.
I was wondering if it is possible to be interrupted by a signal when my program is handling other signal at the same time, I tried to simulate it with:
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
#include<string.h>
void sig_output()
{
sigset_t set;
sigprocmask(0,NULL,&set);
printf("currently blocking:");
if (sigismember(&set,SIGUSR1))
printf("\nSIGUSR1");
if(sigismember(&set,SIGUSR2))
printf("\nSIGUSR2");
printf("\n");
return ;
}
void sig_handler(int sig)
{
raise(SIGUSR1);
printf("start\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end\n");
return ;
}
void other_sig_handler(int sig)
{
printf("start - other\n");
if (sig==SIGUSR1)
printf("SIGUSR1\n");
else if (sig==SIGUSR2)
printf("SIGUSR2\n");
printf("end - other\n");
return ;
}
int main()
{
sig_output();
struct sigaction a;
a.sa_handler=sig_handler;
a.sa_flags=0;
sigset_t set,old;
//blocking SIGUSR1,SIGUSR2
sigemptyset(&set);
sigaddset(&set,SIGUSR1);
sigaddset(&set,SIGUSR2);
printf("blocking SIGUSR1, SIGUSR2\n");
sigprocmask(SIG_SETMASK,&set,&old);
sig_output();
//adding handles for SIGUSR1,SIGUSR2
sigemptyset(&(a.sa_mask));
sigaction(SIGUSR1,&a,NULL);
a.sa_handler=other_sig_handler;
sigaction(SIGUSR2,&a,NULL);
printf("poczatek wysylania \n");
raise(SIGUSR1);
raise(SIGUSR2);
raise(SIGUSR1);
printf("using sigsuspend\n");
sigsuspend(&old);
printf("end of program\n");
return 0;
}
and everytime I run this program I get
currently blocking:
blocking SIGUSR1, SIGUSR2
currently blocking:
SIGUSR1
SIGUSR2
raising
using sigsuspend
start - other
SIGUSR2
end - other
start
SIGUSR1
end
end of program
is it always like that?
Quoting the sigaction(2) manpage:
Signal routines normally execute with the signal that caused their
invocation blocked, but other signals may yet occur. A global signal mask
defines the set of signals currently blocked from delivery to a process.
The signal mask for a process is initialized from that of its parent
(normally empty). It may be changed with a sigprocmask(2) call, or when
a signal is delivered to the process.
You can control whether the signal is automatically blocked in its signal handler with the SA_NODEFER flag.
The order in which these particular pending signals are delivered is not, as far as I know, defined. However, signals are (mostly; there's an exception for SIGCLD, which is traditionally done by "cheating") "non-queueing", except for real-time signals. The non-queuing aspect means that if you have signal X blocked, and then raise it twice (as you do above for SIGUSR1), you only get it delivered once.
The only ordering documented on at least one system (MacOS) is:
If multiple signals are ready to be delivered at the same time, any signals that
could be caused by traps are delivered first.
(These are things like SIGSEGV and SIGBUS.) In general, you can control the order of delivery by use of the signal blocking masks: unblock any particular signal(s) at some point and those are the ones that can be delivered at that point.
If you do not set SA_NODEFER, the blocking mask at the entry to your handler will always block whatever signal your handler is handling, so that you won't have to worry about recursion.
The special case for SIGCLD comes from System V, which originally implemented this by resetting the handler to SIG_DFL on each SIGCLD delivery. (In fact, SysV did this with all signals, effectively implementing SA_RESETHAND whether you wanted it or not.) The default action was to discard the signal, as if the handler were SIG_IGN. This of course created race conditions when multiple child processes finished before the handler could do its thing. Instead of a block/unblock model, though, the SysV folks put in a hack: at the end of your SIGCLD handler, you would call signal(SIGCLD, handler); to fix up the handler. At that point, if there were any exited children that had not yet been wait-ed for, SysV would immediately generate a new SIGCLD, and your handler would be entered recursively. This made it look as though the signals were queued, without actually queueing them.
For more on Linux signals, see (eg) http://www.kernel.org/doc/man-pages/online/pages/man7/signal.7.html.