我有一个我想在程序退出时运行的函数:
void foo() {
std::cout<< "Exiting" << std::endl;
}
如果程序存在,我该如何注册它以运行,无论它何时以及为什么退出 - 由于signal,exit()调用等?
答案 0 :(得分:31)
您可以在cstdlib
标题中使用恰当命名的std::atexit
函数:
#include <cstdlib>
void exiting() {
std::cout << "Exiting";
}
int main() {
std::atexit(exiting);
}
当调用atexit
函数或程序从{{1}返回时,系统将维护一个用exit
注册的函数堆栈,并按照其注册的相反顺序调用它们。 }。您可以通过这种方式注册至少32个功能。
答案 1 :(得分:3)
您可以将它放在具有全局实例的类的析构函数中。
class SomeGlobalStuff {
~SomeGlobalStuff() {
foo();
}
static SomeGlobalStuff instance;
};
// putting this in a single compilation unit.
SomeGlobalStuff SomeGlobalStuff::instance instance;
但与任何其他方法一样,您必须记住,如果您无法保证它仍然存在,则您无法使用任何数据。全局对象的重新分配是以任意顺序完成的,所以基本上,你不能在foo()函数中使用std :: cout。 atexit()在这方面更糟糕,因为它是否在销毁全局对象之前或之后执行取决于编译器和编译器选项。
无论如何,你仍然需要正确处理信号。您必须选择要处理的信号和不处理的信号(您很可能不想处理SIGSEGV)。你无法逃避信号处理。请记住,信号可能会在任何时候中断您的程序(除非被屏蔽),因此您的数据结构可能在更新过程中处于任意状态。
答案 2 :(得分:2)
我作为Linux用户回答,但所有这些都适用于Windows。
我有类似的问题,所以希望我可以总结以前的答案并加上我的两分钱。
信号和abort()
:^C
和^Z
可以“拦截”以在退出之前调用您的函数,大概是使用exit()。无法截获没有击键的信号SIGQUIT
AKA ^\
和SIGKILL
。这是使用csignal
标头和C ++ lambda的示例。
#include <iostream>
#include <csignal>
#include <cstdlib>
using namespace std;
int main()
{
//signal requires lam take an int parameter
//this parameter is equal to the signals value
auto lam =
[] (int i) { cout << "aborting" << endl; exit(0); };
//^C
signal(SIGINT, lam);
//abort()
signal(SIGABRT, lam);
//sent by "kill" command
signal(SIGTERM, lam);
//^Z
signal(SIGTSTP, lam);
while(1)
{
}
return 0;
}
退出:由于我在上面的示例中使用了exit()
,因此必须小心。如果正在运行的函数是仅需要运行一次的清理函数,则可以使用静态变量has_run
。或者在上面的例子中,raise()
是一个你无法拦截的信号。但那些往往会带来核心垃圾堆,只是感觉很脏。你的选择,在这里。
#include <cstdlib>
#include <iostream>
using namespace std;
int main()
{
//called with no parameters
auto lam = [] () { cout << "at exit"; };
atexit(lam);
return 0;
}
请注意,c ++ 11添加了一个quick_exit
,其中包含at_quick_exit
,其行为与上述相同。但是quick_exit
没有执行清理任务。相反,调用exit
对象析构函数并关闭C流,只有自动存储变量无法清除。
答案 3 :(得分:1)
在进程退出后重新获得控制权的唯一方法(在Unix和类Unix操作系统中)为wait(2)
。如果没有电源故障,内核恐慌或强制重启,这应该可行:
#include <sys/types.h>
#include <sys/wait.h>
#include <iostream>
int AtExit() {
pid_t pid = fork();
if(pid < 0) return pid;
if(pid == 0) return pid;
pid = waitpid(pid, 0, 0);
return pid;
}
int main () {
if(AtExit()) {
std::cout << "Exiting\n";
return 0;
}
std::cout << 7 << "\n";
}