信号编程进阶、sigprocmask范例
【信号集】
- 一个进程,必须能够记住 这个进程 当前阻塞了哪些信号
- 我们需要 “信号集 ”的这么一种数据类型(结构),能够把这60多个信号都表示下(都装下)。
- 0000000000,0000000000,0000000000,00,0000000000,0000000000,0000000000,00 (64个二进制位)
- linux 是用sigset_t结构类型来表示信号集的
typedef struct{ unsigned long sig[2]; }sigset_t
- 信号集的定义:信号集表示一组信号的来置 1 或者没来置 0。
- 信号集相关的数据类型: sigset_t。
【信号相关函数】
- sigemtpyset():把信号集中的所有信号都清0,表示这60多个信号都没有来。
- 00000000000000000000000000.....
- sigfillset():把信号集中的所有信号都设置为1,跟sigemptyset()正好相反。
- 11111111111111111111111111.....
- sigaddset(),sigdelset() 可以往信号集中增加信号,或者从信号集中删除特定信号。
- sigprocmask(),sigmember()
- 一个进程,里边会有一个信号集,用来记录当前屏蔽(阻塞)了哪些信号。
- 如果我们把这个信号集中的某个信号位设置为1,就表示屏蔽了同类信号,此时再来个同类信号,那么同类信号会被屏蔽,不能传递给进程。
- 如果这个信号集中有很多个信号位都被设置为1,那么所有这些被设置为1的信号都是属于当前被阻塞的而不能传递到该进程的信号。
- sigprocmask()函数,就能够设置该进程所对应的信号集中的内容。
【sigprocmask等信号函数范例演示】
#include <stdio.h> #include <stdlib.h> //malloc #include <unistd.h> #include <signal.h> //信号处理函数 void sig_quit(int signo) { printf("收到了SIGQUIT信号!\n"); /*if(signal(SIGQUIT,SIG_DFL) == SIG_ERR) { printf("无法为SIGQUIT信号设置缺省处理(终止进程)!\n"); exit(1); }*/ } int main(int argc, char *const *argv) { sigset_t newmask,oldmask; //信号集,新的信号集,原有的信号集 if(signal(SIGQUIT,sig_quit) == SIG_ERR) //注册信号对应的信号处理函数,"ctrl+\" { printf("无法捕捉SIGQUIT信号!\n"); exit(1); //退出程序,参数是错误代码,0表示正常退出,非0表示错误 //但具体什么错误,没有特别规定,这个错误代码一般也用不到,先不管他; } sigemptyset(&newmask); //newmask信号集中所有信号都清0(表示这些信号都没有来); sigaddset(&newmask,SIGQUIT); //设置newmask信号集中的SIGQUIT信号位为1,说白了,再来SIGQUIT信号时,进程就收不到,设置为1就是该信号被阻塞掉呗 //sigprocmask():设置该进程所对应的信号集 if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0) //第一个参数用了SIG_BLOCK表明设置 进程 新的信号屏蔽字 为 “当前信号屏蔽字 和 第二个参数指向的信号集的并集 { //一个 ”进程“ 的当前信号屏蔽字,刚开始全部都是0的;所以相当于把当前 "进程"的信号屏蔽字设置成 newmask(屏蔽了SIGQUIT); //第三个参数不为空,则进程老的(调用本sigprocmask()之前的)信号集会保存到第三个参数里,用于后续, //这样后续可以恢复老的信号集给线程 printf("sigprocmask(SIG_BLOCK)失败!\n"); exit(1); } printf("我要开始休息10秒了--------begin--,此时我无法接收SIGQUIT信号!\n"); sleep(10); //这个期间无法收到SIGQUIT信号的; printf("我已经休息了10秒了--------end----!\n"); if(sigismember(&newmask,SIGQUIT)) //测试一个指定的信号位是否被置位(为1),测试的是newmask { printf("SIGQUIT信号被屏蔽了!\n"); } else { printf("SIGQUIT信号没有被屏蔽!!!!!!\n"); } if(sigismember(&newmask,SIGHUP)) //测试另外一个指定的信号位是否被置位,测试的是newmask { printf("SIGHUP信号被屏蔽了!\n"); } else { printf("SIGHUP信号没有被屏蔽!!!!!!\n"); } //现在我要取消对SIGQUIT信号的屏蔽(阻塞)--把信号集还原回去 if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0) //第一个参数用了SIGSETMASK表明设置 进程 新的信号屏蔽字为 第二个参数 指向的信号集,第三个参数没用 { printf("sigprocmask(SIG_SETMASK)失败!\n"); exit(1); } printf("sigprocmask(SIG_SETMASK)成功!\n"); if(sigismember(&oldmask,SIGQUIT)) //测试一个指定的信号位是否被置位,这里测试的当然是oldmask { printf("SIGQUIT信号被屏蔽了!\n"); } else { printf("SIGQUIT信号没有被屏蔽,您可以发送SIGQUIT信号了,我要sleep(10)秒钟!!!!!!\n"); int mysl = sleep(10); if(mysl > 0) { printf("sleep还没睡够,剩余%d秒\n",mysl); } } printf("再见了!\n"); return 0; }
- 程序执行,不给程序发送信号:
- 程序执行,第一次sleep时,给程序发送信号(按 Ctrl + \):
- 程序执行,第二次sleep时,给程序发送信号(按 Ctrl + \):
- sleep()函数能够被打断:
- 时间到了。
- 来了某个信号,使sleep()提前结束,此时sleep会返回一个值,这个值就是未睡够的时间。
【引申:信号处理函数中途改变】
//信号处理函数 void sig_quit(int signo) { printf("收到了SIGQUIT信号!\n"); if(signal(SIGQUIT,SIG_DFL) == SIG_ERR) { printf("无法为SIGQUIT信号设置缺省处理(终止进程)!\n"); exit(1); } }
- 第一次收到终止信号后,调用信号处理函数,同时将终止信号设置为默认处理方式,即第一次执行的是自己定义的信号处理函数,所以没有终止进程,当第二次收到终止信号,由于第一次终止信号函数中将信号处理函数设置为默认方式,所以采用系统默认处理方式立即终止程序。
- 只发一次终止信号(第一次休眠时发)
- 只发一次终止信号(第二次休眠时发)
- 两次休眠都发