【Linux】信号相关知识点整理

本文详细介绍了Linux信号的概念,包括信号的产生、注册、注销和处理。内容涵盖硬件和软件产生的信号,信号在进程中的注册和注销机制,信号处理的延迟特性,以及自定义信号处理方式。还讨论了信号阻塞、可重入函数与不可重入函数的重要性,并提供了相关代码示例。
摘要由CSDN通过智能技术生成

信号概念

是一个软件中断通知进程发生了某件事情,中断进程当前操作,让进程去处理这个事件。

信号有很多种:62种信号,两大类型可靠信号:34~64/非可靠信号:1~31.进程必须识别这些信号。

查看信号种类:kill -l

 kill杀死进程的原理:向进程发送信号--通知事件,让进程自己退出

信号生命周期:信号的产生--->在进程中注册--->在进程中注销--->信号的处理

信号的产生

硬件产生:ctrl+c        ctrl+|           ctrl+z

软件产生:kill -signum  pid            kill(pid, signum)          raise(signum)        abort()           alarm(nsec)

具体代码如下:

信号在进程中的注册

在进程pcb中做标记,标记进程收到了哪些信号

未决(pending):是一种状态---表示信号从产生到处理之前所处的状态。

非可靠信号注册:判断pcb中的pending位图中相应信号是否已经注册(位图是否已经置1);若未注册,则位图修改为1,并向sigqueue链表中添加一个信号节点;若已经注册,则不作任何操作(事件丢失)。

可靠信号注册:不管信号是否已经注册,都会向链表中添加一个新的信号节点(事件不会丢失

信号在进程中的注销

非可靠信号:节点只有一个,注销就是删除节点,位图置0.

可靠信号:节点可能有多个,注销就是删除一个节点,判断链表中是否还有相同信号的节点;若没有则将位图置0;否则位图不变仍然需要标记这个信号待处理。

信号的处理

信号的处理并不是立即被处理,而是选择一个合适的时机去处理信号。---->即进程的运行从内核态返回用户态的时候。

进程如何从用户态切换到内核态:发起系统调用、程序异常、中断

程序运行的代码若是库函数或是用户自己写的函数,就说进程当前运行在用户态。

信号处理有多种方式

  • 默认处理方式:既定义好的处理方式
  • 忽略处理方式:处理动作中什么都没做
  • 自定义处理方式:用户自己确定信号如何处理----自定义信号的处理函数替换原有的处理函数。

如何修改信号处理方式

sighandler_t signal( int signum,  sighandler_t handler);

       signum:信号编号-----替换signum这个信号的处理函数

       handler:函数指针--用户传入的处理函数

              SIG_DFL:信号的默认处理动作

              SIG_IGN:信号的忽略处理动作

typedef void (*sighandler_t)(int);

在此段代码中,我们使用sigaction将信号SIGINT的处理动作替换成了我们自定义的函数sigcb,并将原处理方式保存在old_act中,当接收到信号SIGINT时,会打印信号编号,接下来我们将信号的处理方式又还原成默认处理方式,所以当我们第二次再给一个SIGINT信号的时候,程序就可以直接退出了,运行结果如下:

自定义信号处理方式的捕捉流程

 内核如何实现信号的捕捉?

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:用户程序注册了SIGQUIT这个信号的处理函数sigcb,当前正在执行main函数,这时发生中断或异常切换到内核态。在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。内核决定返回用户态之后不是恢复main函数的上下文继续执行,而是执行sigcb函数,sigcb和main函数使用不用的堆栈空间,他们之间不存在调用和被调用的关系,是两个独立的控制流程。sigcb函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

信号阻塞

阻止信号被递达------信号依然可以注册,只是暂时不处理。

注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

在pcb中还有一个集合----阻塞信号集合----标记哪些信号暂时不被处理。

在上图中,pending位图中标记了一个1号信号,和一个2号信号,2号信号被阻塞了,但1号没有被阻塞,成功递达并处理,而2号信号没能递达。

int sigprocmask( int how,       const sigset_t *set,        sigset_t *oldset);

        how:

              SIG_BLOCK:向阻塞集合中加入set集合中的信号 block = mask | set

              SIG_UNBLOCK:从阻塞集合中移除set集合中的信号 block = mask & (~set) 

              SIG_SETMASK:将set集合的信号设置为阻塞集合  block = set

        oldset:用于保存修改前,阻塞集合中的信号

具体代码如下:

程序运行后,所有的信号都会被阻塞,直到用户按下回车后,阻塞解除,相应信号处理。

运行结果:

 

在所有信号中,9号信号SIGKILL和19号信号SIGSTOP,无法被阻塞,无法被自定义,无法被忽略。

可重入函数与不可重入函数

函数的重入:多个执行流程同时执行进入相同的函数。

函数的可重入与不可重入:

可重入:多个执行流程同时执行进入相同的函数,不会造成数据二义性以及代码逻辑混乱。

不可重入:多个执行流程同时执行进入相同的函数,有可能造成数据二义性以及代码逻辑混乱。

当用户设计一个函数或使用一个函数的时候,在多个执行流中,那么这时候就需要考虑函数是否可重入情况。

如果一个函数符合以下条件之一则是不可重入的:

  • 调用了malloc或free,因为malloc也是用全局链表来管理堆的。
  • 调用了标准I/O库函数。标准I/O库的很多实现都以不可重入的方式使用全局数据结构。

函数可重入与不可重入的关键点

这个函数中是否对临界资源(全局数据)进行了非原子操作。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值