The POSIX article for sigaction states:
If SA_SIGINFO is set in sa_flags, then subsequent occurrences of sig
generated by sigqueue() or as a result of any signal-generating
function that supports the specification of an application-defined
value (when sig is already pending) shall be queued in FIFO order
until delivered or accepted; the signal-catching function shall be
invoked with three arguments. The application specified value is
passed to the signal-catching function as the si_value member of the
siginfo_t structure.
This says nothing about merging of occurrences of the same signal (signo). And even if it meant merging, then the phrase about FIFO would be incomplete. For example, if the FIFO is [SIGALRM, SIGIO, SIGUSR1, SIGIO], what would it be after merging, [SIGALRM, SIGIO, SIGUSR1] or [SIGALRM, SIGUSR1, SIGIO]?
However, I saw several reports (0, 1, 2) that Linux indeed merges occurrences of the same signal. I also wrote a small test program that confirms that occurrences of the same signal sent with sigqueue when that signal is blocked are merged on ArchLinux. A signal handler was installed with sigaction.
Linux [skipped] 5.3.8-arch1-1 #1 SMP PREEMPT [skipped] x86_64 GNU/Linux
If occurrences of the same signal are merged, then transmitting information via union sigval does not make sense because this information may be unpredictably lost.
Is my understanding of POSIX incorrect, or is Linux implementing it incorrectly?
[Update 2019-11-09 09:51:32+00:00. I found another chunk of documentation that corroborates my point. According to the article “2. General Information”,
If a subsequent occurrence of a pending signal is generated, it is
implementation-defined as to whether the signal is delivered or
accepted more than once in circumstances other than those in which
queuing is required.
and another one on sigqueue,
If SA_SIGINFO is set for signo and if the resources were available to
queue the signal, the signal shall be queued and sent to the receiving
process.
The sigqueue() function shall fail if:
[EAGAIN]
No resources are available to queue the signal. The process has already queued {SIGQUEUE_MAX} signals that are still pending at the
receiver(s), or a system-wide resource limit has been exceeded.
I used sigaction with SA_SIGINFO set and sigqueue. The function sigqueue did not return EAGAIN.]
[Update 2019-11-09 17:45:25+00:00. My test program. The sigprint functions are for printing using write and a statically allocated buffer; they are not important.
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include "sigprint.h"
void signal_handler(int signo, siginfo_t *info, void *context) {
sigprint_string("|");
sigprint_long(info->si_value.sival_int);
sigprint_flush();
}
void sigqueue_check(pid_t pid, int signo, union sigval value) {
if (sigqueue(pid, signo, value) == -1) {
printf(" returned error code %d which is %s to EAGAIN.\n",
errno, errno == EAGAIN ? "==" : "!=");
exit(24);
}
}
int main() {
struct sigaction sigaction0;
union sigval sigval0;
sigset_t sigset0, sigset1;
sigaction0.sa_sigaction = &signal_handler;
sigemptyset(&sigaction0.sa_mask);
sigaction0.sa_flags = SA_SIGINFO | SA_RESTART;
sigaction(SIGALRM, &sigaction0, NULL);
sigemptyset(&sigset0);
sigaddset(&sigset0, SIGALRM);
sigprocmask(SIG_BLOCK, &sigset0, &sigset1);
sigval0.sival_int = 10;
sigqueue_check(getpid(), SIGALRM, sigval0);
sigval0.sival_int = 11;
sigqueue_check(getpid(), SIGALRM, sigval0);
sigval0.sival_int = 12;
sigqueue_check(getpid(), SIGALRM, sigval0);
sigprocmask(SIG_SETMASK, &sigset1, NULL);
sleep(1);
return 0;
}
]
The documented behavior on Linux is to not queue duplicate blocked signals.
From man 7 signal:
Standard signals do not queue. If multiple instances of a standard signal are generated while that signal is blocked, then only one instance of the signal is marked as pending (and the signal will be delivered just once when it is unblocked). In the case where a standard signal is already pending, the siginfo_t structure (see sigaction(2)) associated with that signal is not overwritten on arrival of subsequent instances of the same signal. Thus, the process will receive the information associated with the first instance of the signal.
However... the man page also says:
Multiple instances of real-time signals can be queued. By contrast, if multiple instances of a standard signal are delivered while that signal is currently blocked, then only one instance is queued.
Are you looking at the POSIX documentation for real time signals and testing with standard ones?
Related
I read:
There are two signals that a process can’t ignore – SIGKILL =
terminate the receiving process – SIGSTOP = suspend the receiving
process
And some even claimed there is no way we can declare handlers for them
But In C I can write:
#include <signal.h>
#include <stdio.h>
void sigint_handler(int signum) {
printf("I'm ignoring you!\n");
}
int main() {
signal(SIGKILL,sigint_handler);
for(;;) { /*endless loop*/ } return 0;
}
Isn't this a contradiciton?
Side Question, When I write kill 123 in the terminal what signal will be sent I can't find this information anywhere in the internet?
Per POSIX-1.2017 General Information §2.4.3 Signal Actions
, a signal may have one of three different dispositions or "actions taken" when it is delivered to a process:
SIG_DFL: take the default or "normal" action.
SIG_IGN: ignore the signal (take no action).
user-defined: the signal is "caught" by a user-defined signal handler.
That said, the same section of POSIX also clarifies:
The system shall not allow the action for the signals SIGKILL or SIGSTOP to be set to SIG_IGN.
....
The system shall not allow a process to catch the signals SIGKILL and SIGSTOP.
If you checked the return code and errno of your signal() call, you'd almost certainly see it failing with EINVAL.
I found the following code works differently in macOS and Linux:
#include <signal.h>
#include <unistd.h>
#include <stdio.h>
void catcher( int sig ) {
printf( "Signal catcher called for signal %d\n", sig );
}
int main( int argc, char *argv[] )
{
struct sigaction sigact;
sigset_t waitset;
int sig;
int result = 0;
sigemptyset( &sigact.sa_mask );
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction( SIGINT, &sigact, NULL );
sigemptyset( &waitset );
sigaddset( &waitset, SIGHUP);
result = sigwait(&waitset, &sig) ;
if(result == 0)
{
printf( "sigwait() returned for signal %d\n", sig );
}
}
When run on macOS and a SIGINT is sent to the process, its handler is executed only after a SIGHUP is sent (thus causing sigwait() to return). In other words it looks sigwait() blocks all signals outside its waiting mask during its wait. When the same program is run on Linux, SIGINT is delivered, that is the handler is run, as soon as a SIGINT is sent to the process. Therefore it looks in Linux sigwait() does not block the signals outside its waiting mask.
Which is the standard behaviour? SUSv3 does not make it clear.
sigwait is clearly not specified to block any signals. If it's doing so on MacOS X, this seems to be a bug.
OS X isn't blocking the extraneous signals, but is instead suspending the process à la SIGSTOP.
I find this a reasonable implementation of the specification (IEEE Std 1003.1-2017), which requires that sigwait(set, &s) suspend the calling thread until at least one signal in set becomes pending.
Just like a true STOP, OS X keeps the unblockable SIGKILL will at bay until the process resumes. ps can see a difference between STOP and sigwait, and of course the process resumption is different for each (CONT vs. set), but they're essentially the same process state.
Linux, on the other hand, seems to me to pretend that sigwait is interruptible, and further that user-defined actions were installed with SA_RESTART. User-defined handlers are invoked, and KILL is respected immediately. This behavior is, in my opinion, much more useful, but not to spec — the thread is simply not suspended if it can execute a user-defined signal handler.
Of course, this circumstance is a bit contrived, as sigwait is really designed with multithreaded programs in mind.
I have this assignment to do:
Write a function void myfunct(void(*f)(int sig)) which sets f as handler to all the possible signals.
I have two problems:
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Thank you.
I would personally instead iterate over a static list of signal numbers, and use preprocessor directives to detect which ones are supported (at compile time). For example:
#include <signal.h>
static const all_signals[] = {
#ifdef SIGHUP
SIGHUP, /* POSIX.1 */
#endif
#ifdef SIGQUIT
SIGQUIT, /* POSIX.1 */
#endif
#ifdef SIGTRAP
SIGTRAP, /* POSIX.1 */
#endif
#ifdef SIGIO
SIGIO, /* BSD/Linux */
#endif
/*
* Other signal names omitted for brevity
*/
/* C89/C99/C11 standard signals: */
SIGABRT,
SIGFPE,
SIGILL,
SIGINT,
SIGSEGV,
/* SIGTERM (C89/C99/C11) is also the terminating signal number */
SIGTERM
};
with SIGTERM being the last entry in the array for which a signal handler is installed:
struct sigaction act;
int i = 0;
memset(&act, 0, sizeof act);
sigemptyset(&act.sa_mask);
act.sa_handler = your_signal_handler;
act.sa_flags = 0;
do {
if (sigaction(all_signals[i], &act, NULL)) {
fprintf(stderr, "Cannot install signal %d handler: %s.\n", all_signals[i], strerror(errno));
exit(EXIT_FAILURE);
}
} while (all_signals[i++] != SIGTERM);
This way your code does not require POSIX etc. support to work, but does support POSIX signals if available at compile time.
You can check the Wikipedia Unix signal article and man 7 signal for known signal names.
You can also install the signal handler for POSIX realtime signals using
#if SIGRTMAX-0 > SIGRTMIN-0
for (i = SIGRTMIN; i <= SIGRTMAX; i++)
if (sigaction(i, &act, NULL)) {
fprintf(stderr, "Cannot install realtime signal %d handler: %s.\n", i, strerror(errno));
exit(EXIT_FAILURE);
}
#endif
How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Most implementation provide a constant such as NSIG (Glibc provides NSIG) or _NSIG (Linux provides _NSIG). So, you can loop through that constant and set the same signal handling function for all of them.
There's no POSIX defined value for "highest signal number". There's been a proposal in POSIX to add a macro NSIG_MAX.
{NSIG_MAX}
Maximum possible return value of sysconf(_SC_NSIG). See [cross-ref to XSH sysconf()]. The value of {NSIG_MAX} shall be no greater than the number of signals that the sigset_t type (see [cross-ref to ]) is capable of representing, ignoring any restrictions imposed by sigfillset() or sigaddset().
But it hasn't made it to POSIX yet (most probably it'll a part of the POSIX version - issue 8).
Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
The parameter that the signal handling function takes doesn't matter when you are setting a signal disposition. It takes the signal number but that doesn't prevent you from using it as a handler for multiple signals.
But there are special cases you need to handle. Certain signals that can't caught or ignored (SIGKILL and SIGSTOP). There are other signals (SIGFPE, SIGILL and SIGSEGV) for which, while allowed to caught, the signal handler can't return to its caller (i.e. you need exit from the signal handler).
1. How can a get all the possible signals? Is there a function for this? Can I iterate through them in some way?
Seems like there isn't a standardized list you can iterate, but we actually don't need such a list for your task. Signal numbers are always positive and at most SIGRTMAX. In between 1 and SIGRTMAX there might be some numbers that do not correspond to any signal, but you can still try to set a handler for these. From the posix specification of sigaction(sig, ...) (emphasis mine):
RETURN VALUE
Upon successful completion, sigaction() shall return 0; otherwise, -1 shall be returned, errno shall be set to indicate the error, and no new signal-catching function shall be installed.
ERRORS
The sigaction() function shall fail if:
[EINVAL]
The sig argument is not a valid signal number or an attempt is made to catch a signal that cannot be caught or ignore a signal that cannot be ignored.
So it is perfectly fine to call sigaction(123, ...) where 123 is not a signal.
2. Will it really work to set the function f as handler given that it takes a parameter? Shouldn't it not have any parameters?
Why do you think it should have no parameters?
Again, from the posix specification of sigaction():
Member Type
Member Name
Description
void(*) (int)
sa_handler
Pointer to a signal-catching function
The signal handler is supposed to have an int parameter, just like your function f has, so there is no problem.
Putting everything together
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
struct sigaction siga;
void f(int sig) {
printf("Caught signal %d\n", sig);
}
// sets f as handler to all the possible signals.
void myfunct(void(*f)(int sig)) {
siga.sa_handler = f;
for (int sig = 1; sig <= SIGRTMAX; ++sig) {
// this might return -1 and set errno, but we don't care
sigaction(sig, &siga, NULL);
}
}
int main() {
myfunct(f);
pause(); // wait for signal
return 0;
}
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 am trying to understand how signals work in Linux from the sample program that I found online, but it has some parts which I don't really understand.
This is my sample program:
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
void catcher(int sig) {
printf("catcher() has gained control\n");
}
int main(int argc, char *argv[]) {
struct sigaction sigact;
sigset_t sigset;
sigemptyset(&sigact.sa_mask);
sigact.sa_flags = 0;
sigact.sa_handler = catcher;
sigaction(SIGUSR1, &sigact, NULL);
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
sigemptyset(&sigset);
sigaddset(&sigset, SIGUSR1);
sigprocmask(SIG_SETMASK, &sigset, NULL);
printf("before second kill()\n");
kill(getpid(), SIGUSR1);
printf("after second kill()\n");
return 0;
}
Here is the sample output from my program:
before first kill()
catcher() has gained control
before second kill()
after second kill()
Can I know why the first line in the output is before first kill()? Why doesn't catcher() has gained control appear first?
From what I know, sa_handler consists of two types of signal, signal default and signal ignore.
How do we know which signal it will generate? Why would it trigger the function to print the catcher() has gained control if the signal ignore being generate?
Besides, what is the sa_mask function in this program? In my understanding, sa_mask will block the specified signal.
Can I know why the first line in the output is before first kill()?
Why doesn't catcher() has gained control appear first?
You installed a signal handler that catches SIGUSR1. Until SIGUSR1 is delivered to the process, normal program execution flow keeps happening. So, here:
printf("before first kill()\n");
kill(getpid(), SIGUSR1);
You only generate the signal after printing before first kill(). Why don't you expect this to appear before catcher() has gained control? In other words, when you call printf("before first kill()\n");, no signals have been raised yet, so you can only expect program execution to remain normal.
This line:
kill(getpid(), SIGUSR1);
Generates SIGUSR1. The operating system delivers the signal to the process at a convenient time. Because you installed a handler for SIGUSR1, your signal handler (catcher()) is invoked. You raise the signal after printing the first line, so it is expectable that the next line of output will come from the signal handler.
Note that printf(3) is not async-signal safe, so technically you can't call it from inside a signal handler, but it is usually ok for these toy examples.
From what I know, sa_handler consists of two types of signal, signal
default and signal ignore.
There's more to it than that. The sa_handler field of struct sigaction can have the values SIG_DFL, which corresponds to the default signal action (the default action is listed in man signal), and SIG_IGN, which means the signal is ignored (nothing happens when it is raised). But sa_handler can also be a pointer to a function that you want to be invoked every time the signal is delivered. This is what the code you showed is doing - it is saying: Hey, when SIGUSR1 is delivered, please call catcher().
How do we know which signal it will generate? Why would it trigger the
function to print the catcher() has gained control if the signal
ignore being generate?
You indicated a signal (SIGUSR1) when you called sigaction(2) to setup the handler. So, catcher() will be called when SIGUSR1 is delivered.
Besides, what is the sa_mask function in this program? In my
understanding, sa_mask will block the specified signal.
It's a signal mask that is atomically installed when the signal handler is entered, and uninstalled when the signal handler returns. By default, even if you pass it an empty mask, the signal being caught is always blocked upon entering the handler (unless the SA_NODEFER flag is set in the sa_flags field of struct sigaction). However, you might want to block other signals while the handler is executing - the way you do that is by indicating these signals in sa_mask.