一、信号含义
怎么说咧···信号给我的感觉就是系统达到了某些特定条件产生的事件。比如说,产生中断,又比如说定时器。信号发出后,会有接收方进行处理。一个信号的产生叫生成,接收到一个信号叫捕获。
二、信号注册——signal函数
当有信号生成后,接收信号的进程要做些什么呢???这就需要与之相对应的处理程序啦!
那么,怎样把信号和处理函数关联???
这就需要注册!当然,我也不知道其他人怎么叫这个,我自己瞎编的,反正就是个关联的过程。
signal函数的头文件是#include<signal.h>
使用起来其实挺简单的。
#include <signal.h>
#include <iostream>
#include <unistd.h>
using namespace std;
void ouch(int sig)
{
cout << "Received signal "<< sig<< endl;
//恢复终端中断信号SIGINT的默认行为
signal(SIGINT, SIG_DFL);
}
int main()
{
//改变终端中断信号SIGINT的默认行为,使之执行ouch函数
//而不是终止程序的执行
signal(SIGINT, ouch);
while(1)
{
cout << "Hello World! << endl;
sleep(1);
}
return 0;
}
第一次按下终止命令(ctrl+c)时,进程并没有被终止,面是输出Received signal
前面我们看到了signal函数对信号的处理,但是一般情况下我们可以使用一个更加健壮的信号接口——sigaction函数。它的原型为:
#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);
该函数与signal函数一样,用于设置与信号sig关联的动作,而oact如果不是空指针的话,就用它来保存原先对该信号的动作的位置,act则用于设置指定信号的动作。
sigaction结构体定义在signal.h中,但是它至少包括以下成员:
void (*) (int) sa_handler;处理函数指针,相当于signal函数的func参数
sigset_t sa_mask; 指定一个信号集,在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中。信号屏蔽字是指当前被阻塞的一组信号,它们不能被当前进程接收到。
int sa_flags;信号处理修改器,通常可取以下值:
注意:我们使用signal或sigaction函数来指定处理信号的函数,但是如果这个信号处理函数建立之前就接收到要处理的信号的话,进程会有怎样的反应呢?它就不会像我们想像的那样用我们设定的处理函数来处理了。sa_mask就可以解决这样的问题,sa_mask指定了一个信号集,在调用sa_handler所指向的信号处理函数之前,该信号集将被加入到进程的信号屏蔽字中,设置信号屏蔽字可以防止信号在它的处理函数还未运行结束时就被接收到的情况,即使用sa_mask字段可以消除这一竞态条件。
#include <unistd.h>
#include <iostream>
#include <signal.h>
using namespace std;
void ouch(int sig)
{
cout << "Received signal " << sig << endl;
}
int main()
{
struct sigaction act;
act.sa_handler = ouch;
//创建空的信号屏蔽字,即不屏蔽任何信息
sigemptyset(&act.sa_mask);
//使sigaction函数重置为默认行为
act.sa_flags = SA_RESETHAND;
sigaction(SIGINT, &act, 0);
while(1)
{
cout << "Hello World! << endl;
sleep(1);
}
return 0;
}
四、发送信号
可以使用kill函数向进程发送信号,函数原型为:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
其中pid为接收信号的进程号,sig为信号类型
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <iostream>
#include <signal.h>
static int alarm_fired = 0;
void ouch(int sig)
{
alarm_fired = 1;
}
int main()
{
pid_t pid;
pid = fork();
switch(pid)
{
case -1:
cout << "fork failed"<< endl;
exit(1);
case 0:
//子进程
sleep(5);
//向父进程发送信号
kill(getppid(), SIGALRM);
exit(0);
default:;
}
//设置处理函数
signal(SIGALRM, ouch);
while(!alarm_fired)
{
cout << "Hello World!"<<endl;
sleep(1);
}
if(alarm_fired)
<span style="white-space:pre"> </span>cout << "Receive signal" << SIGALRM << endl;
return 0;
}
在代码中我们使用fork调用复制了一个新进程,在子进程中,5秒后向父进程中发送一个SIGALRM信号,父进程中捕获这个信号,并用ouch函数来处理,变改alarm_fired的值,然后退出循环。从结果中我们也可以看到输出了5个Hello World!之后,程序就收到一个SIGARLM信号,然后结束了进程。