【Linux】详解信号的保存&&信号屏蔽字的设置

打印 上一主题 下一主题

主题 677|帖子 677|积分 2031

一、信号处理的一些常见概念

   

  • 现实实行信号的处理动作称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才实行递达的动作。
  • 留意:阻塞和忽略是差别的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
  • 阻塞一个信号和是否收到这个信号是没有关系的。也就是说,在还充公到一个信号之前就可以在内核中设置对这个信号举行阻塞。
   二、信号保存以及阻塞的内核级理解

在进程的PCB中,实在是有三张表。
        一张为block位图(阻塞位图),也就是一个32位的整形变量,其中取高31位来体现是否阻塞对应的信号,比如说block位图中第0个比特位不用,第1个比特位体现是否阻塞1号信号,第一个比特位为1就体现阻塞1号信号,为0就体现不阻塞1号信号,依次类推,第2到第31个比特位也是同样的道理。
        一张为pending位图(未决位图),也是一个32位的整形变量,其中取高31位来体现是否收到对应的信号,比如说pending位图中第0个比特位不用,第1个比特位体现是否收到1号信号,第一个比特位为1就体现收到1号信号,为0就体现没有收到1号信号。
        另一张是一个函数指针数组,该数组中每一个下标中都存放了收到对应信号后的处理方法。假如我们不对方法做自界说写入,那么进程在收到对应信号后实行的就是默认的方法,假如自界说写入了那实行的就是我们写入的方法。

        在上图中,三个数组(前两张位图也可以看成数组)应该横着看,依次体现该信号是否被阻塞,是否收到该信号,以及实行该信号的处理方法。 常规信号在递达之前产生多次只计一次,也就是说,当在一段时间内有多个类似的信号到来但却来不及被处理时,在pending位图里只会纪录一次而实时信号在递达之前产生多次可以依次放在一个队列里。
三、查看pending位图


        其中sigset_t范例的表明就在下面,set为一个输出型参数,我们传入一个sigset_t范例的参数set,pending位图中的值就被set参数得到了。假如获取成功sigpending函数返回0,是被返回-1。
四、设置信号屏蔽字操作(修改block位图)

        从上面的介绍中我们也可以看到,实在block位图和pending位图的结构黑白常相似的,所以未决和阻塞标志可以用类似的数据范例sigset_t来存储,sigset_t称为信号集,可以用来修改进程block位图中的信号屏蔽字sigset_t就是一种数据范例,跟int、double这些数据范例没有区别。
3.1、信号集操作函数

        sigset_t虽然是一种数据范例,但是我们并不能直接办动的修改sigset_t范例的值,必须要调用对应的系统调用函数。
  1. #include <signal.h>
  2. int sigemptyset(sigset_t *set);
  3. int sigfillset(sigset_t *set);
  4. int sigaddset (sigset_t *set, int signo);
  5. int sigdelset(sigset_t *set, int signo);
  6. int sigismember(const sigset_t *set, int signo);
复制代码
  

  • sigemptyset:初始化set所指向的信号集,使其中所有信号的对应bit清零,体现该信号集不包罗任何有用信号。
  • sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置1位,体现该信号集的有用信号包括系统支持的所有信号。
  • sigaddset:在set信号会合添加signo信号。
  • sigdelset:在set信号会合删除signo信号。
  • sigismember:用于测试一个指定的信号是否已参加至一个特定的信号会合。
          我们设置完信号集set的值后,set并没有被设置进进程的PCB中,还需要我们调用系统调用函数设置。 
 3.2、设置信号屏蔽字

使用sigprocmask系统调用函数可以设置进程的信号屏蔽字

第一个参数how有三个选项:
   

  • SIG_BLOCK:set包罗了我们希望添加到当前信号屏蔽字中的信号,相当于mask=mask|set。
  • SIG_UNBLOCK:set包罗了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set。
  • SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask=set。
          第二个参数set是我们设置的信号屏蔽字,第三个参数为输出型信号屏蔽字,是原来的信号屏蔽字。 
3.3、设置信号屏蔽字的例子

        下面是一个设置屏蔽2号信号,有解除屏蔽2号信号的例子。在程序运行起来到程序运行到20秒期间,我给程序发送2号信号,应该看到pending位图中2号信号的位置为1但程序不退出,到了20秒时程序退出。
        下面是打印pending表的函数,假如收到信号,对应的比特位就置1,假如没有收到就置0。
  1. void print(const sigset_t& pending)
  2. {
  3.     for(int i = 31; i>=1; i--)
  4.     {
  5.         if(sigismember(&pending, i))
  6.             std::cout << "1";
  7.         else
  8.             std::cout << "0";
  9.     }
  10.     std::cout << std::endl;
  11. }
复制代码
 2号信号参加到信号屏蔽会合:
  1.     sigset_t set, oldset;
  2.     sigemptyset(&set);
  3.     sigemptyset(&oldset);
  4.     sigaddset(&set, 2);//将2号信号加入到要屏蔽的信号集中
复制代码
 set设置进进程的PCB中:
  1.     //设置set进block位图中
  2.     int n = sigprocmask(SIG_SETMASK, &set, &oldset);
  3.     if(n == -1)
  4.     {
  5.         std::cout << "设置屏蔽字错误!" << std::endl;
  6.         return 1;
  7.     }
复制代码
查看pending表,观察退出状态:
  1.     int cnt = 0;
  2.     //查看pending位图,给进程发送2号信号,pending位图中应该出现2号信号,但是进程不会退出
  3.     //等到20秒时程序退出
  4.     while(true)
  5.     {
  6.         cnt++;
  7.         if(cnt == 20)
  8.             sigprocmask(SIG_UNBLOCK, &set, &oldset);
  9.         sigset_t pending;
  10.         sigemptyset(&pending);
  11.         int m = sigpending(&pending);
  12.         print(pending);
  13.         sleep(1);
  14.     }
复制代码
发送2号信号,程序到20秒时退出: 


四、总结

        31个信号中并不是所有信号都可以被屏蔽掉,9号信号(SIGKILL)和19号信号(SIGSTOP)是无法被屏蔽掉的。若想获取上述的完整的代码,请移步本人码云:240427 · 沈旭彬/C++代码 - 码云 - 开源中国 (gitee.com) 

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

万有斥力

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表