简体   繁体   English

C中止功能

[英]Abort function in C

Program 1: 程序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: 输出1:

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

As per the reference, the abort function works like raise(SIGABRT) . 根据参考,中止函数的工作方式类似于raise(SIGABRT) So, the signal generated by abort() function is SIGABRT. 因此, abort()函数生成的信号为SIGABRT。 So for that I created the above program. 因此,我创建了上面的程序。

In that program, SIGABRT signal is handled. 在该程序中,将处理SIGABRT信号。 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: 程式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: 输出2:

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

Unlike program 1, program 2 executes as expected. 与程序1不同,程序2按预期执行。 In the above program, the signals are sent to the process via command line through the kill command as shown below. 在上面的程序中,信号通过kill命令通过命令行发送到进程,如下所示。

$ 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? 但是它不会在程序1中发生。为什么它会表现为这样? The abort function and SIGABRT are different? abort功能和SIGABRT不同吗?

See this piece of documentation from man 3 abort : 请参阅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) ). 除非捕获到SIGABRT信号并且信号处理程序不返回,否则这将导致进程异常终止(请参见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. 如果SIGABRT信号被忽略,或者被返回的处理程序捕获,则abort()函数仍将终止该过程。 It does this by restoring the default disposition for SIGABRT and then raising the signal for a second time. 它通过恢复SIGABRT的默认配置,然后第二次提高信号来做到这一点。

So the only way you can prevent abort() from aborting your program is by longjmp() -ing from the signal handler. 因此,防止信号abort()中止程序的唯一方法是通过信号处理程序中的longjmp() ing。

Libc implements abort() . Libc实现abort() In their implementation, abort() checks to see if the process is still alive, because abort() is executing after the raise(SIGABRT) . 在其实现中, abort()检查该进程是否仍然存在,因为abort()raise(SIGABRT) 之后执行。 If it is, then it knows that the user has handled SIGABRT . 如果是,则知道用户已处理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 ): 您可以在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;
}

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. abort函数发送的是SIGABRT信号,但是您捕获(或忽略)该信号并不重要, abort函数仍将退出您的进程。

From the linked manual page: 从链接的手册页:

RETURN VALUE 返回值

The abort() function never returns. abort()函数从不返回。

According to the standard it's not entirely specified what should happen if you handle SIGABRT : 根据标准,并没有完全指定如果处理SIGABRT会发生什么:

The abort function causes abnormal program termination to occur, unless the signal SIGABRT is being caught and the signal handler does not return. 异常终止功能导致程序异常终止,除非捕获到信号SIGABRT并且信号处理程序不返回。 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) . 状态未成功终止的实现定义形式通过函数调用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). 这是通过abort函数来确保的,这是最好的方法,异常终止程序是最好的,它通过尝试以各种方式终止操作来完成此操作,并且如果似乎无济于事,它会进入无限循环(至少要确保它不会返回到呼叫者,召集者)。

They are not the same. 它们不一样。 The abort function calls raise(SIGABRT) twice. abort函数两次调用raise(SIGABRT) If you defined a handler for the SIGABRT , it will call your handler first and call the default one after that. 如果您为SIGABRT定义了一个处理程序,它将首先调用您的处理程序,然后再调用默认处理程序。

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

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