如何设计一个C/c++程序,使其在接收到中断信号后能够保存一些数据
我有一个长时间运行的程序,我可能需要在它结束运行之前杀死它(比如,按Ctrl-C)。当终止(而不是运行到结束)时,程序应该能够将一些变量保存到磁盘上。我有几本大的Linux书籍,但不太确定从哪里开始。一本食谱会很有帮助的。
谢谢。!
要做到这一点,你需要让你的程序观察一些东西,例如一个全局变量,它会告诉他停止它正在做的事情。
例如,假设长时间运行的程序执行一个循环,可以这样做:
g_shouldAbort = 0;
while(!finished)
{
// (do some computing)
if (g_shouldAbort)
{
// save variables and stuff
break; // exit the loop
}
}
将g_shouldport定义为全局volatile变量,如下所示:
static volatile int g_shouldAbort = 0;
(声明它为"volatile"是非常重要的,否则编译器看到没有人在循环中写它,可能会认为if (g_shouldport)总是为假并优化它)
然后,使用其他用户建议的例如信号 API,您可以这样做:void signal_handler(int sig_code)
{
if (sig_code == SIGUSR1) // user-defined signal 1
g_shouldAbort = 1;
}
(当然,您需要注册这个处理程序,参见此处。
signal(SIGUSR, signal_handler);
然后,当你"发送"SIGUSR1信号给你的程序(例如用kill命令),g_shouldport将被设置为1,你的程序将停止它的计算。
希望有帮助!
注意:这种技术很简单,但很粗糙。当然,正如其他用户所描述的那样,使用信号和全局变量使得使用多个线程变得困难。
你想做的不是小事。您可以从使用signal
或sigaction
为SIGINT (C-c)安装信号处理程序开始,但随后硬部分开始。
主要问题是,在信号处理程序中,你只能调用异步信号安全函数(或可重入函数)。大多数库函数不能可靠地认为是可重入的。例如,stdio
函数,malloc
, free
和许多其他函数是不可重入的。
那么你怎么处理这个呢?在处理程序中设置flag
(将某些全局变量done
设置为1
)并查找EINTR
错误。在处理程序之外执行清理应该是安全的。
您正在尝试做的事情属于检查点/重新启动的范畴。
使用信号驱动的检查点/重启方案有几个大问题。一个是信号处理程序必须非常紧凑和原始。不能在信号处理程序中写入检查点。另一个问题是,当发送信号时,您的程序在执行状态的任何位置都可能是。几乎可以肯定的是,这个随机的位置并不是一个安全的点,不能让检查点从那里掉下来。然而,另一个问题是,您需要为您的程序配备一些应用程序端检查点/重新启动功能。
与其滚动自己的检查点/重启功能,我建议您考虑使用已经存在的免费功能。Linux上的GDB提供检查点/重启功能。另一种是DMTCP,参见http://dmtcp.sourceforge.net/index.html。
使用signal(2)
或sigaction(2)
将函数指针分配给SIGINT
信号,并在那里进行清理。
在保存函数中只输入一次
// somewhere in main
signal( SIGTERM, signalHandler );
signal( SIGINT, signalHandler );
void saveMyData()
{
// save some data here
}
void signalHandler( int signalNumber )
{
static pthread_once_t semaphore = PTHREAD_ONCE_INIT;
std::cout << "signal " << signalNumber << " received." << std::endl;
pthread_once( & semaphore, saveMyData );
}
如果你的进程在你写完文件之前得到2个或更多的信号,你会保存奇怪的数据