进程信号的深度剖析

  • 1.信号量概念

    • 临界资源:供多个进程访问的资源(例如:管道,共享内存,消息队列)
    • 临界区:访问临界资源时的代码
    • 信号量:本质是资源计数器(资源计数器为1表示可以访问,为0表示不能访问)
      • 互斥访问:同一个时刻,多个进程当中只有一个进程可以访问临界区资源
      • 多个进程通过信号量保证互斥的时候需要先获取信号量,如果能够获取信号量,才能访问支援,如果获取不了,则阻塞等待.
      • 如果不互斥访问导致的结果:复习:(程序计数器(保存下一步执行的指令),上下文信息(保存寄存器中的内容))会产生二义性:如果两个进程同时往一个临界资源里写时会发生:当一个进程A写a后,还没等读呢,时间片到了.另一个进程B写进去b,等到A读的时候b已经把a覆盖了,A进程读的和期望的就不相符了.
      • 同步访问:临界资源允许一定数量的进程同时访问,如果有进程退出了.可以再加一个进程进行访问.
  • 信号概念

    • 信号是一个软中断,只是告诉进程有个信号,具体怎么执行,什么时候执行进程说了算.
    • 信号的种类:kill -l 罗列信号(1~31是非实时信号,特点,信号可能会丢失)(34~64实时信号特点:信号不会丢失)
  • 信号产生

    • 硬件产生:例如:ctrl+c:是收到并执行了2号信号,ctrl+z 20号信号(这是停止进程,不是终止kill -9 pid强杀信号),进程还在后台运行),ctrl+| 3号信号...kill -2 pid(给进程发送信号)
    • 软件产生:kill函数:给pid的进程发送sig信号

    • raise函数:谁调用给谁发送:这个是调用kill封装的.

    • 程序崩溃收到信号:
      • 查看核心转储文件允许生成的大小

      • 设置核心转储文件的大小

      • 场景1:解引用空指针:结果如下,收到11号信号

      • 场景2:内存访问越界:收到的也是11号信号
      • 场景3:除0(收到的是8号信号)
      • 多次free:(收到的是6号信号)
  • 信号处理方式

    • 1.默认处理方式也是操作系统对信号的处理方式:SIG_DFL(man 7 signal)

    • 2.忽略处理方式:SIG_DFL(进程收到忽略处理方式的信息后是不进行处理的
      • 举例:僵尸进程,子进程先于父进程退出,子进程会给父进程发送SIGCHLD(17号信号),父进程接受到这个信号后是忽略处理的,导致父进程没有回收子进程的状态信息,从而子进程变成僵尸进程.
    • 3.自定义处理方式(下面有)
  • 信号的注册

    • 一个进程收到一个信号,这个过程称为注册,操作系统给注册的.操作系统给内核的pcb里注册信号,进程处理信号称为信号注销.
    • PCB里的的关系 sigset_t这个结构体里是位图

    • 操作系统给进程注册的过程(位图进行标记的)
      • 信号注册时会把对应的比特位置为1,表示当前进程收到这个信号,还会把sigqueue队列里添加一个节点,这个队列在操作系统内核本质上就是一个双向链表
      • 实时信号和非实时信号注册的区别
        • 非实时信号注册:第一次注册,修改位图,修改sigqueue.第二次注册相同的信号时位图时1~1,sigqueue节点并不会再添加
        • 实时信号注册:第一次注册,修改位图,修改sigqueue.第二次注册相同的信号时位图是1~1变化,sigqueue节点还会添加
  • 信号的注销

    • 非可靠信号:位图1-0 ;sigqueue进行出队操作(非实时信号的节点在双向链表里只有一份,拿到节点后,找上面的信号处理方式)
    • 可靠信号:在链表里找对应的信号,如果有就出队,出完队之后再检测是不是还有这个信号的节点,如果有,位图不改变.如果没有了,位图1-0;
  • 信号的自定义处理方式(程序员自定义某个信号执行什么操作)函数也有信号捕捉的作用
    • 函数1:函数第一个参数是信号值,例如:本来进程收到2号信号后会调用一个终止自己的函数,但是用了signal后,进程收到2号信号时,会调用程序员自定义的这个函数

    • 并不是所有的信号都能被自定义处理:9号强杀信号不能被修改了.
    • 函数2:

    • struct sigaction结构体里存放的是信号值对应的具体操作.
    • 函数的第一个参数是信号值,第二个参数是自定义的结构体,第三个参数(出参)原本信号值对应的结构体,如果,函数调用成功后,改掉了信号值对应的结构体,但是第三个参数会保存原本的信息,不至于丢失
    • 结构体里只关系第一个成员和第三个成员,第一个成员是,当收到信号值后会执行哪个函数,我们要做的是定义一个结构体,自定义里面的第一成员,并把第三个成员全置为0,其他三个成员爱是啥是啥,第三个成员保存当进程在处理信号的时候,如果收到了其他信号,就放在这个位图里,后续再放在进程信号位图里,上面的PCB里的位图

  • 信号的捕捉流程(如果信号的处理动作是用户自定义的函数,在信号递达时就调用这个函数称为信号的捕捉)

    • 信号的处理时机:(信号是任何时候都能进入进程的)只有进程从内核态进入用户态的时候会调用do_signal函数(用来判断在出内核态之前有没有收到信号),如果函数发现收到了信号就处理,如果没有收到信号直接返回用户态
      • 如果发现收到了信号,是怎么处理的
        • 1.判断当前信号是否被阻塞,如果阻塞了就不处理了
        • 2.执行信号的注销
        • 3.调用信号的处理方式,如果是默认或者是忽略处理,直接在内核就处理了...如果是自定义处理的方式,要回到用户态执行自定义的函数,执行完后调用sigreturn函数回到内核态,再调用do_signal函数,看在上一次调用这个函数到现在有没有收到信号,如果有就重复这个过程,没有的话调用sys_signal函数回到用户态
        • 核心:当进程从内核态进入用户态的时候会调用do_signal函数,检测是否收到信号

    • 进入内核的方式(中断,异常,系统调用)
      • 调用系统调用函数和库函数
      • 内存访问越界和访问空指针
  • 信号的阻塞

    • 和信号注册一样,在PCB里有信号阻塞的位图,当需要阻塞一个信号的时候,将信号对应的比特位置为1,0是不阻塞........信号的阻塞和信号的注册没关系
    • 信号阻塞不是不处理信号,而是等该信号不阻塞之后再进行处理
    • 信号注册和信号阻塞的位图用的是一个结构体,只是对象不一样
    • 加上阻塞之后的信号捕捉流程,上面有
    • 阻塞接口:
    • int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
    • how:想让sigprocmask做什么事情
      • SIG_BIOCK:设置某个信号为阻塞状态
      • SIG_UNBLOCK :设置某个信号为非阻塞状态
      • SIG_SETMASK :用第二个参数“set”,替换原来的阻塞位图。(替换的意思)
    • set :新设置的阻塞位图
    • oldset :原来老的阻塞位图
    • 原理解析:当how为SIG_BLOCK时,函数会根据set,计算新的阻塞位图,方式为:block(new) = block(o1d) |set;当how为为SIG_UNBLOCK时,函数会根据set,计算新的阻塞位图,方式为:block(new) = block(o1d)& (~set) ;当how为为SIG_SETMASK时,函数会根据set,计算新的阻塞位图,方式为:block(new) = set;
    • 9号信号和19号信号不能被阻塞
    • 验证可靠信号和非可靠信号的阻塞:
      • 1,自定义2号和40号的处理方式,以便我们观察
      • 2.将2 40设置为阻塞状态
      • 3.给进程疯狂发2 和40号信号
      • 4.看看都执行了几次



    • 解决wait等待子进程时,啥也不干.本来wait一直在等子进程的退出信号...父进程一直运行自己的程序,等子进程退出后会给父进程一个退出信号,当接收到这个退出信号后就会调用自定义的函数,
    • 扩展内容:
    • 可重入函数
      • 可以被一个以上的任务调用的函数(),且不用担心数据被破坏,可重入函数任何时候都可以被中断,一段时间以后又可以运行,而相应的数据不会丢失。可重入函数或者只使用局部变量,即保存在CPU寄存器中或堆栈中;或者使用全局变量,则要对全局变量予以保护。
      • 通俗理解,第一次调用还没返回呢,就调用第二次...如果包含全局变量()

      • 如何写出可重入函数,不使用全局变量,不适用静态局部变量,如果必须使用全局变量,用互斥信号量进行保护
    • volatile关键字的作用
      • cpu每次拿值可能从内存里拿,也可能从寄存器里拿,这取决于gcc/g++编译时的优化等级,如果优化等级底,直接从内存拿了(就是想要的值).如果优化等级高从寄存器拿,可能和内存里的值不一样所以加volatile关键字,告诉cpu每次拿的时候都从内存拿
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值