exit()无法终止进程吗?

时间:2012-01-12 10:18:23

标签: c linux process exit

我的程序中注册了一个信号处理程序。收到不需要的信号(SIGABRT)后,我在信号处理程序中调用'exit(-1)'退出进程。但正如在几个ocassions中注意到的那样,它调用exit()但是无法终止进程。

该问题是随机生成的,我强烈怀疑执行exit()。

可能有任何原因或情况,exit()无法终止该过程。

感谢。

3 个答案:

答案 0 :(得分:15)

你是从信号处理程序调用{​​{1}}吗?

exit()异步信号安全函数部分,您可以看到从信号处理程序调用时保证可以正常工作的所有函数:

  

信号处理函数必须非常小心,因为其他地方的处理可能会被中断          程序执行中的一些任意点。 POSIX具有“安全功能”的概念。如果          一个信号中断不安全函数的执行,然后处理程序调用一个不安全的函数          程序的行为未定义。

     

POSIX.1-2004(也称为POSIX.1-2001技术勘误2)要求实施          保证可以在信号处理程序中安全地调用以下函数:

在那里,你可以看到man 7 signal_Exit()_exit()的功能,但显然不是abort()。所以你不应该从信号处理程序中调用它。

令人讨厌的是,即使你从一个信号处理程序(exit()任何?)调用一个不安全的函数,它只会在大部分时间工作......但并非总是如此。

答案 1 :(得分:3)

是的,有些情况,例如:

  

exit()函数应首先按照与注册相反的顺序调用atexit()注册的所有函数,但在注册之前已经调用的任何先前注册的函数之后调用函数除外。每个函数都会在注册时多次调用。如果在调用任何此类函数期间,调用longjmp()函数将终止对已注册函数的调用,则行为未定义。

     

如果通过调用atexit()注册的函数无法返回,则不应调用其余的注册函数,并且不应完成exit()处理的其余部分。如果多次调用exit(),则行为未定义。

请参阅exit上的POSIX页面。

有关详细信息,请在遇到此情况时附加调试器并查看调用堆栈。

答案 2 :(得分:1)

我遇到的问题类似于Madar描述的问题。我需要对每个信号执行一个操作并正确退出。我想通过对类似问题的几个答案进行思考,并提出以下解释/解决方案。

说明: 一个问题是exit()不应在信号处理程序中使用,因为它不是异步信号安全功能之一(请参见man signal-safety)。就是说,它可以但不能保证在信号处理程序中工作。结果,您需要调用_exit() / _Exit()(异步信号安全)。但是,它们无需调用atexit回调和静态析构函数即可立即终止该过程。我的理解是,对于某些信号,可以完成比这些功能所提供的功能更多的清洁工作。

解决方案:我想到的解决方案是为所有信号注册信号处理程序,并执行任何其他步骤。然后,您可以重置为默认处理程序,并调用异步信号安全的raise(signal_number)来重新发送信号并执行默认处理程序。

这是一个仅在SIGINT上运行默认处理程序的工作示例。我认为如果您在处理程序中使用过“失败” exit(),这太简单了。我使用备用堆栈测试了类似的代码,以处理SIGSEGV

注意:如果您希望它在多线程上下文中正常运行(例如,多个线程同时导致SIGSEGV),则需要注意同步。线程共享相同的处理程序,但具有单独的信号屏蔽。

#include <csignal>
#include <cstdlib>
#include <cstring>

#include <vector>

#include <unistd.h>

// The actual signal handler
extern "C" void handleSignal(int sig, siginfo_t *siginfo, void *) {
  // Cannot use printf() - not async-signal-safe 
  // For simplicity I use a single call to write here
  // though it is not guaranteed to write the whole message
  // You need to wrap it in a loop

  // Die only on Ctrl+C
  if(sig == SIGINT) {
    const char *msg = "Die\n";
    write(STDERR_FILENO, msg, ::strlen(msg));
    // Reset to use the default handler to do proper clean-up
    // If you want to call the default handler for every singal
    // You can avoid the call below by adding SA_RESETHAND to sa_flags
    signal(sig, SIG_DFL);
    raise(sig);
    return;
  }

  // Here we want to handle the signal ourselves
  // We have all the info available
  const char *msg = "Continue\n";
  write(STDERR_FILENO, msg, ::strlen(msg));
}

int main() {
  // You might want to setup your own alternative stack
  // eg. to handle SIGSEGV correctly
  // sigaltstack() + SA_ONSTACK flag in sa_flag

  // Prepare a signal action for handling any signal
  struct sigaction signal_action;

  signal_action.sa_sigaction = ::handleSignal;
  signal_action.sa_flags = SA_SIGINFO;
  ::sigfillset(&signal_action.sa_mask);
  // A vector of all signals that lead to process termination by default
  // (see man -s 7 signal)
  const int TERM_SIGNALS[] = {
        SIGHUP,  SIGINT,  SIGQUIT, SIGILL,    SIGABRT, SIGFPE, SIGSEGV,
        SIGPIPE, SIGALRM, SIGTERM, SIGUSR1,   SIGUSR2, SIGBUS, SIGPOLL,
        SIGPROF, SIGSYS,  SIGTRAP, SIGVTALRM, SIGXCPU, SIGXFSZ};

  // Register the signal event handler for every terminating signal
  for (auto sig : TERM_SIGNALS) {
    ::sigaction(sig, &signal_action, 0);
  }

  while(true);

  return 0;
}