linux信号屏蔽字

来源:互联网 发布:为什么动物会怕死知乎 编辑:程序博客网 时间:2024/04/24 20:47

一个进程的信号屏蔽字规定了当前阻塞而不能递送给该进程的信号集调用函数sigprocmask可以检测或更改其信号屏蔽字,或者在一个步骤中同时执行这两个操作。

#include <signal.h>int sigprocmask( int how, const sigset_t *restrict set, sigset_t *restrict oset );返回值:若成功则返回0,若出错则返回-1

首先,若oset是非空指针,那么进程的当前信号屏蔽字通过oset返回。

其次,若set是一个非空指针,则参数how指示如何修改当前信号屏蔽字。

表10-4说明了how可选用的值。注意,不能阻塞SIGKILL和SIGSTOP信号

表10-4 用sigprocmask更改当前信号屏蔽字的方法

  how

  说明

  SIG_BLOCK  该进程新的信号屏蔽字是其当前信号屏蔽字和set指向信号集的并集。set包含了我们希望阻塞的附加信号  SIG_UNBLOCK  该进程新的信号屏蔽字是其当前信号屏蔽字和set所指向信号集补集的交集。set包含了我希望解除阻塞的信号  SIG_SETMASK  该进程新的信号屏蔽字将被set指向的信号集的值代替

 

如果set是空指针,则不改变该进程的信号屏蔽字,how的值也无意义

在调用sigprocmask后如果有任何未决的、不再阻塞的信号,则在sigprocmask返回前,至少会将其中一个信号递送给该进程。

1、有时候不希望在接到信号时就立即停止当前执行,去处理信号,同时也不希望忽略该信号,而是延时一段时间去调用信号处理函数。这种情况是通过阻塞信号实现的。

 

2、信号阻塞和忽略信号的区别。

阻塞的概念和忽略信号是不同的。操作系统在信号被进程解除阻塞之前不会讲信号传递出去,被阻塞的信号也不会影响进程的行为,信号只是暂时被阻止传递。当进程忽略一个信号时,信号会被传递出去但进程会将信号丢弃。


1)头文件:#include <signal.h>

2)一个保护临界区代码的错误实例:(sigprocmask()和pause()实现)

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

 

void handler(intsig)   //信号处理函数的实现

{

   printf("SIGINT sig");

}

int main()

{

    sigset_tnew,old;

    structsigaction act;

   act.sa_handler = handler;  //信号处理函数handler

   sigemptyset(&act.sa_mask);

   act.sa_flags = 0;

   sigaction(SIGINT, &act, 0); //准备捕捉SIGINT信号

   sigemptyset(&new);

   sigaddset(&new, SIGINT);

   sigprocmask(SIG_BLOCK, &new,&old); //将SIGINT信号阻塞,同时保存当前信号集

   printf("Blocked");

   sigprocmask(SIG_SETMASK, &old,NULL);  //取消阻塞

   pause();

    return0;

}

上面实例的问题是:本来期望pause()之后,来SIGINT信号,可以结束程序;可是,如果当“取消阻塞”和“pause”之间,正好来了SIGINT信号,结果程序因为pause的原因会一直挂起。。。

解决的方式,当然是sigsuspend()函数了。

 

3)使用sigsuspend()的程序

#include <unistd.h>

#include <signal.h>

#include <stdio.h>

void handler(int sig)  //信号处理程序

{

   if(sig == SIGINT)

     printf("SIGINT sig");

   else if(sig == SIGQUIT)

     printf("SIGQUIT sig");

   else

     printf("SIGUSR1 sig");

}

 

int main()

{

    sigset_tnew,old,wait;   //三个信号集

    structsigaction act;

   act.sa_handler = handler;

   sigemptyset(&act.sa_mask);

   act.sa_flags = 0;

   sigaction(SIGINT, &act,0);   //可以捕捉以下三个信号:SIGINT/SIGQUIT/SIGUSR1

   sigaction(SIGQUIT, &act, 0);

   sigaction(SIGUSR1, &act, 0);

   

   sigemptyset(&new);

    sigaddset(&new,SIGINT);  //SIGINT信号加入到new信号集中

   sigemptyset(&wait);

   sigaddset(&wait, SIGUSR1); //SIGUSR1信号加入wait

   sigprocmask(SIG_BLOCK, &new,&old);      //将SIGINT阻塞,保存当前信号集到old中

   

    //临界区代码执行    

  

   if(sigsuspend(&wait) != -1) //程序在此处挂起;用wait信号集替换new信号集。即:过来SIGUSR1信 号,阻塞掉,程序继续挂起;过来其他信号,例如SIGINT,则会唤醒程序。执行sigsuspend的原子操作。注意:如果“sigaddset(&wait,SIGUSR1);”这句没有,则此处不会阻塞任何信号,即过来任何信号均会唤醒程序。

       printf("sigsuspend error");

    printf("Aftersigsuspend");

   sigprocmask(SIG_SETMASK, &old, NULL);

    return0;

}

sigsuspend的原子操作是:

(1)设置新的mask阻塞当前进程(上面是用wait替换new,即阻塞SIGUSR1信号)

(2)收到SIGUSR1信号,阻塞,程序继续挂起;收到其他信号,恢复原先的mask(即包含SIGINT信号的)。

(3)调用该进程设置的信号处理函数(程序中如果先来SIGUSR1信号,然后过来SIGINT信号,则信号处理函数会调用两次,打印不同的内容。第一次打印SIGINT,第二次打印SIGUSR1,因为SIGUSR1是前面阻塞的)

(4)待信号处理函数返回,sigsuspend返回了。(sigsuspend将捕捉信号和信号处理函数集成到一起了)

总结:

在nginx源码中,ngx_master_process_cycle函数中,首先调用了sigprocmask()函数阻塞了部分信号,在for循环中调用了sigsuspend函数 ,但是sigsuspend函数中的set为空,也就是说,在for循环之前,如果有set集合中的信号到来就阻塞,在for循环之内,任何信号到来都进程处理。函数返回之后同时也恢复了原来的信号掩码!


sigsuspend的整个原子操作过程为:
(1) 设置新的mask阻塞当前进程;
(2) 收到信号,恢复原先mask;
(3) 调用该进程设置的信号处理函数;
(4) 待信号处理函数返回后,sigsuspend返回。
大致就是上面这个过程,噢,原来signal handler是原子操作的一部分,而且是在恢复屏蔽字后执行的,

int sigsuspend(const sigset_t *sigmask);

此函数用于进程的挂起,sigmask指向一个信号集。当此函数被调用时,sigmask所指向的信号集中的信号将赋值给信号掩码。之后进程挂起。直到进程捕捉到信号,并调用处理函数返回时,函数sigsuspend返回。信号掩码恢复为信号调用前的值,同时将errno设为EINTR。进程结束信号可将其立即停止。

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #include <stdio.h>    
  2. #include <signal.h>    
  3.     
  4. void checkset();    
  5. void func();    
  6. void main()    
  7. {    
  8.      sigset_tblockset,oldblockset,zeroset,pendmask;    
  9.      printf("pid:%ld\n",(long)getpid());    
  10.      signal(SIGINT,func);    
  11.     
  12.      sigemptyset(&blockset);    
  13.      sigemptyset(&zeroset);    
  14.      sigaddset(&blockset,SIGINT);    
  15.     
  16.      sigprocmask(SIG_SETMASK,&blockset,&oldblockset);    
  17.      checkset();    
  18.      sigpending(&pendmask);    
  19.     
  20.      if(sigismember(&pendmask,SIGINT))    
  21.          printf("SIGINTpending\n");    
  22.   //不阻塞任何信号 任何信号到来都会激活进程  
  23.      if(sigsuspend(&zeroset)!= -1)    
  24.      {    
  25.      printf("sigsuspenderror\n");    
  26.      exit(0);    
  27.      }    
  28.     
  29.      printf("afterreturn\n");    
  30.      sigprocmask(SIG_SETMASK,&oldblockset,NULL);    
  31.     
  32.      printf("SIGINTunblocked\n");    
  33. }    
  34.     
  35. void checkset()    
  36. {    sigset_tset;    
  37.      printf("checksetstart:\n");    
  38.      if(sigprocmask(0,NULL,&set)<0)    
  39.      {    
  40.      printf("checksetsigprocmask error!!\n");    
  41.      exit(0);    
  42.      }    
  43.     
  44.      if(sigismember(&set,SIGINT))    
  45.      printf("sigint\n");    
  46.     
  47.      if(sigismember(&set,SIGTSTP))    
  48.      printf("sigtstp\n");    
  49.     
  50.      if(sigismember(&set,SIGTERM))    
  51.      printf("sigterm\n");    
  52.      printf("checksetend\n");    
  53. }    
  54.     
  55. void func()    
  56. {    
  57.      printf("hellofunc\n");    
  58. }    
结果:

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. pid:5474  
  2. checksetstart:  
  3. sigint  
  4. checksetend  
  5. ^Chellofunc  
  6. afterreturn  
  7. checksetstart:  
  8. checksetend  
  9. SIGINTunblocked  

从运行的结果 以及和上面的例子进行比较较就可以知道这两个函数的函数。
0 0
原创粉丝点击