I am playing with the signal.h and unistd.h libraries, and I am having some issues. In the code below, when I send the SIGINT signal to my running program by calling CTRL-C, the signal is caught. However, when pressing CTRL-C again, the program terminates. As I understand it, the print statement "Received signal 2" should be printed every time I press CTRL-C.
Is my understanding of this signal incorrect, or is there a bug in my code?
Thanks for your input!
#include "handle_signals.h"
void sig_handler(int signum)
{
printf("\nReceived signal %d\n", signum);
}
int main()
{
signal(SIGINT, sig_handler);
while(1)
{
sleep(1);
}
return 0;
}
Terminal output:
xxx#ubuntu:~/Dropbox/xxx/handle_signals$ ./handle_signals
^C
Received signal 2
^C
xxx#ubuntu:~/Dropbox/xxx/handle_signals$
Edit: Here is the header I've included
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
void sig_handler(int signum);
Thanks for your responses. Reading through them now!
Don't use signal, use sigaction:
The behavior of signal() varies across UNIX versions, and has also varied historically across different versions of Linux. Avoid its use: use sigaction(2) instead.
http://man7.org/linux/man-pages/man2/signal.2.html
In the original UNIX systems, when a handler that was established using signal() was invoked by the delivery of a signal, the disposition of the signal would be reset to SIG_DFL, and the system did not block delivery of further instances of the signal.
Linux implements the same semantics: the handler is reset when the signal is delivered.
The behaviour of signal upon receiving the first signal varies on different implementation. Typically, it requires reinstalling the handler after receiving the signal as handler is reset to its default action:
void sig_handler(int signum)
{
signal(SIGINT, sig_handler);
printf("\nReceived signal %d\n", signum);
}
which is one of the reasons you shouldn't use signal anymore and use sigaction. You can see a bare bone example of using sigaction here.
Related
Consider the simple example below which registers for a signal handler for SIGABRT, then calls abort(). When I run it, the program terminates before printing Done but after async-signal-safe printing in the trapped signal.
This implies that SIGABRT is not a blockable signal. This seems to be supported by this StackOverflow answer. However, I cannot find any corroborating evidence of that behavior in the signal man page, which clearly states that The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored but makes no similar mention for SIGABRT.
Can someone please enlighten me on this behavior?
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static struct sigaction old_sa;
static void my_handler(int signo)
{
const char x[] = "In Handler\n";
write(STDOUT_FILENO, x, strlen(x));
}
int main()
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_handler = my_handler;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask, SIGABRT);
if (0 != sigaction(SIGABRT, &sa, &old_sa))
{
perror("sigaction");
exit(EXIT_FAILURE);
}
printf("Ready\n");
abort();
printf("Done\n");
exit(0);
}
Compiled with gcc ./try.c && ./a.out generates the following output
Ready
In Handler
Aborted
SIGABRT can be blocked. But the abort() function unblocks the signal before sending the signal.
This is specified in POSIX:
The abort() function shall override blocking or ignoring the SIGABRT signal.
You'll get the expected result if you use
kill(getpid(), SIGABRT);
instead of calling abort()
Your handler caught the signal. Then it returned. C 2018 7.22.4.1 2 says “The abort function causes abnormal program termination to occur, unless the signal SIGABRT is being caught and the signal handler does not return.” So, once your handler returns, the abort routine continues doing its thing, which is to terminate your program.
If instead of abort(); you use raise(SIGABRT); to raise the signal without calling the abort routine, then the signal handler will be called, will print, and will return, after which printf("Done\n"); will be executed.
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.
Can SIGCONT wake up the sleeping process?
I learned the day before yesterday that signal handlers fail the "sleep" of the process.
In the same way, I tried to fail "read" with a signal handler.
The code is as follows.
#include <signal.h>
#include <unistd.h>
void signal_handler(int signo)
{
write(1, "\nI've got signal\n", 17);
return;
}
int main()
{
char buf[10];
signal(SIGINT, signal_handler);
read(0, buf, 1);
write(1, buf, 1);
return 0;
}
However, after the signal handler was carried out, the process went back into I/O block state.
The following code was also executed for re-verification.
#include <signal.h>
#include <unistd.h>
void signal_handler(int signo)
{
write(1, "\nI've got signal\n", 17);
return;
}
int main()
{
char buf[10];
signal(SIGINT, signal_handler);
sleep(100);
write(1, "awake", 5);
return 0;
}
In this case, after receiving the signal handler, the process was no longer asleep.
Is there a way to get out of the blocked state after receiving the signal and continue the process? (with the input failed)
According to documentation of signal() function:
The behavior of signal() varies across UNIX versions, and has also
varied historically across different versions of Linux. Avoid its
use: use sigaction(2) instead. See Portability below.
...
Portability
The only portable use of signal() is to set a signal's disposition to
SIG_DFL or SIG_IGN. The semantics when using signal() to establish a
signal handler vary across systems (and POSIX.1 explicitly permits
this variation); do not use it for this purpose.
Thus, you should use sigaction() instead of signal().
If you replace signal() with sigaction() in your program it should work as expected.
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.
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.