Linux操作系统之信号概念启程
前言:
在前段时间,我们开始了对于Linux操作系统中的进程间通信的章节内容,并为大家介绍了关于管道和system V共享内存的相关知识。
但是system V标准中还有信号量与消息队列两个知识点。这两个的系统调用的使用方法与共享内存差不多,因为三者同属于system V标准。所以我们就不再过多赘述,后面可以作为大家的补充知识点,感兴趣的同学可以自己去了解一下。
我们从今天开始进入信号的章节。
信号其实勉强也能算得上是进程间通信的一种,但严格意义上来说,二者区别还是蛮大的,所欲离我们这里把分开。
我们前面在进程终止那里曾经提到过进程终止的方案就有信号,但是没有深入了解。
今天这篇文章,将会为大家带来初步的关于信号的知识点,难度比较简单,大家学起来还是蛮容易的。话不多说,马上开始正文吧!
一、什么是信号
在我们生活中无处不存在着信号:
古代的狼烟烽火,现在马路上随处可见的红绿灯,甚至于你每天上早八定的闹钟,都是一种信号
信号有一个特点,就是异步
什么是异步性
就是突发的,不可预料。我们用官方的点语言来说就是:信号可能在任何时刻到达,打断进程的正常执行流。
你说闹钟和红绿灯我们可以预料,怎么会有异步性呢?
那如果你的手机没电了!如果交通拥堵有交警专门去手动控制交通!
这种情况的出现我们无法预料,所以,闹钟与红绿灯也是一种信号。
敌人多久来?我们多久点燃烽火?你的一个电话什么时候打过来,都是突发的,我们把这个称为信号的异步性。
二、信号的处理阶段
在linux系统中,你怎么能识别信号呢?
:识别信号是内置的,进程识别信号,是内核程序员写的内置特性。
所以,信号的处理可分为三个阶段:
信号产生:由硬件、软件或用户触发。
信号保存:内核暂存信号,等待进程处理。
信号处理:进程执行注册的处理函数。
1. 信号产生方式
类型 | 示例信号 | 触发条件 |
---|---|---|
硬件异常 | SIGSEGV | 非法内存访问(段错误) |
终端控制 | SIGINT (Ctrl+C ) | 用户中断进程 |
系统调用 | SIGKILL (kill -9 ) | 强制终止进程 |
定时器 | SIGALRM | alarm() 或 setitimer() 触发 |
进程间通信 | SIGUSR1 | 用户自定义信号 |
即:1、终端按键(键盘)。2、系统命令。3、函数。4、软件条件。5、硬件异常
2. 信号保存
信号保存是Linux信号处理机制中至关重要的一个环节,它确保了信号在产生后能够被正确地暂存和管理,直到进程准备好处理它们。
内核通过进程PCB中的信号位图记录未决信号(我们后面会讲到,本文只是给大家提概念,不带你挖坑):
如果信号被阻塞(屏蔽),则暂存为未决状态。
解除阻塞后,信号才会递送给进程。
3. 信号处理
进程对信号的处理方式有三种:
默认行为(如
SIGINT
终止进程)。忽略信号(如
SIGCHLD
避免僵尸进程)。自定义处理:通过
signal()
或sigaction()
注册处理函数。
三、举个样例认识信号
我们平时在启动一个进程时,我们想要提前让它结束,我们会输入什么呢?
:
#include <iostream>
#include <unistd.h>int main()
{while (true){std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);}return 0;
}
可以看见,我们在bash的前台启动一个进程,当我们用键盘按下ctrl + C时,会产⼀个硬件中断,随后被OS获取,于是被解释成信号,发送给⽬标前台进程。前台进程因为收到信号,进而引起进程退出。
其实,ctrl + C的本质是向前台进程发送 SIGINT 即 2 号信号。
我们可以证明一下,但这里要引入一个系统调用:signal
我们前文说过,处理信号有三种,默认方式,忽略,和自定义。
而signal就是实现自定义的一种调用。signum参数是我们要捕获的信号编号:如SIHINT,SIGTERM,handler是一个信号处理函数的指针,这个函数一般由我们自己设置,或者是一个特殊值:SIG_IGN(忽略信号),SIG_DEL(默认行为)
所以我们可以这些写:
#include <iostream>
#include <unistd.h>
#include<signal.h>void handler(int signumber)
{std::cout<<"捕获到信号2,开始执行自定义处理方法"<<std::endl;
}int main()
{signal(2,handler);while(true){std::cout<<"I am a process, I am waiting signal!"<<std::endl;sleep(1);}return 0;
}
这里有一点要注意,为了符合参数类型一致,必须得在我们的handler加一个参数 int signumber,哪怕你的代码中不会用到这个参数。
通过以上这个代码,我们就把对二号信号的捕捉后的处理方法,变成了我们自己定义的方法。
可以看见,我们现在从键盘输入strl c就不会杀死进程了。我们只能手动给该进程kill发送信号杀死。
总结
本文我们只是简单的讲一些关于信号的概念性知识,目的是为了给大家引出后面的重点内容。所以都是一下简单的小点,理解起来还是比较容易的。
明天我将会给大家带来更详细的信号的内容。