基于命令行的程序在当今各个操作系统上都应用得非常广泛,但是这些程序没有GUI,所以用户要终止它们时通常都是按"Ctrl+C"或用系统命令直接结束进程,这样可能会造成数据丢失或者一些特殊资源没有释放,因此须要编写一段代码在程序被终止前作一些自己的结束工作。
ANSI C已经提供了一个标准函数signal来捕获命令行的中断信号。
这是该函数在Linux下GCC中的原形:
sighandler_t signal( int signum, sighandler_t handler);

这是该函数在Microsoft C中的原形:

以上两个函数声明表面上看是乎不同,其实是一样的。
下面介绍该函数的用法。
第一个参数signum为要捕获的信号值,ANSI C定义了以下信号值。
SIGABRT | 程序调用了abort函数 |
SIGFPE | 浮点数错误 |
SIGILL | 违例的调用 |
SIGINT | 用户按了CTRL+C |
SIGSEGV | 违例的内存访问 |
SIGTERM | 用户调用了exit函数 |
第二个参数handler是一个指向捕获信号回调函数的指针,如果产生了signum指定的信号将执行handler指向的回调函数。
如果singal函数执行成功则返回一个指向上次指定的信号捕获函数的指针,如果执行失败则返回SIG_ERR。
通常要判断程序被终止只须要捕获SIGABRT、SIGINT和SIGTERM这三个信号就行了。
现在来看一个例子:
#include < singal.h >
int end = 0 ;
/**
* 中断信号处理函数。
*/
void signal_handler( int sig )
{
switch ( sig )
{
case SIGINT:
case SIGABRT:
case SIGTERM:
end = 1 ; // 标记为结束
break ;
default :
// 其它信号的处理
break ;
}
}
int main( int argc, char * argv[] )
{
if ( signal(SIGINT,signal_handler) == SIG_ERR ) return - 1 ; // 设置处理函数失败
while ( ! end ) // 等待结束
{
#ifdef WIN32
_sleep( 1000 );
# else
sleep( 1 );
#endif
printf( " wait... " );
}
printf( " end. " );
return 0 ;
}
将以上代码编译为可执行程序,运行起来然后按Ctrl+C看看有什么效果吧。
但是要注意的是在Windows下命令行也是一个窗口,如果用户直接把命令行窗口关闭的话是不会触发中断信号的,所以Windows API提供了一个函数SetConsoleCtrlHandler用来接收Windows控制信号。
这是该函数的原形:

参数HandlerRoutine为接收控制信号的回调函数。参数Add决定是添加还是删除回调函数,这一点和signal函数是不同的,通过SetConsoleCtrlHandler可以添加多个接收控制信号的回调函数。
接收控制信号的回调函数原形:

参数dwCtrlType为控制信号的类型,是下列值之一。
CTRL_C_EVENT | 用户按了Ctrl+C。 |
CTRL_BREAK_EVENT | 用户按了CTRL+BREAK。 |
CTRL_CLOSE_EVENT | 控制台窗口被关闭。 |
CTRL_LOGOFF_EVENT | 当前用户注销。 |
CTRL_SHUTDOWN_EVENT | 系统关闭。 |
如果回调函数返回非0值,系统将不再处理这个控制信号。如果回调函数返回0,系统就将控制信号交给下一个回调函数处理。
SetConsoleCtrlHandler函数和signal的用法大致相同,这里就不多说了。