信号那点事(二)-信号集及相关操作
前言
POSIX.1 定义了一个数据类型sigset_t用于表示信号集。
使用信号集处理信号的方式与原来的信号方式不一样。主要体现在sigaction函数上。一旦对给定的的信号设置了一个动作,那么在调用sigaction显示的改变信号的处理方式之前,该设置就一直一样。
信号集操作
增加信号到信号集
int sigaddset(sigset_t *set, int signum);
addrespectively signal signum from set.
删处信号集中的信号
int sigdelset(sigset_t *set, int signum);
delete respectively signal signum from set.
查找信号集中的信号
int sigismember(const sigset_t *set, intsignum);
sigismember() tests whether signum is amember of set.
初始化信号集
int sigemptyset(sigset_t *set);
sigemptyset() initializes the signal setgiven by set to empty, with all signals excluded from the set.
int sigfillset(sigset_t *set);
sigfillset() initializes set to full,including all signals.
返回值
and return 0 on success and -1 on error.
基于信号集的应用
屏蔽信号
#include <signal.h>
int sigprocmask(int how, const sigset_t*set, sigset_t *oldset);
调用的行为取决于如何的值,如下所示。
SIG_BLOCK
阻塞信号的集合是进程当前信号集和set参数的并集。
SIG_UNBLOCK
set信号从当前进程被阻塞的信号中移除。允许尝试解除未被阻止的信号。
SIG_SETMASK
被阻塞的信号集被设置为set 。
sigprocmask函数用于获取或更改调用线程的信号掩码。信号掩码是当前调用线程希望阻止传送的一组信号。
如果oldset不是NULL,则设置新信号掩码之前的信号将存储在oldset中。
在多线程进程中使用pthread_sigmask。
查看当前进程或线程是否允许接收指定的信号
查看系统支持的信号
参考:https://blog.csdn.net/xiangguiwang/article/details/80148141
测试代码
#include <errno.h> #include<stdio.h> #include<signal.h> #include<stdlib.h> void pr_mask(intsig_num) { sigset_t sigset; if (sigprocmask(0, NULL, &sigset)< 0) { printf("sigprocmaskerror"); exit(0); } else { //返回0表示当前线程支持接收SIGINT信号。 if (sigismember(&sigset,sig_num)==0){ printf("signal %dexist \n",sig_num); }else{ perror("error"); } } } int main(){ pr_mask(77); pr_mask(1); pr_mask(64); pr_mask(65); return 0; }
输出结果
error: Invalid argument
signal 1 exist
signal 64 exist
error: Invalid argument
查看是否有未决信号
SIGQUIT信号说明
当用户请求退出进程并执行核心转储时,SIGQUIT信号由其控制终端发送到进程。也就是说SIGQUIT信号会产生core dump文件。
测试代码
#include <signal.h> #include <stdlib.h> #include <stdio.h> #include <errno.h> static void sig_quit(int); int main(void) { sigset_t newmask, oldmask, pendmask; if(signal(SIGQUIT, sig_quit) == SIG_ERR) printf("can'tcatch SIGQUIT"); /* * Block SIGQUIT and save current signal mask. */ sigemptyset(&newmask); sigaddset(&newmask,SIGQUIT); if(sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0){ perror("SIG_BLOCK error"); } sleep(5); /* SIGQUIT here will remain pending */ if(sigpending(&pendmask) < 0) perror("sigpending error"); if(sigismember(&pendmask, SIGQUIT)) printf("\nSIGQUITpending\n"); /* * Restore signal mask which unblocks SIGQUIT. */ if(sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0) perror("SIG_SETMASK error"); printf("SIGQUITunblocked\n"); sleep(5); /* SIGQUIT here will terminate with corefile */ exit(0); } static void sig_quit(int signo) { printf("caughtSIGQUIT\n"); if(signal(SIGQUIT, SIG_DFL) == SIG_ERR) perror("can't reset SIGQUIT"); }
测试结果
^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\^\
SIGQUIT pending
caught SIGQUIT
SIGQUIT unblocked
^\Quit (core dumped)
从改测试用例可以看出来,信号在centos上是不会排队处理的。程序接收多个信号只会处理一次。
sigaction指定信号相关联的处理动作
int sigaction(int signum, const struct sigaction *act, structsigaction *oldact);
sigaction系统调用用于在接收到特定信号时更改进程所采取的操作。
signum指定信号,可以是除SIGKILL和SIGSTOP以外的任何有效信号。
如果act为非NULL,则从act中设置信号signum的新操作。如果oldact为非NULL,则先前的操作将保存为oldact。
测试代码
#include<setjmp.h> #include<time.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<signal.h> #include<unistd.h> static void sig_usr1(int); static void sig_alrm(int); int main(void) { struct sigaction act, oact; act.sa_handler = sig_usr1; sigemptyset(&act.sa_mask); act.sa_flags = 0; if(sigaction(SIGUSR1, &act,&oact) < 0) return 0; struct sigaction act1, oact1; act1.sa_handler = sig_alrm; sigemptyset(&act1.sa_mask); act1.sa_flags = 0; #ifdef SA_INTERRUPT act1.sa_flags |= SA_INTERRUPT; #endif act1.sa_flags |= SA_RESTART; if(sigaction(SIGALRM, &act1,&oact1) < 0) return 0; for ( ; ; ) pause(); } static void sig_usr1(intsigno) { printf("catch USR1\n"); alarm(3); /*SIGALRM in 3 seconds */ } static void sig_alrm(intsigno) { printf("in sig_alrm: \n"); }
测试结果
./test &
ps -ef |grep mask | grep -v"grep" | awk -F ' ' '{print $2}' | xargs kill -USR1
从测试结果可以看出来sigaction函数取代看signal函数。
信号处理中的非局部跳转
int sigsetjmp(sigjmp_buf env, intsavesigs);
void siglongjmp(sigjmp_buf env, int val);
sigsetjmp()与setjmp()类似。 如果且只有当savingigs非零时,进程的当前信号掩码才会保存在env中,并且如果稍后调用siglongjmp它将被恢复。
测试代码
#include <setjmp.h> #include<time.h> #include<stdlib.h> #include<stdio.h> #include<errno.h> #include<signal.h> #include<unistd.h> static void sig_usr1(int); static void sig_alrm(int); static sigjmp_buf jmpbuf; /*提供一种保护机制,防止在未使用sigsetjmp初始化jmpbuf ,就调用USR1信号的处理函数*/ static volatilesig_atomic_t canjump; void pr_mask(constchar *str) { sigset_t sigset; int errno_save; errno_save = errno; /* we can be called by signal handlers*/ if (sigprocmask(0, NULL, &sigset)< 0) { printf("sigprocmaskerror"); exit(0); } else { printf("%s", str); if (sigismember(&sigset,SIGUSR1)==0){ printf(" SIGUSR1not shield \n"); }else{ printf("SIGUSR1 shield \n"); } if (sigismember(&sigset, SIGALRM)==0){ printf(" SIGALRMnot shield \n"); }else{ printf("SIGALRM shield \n"); } printf("\n"); } errno = errno_save; /* restore errno */ } int main(void) { if (signal(SIGUSR1, sig_usr1) ==SIG_ERR){ printf("signal(SIGUSR1)error\n"); return 0; } if (signal(SIGALRM, sig_alrm) ==SIG_ERR){ printf("signal(SIGALRM)error"); return 0; } pr_mask("starting main: "); /* {Prog prmask} */ if (sigsetjmp(jmpbuf, 1)) { pr_mask("ending main:"); exit(0); } canjump = 1; /* now sigsetjmp() is OK */ for ( ; ; ) pause(); } static void sig_usr1(intsigno) { time_t starttime; if (canjump == 0){ printf("%s\n","catch and not deal !!!"); return; /* unexpected signal, ignore */ } pr_mask("starting sig_usr1: "); alarm(3); /*SIGALRM in 3 seconds */ starttime = time(NULL); for ( ; ; ) /* busy wait for 5 seconds */ if (time(NULL) > starttime + 5) break; pr_mask("finishing sig_usr1:"); canjump = 0; siglongjmp(jmpbuf, 1); /* jump back to main, don't return */ } static void sig_alrm(intsigno) { pr_mask("in sig_alrm: "); }
测试结果
ps -ef |grep mask | grep -v"grep" | awk -F ' ' '{print $2}' | xargs kill -USR1
starting main: SIGUSR1 not shield
SIGALRM not shield
starting sig_usr1: SIGUSR1 shield
SIGALRM not shield
in sig_alrm: SIGUSR1 shield
SIGALRM shield
finishing sig_usr1: SIGUSR1 shield
SIGALRM not shield
ending main: SIGUSR1 not shield
SIGALRM not shield
从上述测试结果可以看出,在调用sigsetjmp后,USR1信号的处理函数中USR1 信号被屏蔽。在调用siglongjmp之前,ALRM信号处理函数中ALRM、USR1信号同时被屏蔽。
参考资料
[1].https://en.wikipedia.org/wiki/Signal_(IPC)
[2].《Unix 环境高级编程》
[3]. http://man7.org/linux/man-pages/man3/errno.3.html
[4].https://blog.csdn.net/xiangguiwang/article/details/68313136
[5].http://www.man7.org/linux/man-pages/man2/sigaction.2.html