信号及信号处理--信号处理--信号的屏蔽

信号及信号处理--信号处理--信号的屏蔽
时  间: Thu Sep  3 13:49:38 2009
点  击: 18

2009-07-04 22:34    1、信号集
    前面列出的信号总数目达64个,超过了一个整型数能表示的位数(一个整型变量通常
为32位),因此不能用整型量中的一位代表一种信号。POSIX标准定义了数据类型sigset_
t来表示信号集,并且定义了一系列函数操作信号集。在Shell下输入man sigsetops可查看
它们的函数原型如下:
#include <signal.h>
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);
    这些函数的具体含义为:
    函数sigemptyset用来初始化一个信号集,使其不包括任何信号。
    函数sigfillset用来初始化一个信号集,使其包括所有信号。
    函数sigaddset用来向set指定的信号集中添加由signum指定的信号。
    函数sigdelset用来从set指定的信号集中删除由signum指定的信号。
    函数sigismember用来测试信号signum是否包括在set指定的信号集中。
    函数sigemptyset,sigfillse,sigaddset,以及sigdelset在执行成功时返回0,失败返
回-1。函数sigismember返回1表示测试的信号在信号集中,返回0表示测试的信号不在信
号集中,出错返回-1。
    注意:所有应用程序在使用信号集中,要对该信号集调用一次sigemptyset或
sigfillset以初始化信号集。这是因为C语言编译器将不赋值的外部和静态变量都初始化为
0。
    2、信号屏蔽
    信号屏蔽又称为信号阻塞,在Shell下输入man sigprocmask可获取信号阻塞的一系列
函数的说明。
#include <signal.h>
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);
    这些函数的具体解释如下:
    (1)sigprocmask函数:
    每个进程都有一个信号屏蔽码,它规定了当前阻塞而不能递送给该进程的信号集。调
用函数sigprocmask可以检测或更改进程的信号屏蔽码。如果参数oldset是非空指针,则该
进程之前的信号屏蔽码通过oldset返回;如果参数set是非空指针,则该函数将根据参数
how来修改信号当前屏蔽码,how的取值如下:
    SIG_BLOCK:将进程新的信号屏蔽码设置为当前信号屏蔽码和set指向信号集的并集。
    SIG_UNBLOC:将进程新的信号屏蔽码设置为当前信号屏蔽码中,删除set所指向信号集
,即set包含了我们希望解除阻塞的信号。即使对当前信号屏蔽码中不存在的信号使用SIG
_UNBLOCK也是合法操作。
    SIG_SETMASK:将进程新的信号屏蔽码设置为set指向的值。
    函数执行成功返回0,当有错误发生时则返回-1,错误代码存入errno中,详细的错误
代码说明请参考man手册。
    (2)sigpending函数
    函数sigpending用来获取调用进程因被阻塞而不能递送和当前未决的信号集。该信号
集通过参数set返回。
    函数执行成功返回0,当有错误发生时则返回-1,错误代码存入errno中,详细的错误
代码说明请参考man手册,例9-8演示了函数sigprocmask和函数sigpending的用法。
    例9-8
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

/*自定义的错误处理函数*/
void my_err(const char *err_string,int line)
{
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}

/*SIGINT的处理函数*/
void hander_sigint(int signo)
{
printf("recv SIGINT/n");
}

int main()
{
sigset_t newmask,oldmask,pendmask;//定义信号集

/*安装信号处理函数*/
if (signal(SIGINT,hander_sigint) == SIG_ERR){
    my_err("signal",__LINE__);
}

/*睡眠10秒*/
sleep(10);

/*初始化信号集newmask并将SIGINT添加进去*/
sigemptyset(&newmask);
sigaddset(&newmask,SIGINT);

/*屏蔽信号SIGINT*/
if (sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0){
    my_err("sigprocmask",__LINE__);
}else{
    printf("SIGINT blocked/n");
}

sleep(10);

/*获取未决信号队列*/
if (sigpending(&pendmask) < 0){
    my_err("sigpending",__LINE__);
}

/*检查未决信号队列里面是否有SIGINT*/
switch (sigismember(&pendmask,SIGINT)){
    case 0:
      printf("SIGINT is not in pending queue/n");
      break;
    case 1:
      printf("SIGINT is in pending queue/n");
      break;
    case -1:
      my_err("sigismember",__LINE__);
      break;
    default:
      break;
}

/*解除对SIGINT的屏蔽*/
if (sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0){
    my_err("sigprocmask",__LINE__);
}else{
    printf("SIGINT unblock/n");
}

while(1)
    ;

return 0;
}
    程序说明:
    程序首先安装SIGINT的信号处理函数,然后睡眠10秒(此时按下<Ctrl+c>组合键)
。之后将SIGINT阻塞,然后再睡眠10秒(此时可以多次按下<Ctrl+c>键),并检测SIGI
NT是否在未决信号队列中,最后解除SIGINT信号的阻塞。执行结果如下:
$ ./9-8
^Crecv SIGINT
SIGINT blocked
^C
SIGINT is in pending queue
recv SIGINT
SIGINT unblock
^Crecv SIGINT
^/退出
    从结果可以看出,第二个“recv SIGINT”,先于“SIGINT unblocked”打印出来。这
是因为第二次调用sigprocmask解除对SIGINT信号的阻塞以后,进程未决信号队列非空,首
先执行信号处理函数,然后才执行后面的“printf("SIGINT unblocked/n");”
    程序中设置SIGINT为阻塞时,先保存了进程的信号屏蔽字在oldmask中,以方便后面解
除对SIGINT的阻塞。在解除SIGINT阻塞时,重新设置进程的信号屏蔽字(SIG_SETMASK)为
oldmask。或者也可以使用SIG_UNBLOCK使信号不被阻塞,但是这样可能会产生一个问题,
当一个大的程序中其他地方可能也阻塞了些信号时,使用SIG_UNBLOCK就会把其它地方的设
置修改掉,因此,建议使用SIG_SETMASK恢复进程的信号屏蔽字而不是使用SIG_UNBLOCK解
除特定信号的阻塞。
    程序的结果也再次证明了不可靠信号不支持排队,有可能丢失信号。因为第二次按下
<Ctrl+c>多次,在解除对SIGINT信号的阻塞后,只打印了一个“recv SIGINT”。
    (3)sigsuspend函数
    函数sigsuspend将进程的信号屏蔽码设置为mask,然后与pause函数一样等待信号的发
生并执行完信号处理函数。信号处理函数执行完成后再把进程的信号屏蔽码设置为原来的
屏蔽字,然后sigsuspend函数才返回。sigsuspend函数保证改变进程的屏蔽码和将进程挂
起等待信号是原子操作。
    sigsuspend函数总是返回-1,并将errno置为EINTR,例9-9演示了sigsuspend函数的
用法。
    例9-9
#include <stdio.h>
#include <signal.h>
#include <unistd.h>

/*自定义的错误处理函数*/
void my_err(const char *err_string,int line)
{
fprintf(stderr,"line:%d ",line);
perror(err_string);
exit(1);
}

/*SIGINT的处理函数*/
void handler_sigint(int signo)
{
printf("recv SIGINT/n");
}

int main()
{
sigset_t newmask,oldmask,zeromask; //定义信号集
/*安装信号*/
if (signal(SIGINT,handler_sigint) == SIG_ERR){
    my_err("signal",__LINE__);
}

sigemptyset(&newmask);
sigemptyset(&zeromask);
sigaddset("signal",__LINE__);

/*屏蔽信号SIGINT*/
if (sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0){
    my_err("sigprocmask",__LINE__);
}else{
    printf("SIGINT block/n");
}

/*临界区*/

/*使用sigsuspend取消所有信号的屏蔽并等待信号的触发*/
if (sigsuspend(&zeromask) != -1) { //sigsuspend总是返回-1
    my_err("sigsuspend",__LINE__);
}else{
    printf("recv a signo,return from sigsuspend/n");
}

/*---------------------------------------------
//如果使用sigprocmask加上pause可能会出现错误
if (sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0){
    my_err("sigprocmask",__LINE__);
}
pause();
---------------------------------------------*/

/*将信号屏蔽字恢复*/
if (sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0){
    my_err("sigprocmask",__LINE__);
}
while(1)
    ;

return 0;

}
    程序说明:
    程序使用sigprocmask()屏蔽信号SIGINT,然后使用sigsuspend()取消对所有信号的屏
蔽,并挂起等待信号的触发,执行程序结果如下:
$ ./9-9 &
SIGINT block
[1] 16130
$ ps -a
PID TTY          TIME CMD
16130 pts/0    00:00:00 9-9
16135 pts/0    00:00:00 ps
$ kill -s SIGRTMIN 16130
$
[1]+ 实时信号 0          ./9-9
    由于所有实时信号的默认响应都是终止进程,因此程序在收到信号SIGRTMIN之后,解
除sigsuspend()的挂起状态,并将信号屏蔽字修改为第一次调用sigprocmask()前的状态。
    如果为了达到相同的效果,不使用sigsuspend()而是使用注释掉的那部分代码,虽然
表面上看没有问题,并且执行程序也基本上不会发现问题,但潜在的bug将一直存在,特别
是在复杂的实际应用中就有可能暴露出来。如果信号发生在sigprocmask()之后pause()之
前,则这个信号就会丢失掉了,且如果信号只发生一次,程序将永远挂起在pause()上。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值