1、前言
信号有三种处理方式
1)执行默认动作;
2)忽略;//什么都不做,忽略默认动作
3)捕捉;//捕捉到信号后,不执行默认动作,而让其执行捕捉函数中内容
产生信号的五种形式:
1)按键产生,如 ctrl +c、ctrl+z,ctrl+\
2)系统调用产生,如kill函数,raise函数,abort函数
3)软件条件产生,如定时器alarm,调用函数,开始计时,时间到,发送信号
4)硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
5)命令产生,如kill命令
————————————————
#define SIG_ERR ((void(*)(_SIG_ARGS)) -1)
#define SIG_DFL ((void(*)(_SIG_ARGS)) 0)
#define SIG_IGN ((void(*)(_SIG_ARGS)) 1)
#define SIG_HOLD ((void(*)(_SIG_ARGS)) 2)
以上宏定义是将-1、0、1、2强制类型转换成了指针,即以-1、0、1、2为地址的空间。
2、我们在捕捉函数中,会将一个标志位置1,
该标志位==1时,会完成清理线程资源,终止死循环等工作。
编程中经常会用到终止进程,比如,终端中输入kill -15 进程号 命令来终止进程,终止前我们需要先做以下工作:
1)清理进程用到的系统资源,比如关闭数据库,关闭socket,free/delete 内存资源;
2) 终止死循环,通过iExitFlag = 1;的方式退出循环。
以上工作我们会在死循环中判断标志位:
void* thread_func(void *arg)
{
if(iExitFlag == 1)
{
free(pt);
close();
......
exit(0);
}
//do sth.
}
3、示例
示例1:
//我们只用kill -15 进程号 命令来发出命令终止进程,信号捕捉后,进行终止线程,终止死循环操作。
void signal_func(int flag)
{
if(flag == SIGTERM)
{
iExitFlag = 1; //终止线程标志位
cout<<"process exit"<<endl;
sleep(1);//等待线程结束
exit(0);
}
}
int main()
{
//屏蔽前31个信号,但其实信号9是无法忽略的。
for(int sign = 1; sign <=31; sign ++)
signal(sign, SIG_IGN);
if(signal(SIGTERM, signal_func) == SIG_ERR)
{
printf("SIGTERM(signal_func) is fail!");
exit(0);
}
return 0;
}
示例2:
SIGTERM ---->kill -15
SIGQUIT-------> ctrl +/
SIGABR------->abort()函数产生
void signal_func(int flag)
{
if(flag == SIGTERM || flag == SIGQUIT || flag == SIGABRT)
{
cout << "signal sucess" << flag;
iExitFlag = 1;
cout << "Exit!";
sleep(3); // 等待线程结束
exit(EXIT_SUCCESS);
}
}
int main()
{
if(signal(SIGABRT, signal_func) < 0 ||signal(SIGTERM, signal_func) < 0 ||
signal(SIGQUIT, signal_func) < 0 )
{
cout << "signal failed";
exit(EXIT_FAILURE);
}
return 0;
}
4总结:
由于sighandler函数(信号捕捉函数)和main函数使用不同的堆栈空间,两者之间不存在调用和被调用的关系,属于两个独立的控制流程。
所以,调用sighandler的时候,并不会影响原程序的执行。
只是将原程序中的标志位置1了,然后sleep(1);等待原程序中资源释放,线程退出,执行exit(0);命令。