Program 1:
#include<stdio.h>
#include<signal.h>
void handler(int sig);
void main()
{
printf("PID: %d\n",getpid());
signal(SIGABRT,handler);
while(1){
printf("Hai\n");
sleep(1);
abort();
}
}
void handler(int sig)
{
printf("Signal handled\n");
}
Output 1:
$ ./a.out
PID: 32235
Hai
Signal handled
Aborted (core dumped)
$
As per the reference, the abort function works like raise(SIGABRT)
. So, the signal generated by abort()
function is SIGABRT. So for that I created the above program.
In that program, SIGABRT signal is handled. After the execution of signal handler, it doesn't return to the main function from where it is called. Why does it not return to the main function after the handler is completed?
Program 2:
#include<stdio.h>
#include<signal.h>
void handler(int sig);
void main()
{
printf("PID: %d\n",getpid());
signal(SIGABRT,handler);
while(1){
printf("Hai\n");
sleep(1);
}
}
void handler(int sig)
{
printf("Signal handled\n");
}
Output 2:
$ ./a.out
PID: 32247
Hai
Hai
Hai
Signal handled
Hai
Signal handled
Hai
Hai
^C
$
Unlike program 1, program 2 executes as expected. In the above program, the signals are sent to the process via command line through the kill command as shown below.
$ kill -6 32247
$ kill -6 32247
So once the signal occurred, the handler function executed and then it returns to the main function. But it does not happen in program 1. Why does it behave like this? The abort
function and SIGABRT are different?
In the C Programming Language, the abort function raises the SIGABRT signal, and causes abnormal program termination that returns an implementation defined code indicating unsuccessful termination.
The abort function causes abnormal program termination. This does not execute cleanup functions registered with atexit or on_exit . This function actually terminates the process by raising a SIGABRT signal, and your program can include a handler to intercept this signal; see Signal Handling.
abort sends a SIGABRT signal, exit just closes the application performing normal cleanup. You can handle an abort signal however you want, but the default behavior is to close the application as well with an error code. abort will not perform object destruction of your static and global members, but exit will.
On many computer operating systems, a computer process terminates its execution by making an exit system call. More generally, an exit in a multithreading environment means that a thread of execution has stopped running. For resource management, the operating system reclaims resources (memory, files, etc.)
See this piece of documentation from man 3 abort
:
This results in the abnormal termination of the process unless the SIGABRT signal is caught and the signal handler does not return (see
longjmp(3)
).
And also this:
If the
SIGABRT
signal is ignored, or caught by a handler that returns, theabort()
function will still terminate the process. It does this by restoring the default disposition forSIGABRT
and then raising the signal for a second time.
So the only way you can prevent abort()
from aborting your program is by longjmp()
-ing from the signal handler.
Libc implements abort()
. In their implementation, abort()
checks to see if the process is still alive, because abort()
is executing after the raise(SIGABRT)
. If it is, then it knows that the user has handled SIGABRT
. According to the documentation, it doesn't matter, because the process will still exit:
You can see the exact implementation in the GLIBC source code (stdlib/abort.c
):
/* Cause an abnormal program termination with core-dump. */
void
abort (void)
{
struct sigaction act;
sigset_t sigs;
/* First acquire the lock. */
__libc_lock_lock_recursive (lock);
/* Now it's for sure we are alone. But recursive calls are possible. */
/* Unlock SIGABRT. */
if (stage == 0)
{
++stage;
if (__sigemptyset (&sigs) == 0 &&
__sigaddset (&sigs, SIGABRT) == 0)
__sigprocmask (SIG_UNBLOCK, &sigs, (sigset_t *) NULL);
}
/* Flush all streams. We cannot close them now because the user
might have registered a handler for SIGABRT. */
if (stage == 1)
{
++stage;
fflush (NULL);
}
/* Send signal which possibly calls a user handler. */
if (stage == 2)
{
/* This stage is special: we must allow repeated calls of
`abort' when a user defined handler for SIGABRT is installed.
This is risky since the `raise' implementation might also
fail but I don't see another possibility. */
int save_stage = stage;
stage = 0;
__libc_lock_unlock_recursive (lock);
raise (SIGABRT);
__libc_lock_lock_recursive (lock);
stage = save_stage + 1;
}
/* There was a handler installed. Now remove it. */
if (stage == 3)
{
++stage;
memset (&act, '\0', sizeof (struct sigaction));
act.sa_handler = SIG_DFL;
__sigfillset (&act.sa_mask);
act.sa_flags = 0;
__sigaction (SIGABRT, &act, NULL);
}
/* Now close the streams which also flushes the output the user
defined handler might has produced. */
if (stage == 4)
{
++stage;
__fcloseall ();
}
/* Try again. */
if (stage == 5)
{
++stage;
raise (SIGABRT);
}
/* Now try to abort using the system specific command. */
if (stage == 6)
{
++stage;
ABORT_INSTRUCTION;
}
/* If we can't signal ourselves and the abort instruction failed, exit. */
if (stage == 7)
{
++stage;
_exit (127);
}
/* If even this fails try to use the provided instruction to crash
or otherwise make sure we never return. */
while (1)
/* Try for ever and ever. */
ABORT_INSTRUCTION;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With