(笔记demo)Linux系统编程 信号部分笔记

本文详细介绍了Linux系统编程中的信号机制,包括信号的概念、类型、发送方式、阻塞与未决信号的处理,以及如何实现mysleep函数。重点讨论了信号在进程中的作用,如信号如何影响进程执行,并提供了信号处理的优化建议,如使用sigsuspend避免信号错过问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.信号是什么?

        可以直接通过内核对进程进行操作的手段,信号被记录在PCB(进程控制块)里,当进程被杀死后,PCB也就是留下的尸体,包含了该进程的死亡信息

        流程:

                1.触发发送信号操作

                2.由用户空间转为系统内核操作,对进程PCB施加信号

                3.回到用户空间前会逐一检查各PCB中的信号

        解释3(因为信号操作必定进入系统内核,所以在每次从系统内核回到用户空间时都会“顺便”检查是否有递达的信号,而不是触发一次信号就进一次内核,而是每次从内核切换出来时“顺便”检查,处理了一个信号,,,以此提高了效率)

2.信号种类

        常规与实时,区别在后续有写,比如5,

        TERM,CORE,STOP等等,分别有各自的功能

        CORE:杀死程序的同时会执行内核转储,生成一个core.pid的文件,可用gdb打开,bt指令查看其程序终止原因

        要想生成core文件,需要设置core文件大小,默认为0

3.发送信号指令

        kill:向任意进程发送任意信号

        reise:向自身发送信号

        abort:特殊,结束自身进程,且无返回,但会进行内核转储

4.alarm秒级定时关闭进程

5.信号未决与阻塞

        PCB中有信号表,包括未决,阻塞,触发函数,都是可以读取或改写的,以此来设置各种信号

        信号产生后可以被进程所屏蔽,被屏蔽的信号不能被进程所接收到,也就不会执行

        信号发送流程:

                信号产生但被阻塞,此时属于信号未决,此时信号的未决标志位置1,阻塞标志位也置        

        每个进程的PCB都有信号未决集合和阻塞集合,集合里的序号与系统信号的序号所对应,对应序号置1,则代表该信号未决,阻塞。

        捕鼠夹效应:信号被捕捉以后,如果没来得及处理(触发函数未进行),即便此时有新的信号到来,也不会被捕捉,导致非阻塞状态的信号丢失,类比捕鼠夹抓住老鼠后,只有处理了老鼠才能继续捕捉新的老鼠,常规信号多次未决只取一次,实时信号可以多次未决,按顺序生成一个未决序列

6.信号阻塞集合设置流程

       先操作集合,再将集合传入对应函数,保留老集合用于复原

        1.使用s_sigset类生成new,old两个集合,(s_sigset生成集合初始化都是当前进程的集合)

        2.用函数将new初始化并传入想要改变的信号序号

        3.将新集合与老集合进行操作,例如,增加,删除。。

        4.老集合用于复原(需要的话)

7.信号未决集合的应用

        自己man

8.信号捕捉操作

        改变信号的触发操作

        逻辑实现:对一个结构体设置

                1.将自定义的函数接口传参

                2.可以设置信号函数触发期间屏蔽其他信号,保证信号触发的完整性

                3.标志位置0,具体情况自己man

                解释2,信号函数触发时,如果有别的信号或者自己再次触发,会打断当前的函数操作,这显示不是想看到的,所以当进行信号函数处理时,可临时屏蔽其他信号,处理结束再重新开放。

方便的是,linux提供的结构体接口默认屏蔽自身信号,以及只需要传入需要屏蔽的信号集(s_sigset),阻塞和复原部分由结构体内部实现。

9.mysleep()的实现(仿sleep()

        思路:利用alarm信号的定时触发信号功能和pause()的挂起等待信号功能,实现挂起等待定时发送的信号,也就是睡眠

        1.信号捕捉操作,修改alarm的action,因为睡眠不需要退出进程,最好是一个空action,这样可以提高计时精度

        2.接收alarm(0)的返回值并返回

        3.复原alarm信号的action

        解释2:alarm(0)会关闭所有的alarm并且返回剩余没有睡眠的时间

                设想:一个外界信号被pause()所接收,也就是睡眠被惊醒,但alarm其实还在定时中,此时我们需要获得被惊醒后还未睡眠的时间,也就是alarm程序剩余的定时时间,定时相关的数据全由alarm负责,此时立马调用并接收alarm(0)的返回值就可以得到未睡眠时间,比如睡3s,睡1s被其他原因惊醒,将返回2s

10. mysleep()优化  --  sigsuspend()

        时序要求严格时应当使用sigsuspend()而不是pause()

        原因:

                pause()在等待信号的过程中,cpu可能被其他进程占用,此时信号递达会记录在PCB中,然后当cpu从内核返回时,会至少处理一个信号,也就把pause()所等待的信号处理了,此时pause()就出现了“错过”的情况

       设想解决方案:

                在发出信号前,将正在等待的信号加入当前进程的信号屏蔽字,在pause()前复原信号屏蔽字

                不可行原因:复原信号屏蔽字后依然可能出现cpu被占用导致的上述问题,解除阻塞和等待信号是两条命令,就依然有可能被“见缝插针”,必须将两个操作融合为一个原子操作

        解决方案:

                sigsuspend(解除屏蔽字的集合)函数是系统为这一问题提供的最优解,它的功能是临时解除屏蔽字的同时挂起进程等待信号,此时确定cpu已经被sigsuspend()所占用,不会再被其他进程抢占      

        实现逻辑:

                发出信号前,先屏蔽待等待信号

                向sigsuspend()传入待等待信号用于临时解除阻塞

                复原信号函数

                复原信号屏蔽字

                两个复原顺序固定,因为如果先解除阻塞,可能会导致其他进程调用自定义的信号函数,这显然是不严谨的

                

        注意:

        两个复原顺序固定,因为如果先解除阻塞,可能会导致其他进程调用自定义的信号函数,这显然是不严谨的

        sigsuspend()仅仅是临时解除阻塞,不要忘了复原信号屏蔽字

        对信号屏蔽字操作时,尽量只对一个信号操作,不要改动其他信号,保持严谨

              

11.sigchild信号

        子进程死亡时会发出sigchild信号

        历史原因,unix子进程死亡会留下僵尸进程

        仅限于linux中,有一个改进,如果用sigaction()将sigchild触发函数自定义为SIG_IGN,子进程死亡就不会留下僵尸进程,属于是“自己把自己给埋了”,不再需要父亲收尸

        当然,可以使用sigaction()对其函数进行自定义,为我们所用,但是如果这样的话,就要在自定义函数中加入wait()用于收尸

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值