繁体   English   中英

C中止功能

[英]Abort function in C

程序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信号。 执行信号处理程序后,它不会从调用它的位置返回到主函数。 处理程序完成后,为什么不返回主函数?

程式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不同吗?

请参阅man 3 abort此文档:

除非捕获到SIGABRT信号并且信号处理程序不返回,否则这将导致进程异常终止(请参见longjmp(3) )。

还有这个:

如果SIGABRT信号被忽略,或者被返回的处理程序捕获,则abort()函数仍将终止该过程。 它通过恢复SIGABRT的默认配置,然后第二次提高信号来做到这一点。

因此,防止信号abort()中止程序的唯一方法是通过信号处理程序中的longjmp() ing。

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

abort函数发送的是SIGABRT信号,但是您捕获(或忽略)该信号并不重要, abort函数仍将退出您的进程。

从链接的手册页:

返回值

abort()函数从不返回。

根据标准,并没有完全指定如果处理SIGABRT会发生什么:

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

然而,它的规定什么是应该的:

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

因此,正确的行为是确保发生“异常终止”。 这是通过abort函数来确保的,这是最好的方法,异常终止程序是最好的,它通过尝试以各种方式终止操作来完成此操作,并且如果似乎无济于事,它会进入无限循环(至少要确保它不会返回到呼叫者,召集者)。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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