Linux信号详解

在这里插入图片描述

什么是信号

信号是Linux系统中定义的宏值,我们可以通过kill -l 命令查看信号
在这里插入图片描述
我们要注意,没有0号信号,同时31号信号以后的信号都是实时信号,在当今计算机都是分时计算机,这些信号用不到。
从内核来说,信号是进程/用户给另一个进行发送的异步信息的方式。
当发送信号后,接收信号的进程会在合适的时候处理该信号,完成异步操作。


当我们在详谈信号前,我们需要先了解一些事情。
1).在进程没有收到确切的信号时,我们是否已经知道了这些信号的处理方法?

答案是对的。

2).进程需要在合适的时候处理,那么我们是不是应该要能存储一个信号?

答案是对的。

3).我们是不是不知道信号会在何时产生,及信号是随时产生的。

答案是对的。

在我们了解上面三个问题后就可以详谈信号了。

信号的产生

信号的产生有很多方式,但其实都需要经过操作系统。
1).一个进程可以给另一个进程通过系统调用/非系统调用发送信号。
系统调用raise
在这里插入图片描述
系统调用kill
在这里插入图片描述

可以向当前自己发送sig信号。
发送9号信号。在这里插入图片描述

非系统调用abort
在这里插入图片描述

abort信号给自己发送SIGABRT。
在这里插入图片描述

2).键盘是可以产生信号的。
我们有时候向退出当前进程,我们会直接使用键盘输入 CTRL + C,这实际上就是给前台进程发送2号信号。

在这里插入图片描述

3).当软件条件不满足时,会产生信号。
举个例子,当管道读端不读,并且关闭读端,若写端在写,则OS会向写段进程发送13号信号,这就是软件条件的经典例子。
还有一个例子,系统调用alarm。
在这里插入图片描述
alarm是一个闹钟,seconds是秒数,到达seconds秒后,OS就会向进程发送14号信号。

alarm函数的返回值代表剩余秒数,及当第一个alarm函数还未结束时,第二个alarm函数在此设置后,alarm函数的seconds不会累加,而是返回第一个时间的剩余时间

4).在shell中输入的指令也可以产生信号
在linux指令使用kill指令。

kill -signum pid在这里插入图片描述

5).出现异常,如除0,已及野指针。
在这里插入图片描述
运行上述代码的结果如下。
在这里插入图片描述
野指针也同样,不过野指针发送的是SIGSEGV信号表示段错误。
在这里插入图片描述

信号的捕捉

我们需要在了解信号捕捉时,我们需要了解一些信息。
1).信号的抵达:信号的处理动作被称为抵达。
2).信号的未决:信号从产生到抵达之间的状态都是未决的。
我们捕捉到信号后,需要将该信号存储起来,等到合适的时候处理该信号,存储信号的是进程内的数据,如图。
在这里插入图片描述
其中pending结构体就是存储信号的结构,从[0-31]刚好是一个整形,每一个位的0/1代表该信号未决。
同时还有第二个结构block,其大小和pending结构一样,而block结构0/1表示信号是否阻塞。
最后还存在一个函数指针数组,其中代表该信号的抵达,我们可以自己设置,但每个信号本身就有默认处理方式。

man 7 signal可以查看信号已经信号的默认处理方法。
在这里插入图片描述
其中信号的默认处理方法大抵有4种,Term代表终止进程,而Core也代表终止进程,但是两者是不一样的,以Core动作被终止的进程如果开启了核心转储,则将会吧错误代码存储到核心转储到磁盘内,形成core*文件,方便调试处理
现在我们就可以解释在进程状态表示中,低16位的高八位代表退出码,低7位代表信号,而剩余的哪一位就代表核心转储标志符,0代表没有开启核心转储,1代表开启了核心转储并且核心转储了。
在这里插入图片描述
Ign代表该信号的默认处理动作为忽略,Stop代表该信号的默认处理动作为停止。Cont代表恢复一个已停止的信号。

实际上,在内核中,内核定义了一个内核数据结构来表示和修改blockpending

sigset_t,该类型是内核数据结构,该类型用来修改或获得block,pending位图,该参数不是通过直接修改,而是作为系统调用的参数传入修改。
sigprocmask系统调用
该系统调用用于操作block结构。在这里插入图片描述
参数how:代表对block进行何种操作, how有三种选项。
SIG_BLOCK:屏蔽某个信号。

实际上就是block位图和set位图进行按位或操作

SIG_UNBLOCK:解除某个信号的屏蔽。

实际上就是block位图和set位图进行按位与操作

SIG_SETMASK:重新设置block位图。

用新的set代替block

参数set:是sigset_t类型,间接操作block结构。
参数oldset:该参数是一个返回参数,返回旧的block位图。


系统调用sigpending
该系统调用可以获得未决位图。
在这里插入图片描述
参数set:set是返回参数,可以获得pending位图的信息。

我们无法对sigset_t操作,我们对sigset_t的操作都要通过接口实现。

在这里插入图片描述
接口sigemptysetsigfillset可以初始化位图。
接口sigaddsetsigdelset可以决定某个信号是否屏蔽。
接口sigismember可以判断set中是否包含某个信号,返回值为bool类型。
在这里插入图片描述
该代码屏蔽了2号信号,及ctrl + c,running后如下图。
在这里插入图片描述
在前面三跳中,pending没有收到任何信号,在第四跳中该进程收到了2号信号,但是却没有退出,这是因为2号信号被屏蔽了。

在信号的捕捉的最后,我们需要了解到,9号信号,18号信号无法被屏蔽,而19号信号被屏蔽可能会导致某些信号接触屏蔽。
还有一些细节,就是我们对于pending位图,应该是先处理信号,在清零该信号所对应的某个位置。

信号的处理

在捕捉到信号后,我们就需要对信号进行处理了,信号本身有默认处理方法。
我们也可以重新定义某个信号的处理方法。

系统调用signal
在这里插入图片描述
参数signum:对那个信号进行操作。
参数handler:是signum的新的抵达函数。


系统调用sigaction
在这里插入图片描述
参数signum:对那个信号进行操作。
参数act:是一个内置结构体。
在这里插入图片描述

sa_handler:参数是信号处理函数,可以自定义,也可以使用库中存在的一些宏。
在这里插入图片描述
sa_mask:参数是信号屏蔽集,及代表在处理该信号的函数时,屏蔽某些信号。
这是为了某个信号不会被嵌套处理。

我们在聊一聊信号的处理时间问题,信号究竟在什么时候处理呢?如图
在这里插入图片描述

答案是在用户态内核态切换的过程中处理,如上图,如果信号抵达函数是用户定义的,则需要从内核态在切换到用户态,在从用户态切换到内核态,在切换回去。

要注意,子进程退出时,会向父进程发送SIGCHLD信号,SIGCHLD信号的默认处理方法是Ign。

  • 28
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值