🏠关于专栏:Linux的浅学到熟知专栏用于记录Linux系统编程、网络编程等内容。
🎯每天努力一点点,技术变化看得见
文章目录
生活中的信号
在讨论Linux中的信号前,我们一起回想一下生活中的信号。生活中存在大量的信号,如下课铃、红绿灯、发令枪、闹钟、外卖电话…当我们看到红灯,就会停下来等待;当我们听到下课铃就知道下课时间到了;当我们听到起床闹铃,就知道该起床洗漱了…
a. 我们是怎么认识这些信号的呢?有人教我们认识这些信号,并且告诉我们处理这些信号的方法;
b. 即使是我们现在没有信号产生,我也知道信号产生之后,我应该干什么;
c. 信号产生了,我们可能并不立即处理这个信号,在合适的时候,因为我们可能正在做更重要的事情。
【例子】小明买了个零食快递
Ⅰ 小明买了一个零食快递,虽然快递还没有到达,但小明已经知道快递到达时,他已经知道什么是快递,他能够认识快递。也就是说,在快递信号到达前,小明已经具备信号的识别能力。
Ⅱ 当快递到达时,快递小哥给小明打了个电话,小明正在推对方的王者高地,他选择在他打完这局后再取快递,同时他记得他打完这局后要处理这个信号。也就是说,小明收到信号不一定要马上处理信号,可以选择合适的时候再处理信号,但处理信号前,需要保存信号;
Ⅲ 小明取到快递后,对于这个快递的处理方式有三种:①直接开心的打开(默认动作)②送给他的女朋友翠花(自定义动作)③直接将快递放在杂物间(忽略);
同理,Linux系统中的进程需要具备如下能力:
- 进程必须能够识别+处理信号(信号的识别和处理能力,属于进程内置的);
- 进程即使没有收到信号,也能知道哪些信号该怎么处理;
- 进程真的收到了一个具体的信号的时候,进程可能不会立即处理这个信号,而是在合适的时候;
- 在进程收到信号,到信号开始被处理,一定会存在一个窗口期,进程具有临时保存哪些信号已经发生了的能力。
★ps:进程处理信号的方式有三种:①默认动作;②自定义动作;③忽略
Linux中的信号
信号示例及概念
那Linux中哪里存在信号呢?当我们按下热键ctrl+C为什么能够杀死我们当前bash中在运行的前台进程呢?
用户按下ctrl+C这个热键组合时,本质是:产生了一个硬件中断,该硬件中断被操作系统获取并解释成了信号,发送给了目标前台进程。进程收到该信号后,执行该信号的内置默认动作——终止进程。
★ps:Linux中,一次登陆会启动一个终端,一个终端一般会配置一个bash。每个bash中只允许一个进程是前台进程,并允许多个后台进程。前台进程和后台进程的区别在于:能否接收键盘的输入(前台进程能够接收键盘数据)。我们执行一个程序./process
默认是前台进程,但如果以./process &
方式执行,则为后台进程。
★ps:前台进程在运行过程中,用户随时可能按下ctrl+C而产生一个信号,也就是说该进程的用户空间代码指定到任何地方都可能收到2号SIGINT信号而终止。所以进程收到信号相对于进程的控制流程是异步,意味着进程无法控制信号何时到达。
★ps:信号到达时应及时进行处理,以保证系统的正确性和稳定性。
由此,我们可以得到信号的概念:信号是进程之间事件异步通知的一种方式,属于软中断。(我们学习的信号就是用软件方式,对进程硬件中断的模拟)。
查看各种信号
如果需要查看系统定义的信号列表,可以使用如下命令↓↓↓
kill-l
上图中,1到31号信号属于普通信号,编号34及以上的信号属于实时信号,对于实时信号这里不做讨论。这些信号在signal.h中均有定义,例如2号信号定义为#define SIGINT 2
对于每个信号,系统都有内置默认的处理动作,我们可以通过man 7 signal
来查看↓↓↓
初始信号的捕捉
系统中对于信号可以选择3种处理方式:忽略、执行该信号的默认动作及自定义动作。各个信号在用户没有指定的情况下,都是执行默认动作的。那如何忽略和自定义呢?
操作系统提供了一个自定义信号处理方式的函数,要求内核在处理对应信号时,切换到用户态执行这个处理函数(或直接忽略),这种方式称为捕捉一个信号。↓↓↓
第一个参数signum是信号的编号(kill -l
可以列出所有信号编号),第二个参数handler的类型是sighandler_t,这个类型是一个返回值为void,参数列表需要传入一个int参数的函数指针类型。第二个参数我们可以将其设置为SIG_DFL,表示执行系统默认操作;设置为SIG_IGN表示忽略改信号。
上面讲述到,ctrl+C组合热键是给当前前台进程发送2号信号,下面代码使用signal捕捉了2号信号,如果当前进程收到2号进程,则会执行Handler函数↓↓↓
#include <stdio.h>
#include <unistd.h>
#include <signal.h>
void Handler(int signo)
{
printf("I have recieve a signo = %d\n",signo);
sleep(1);
}
int main()
{
signal(2, Handler);
while(1)
{
printf("I am running...\n");
sleep(1);
}
return 0;