简体   繁体   中英

Abort function in C

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?

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.

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;
}

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.

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).

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM