Linux——进程信号(二):信号的捕捉流程,信号的阻塞,代码验证可靠信号和非可靠信号的注册,volatile关键字


1. 信号的捕捉流程

1.1 前言

信号的注册,是不是和操作系统维护的进程的PCB有关系?

因为struct task_struc中有一个struct sigpending中有一个sigset_t signal在sigset_t结构体中有一个sig数组(sig位图),信号注册时操作

答:信号的注册,注册在进程的PCB当中,是在和操作系统维护的的进程PCB打交道

1.2 捕捉流程:信号什么时候进行处理?(信号的处理在内核态完成)

进入到操作系统内核才去处理信号

  • (1)从内核态切换回用户态时,一定会调用do_signal函数,来处理进程收到的信号
       1.1 sig位图当中有信号注册,则执行信号注册的逻辑
       1.2 sig位图当中没有信号注册,则直接返回用户态
  • (2)处理信号(信号注销)–>针对上面1.1
       2.1 默认处理方式(直接在操作系统内核的代码当中完成)
        2.2 忽略处理方式(直接在操作系统内核的代码当中完成)
        2.3 自定义处理方式(调用程序员自己定义的函数)
  • (3)自定义处理方式–>针对上面 2.3
       3.1 在用户态执行程序员自己定义的函数
       3.2 调用sigreturn函数再次回到操作系统内核
       3.3 再次调用do_signal函数判断是否有信号注册(若有执行(2),若没有执行(3.4))
       3.4 调用sys_sigreturn函数回到用户态继续执行代码

如上内容图解如下:

在这里插入图片描述
在这里插入图片描述

1.3 什么时候进入操作系统内核

  • (1)调用系统调用函数的时候
  • (2) 调用C库函数的时候,因为C库函数的内部也是调用系统调用函数
  • (3) 内存访问越界,访问空指针

2. 信号的阻塞

2.1 信号的阻塞,不会影响信号的注册

2.2 从内核源码角度分析信号阻塞

查看源码,在 /root/rpmbuild/BUILD/kernel-3.10.0-957.el7/linux-3.10.0-957.el7.x86_64/include/linux 路径下,我们可看到一个 “sigset_t blocked”,block实则是一个位图,在 task_strcut 结构体中,如下图所示
在这里插入图片描述
在这里插入图片描述

那么信号的阻塞过程究竟是什么样呢?如下图所示:

在这里插入图片描述

如上图所示,假设在sig位图中注册了2号信号,则其在sig位图中对应的位置的比特位就被置为1

block位图

  • 作用: 阻塞某一个信号的处理,也就是,执行流进入到内核之后,调用了do_signal函数判断是否有信号需要被处理的时候,发现某一个信号注册了,同时也需要判断block位图当中该信号对应的比特位是否为1
      如果为1:则暂时不处理该信号(暂时不信号注销)
      如果为0:则进行处理

2.3 信号阻塞的接口

  • int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:想让sigprocmask函数做什么
    (1)SIG_BLOCK:设置某个信号为阻塞状态
    (2)SIG_UNBLOCK:设置某个信号为非阻塞状态
    (3)SIG_SETMASK:设置新位图
  • set:使用set去设置block位图
    (1)SIG_BLOCK:设置某个信号为阻塞状态
       block(new) = block(old) | set
       eg:block(old):0101 0000
           set :0000 1000  -->0101 1000
    (2)SIG_UNBLOCK:设置某个信号为非阻塞状态
       block(new) = block(old) & (~set)
       eg:block(old):0101 0000
          (~set) :1011 1111    -->0001 0000
    (3)SIG_SETMASK:设置新位图
       block(new) = set
  • 注意:信号的阻塞不会将9好信号和19号信号阻塞的

我们让所有的信号都阻塞掉,此时ctel+c等信号将不起作用,验证代码如下:

  1 #include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<signal.h>  
  4   
  5 int main()  
  6 {  
  7     sigset_t set;
  8     //把传进来的set的所以比特位都设为1
  9     sigfillset(&set);
 10     sigprocmask(SIG_SETMASK,&set,NULL);
 11     while(1)
 12     {
 13         sleep(1);                                                                                                                                   
 14     }                                                                                                                             
 15     return 0;                                                                                                                     
 16 }       

在这里插入图片描述

此时这个进程没办法退出,但9号信号和19号信号不会阻塞,可以使用kill -9 [pid]或kill -19 [pid]退出进程
在这里插入图片描述
在这里插入图片描述

2.4 验证可靠信号的注册和非可靠信号的注册

我们首先保存之前的位图然后将所有信号进行屏蔽,在获得一个字符串后,将位图设置为之前的位图oldset,此时我们相当于没有屏蔽任何信号,代码如下:

  1 #include<stdio.h>                                                                                                                                   
  2 #include<unistd.h>
  3 #include<signal.h>
  4 
  5 int main()
  6 {
  7     sigset_t set;
  8     //把传进来的set的所以比特位都设为1
  9     sigfillset(&set);
 10     sigset_t oldset;
 11     sigprocmask(SIG_SETMASK,&set,&oldset);
 12 
 13     getchar();
 14 
 15     sigprocmask(SIG_SETMASK,&oldset,NULL);
 16 
 17 
 18 
 19     while(1)
 20     {
 21         sleep(1);
 22     }
 23     return 0;
 24 }

让程序跑起来,此时程序阻塞在getchar()位置
在这里插入图片描述
此时我们向进程发送5次2号信号(非可靠信号)和5次40号信号(可靠信号)
在这里插入图片描述
然后随便输入一个字符后,看到程序直接结束了,我们并没有看到任何现象,此时只能传递的某一个信号结束了进程
在这里插入图片描述
为了方便我们看到现象,我们将信号的处理方式改为自定义处理方式,改进后代码如下:

  1 #include<stdio.h>  
  2 #include<unistd.h>  
  3 #include<signal.h>  
  4   
  5 void sigcallback(int signo)  
  6 {  
  7     printf("signal:%d\n",signo);                                                                                                                    
  8 }                                                                                                                           
  9                                                                                                                             
 10 int main()                                                                                                                  
 11 {                                                                                                                           
 12     signal(2,sigcallback);                                                                                                  
 13     signal(40,sigcallback);                                                                                                 
 14     sigset_t set;                                                                                                           
 15     //把传进来的set的所以比特位都设为1                                                                                      
 16     sigfillset(&set);                                                                                                       
 17     sigset_t oldset;                                                                                                        
 18     sigprocmask(SIG_SETMASK,&set,&oldset);                                                                                  
 19                                                                                                                             
 20     getchar();            
 21                           
 22     sigprocmask(SIG_SETMASK,&oldset,NULL);  
 23                           
 24                           
 25                           
 26     while(1)              
 27     {                     
 28         sleep(1);         
 29     }                     
 30     return 0;             
 31 }      

让程序跑起来,此时程序阻塞在getchar()位置
在这里插入图片描述
此时我们向进程发送5次2号信号(非可靠信号)和5次40号信号(可靠信号)
在这里插入图片描述
然后随便输入一个字符后,此时我们可以看到程序并没有结束,40号信号(可靠信号)处理了5次,2号信号(非可靠信号)处理了1次,此时可以证明同一个可靠信号在多次注册时,会会添加多次sigqueue节点,会注册多次,而同一个非可靠信号在多次注册时,只会添加一次sigqueue节点,注册一次
在这里插入图片描述

3. volatile关键字

  • 作用:使变量保证内存的可见性(加上这个关键字,只会从内存中读取数据)
  • 解释:内存–>缓存–>寄存器–>CPU
    gcc/g++都有编译代码的优化选项,CPU在计算数据的时候,为了快,对已经从内存当中拿回来的数据,不会再从内存中获取,而是直接从寄存器当中获取,而加上volatile关键字只会从内存当中获取数据
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值