C中的中止功能

时间:2015-11-06 11:53:17

标签: c linux unix abort

计划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");
}

输出1:

$ ./a.out 
PID: 32235
Hai
Signal handled
Aborted (core dumped)
$

根据参考,中止功能的作用类似于raise(SIGABRT)。因此,abort()函数生成的信号是SIGABRT。为此,我创建了上述程序。

在该程序中,处理SIGABRT信号。在执行信号处理程序之后,它不会返回到调用它的主函数。为什么在处理程序完成后它不会返回main函数?

计划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");
}

输出2:

$ ./a.out 
PID: 32247
Hai
Hai
Hai
Signal handled
Hai
Signal handled
Hai
Hai
^C
$ 

与程序1不同,程序2按预期执行。在上面的程序中,信号通过命令行通过kill命令发送到进程,如下所示。

$ kill -6 32247
$ kill -6 32247

因此,一旦信号发生,执行处理程序函数,然后返回主函数。但它不会发生在程序1中。为什么它会像这样? abort函数和SIGABRT有什么不同?

5 个答案:

答案 0 :(得分:6)

请参阅man 3 abort中的这篇文档:

  

这导致进程异常终止,除非SIGABRT信号被捕获且信号处理程序未返回(参见longjmp(3))。

还有这个:

  

如果SIGABRT信号被忽略,或被返回的处理程序捕获,abort()函数仍将终止该进程。它通过恢复SIGABRT的默认处置,然后再次提升信号来实现此目的。

因此,阻止abort()中止程序的唯一方法是longjmp()来自信号处理程序。

答案 1 :(得分:2)

Libc实施abort()。在他们的实现中,abort()检查进程是否仍然存在,因为abort()正在 raise(SIGABRT)之后执行。如果是,则表示用户已处理SIGABRT。根据文档,它并不重要,因为该过程仍将退出:

您可以在GLIBC源代码(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;
}

答案 2 :(得分:1)

abort函数发送SIGABRT信号为真,但如果你捕获(或忽略)该信号abort函数则无关紧要将仍然退出您的流程。

从链接的手册页:

  

返回值

     

abort()函数永远不会返回。

答案 3 :(得分:0)

根据标准,如果处理SIGABRT

,则不完全具体说明会发生什么
  

中止功能导致程序异常终止,   除非信号SIGABRT被捕获并且信号处理程序被捕获   不回来是否使用未写入的缓冲数据的开放流   刷新,打开的流被关闭,或临时文件被删除   实现定义。实现定义的状态形式   不成功的终止通过方式返回到主机环境   函数调用raise(SIGABRT)。

然而,它指明应该发生什么:

  

中止功能不会返回其调用者。

因此,正确的行为是确保发生“异常终止”。这通过abort函数确保最好地异常终止程序,它通过尝试以各种方式终止来做到这一点,如果没有什么似乎可以做到它进入无限循环(并且至少确保它不会回到来电者那里。)

答案 4 :(得分:0)

他们不一样。 abort函数调用raise(SIGABRT)两次。如果您为SIGABRT定义了一个处理程序,它将首先调用您的处理程序并在此之后调用默认处理程序。