I have a registered a signal handler in my program. Upon receiving an undesired signal (SIGABRT), i call 'exit(-1)' in signal handler to exit the process. But as noticed on few ocassions, it calls exit() but fails to terminate the process.
The issue was randomly generated and I strongly suspect on execution of exit().
Can there be any reasons or cases in which the exit() can fail to terminate the process.
Thanks.
Are you calling exit() from the signal handler?
In man 7 signal, section Async-signal-safe functions you can see all the functions that are guaranteed to work when called from an signal handler:
A signal handler function must be very careful, since processing elsewhere may be interrupted at
some arbitrary point in the execution of the program. POSIX has the concept of "safe function". If
a signal interrupts the execution of an unsafe function, and handler calls an unsafe function, then
the behavior of the program is undefined.
POSIX.1-2004 (also known as POSIX.1-2001 Technical Corrigendum 2) requires an implementation to
guarantee that the following functions can be safely called inside a signal handler:
There you can see functions _Exit(), _exit() and abort(), but notably not exit(). So you should not call it from a signal handler.
The nasty thing is that even if you call an unsafe function from a signal handler (printf() any?) it will just work most of the time... but not always.
Yes, there are some circumstances, such as:
The exit() function shall first call all functions registered by atexit(), in the reverse order of their registration, except that a function is called after any previously registered functions that had already been called at the time it was registered. Each function is called as many times as it was registered. If, during the call to any such function, a call to the longjmp() function is made that would terminate the call to the registered function, the behavior is undefined.
If a function registered by a call to atexit() fails to return, the remaining registered functions shall not be called and the rest of the exit() processing shall not be completed. If exit() is called more than once, the behavior is undefined.
See the POSIX page on exit.
For more information, attach a debugger when you reach the situation and take a look at the call stack.
I had analogous problem to the one described by Madar. I needed to perform an action for every signal and quit properly. I wondered through a couple of answers to similar issues and came up with the following explanation/solution.
Explanation:
One issue is that exit() should not be used in signal handlers because it is not one of the async-signal-safe functions (see man signal-safety). This is to say that it may but is not guaranteed to work in signal handlers. As a result you would need to call _exit()/_Exit() (which are async-signal-safe). These however terminate the process instantly, without calling the atexit callbacks and static destructors. My understanding is that for some signals a bit more cleaning can be done than what those functions provide.
Solution: The solution I came up with is to register your signal handler for all signals and do any extra steps. Then you can reset to the default handler and call raise(signal_number), which is async-signal-safe, to re-send the singal and so execute the default handler.
Here is a working example that runs default handler only on SIGINT. I think this is too simple to experience the "failing" exit() if you used it in the handler. I tested similar code with an alternative stack to also handle SIGSEGV.
Note If you want this to work properly in multi-threaded context (e.g. multiple threads causing SIGSEGV at the same time) you need to take some care about synchronization. Threads share the same handler but have separate signal masking.
#include <csignal>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <unistd.h>
// The actual signal handler
extern "C" void handleSignal(int sig, siginfo_t *siginfo, void *) {
// Cannot use printf() - not async-signal-safe
// For simplicity I use a single call to write here
// though it is not guaranteed to write the whole message
// You need to wrap it in a loop
// Die only on Ctrl+C
if(sig == SIGINT) {
const char *msg = "Die\n";
write(STDERR_FILENO, msg, ::strlen(msg));
// Reset to use the default handler to do proper clean-up
// If you want to call the default handler for every singal
// You can avoid the call below by adding SA_RESETHAND to sa_flags
signal(sig, SIG_DFL);
raise(sig);
return;
}
// Here we want to handle the signal ourselves
// We have all the info available
const char *msg = "Continue\n";
write(STDERR_FILENO, msg, ::strlen(msg));
}
int main() {
// You might want to setup your own alternative stack
// eg. to handle SIGSEGV correctly
// sigaltstack() + SA_ONSTACK flag in sa_flag
// Prepare a signal action for handling any signal
struct sigaction signal_action;
signal_action.sa_sigaction = ::handleSignal;
signal_action.sa_flags = SA_SIGINFO;
::sigfillset(&signal_action.sa_mask);
// A vector of all signals that lead to process termination by default
// (see man -s 7 signal)
const int TERM_SIGNALS[] = {
SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGABRT, SIGFPE, SIGSEGV,
SIGPIPE, SIGALRM, SIGTERM, SIGUSR1, SIGUSR2, SIGBUS, SIGPOLL,
SIGPROF, SIGSYS, SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ};
// Register the signal event handler for every terminating signal
for (auto sig : TERM_SIGNALS) {
::sigaction(sig, &signal_action, 0);
}
while(true);
return 0;
}
Related
Apparently, signal() resets to SIG_DFL (default) which is why another another signal of the same type arriving immediately after the first causes default behaviour to occur. My question is: why does this thing happen only if the second signal occurs immediately? Shouldn't it happen regardless of whether the second signal is immediate or not considering that the signal has been reset? I am mighty confused. Please clarify.
The specification of §7.14.1.1 The signal function in the C standard is carefully worded to allow quite a variety of behaviours, mainly because when the standard was created, existing implementations of signal() provided a wide variety of behaviours.
The POSIX specification for signal() follows the C standard, but places fewer restrictions on the actions in the signal handler.
The C standard says:
#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);
Description
2 The signal function chooses one of three ways in which receipt of the signal number sig is to be subsequently handled. If the value of func is SIG_DFL, default handling for that signal will occur. If the value of func is SIG_IGN, the signal will be ignored. Otherwise, func shall point to a function to be called when that signal occurs. An invocation of such a function because of a signal, or (recursively) of any further functions called by that invocation (other than functions in the standard library),251) is called a signal handler.
3 When a signal occurs and func points to a function, it is implementation-defined whether the equivalent of 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; in the case of SIGILL, the implementation may alternatively define that no action is taken. Then the equivalent of (*func)(sig); is executed. If and when the function returns, if the value of sig is SIGFPE, SIGILL, SIGSEGV, or any other implementation-defined value corresponding to a computational exception, the behavior is undefined; otherwise the program will resume execution at the point it was interrupted.
4 If the signal occurs as the result of calling the abort or raise function, the signal handler shall not call the raise function.
5 If the signal occurs other than as the result of calling the abort or raise function, the behavior is undefined if the signal handler refers to any object with static or thread storage duration that is not a lock-free atomic object other than by assigning a value to an object declared as volatile sig_atomic_t, or the signal handler calls any function in the standard library other than the abort function, the _Exit function, the quick_exit function, or the signal function with the first argument equal to the signal number corresponding to the signal that caused the invocation of the handler. Furthermore, if such a call to the signal function results in a SIG_ERR return, the value of errno is indeterminate.252)
6 At program startup, the equivalent of
signal(sig, SIG_IGN);
may be executed for some signals selected in an implementation-defined manner; the equivalent of
signal(sig, SIG_DFL);
is executed for all other signals defined by the implementation.
7 Use of this function in a multi-threaded program results in undefined behavior. The implementation shall behave as if no library function calls the signal function.
The classic structure for a signal handler assumed that the system would reset the signal handling to SIG_DFL and carefully restored the signal handler as the first action in the handler:
static void handler(int signum)
{
signal(signum, handler);
// Other actions
}
However, there is a small window of time during which the signal handling is the default, so if a second signal arrives during that (short) interval, the program may be stopped. That's why "another another signal of the same type arriving immediately after the first causes default behaviour to occur". And because the handler is written as shown, there is only a small interval during which the default signal handling is in effect. If the handler does not reset the signal handling, then it doesn't matter when the second signal arrives.
If you want control over the signal handling, use POSIX
sigaction(). It has options to support all the behaviours of signal() and provides much better control. By default, it does not reset the signal handling to SIG_DFL; it avoids that window of vulnerability.
I have a program with a function registered with atexit():
atexit(exitFunc);
And a signal handler like this:
void sigHandler(int sigNo)
{
switch (sigNo)
{
case SIGINT:
case SIGTERM:
case SIGHUP:
exit(0);
break;
}
}
I have checked that first time I send SIGINT to the process, exitFunc() is called when exit(0) is executed. However, if the process is still running (exitFunc() make take a while) and I send SIGNIT again, exitFunc() is not called again when exit(0) terminates the process instantanly.
Is this normal behaviour? A function registered with atexit() is only called one time? Should I re-registered exitFunc() again if I want it to be called next time I reach exit(0)?
(I have had a look to atexit documentation, but that specific point is not clear)
It seems clear from man 3 atexit that functions registered with atexit are only called once:
The same function may be registered multiple times: it is called once for each registration.
If you want the function to be called multiple times, you should re-register it multiple times when/if needed. However, be careful, since what you are trying to accomplish has very dubious utility. If whatever you are doing requires to re-register the same function with atexit after exit() is called, you might want to think about other approaches.
In your example, since sigHandler() is a signal handler, you are restricted and can only call async signal safe functions, see man 7 signal safety for a list. Note that exit() is not amongst those, so calling exit() from sigHandler() is undefined behavior. Whatever the specification says, you're breaking it. The only way you can safely exit from a signal handler it by using _exit() (note the leading underscore), which does a direct exit without any cleanup.
If you need to do cleanup, but you still want to exit from a signal handler, then do the cleanup in the signal handler itself and call _exit() when done. If you need non async signal safe functions to do cleanup, use the signal handler to simply set a global variable and then check it when needed in your main program: if set, do the cleanup and exit (now you can use exit()).
I am trying to save certain information about the signal in a file, e.g. time it was caught, etc, in the signal handler.
void sig_handler(int signo){
curr_signal = time(NULL);
receivedtime[z] = curr_signal;
signumber = signo;
diff = curr_signal - receivedtime[z-1];
z++;
write(f, &diff, sizeof(diff));
}
I have updated my program to write inside the file (f). My problem is that nothing is being written inside the file.The file is being created.
Be careful about the calls from inside your signal handler.
From man 7 signal:
Async-signal-safe functions
A signal handler function must be very careful, since processing else‐
where may be interrupted at some arbitrary point in the execution of
the program. POSIX has the concept of "safe function". If a signal
interrupts the execution of an unsafe function, and handler either
calls an unsafe function [...],
then the behavior of the program is undefined.
And follows a list of the functions that are async-signal-safe.
Your signal handler calls:
time(): ok, it is async-signal-safe
fprintf(): nope, it is not.
The existing answer tells you why it doesn't work: you call signal-unsafe functions in your signal handler. I want to add what to do instead: it's best practice to do as little as possible inside the handler and instead inform your "normal" code about the signal. This can be done using a volatile sig_atomic_t variable. Just update your data structures and set a volatile sig_atomic_t to 1. In your main code, check that variable periodically, and if it is set, do the desired output and set it back to 0.
How signal handler takes the signal number without passing arguments to signalhandler() function in main()?
For example in the below source code inside main() passing 2nd argument of signal system call as signalhandler without any argument in signal handler but while coming to signalhandler definition it is collecting an argument with name sig_num ...
How actually it is possible?
According ANSI C if we don't pass any arguments then function definition will should not collect any argument.
Please help me regarding this.
#include<stdio.h>
#include<signal.h>
void signalhandler(int sig_num)
{
printf("caught signal number: %d\n", sig_num);
}
int main(void)
{
while(1)
{
printf("hello world\n");
sleep(1);
signal(SIGINT, signalhandler);
}
}
Your function signalhandler is not called when you give it as an argument of signal(2). The prototype is :
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
So you can see that the second argument is a function pointer. Your function signalhandler will be called later (when a signal is delivered) and the code that will call it will give it an int argument.
Take a look at some other function pointer examples to understand better this callback mechanism.
The signal API registers the provided function pointer to be called upon receipt of the specified signal. It is possible to register the same function pointer against multiple signals, so the function when called will be provided the signal value that was received.
In Linux (and all operating systems that I am aware of) signal delivery is asynchronous to the process's execution, and the notification is immediate. The OS behaves as if it preempts the program and injects the signal handler function call on top of whatever the program is currently doing. The OS is aware which signal was delivered, and passes it as the parameter to the function call.
Note: It is possible to use raise to generate a synchronous signal to the program. But, it will typically use the OS service to deliver it to the process rather than directly invoke the signal handler.
If you use a debugger and set a breakpoint within your signal handler, and deliver the appropriate signal, you might see that the backtrace will show that the signal handler is being injected by the OS.
For example, consider the program:
void signalhandler (int sig)
{
write(2, "signal!\n", 8);
}
void foo (void)
{
for (;;) {}
}
int main (void)
{
signal(SIGINT, signalhandler);
foo();
}
When running the program in gdb, you can deliver the signal with the signal command. The resulting backtrace would look like:
(gdb) signal SIGINT
Continuing with signal SIGINT.
Breakpoint 1, signalhandler (sig=2) at s.c:5
5 write(2, "signal!\n", 8);
(gdb) bt
#0 signalhandler (sig=2) at s.c:5
#1 <signal handler called>
#2 foo () at s.c:10
#3 0x08048498 in main () at s.c:16
Note: It is important to realize that the signal handler function call should not be treated like a regular function call. Since the OS injects the call, it has limitations that are discussed below.
The signal call can be injected at some arbitrary place in your code execution. For this reason, POSIX mandates certain functions to be safe to be called from signal handlers. Functions that are not designed to be re-entrant run the risk of being in an inconsistent state if it is interrupted by a injected call to the signal handler function, which then in turn calls the interrupted function.
As an example of a problem that could happen, suppose you are writing code that is manipulating a data structure, such as removing a node from a linked list. However, if the pointers of the elements have not been completely fixed up when the signal is delivered, then the signal handler may see a corrupted linked list. This situation can happen more often than you might assume, especially if you call a function that requires heap allocation.
Thus, it is often safest to make the signal handler dead simple. For example, the signal handler may simply set a flag, and your application code would then need code to detect whether or not the flag was set.
I will explain in simple terms.
Signal are way of delivering messages to a program. For example when the program is running in a terminal window and you press Ctrl+C then the terminal window sends SIGINT to the program.
Now, for each process, the kernel maintains a table which maps from each signal to what is to be called when the signal is received. By default, on SIGINT, the behavior is set to terminate. But, a program can change the default behavior for some signals by a call to the function signal.
Eg. signal(SIGINT, funcHandler) changes the behavior on receiving SIGINT from termination to calling funcHandler. But it never calls funcHandler. funcHandler will be called when the program receives a SIGINT.
Now, when the program receives SIGINT then the kernel looks up the table to decide which function to call(say handler), then the kernel sets up handler's stack with the received signal as the argument and then returns to the program. As effect funcHandler is called with signal_number as the argument in the program's context.
I am a novice to signal handling using c language. I am analyzing below signal handling code which extracted from specific resource.
Here is that code .
#include <stdio.h>
#include <signal.h>
void intproc();
void quitproc();
main()
{
int i;
signal(SIGINT,intproc);
signal(SIGQUIT,quitproc);
printf("Ctrl+c is disabled. Use ctrl+\\ to quit\n");
for (i=0;;i++) {
printf("In an infinite loop...\n");
sleep(200);
}
}
void intproc()
{
signal(SIGINT,intproc);
printf("You have pressed ctrl+c.\n");
}
void quitproc()
{ signal(SIGQUIT,intproc);
printf("You have pressed ctrl+\\. Now the program quits.\n");
exit(0);
}
what I want to know is why we call again Signal handler "(SIGINT,intproc)" inside intproc() function ?
I tried to run this code without that signal handler within that function ,and its also working .
This is very old code. In the old days (perhaps SunOS3, 1990-s) a signal handler was automatically uninstalled when executed. See signal(2) (difference between SysV and BSD behavior) and avoid using signal.
Carefully read signal(7) then use sigaction(2). Don't use signal(2). Care about async signal safe functions (the only ones you can call from a signal handler; you should not use printf inside a signal handler!). Consider simply setting some volatile sig_atomic_t global (or static) variable inside your signal handler (and test it outside).
Read Advanced Linux Programming which explains these things in detail.
After the function intproc has completed, the program carries on, but the signal action is restored to the default. When it receives a second SIGINT signal, the program takes the default action, which is to terminate the program.
If you want to retain the signal handler, you would need to re-establish it by calling signal again.
This is the reason you should always prefer the more robust sigaction over the signal function.