(接上篇信号(1))
四 信号集
以一个二进制位代表一个信号,考虑到扩展的问题,因此用一个很大的整数代表多个信号,这个很大的整数就是信号集,信号集的类型 sigset_t.
集合必须提供的功能:
增加元素、删除元素、查找元素、取出元素
信号集的功能函数:
sigemptyset() - 清空全部信号(二进制位置0)
sigfillset() - 填满全部信号(二进制位置1)
sigaddset() - 增加一个信号
sigdelset() - 删除一个信号
sigismember()- 判断是否有某个信号(有返回1)
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
int main(){
printf("size=%d\n",sizeof(sigset_t));
sigset_t set;
sigemptyset(&set);
printf("set=%d\n",set);
sigaddset(&set,2);
printf("set=%d\n",set);
sigaddset(&set,3);
printf("set=%d\n",set);
sigaddset(&set,10);
printf("set=%d\n",set);
sigdelset(&set,3);
printf("set=%d\n",set);
if(sigismember(&set,2)){
printf("信号2存在\n");
}
}
五 信号屏蔽
在执行某些关键代码时,不希望代码被信号中断。此时可以使用信号屏蔽,信号屏蔽不是阻止信号的到来,而是延后信号的处理(信号来了但不处理)。解除信号屏蔽以后再处理来过的信号。
设置信号屏蔽字函数:
sigprocmask(int how,sigset_t* new,sigset_t* old)
参数how是屏蔽方式,有三种:
SIG_BLOCK - 相当于加法在原有的基础加上新的
a b c + c d e -> a b c d e
SIG_UNBLOCK - 相当于减法在原有的基础上减去新的
a b c - c d e -> a b
SIG_SETMASK - 重新设置,与原有的无关
a b c c d e -> c d e
一般使用SIG_SETMASK
参数new是新的屏蔽信号集,old可以把之前的屏蔽信号集保存起来,如果old为NULL就是不保存。如果保存了old,再次设置信号屏蔽字为old就是取消屏蔽。
#include <stdio.h>
#include <signal.h>
#include <stdlib.h>
void fa(int signo){
printf("捕获了信号%d\n",signo);
}
int main(){
signal(SIGINT,fa);
signal(SIGQUIT,fa);
printf("pid=%d\n",getpid());
printf("执行普通代码,不屏蔽信号\n");
sleep(15);//sleep会被未忽略的信号打断
printf("执行关键代码,屏蔽信号\n");
sigset_t set,old;
sigemptyset(&set);
sigaddset(&set,2);
sigaddset(&set,3);
sigaddset(&set,50);
sigprocmask(SIG_SETMASK,&set,&old);//屏蔽
sleep(20);
printf("关键代码结束,解除屏蔽\n");
sigset_t pend;
sigpending(&pend);//取信号屏蔽期间来过的信号
if(sigismember(&pend,2)){
printf("信号2来过\n");
}
if(sigismember(&pend,3)){
printf("信号3来过\n");
}
sigprocmask(SIG_SETMASK,&old,NULL);
}
六 sigaction()/sigqueue()函数
(常用信号的知识点over)
sigaction() 可以注册信号的处理方式,功能和signal一样,但sigaction是增强版的signal。
#include <stdio.h>
#include <signal.h>
void fa(int signo){
printf("捕获了信号%d\n",signo);
sleep(5);
}//在信号处理函数的执行期间,会屏蔽相同信号
int main(){
struct sigaction action = {};
action.sa_handler = fa;//设置信号处理函数
//在信号处理函数执行期间,会屏蔽信号3
sigemptyset(&action.sa_mask);
sigaddset(&action.sa_mask,3);//屏蔽信号3
//设置信号第一次是处理函数,然后回复默认处理
//action.sa_flags = SA_RESETHAND;
//解除了信号处理函数执行期间对相同信号的屏蔽
action.sa_flags = SA_NOMASK;
sigaction(SIGINT,&action,NULL);//注册信号2
while(1);
}
#include <stdio.h>
#include <signal.h>
void fa(int signo,siginfo_t* info,void* p){
printf("进程%d发来了信号%d\n",info->si_pid,
signo);
}//有更多的 信号相关信息
int main(){
struct sigaction action = {};
action.sa_flags = SA_SIGINFO;//和fa一致
action.sa_sigaction = fa;//处理函数用第2个
sigaction(SIGINT,&action,NULL);
printf("pid=%d\n",getpid());
while(1);
}
sigqueue() 函数可以发送信号,并且在发送信号的同时附加数据。
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void fa(int signo,siginfo_t* info,void* p){
printf("进程%d发来了信号%d,附带了值%d\n",
info->si_pid,signo,info->si_value);
}
int main(){
struct sigaction action = {};
action.sa_flags = SA_SIGINFO;
action.sa_sigaction = fa;
sigaction(50,&action,NULL);//2 可靠信号
pid_t pid = fork();
if(pid == 0){
int i;
for(i=0;i<100;i++){
union sigval v;
v.sival_int = i;
sigqueue(getppid(),50,v);
}//2
exit(0);
}
while(1);
}
七 计时器
Linux系统给每个进程维护了3个计时器,真实计时器、虚拟计时器和实用计时器。其中真实计时器最常用。真实计时器是每隔一段时间产生一个SIGALRM信号。
设置/获取计时器的函数:
setitimer() getitimer()
#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
void fa(int signo){
printf("执行了一次\n");
}
int main(){
signal(SIGALRM,fa);
struct itimerval timer;
timer.it_interval.tv_sec = 1;//间隔秒数
timer.it_interval.tv_usec = 100000;//微秒
timer.it_value.tv_sec = 3;//开始秒数
timer.it_value.tv_usec = 0;//微秒
setitimer(ITIMER_REAL,&timer,0);
while(1);
}