发送信号是进程间最常见的通信方式。信号可由系统内核或者某个进程产生并发送给其它进程。进程收到信号,可以作出以下几种反应:执行信号的默认动作;忽略该信号;捕捉该信号并转向相应的信号处理函数。
#include<iostream>
#include<signal.h>
#include<unistd.h>
int main(){
pid_t pid;
pid = fork();
if(pid < 0){
cout<<"fork failed!"<<endl;
}
else if(pid == 0){
cout<<"new progress: "<<getpid()<<endl;
if(signal(SIGINT,SIG_IGN)){
cout<<"can't receive SIGINT"<<endl;
}
if(signal(SIGTERM,SIG_DFL)){
cout<<"can't receive SIGTERM"<<endl;
}
while(true){
sleep(1);
cout<<"sleep 1 second"<<endl;
}
}
else
{
cout<<"main progress: "<<getpid()<<endl;
sleep(10);
cout<<"send SIGINT"<<endl;
kill(pid,SIGINT);
cout<<"send SIGTERM"<<endl;
sleep(10);
kill(pid,SIGTERM);
}
return 0;
}
kill除了可以用作命令外,也可以作为函数使用,用来发送信号。上述代码中有两个进程,在主进程中,首先休眠10s,然后发送SIGINT信号,再次休眠10s后,发送SIGTERM信号。其中,前者被子进程忽略,后者则执行默认操作。通过分别追踪两个进程可以发现,主进程确实发送了两次信号,而子进程则收到了两个信号。子进程被SIGTERM信号终止,而主进程正常退出。
signal函数的第二个参数是一个函数指针,表示相应的信号处理动作,可以预设为SIG_DFL或SIG_IGN,也可以由用户自定义。当进程执行相应的信号处理动作时,如果又接收到相同信号,则系统会屏蔽该信号,由于信号有等待机制,待解除屏蔽后,仍可再次执行相应的信号处理动作。但无论屏蔽期间收到多少次相同信号,都只会认为收到了一次。
在UNIX中,提供了信号集数据类型sigset_t,可以保存信号的状态。进程可以通过该数据类型保存当前阻塞的信号。该数据类型其实是一个包含两个无符号长整型的结构体,每一位对应一种信号,0表示信号没来,1表示信号来了。同时,系统提供了一些信号相关函数:sigemptyset、sigfillset、sigaddset、sigdeset、sigprocmask、sigismember。