信号集与屏蔽字

本文详细介绍了Linux系统中信号处理的基本概念和技术,包括信号集的创建与管理、信号的屏蔽与处理、未决信号的检测、高级信号处理函数的应用等。通过具体的代码示例,深入浅出地讲解了如何在程序中有效地利用信号处理机制。
摘要由CSDN通过智能技术生成

1.信号集和信号集处理函数

信号集是一个位向量,其中每一位对应着linux系统的一个信号。可使用如下函数对信号集进行处理:

1#include <signal.h>
2 
3int sigemptyset(sigset_t * set);
4 
5int sigfillset(sigset_t * set);
6 
7int sigaddset(sigset_t * set);
8 
9int sigdelset(sigset_t * set);

sigemptyset将一个信号集清空;sigfillset将信号集的所有位置位;sigaddset函数将参数signo指定的信号所对应的位设置为1;sigdelset将signo的对应位设置为0。

使用如下函数检测信号集的相应位是否被设置:

1#include <signal.h>
2 
3int sigismember(sigset_t * set,int aigno);

返回值为1时,代表该位被设置,返回值为0时,代表该位未被设置,失败则返回-1。

//sigset.c 使用信号集处理函数测试并设置相应信号的位

01#include <stdio.h>
02 
03#include <signal.h>
04 
05int main()
06 
07{
08 
09sigset_t sig_set;
10 
11sigemptyset(&sig_set);//清空信号集
12 
13sigaddset(&sig_set,SIGKILL-1);//设置SIGKILL的相应位
14 
15if(sigismember(&sig_set,SIGKILL))==1){
16 
17printf("SIGKILL has been set/n");
18 
19else
20 
21printf("can't set signal set/n");
22 
23return 0;
24 
25}

2.屏蔽信号

阻塞一些信号,使进程即使接收到信号,也不做处理。

使用如下函数:

1#include <signal.h>
2 
3int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);

函数说明 sigprocmask()可以用来改变目前的信号屏蔽,其操作依参数how来决定
SIG_BLOCK 新的信号屏蔽由目前的信号屏蔽和参数set 指定的信号屏蔽作联集
SIG_UNBLOCK 将目前的信号屏蔽删除掉参数set指定的信号屏蔽
SIG_SETMASK 将目前的信号屏蔽设成参数set指定的信号屏蔽。
如果参数oldset不是NULL指针,那么目前的信号屏蔽会由此指针返回。
返回值 执行成功则返回0,如果有错误则返回-1。
错误代码 EFAULT 参数set,oldset指针地址无法存取。
EINTR 此调用被中断

如果set的值为NULL,则无论how是何值都不会更改信号屏蔽字。这种方法用于得到当前进程的信号屏蔽字。

sigprocmask(0,NULL,&oset);

01#include <stdio.h>
02#include <stdlib.h>
03#include <signal.h>
04 
05void sigusr1_handler(int signo)
06{
07    printf("catch SIGUSR1/n");
08}
09 
10int main(void)
11{
12    sigset_t set;
13 
14    if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){
15        perror("can¡¯t set handler for SIGUSR1");
16        exit(1);
17    }
18 
19    sigemptyset(&set);//清空信号集
20    sigaddset(&set, SIGUSR1 - 1);//设置SIGUSR1
21 
22    if(sigprocmask(SIG_BLOCK, &set, NULL) == -1){//屏蔽该信号
23        perror("fail to set signal-mask");
24        exit(1);
25    }
26 
27    printf("SIGUSR1 is not available/n");
28 
29    sleep(10);//休眠,等待用户发送SIGUSR1信号
30 
31    if(sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //恢复屏蔽的信号
32        perror("fail to set signal-mask");
33        exit(1);
34    }
35 
36    printf("SIGUSR1 is available now/n");
37 
38    sleep(10);//休眠,等待用户发送SIGUSR1信号
39 
40    return 0;
41}

3.处理未决信号

如果屏蔽了一个信号,但是进程还是从某处接收到了此信号,这种信号叫做未决的。这种信号是悬而未决的。

如果调用sigprocmask后有任何未决的但是已经不再阻塞的信号时,在该函数返回之前,至少会将这些解放了的未决信号中的一个发送给该进程。linux中使用sigpending函数检查未决信号,其函数的原型如下;

1#include <signal.h>
2 
3int sigpending(sigset_t * set);

set表示当前进程中所有未决的信号。如果成功得到未决信号集,返回0,否则返回-1。

下例先阻塞SIGUSR1信号,之后向该进程发送SIGUSR1信号,此信号为未决的,使用sigpending函数测试是否有一个未决的SIGUSR1信号,最后取消对该信号的阻塞,处理这个信号。

01#include <stdio.h>
02#include <stdlib.h>
03#include <signal.h>
04 
05void sigusr1_handler(int signo)
06{
07    printf("catch SIGUSR1/n");
08}
09 
10int main(void)
11{
12    sigset_t set;
13    sigset_t sig_pend;
14 
15    sigemptyset(&set);
16    sigemptyset(&sig_pend);
17 
18    if(signal(SIGUSR1, sigusr1_handler) == SIG_ERR){
19        perror("can¡¯t set handler for SIGUSR1");
20        exit(1);
21    }
22 
23    sigaddset(&set, SIGUSR1 - 1); //添加SIGUSR1信号
24 
25    if(sigprocmask(SIG_BLOCK, &set, NULL) == -1){ //阻塞SIGUSR1
26        perror("fail to set signal-mask");
27        exit(1);
28    }
29 
30    sleep(10); //休眠10,期间接收信号,注意进程休眠后不会被未决的信号唤醒
31 
32    if(sigpending(&sig_pend) == -1){  //得到所有的未决信号集
33        perror("fail to get pending signal");
34        exit(1);
35    }
36 
37    if(sigismember(&sig_pend, SIGUSR1 - 1) == 1) //测试是否有SIGUSR1信号是未决的
38        printf("there is a signal, SIGUSR1, is pending/n");
39    else{
40        perror("fail to test signal-set");
41        exit(1);
42    }
43 
44    if(sigprocmask(SIG_UNBLOCK, &set, NULL) == -1){ //取消对SIGUSR1的阻塞
45        perror("fail to set signal-mask");
46        exit(1);
47    }
48 
49    printf("SIGUSR1 is available again/n");
50 
51    return 0;
52}

4.高级信号处理函数

原型:

#include
定义函数 int sigaction(int signum,const struct sigaction *act ,struct sigaction *oldact);
函数说明 sigaction()会依参数signum指定的信号编号来设置该信号的处理函数。参数signum可以指定SIGKILL和SIGSTOP以外的所有信号。
如参数结构sigaction定义如下

1struct sigaction
2{
3void (*sa_handler) (int);
4sigset_t sa_mask;
5int sa_flags;
6void (*sa_restorer) (void);
7}

sa_handler此参数和signal()的参数handler相同,代表新的信号处理函数,其他意义请参考signal()。
sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号搁置。
sa_restorer 此参数没有使用。
sa_flags 用来设置信号处理的其他相关操作,下列的数值可用。
OR 运算(|)组合A_NOCLDSTOP : 如果参数signum为SIGCHLD,则当子进程暂停时并不会通知父进程SA_ONESHOT/SA_RESETHAND:当调用新的信号处理函数前,将此信号处理方式改为系统预设的方式。
SA_RESTART:被信号中断的系统调用会自行重启
SA_NOMASK/SA_NODEFER:在处理此信号未结束前不理会此信号的再次到来。如果参数oldact不是NULL指针,则原来的信号处理方式会由此结构sigaction 返回。
返回值 执行成功则返回0,如果有错误则返回-1。
错误代码 EINVAL 参数signum 不合法, 或是企图拦截SIGKILL/SIGSTOPSIGKILL信号
EFAULT 参数act,oldact指针地址无法存取。
EINTR 此调用被中断

5.SA_NOCLDWAIT选项

设置SA_NOCLDWAIT选项后,当信号为SIGCHILD时,则调用进程的子进程终止,立即释放系统资源。如果调用进程调用wait函数,则会导致该进程阻塞,知道其所有的子进程全部释放后wait函数返回-1,并将errno错误号设置为ECHILD,该选项可以用来避免僵尸进程的产生。

01//nowait.c
02 
03#include <stdio.h>
04#include <stdlib.h>
05#include <signal.h>
06#include <unistd.h>
07#include <errno.h>
08 
09int main(void)
10{
11    struct sigaction act;
12    pid_t pid;
13 
14    act.sa_handler = SIG_DFL;
15    act.sa_flags = SA_NOCLDWAIT;
16    act.sa_sigaction = NULL;
17    sigemptyset(&act.sa_mask);
18 
19    if(sigaction(SIGCHLD, &act, NULL) == -1){
20        perror("fail to set handler for SIGCHILD");
21        exit(1);
22    }
23 
24    pid = fork();
25 
26    if(pid < 0){
27        perror("fail to fork");
28        exit(1);
29    }else if(pid == 0){
30        printf("the 1st child/n");
31        exit(0);//第一个进程立即退出
32    }else{
33        pid = fork();
34 
35        if(pid < 0){
36            perror("fail to fork");
37            exit(1);
38        }else if(pid == 0){
39            printf("the 2nd child/n");
40            sleep(5);//休眠5秒退出
41            exit(0);
42        }else{
43            if(wait(NULL) == -1)//调用wait函数,该函数必定出错返回
44                if(errno == ECHILD)
45                    printf("all child quit, no child is zome/n");
46 
47            printf("the parent/n");
48        }
49    }
50 
51    return 0;
52}

执行:

./nowait
the 1st child
the 2nd child
all child quit, no child is zome
the parent

6.SA_NODEFER选项

如果设置SA_NODEFER选项,当捕捉到该信号时该信号正在执行处理函数时,不阻塞该信号,除非sa_mask中指定阻塞该信号。

01//nodefer.c
02 
03#include <stdio.h>
04#include <stdlib.h>
05#include <signal.h>
06 
07void sigusr1_handler(int signo)
08{
09    printf("catch SIGUSR1/n");
10 
11    sleep(5);//等待下一个SIGUSR1信号
12 
13    printf("back to main/n");
14}
15 
16int main(void)
17{
18    struct sigaction act;
19 
20    act.sa_handler = sigusr1_handler;
21    act.sa_flags = SA_NODEFER;
22    act.sa_sigaction = NULL;
23    sigemptyset(&act.sa_mask);
24 
25    if(sigaction(SIGUSR1,&act, NULL) == -1){
26        perror("fail to set handler for SIGCHILD");
27        exit(1);
28    }
29 
30    printf("process begin/n");
31 
32    sleep(10);//等待SIGUSR1信号
33 
34    printf("done/n");
35 
36    return 0;
37}

7.SA_RESETHAND选项

如果设置SA_RESETHAND选项,当信号处理返回后,该信号的处理函数乎恢复为默认的信号处理函数。

01#include <stdio.h>
02#include <stdlib.h>
03#include <signal.h>
04 
05void sigusr1_handler(int signo)
06{
07    printf("catch SIGUSR1/n");
08}
09 
10int main(void)
11{
12    struct sigaction act;
13 
14    act.sa_handler = sigusr1_handler;
15    act.sa_flags = SA_RESETHAND;
16    act.sa_sigaction = NULL;
17    sigemptyset(&act.sa_mask);
18 
19    if(sigaction(SIGUSR1, &act, NULL) == -1){
20        perror("fail to set handler for SIGCHILD");
21        exit(1);
22    }
23 
24    printf("process begin/n");
25 
26    sleep(5);//等待第一个SIGUSR1
27 
28    sleep(5);//等待第二个SIGUSR1
29 
30    printf("done/n");
31 
32    return 0;

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值