我的程序中注册了一个信号处理程序。收到不需要的信号(SIGABRT)后,我在信号处理程序中调用'exit(-1)'退出进程。但正如在几个ocassions中注意到的那样,它调用exit()但是无法终止进程。
该问题是随机生成的,我强烈怀疑执行exit()。
可能有任何原因或情况,exit()无法终止该过程。
感谢。
答案 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;
}