6

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?

AlG
  • 14,697
  • 4
  • 41
  • 54
mohangraj
  • 9,842
  • 19
  • 59
  • 94
  • i think you want to know the difference between these two calls. [Possible duplicate here](http://stackoverflow.com/questions/20212927/difference-between-raisesigabrt-and-abort-methods) – Bilal Nov 06 '15 at 12:06
  • 1
    Read [signal(7)](http://man7.org/linux/man-pages/man7/signal.7.html). Don't call `printf` inside signal handlers. – Basile Starynkevitch Nov 06 '15 at 12:10
  • @BasileStarynkevitch: Actually, AIUI, you can call `printf()` from a signal handler if you are sure that the signal is not being generated asynchronously. A call to `abort()` is not async, while `kill -6` from the shell is async. – rodrigo Nov 06 '15 at 13:36

5 Answers5

6

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, the abort() function will still terminate the process. It does this by restoring the default disposition for SIGABRT 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.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
2

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;
}
Jonathon Reinhart
  • 132,704
  • 33
  • 254
  • 328
1

The abort function sends the SIGABRT signal that's true, but it doesn't matter if you catch (or ignore) that signal, the abort function will still exit your process.

From the linked manual page:

RETURN VALUE

The abort() function never returns.

Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
0

According to the standard it's not entirely specified what should happen if you handle SIGABRT:

The abort function causes abnormal program termination to occur, unless the signal SIGABRT is being caught and the signal handler does not return. Whether open streams with unwritten buffered data are flushed, open streams are closed, or temporary files are removed is implementation-defined. An implementation-defined form of the status unsuccessful termination is returned to the host environment by means of the function call raise(SIGABRT) .

However it's specified what should not happen:

The abort function does not return to its caller.

So the correct behavior is to ensure that an "abnormal termination" occurs. This ensured by the abort function doing it's very best to terminate the program abnormally, it does this by trying to terminate in various ways and if nothing seem to do the trick it enters an infinite loop (and at least ensure that it does not return to the caller).

skyking
  • 13,817
  • 1
  • 35
  • 57
0

They are not the same. The abort function calls raise(SIGABRT) twice. If you defined a handler for the SIGABRT, it will call your handler first and call the default one after that.

Neuron
  • 5,141
  • 5
  • 38
  • 59