信号集操作函数:
sigset_t set ; 自定义信号集
int sigemptyset(sigset_t* set); 清空信号集
int sigfillset(sigset_t* set); 全部置一
int sigaddset(sigset_t* set,int signum); 讲一个信号添加到集合中
int sigdelset(sigset_t* set,int signum); 讲一个信号从集合中移除
int sigismember(const sigset_t* set,int signum); 判断一个信号是否在集合中 在--》1; 不在——》0
设置信号屏蔽字和解除屏蔽:
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how: SIG_BLOCK 设置阻塞 相当于用自定义集合和mask 做位或
SIG_UNBLOCK 取消阻塞 相当于 mask & ~set
SIG_SETMASK 用自定义set替换mask
set: 自定义set
oldset:旧有的 mask
查看未决信号集:
int sigpending(sigset_t* set);
set:传出的 未决信号集
// 设置信号屏蔽字
#include<signal.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void print_set(sigset_t* set){
int i;
for(i=1;i<32;i++){
if(sigismember(set,i)){
putchar('1');
}
else{
putchar('0');
}
}
printf("\n");
}
int main(){
sigset_t set,oldset,pedset;
int ret =0;
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigaddset(&set,SIGQUIT);
sigaddset(&set,SIGKILL);
sigaddset(&set,SIGBUS);
ret = sigprocmask(SIG_BLOCK,&set,&oldset);
if(ret==-1){
sys_err("sigprocmask error");
}
while(1){
ret = sigpending(&pedset);
if(ret==-1){
sys_err("sigpending error");
}
print_set(&pedset);
sleep(1);
}
return 0;
}
输出:
0000000000000000000000000000000
0000000000000000000000000000000
0000000000000000000000000000000
^C0100000000000000000000000000000
0100000000000000000000000000000
^\0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000
0110000000000000000000000000000
注册信号捕捉函数:
注意 仅仅只是注册 真正抓信号的 是内核
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<string.h>
#include<signal.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
void sig_catch(int signo){
printf("catch you %d\n",signo);
}
int main(){
signal(SIGINT,sig_catch);
while(1);
return 0;
}
输出:
^Ccatch you 2
^Ccatch you 2
^Ccatch you 2
sigaction();重点
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
act: 传入参数, 新的处理方式
oldact:传出参数 旧的处理方式
成功 :0
失败: -1 errno
eg:
struct sigaction act;
act.sa_handler = catch_child; //设置回调函数
sigemptyset(&act.sa_mask); //设置捕捉函数执行期间屏蔽字
act.sa_flags = 0; //设置默认属性,本信号自动屏蔽
sigaction(SIGCHLD,&act,NULL); //注册信号捕捉函数
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<string.h>
#include<signal.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
//回调函数
void sig_catch(int signo){
if(signo==SIGINT){
printf("catch you %d\n",signo);
sleep(10);
}
else if(signo==SIGQUIT)
printf("--------catch you %d\n",signo);
return ;
}
int main(){
struct sigaction act,oldact;
act.sa_handler = sig_catch;//设置回调函数
sigemptyset(&(act.sa_mask));//清空sa_mask 只在 sig_catch工作时有效
act.sa_flags = 0;// 默认值
int ret = sigaction(SIGINT,&act,&oldact);
if(ret==-1){
sys_err("sigaction error");
}
ret = sigaction(SIGQUIT,&act,&oldact);
if(ret==-1){
sys_err("sigaction error");
}
while(1);
return 0;
}
输出:
^Ccatch you 2
^C^C^C^C^C^C^Ccatch you 2
信号捕捉特性:
1.捕捉函数执行期间 ,信号屏蔽字 由 mask--》sa_mask, 捕捉函数执行结束。 恢复回mask
2.捕捉函数执行期间,本信号自动被屏蔽(sa_flgs =0)
3.捕捉函数执行期间,被屏蔽的信号多次发送,解除屏蔽后只处理一次
借助信号完成子进程回收
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
void sys_err(const char* str){
perror(str);
exit(1);
}
//有子进程终止,发送SIGCHLD信号时,该函数会被内核回调
void catch_child(int signo){
(void)signo;
pid_t wpid;
int status;
while((wpid= waitpid(-1,&status,0))!=-1){ //循环回收防止僵尸进程出现
if(WIFEXITED(status))
printf("------------catch child id %d,ret=%d\n",wpid,WEXITSTATUS(status));
}
return;
}
int main(){
pid_t pid;
//设置阻塞 防止捕捉函数还未注册 信号到达
sigset_t set;
sigemptyset(&set);
sigaddset(&set,SIGCHLD);
sigprocmask(SIG_BLOCK,&set,NULL);
int i;
for(i=0;i<15;++i){ //创建多个子进程
if((pid=fork())==0){
break;
}
}
if(i==15){
struct sigaction act;
act.sa_handler = catch_child; //设置回调函数
sigemptyset(&act.sa_mask); //设置捕捉函数执行期间屏蔽字
act.sa_flags = 0; //设置默认属性,本信号自动屏蔽
sigaction(SIGCHLD,&act,NULL); //注册信号捕捉函数
//解除阻塞
sigprocmask(SIG_UNBLOCK,&set,NULL);
printf("i'm patent,pid=%d\n",getpid());
while(1); //模拟父进程后续逻辑
}
else{
printf("i'm child,pid=%d\n",getpid());
return i;
}
return 0;
}
慢速系统调用中断:
系统调用分为两类:
1.慢速系统调用: 可能会使进程永久阻塞的一类。如果在阻塞期间收到一个信号,该系统调用就被中断,不在继续执行;
也可以设定系统调用是否重启。 如 read、write、wait....
可修改 sa_flags 参数来设置被信号中断后系统调用是否重启。 SA_INTERRURT 不重启 。 SA_RESTART 重启
2.其他系统调用: getpid( ) fork()