sigprocmask
sigaction函数的功能是检查或修改与指定信号相关联的处理动作(可同时两种操作)。
他是POSIX的信号接口,而signal()是标准C的信号接口(如果程序必须在非POSIX系统上运行,那么就应该使用这个接口)
给信号signum设置新的信号处理函数act, 同时保留该信号原有的信号处理函数oldact
int sigaction(int signo,const struct sigaction *restrict act, struct sigaction *restrict oact); |
结构sigaction定义如下:
struct sigaction{ |
sa_handler字段包含一个信号捕捉函数的地址
sa_mask字段说明了一个信号集,在调用该信号捕捉函数之前,这一信号集要加进进程的信号屏蔽字中。仅当从信号捕捉函数返回时再将进程的信号屏蔽字复位为原先值。
sa_flag是一个选项,主要理解两个
SA_INTERRUPT 由此信号中断的系统调用不会自动重启 SA_SIGINFO 提供附加信息,一个指向siginfo结构的指针以及一个指向进程上下文标识符的指针 |
最后一个参数是一个替代的信号处理程序,当设置SA_SIGINFO时才会用他。
sa_flags中包含了许多标志位,包括刚刚提到的SA_NODEFER及SA_NOMASK标志位。另一个比较重要的标志位是SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以被传递到信号处理函数中,因此,应该为sigaction结构中的sa_sigaction指定处理函数,而不应该为sa_handler指定信号处理函数,否则,设置该标志变得毫无意义。即使为sa_sigaction指定了信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误(Segmentation fault)。
- sa_handler:与signal()中相同,指定一个入参为int(信号值)/无返回值的自定义函数作为信号处理函数;也可以是SIG_DFT/SIG_IGN分别表示使用系统默认处理/忽略
- sa_sigaction:入参为int(信号值)/siginfo_t*(siginfo_t指针,给出与信号相关的其他信息)/void*(被转换的ucontext_t指针,给出信号相关的用户上下文信息),无返回值的用户信号处理函数地址。
sa_handler与sa_sigaction均可以指定用户信号处理函数。当sa_flags字段中设置了SA_SIGINFO位时,系统使用 sa_sigaction指定的函数;否则使用sa_handler。考虑到不同系统兼容性的问题(sa_handler和sa_sigaction共用 一个union),不要同时指定两个字段。 - sa_mask:信号处理期间需要阻塞的信号掩码。此外,触发了信号处理函数的信号本身也会被阻塞,除非设置了SA_NODEFER标志 ,在处理信号1的时候对信号2不处理,直到信号1处理完毕;如果没有设置MASK则信号一到都立即处理。
- sa_flags:信号行为标识,可以是以下标置位(bit)或组合:
- SA_NOCLDSTOP:仅当信号值(signum)为SIGCHLD时有效(为SIGCHLD设置自定义处理函数时),表示不接收子进程停/启(stop/resume)信号
- SA_NOCLDWAIT:当信号值(sginum)为SIGCHILD时,表示当子进程终止时不要将子进程变为僵尸进程(zombie)。此标志位仅当为SIGCHLD设置自定义处理函数或将SIGCHLD处理函数恢复为系统缺省时(SIG_DFL)时有效。
- SA_NODEFER:在触发处理函数的信号处理过程中,不要阻止再次接收此信号(缺省情况下,正在处理中的信号会被系统阻塞)。SA_NOMASK含义与之相同,但已废弃。
- SA_ONSTACK:调用使用sigaltstack()设置的另一个信号处理函数,若其不可用则调用缺省栈内处理函数。
- SA_RESETHAND:在某个信号触发过自定义处理函数后,将此信号的处理方式恢复为系统默认。SA_ONSHOT含义相同,但已废弃
- SA_RESTART:与BSD兼容,在信号处理中,将某些系统调用标记为可重用
- SA_SIGINFO:使用sa_sigaction指定的3参数信号处理函数而非sa_handler指定的单参数处理函数。
- sa_restorer:已废弃
例子:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
void show_handler(int sig)
{
printf("I got signal %d\n", sig);
int i;
for(i = 0; i < 5; i++) {
printf("i = %d\n", i);
sleep(1);
}
}
int main(void)
{
int i = 0;
struct sigaction act, oldact;
act.sa_handler = show_handler;
sigaddset(&act.sa_mask, SIGQUIT); //见注(1)
act.sa_flags = SA_RESETHAND | SA_NODEFER; //见注(2)
//act.sa_flags = 0; //见注(3)
sigaction(SIGINT, &act, &oldact);
while(1) {
sleep(1);
printf("sleeping %d\n", i);
i++;
}
}
注:
(1) 如果在信号SIGINT(Ctrl + c)的信号处理函数show_handler执行过程中,本进程收到信号SIGQUIT(Crt+\),将阻塞该信号,直到show_handler执行结束才会处理信号SIGQUIT。
(2) SA_NODEFER 一般情况下, 当信号处理函数运行时,内核将阻塞<该给定信号 -- SIGINT>。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号。 SA_NODEFER是这个标记的正式的POSIX名字(还有一个名字SA_NOMASK,为了软件的可移植性,一般不用这个名字)
SA_RESETHAND 当调用信号处理函数时,将信号的处理函数重置为缺省值。 SA_RESETHAND是这个标记的正式的POSIX名字(还有一个名字SA_ONESHOT,为了软件的可移植性,一般不用这个名字)
(3) 如果不需要重置该给定信号的处理函数为缺省值;并且不需要阻塞该给定信号(无须设置sa_flags标志),那么必须将sa_flags清零,否则运行将会产生段错误。但是sa_flags清零后可能会造成信号丢失!
----------------------------------------------------------------
表头文件 #include<signal.h>
函数原型 int sigaddset(sigset_t *set,int signum);
sigaddset()用来将参数signum 代表的信号加入至参数set 信号集里。
执行成功则返回0,如果有错误则返回-1。
函数原型:
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oldset);
函数说明:一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集。sigprocmask()可以用来检测或改变目前的信号屏蔽字,
其操作依参数how来决定,如果参数oldset不是NULL指针,那么目前的信号屏蔽字会由此指针返回。如果set是一个非空指针,则参数how指示
如何修改当前信号屏蔽字。每个进程都有一个用来描述哪些信号递送到进程时将被阻塞的信号集,该信号集中的所有信号在递送到进程后都将被阻塞。
参数how的取值不同,带来的操作行为也不同,该参数可选值如下:
1.SIG_BLOCK: 该值代表的功能是将newset所指向的信号集中所包含的信号加到当前的信号掩码中,作为新的信号屏蔽字(原有信号屏蔽字 + set屏蔽字)。
2.SIG_UNBLOCK:将参数newset所指向的信号集中的信号从当前的信号掩码中移除。
3.SIG_SETMASK:设置当前信号掩码为参数newset所指向的信号集中所包含的信号。
oldset 记录之前的信号屏蔽字,程序开始第一次调用sigprocmask() 后,oldset里面理论上是不含有任何信号的。因为调用sigprocmask()前,我们没有取拦截任何信号
注意事项:sigprocmask()函数只为单线程的进程定义的,在多线程中要使用pthread_sigmask变量,在使用之前需要声明和初始化。
返回值:执行成功返回0,失败返回-1。
函数定义
int sigemptyset(sigset_t *set);
sigemptyset()用来将参数set信号集初始化并清空。
返回值:执行成功则返回0,如果有错误则返回-1。
#include <stdio.h> #include <unistd.h> #include <signal.h> void handler(int sig) { printf("Deal with SIGINT\n"); //SIGINT信号处理函数 } int main() { sigset_t newmask; sigset_t oldmask; sigset_t pendmask; struct sigaction act; act.sa_handler = handler; //handler为信号处理函数首地址 sigemptyset(&act.sa_mask); act.sa_flags = 0; sigaction(SIGINT, &act, 0); //信号捕捉函数,捕捉Ctrl+C sigemptyset(&newmask);//初始化信号量集 sigaddset(&newmask, SIGINT);//将SIGINT添加到信号量集中 sigprocmask(SIG_BLOCK, &newmask, &oldmask);//将newmask中的SIGINT阻塞掉,并保存当前信号屏蔽字到Oldmask sleep (5);//休眠5秒钟,说明:在5s休眠期间,任何SIGINT信号都会被阻塞,如果在5s内收到任何键盘的Ctrl+C信号,则此时会把这些信息存在内核的队列中,等待5s结束后,可能要处理此信号。 sigpending(&pendmask);//检查信号是悬而未决的,所谓悬而未决,是指SIGINT被阻塞还没有被处理 if (sigismember(&pendmask, SIGINT)) { printf("SIGINT pending\n"); } printf("SIGINT unblocked\n"); sigprocmask(SIG_SETMASK, &oldmask, NULL);//恢复被屏蔽的信号SIGINT //此处开始处理信号,调用信号处理函数 sleep(5); return (0); }
^C^C^CSIGINT pending
SIGINT unblocked
Deal with SIGINT
注意:上面还有一种方式:
sigprocmask(SIG_BLOCK, &newmask, NULL); //阻塞
sigprocmask(SIG_UNBLOCK, &newmask, NULL);//取消阻塞
#include <stdio.h> #include <signal.h> #include <stdlib.h> void checkset(); void main() { sigset_t blockset; sigemptyset(&blockset); checkset(); sigaddset(&blockset,SIGINT); sigaddset(&blockset,SIGTSTP); sigprocmask(SIG_SETMASK, &blockset, NULL); checkset(); sigaddset(&blockset, SIGTERM); sigprocmask(SIG_BLOCK, &blockset, NULL); checkset(); sigdelset(&blockset, SIGTERM); sigprocmask(SIG_UNBLOCK, &blockset, NULL); checkset(); } void checkset() { sigset_t set; printf("checksetstart:\n"); if(sigprocmask(SIG_BLOCK, NULL, &set)<0) // 阻塞信号 = 原有信号 + NULL 所以这样只是取出当前阻塞的信号放入set中 { printf("checksetsigprocmask error!!\n"); exit(0); } if(sigismember(&set,SIGINT)) printf("sigint\n"); if(sigismember(&set,SIGTSTP)) printf("sigtstp\n"); if(sigismember(&set,SIGTERM)) printf("sigterm\n"); printf("checksetend\n\n"); }
checksetstart:
checksetend
checksetstart:
sigint
sigtstp
checksetend
checksetstart:
sigint
sigtstp
sigterm
checksetend
checksetstart:
sigterm
checksetend
#include <stdio.h> #include <signal.h> #include <stdlib.h> void checkset(); void func(); void main() { sigset_t blockset, oldblockset, pendmask; printf("pid:%ld\n",(long)getpid()); signal(SIGINT,func); //信号量捕捉函数,捕捉到SIGINT,跳转到函数指针func处执行 sigemptyset(&blockset); //初始化信号量集 sigaddset(&blockset,SIGTSTP); //将SIGTSTP添加到信号量集中 sigaddset(&blockset,SIGINT);//将SIGINT添加到信号量集中 sigprocmask(SIG_SETMASK,&blockset,&oldblockset); //将blockset中的SIGINT,SIGTSTP阻塞掉,并保存当前信号屏蔽字 /*执行以下程序时,不会被信号打搅*/ checkset(); printf("begin to sleep(5)\n"); sleep(5); sigpending(&pendmask); //检查信号是悬而未决的 if(sigismember(&pendmask,SIGINT)) //SIGINT是悬而未决的。所谓悬而未决,是指SIGQUIT被阻塞还没有被处理 printf("SIGINTpending\n"); /*免打搅结束*/ sigprocmask(SIG_SETMASK,&oldblockset,NULL); //恢复被屏蔽的信号SIGINT SIGTSTP printf("SIGINTunblocked\n"); sleep(6); } void checkset() { sigset_t set; printf("checksetstart:\n"); if(sigprocmask(SIG_BLOCK, NULL, &set)<0) { printf("checksetsigprocmask error!!\n"); exit(0); } if(sigismember(&set,SIGINT)) printf("sigint\n"); if(sigismember(&set,SIGTSTP)) printf("sigtstp\n"); if(sigismember(&set,SIGTERM)) printf("sigterm\n"); printf("checksetend\n"); } void func() { printf("hellofunc\n"); }
pid:11629
checksetstart:
sigint
sigtstp
checksetend
begin to sleep(5)
^C
SIGINTpending
hellofunc
SIGINTunblocked