Linux进程信号(一)之信号的入门
文章目录
- 信号入门
- 1. 生活角度的信号
- 2. 技术应用角度的信号
- 3. 注意
- 4. 信号概念
- 5.用kill -l命令可以察看系统定义的信号列表
- 6. 信号处理常见方式
信号入门
1. 生活角度的信号
你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,
你该怎么处理快递。也就是你能“识别快递”
当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。
那么在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。
也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
在收到通知,再到你拿到快递期间,是有一个时间窗口的,
在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。
本质上是你“记住了有一个快递要去取”。
当你时间合适,顺利拿到快递之后,就要开始处理快递了。
而处理快递一般方式有三种:
- 执行默认动作(幸福的打开快递,使用商品)
- 执行自定义动作(快递是零食,你要送给你你的女朋友)
- 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话。
进程就是你,操作系统就是快递员,信号就是快递。
信号举例:信号弹、上下课铃声、红绿灯、快递发短信取件码、狼烟、发令枪、闹钟等……
问题:
a.你(进程)怎么认识这些信号的?有人教 - > 我记住了
认识:1.识别信号 2.(进程)知道信号产生后的处理方法
b.即便是现在没有信号产生,但我也知道信号产生之后,我该干什么
c.信号产生了,我们可能并不会立即处理这个信号,
在合适的时候再处理,因为我们可能在处理更重要的事情。
– 所以信号产生 ---------时间窗口---------> 信号处理
在这个时间窗口内,必须记住信号的到来!
结论:
-
进程 必须 能够识别+处理信号 – 信号没有产生,也要具备处理信号的能力。
– 信号的处理能力,属于进程内置功能的一部分!
-
进程即便是没有收到信号,也能知道哪些信号该怎么处理。
-
当进程真的收到了一个具体的信号的时候,进程可能并不会立即处理这个信号,
因为进程可能在运行更重要的代码(优先级更高),进程会在合适的时候处理这个信号。
-
一个进程当信号产生到信号开始被处理,中间一定会有时间窗口,
所以进程具有临时保存 哪些信号已经发生了的 能力。
2. 技术应用角度的信号
makefile
myprocess:mysignal.ccg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -rf myprocess
mysignal.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;int main()
{while(1){cout<<"I am a crazy process,pid: "<<getpid()<<endl;sleep(1);}return 0;
}
运行结果:
前台进程:
后台进程:
ctrl + c
是输入给前台进程(bash
),所以myprocess
不会终止。
bash
进程收到了ctrl + c
为什么不退出,因为bash
对这个信号做了特定的处理。
Linux中,一次登录,一个终端,一般会配上一个bash
,每一个登录,
只允许一个进程是前台进程,可以允许多个进程是后台进程!
bash
是一个前台进程,允许bash
接受用户指令,
但是当你的代码(./myprocess
)跑起来了,你的进程就变成了前台进程,
bash
就变成了后台进程,所以你输入ls
、pwd
输给了myprocess
,
myprocess
不会对你输出的字符串做处理,所以就没用了。
前台进程、后台进程的本质区别:谁来获取键盘输入(就是前台)。
看似信息干扰了,但是实际上并没有干扰。
在键盘输入不会被干扰,回现到显示器上是乱的而已。(输出上互相干扰)
显示器文件、键盘文件都有独立的缓冲区,所以输入不会乱。
输出为什么会乱呢?
因为多进程访问显示器资源,显示器文件就叫共享资源(没有保护机制),所以就会乱。
ctrl + c
为什么可以杀掉前台进程?
键盘输入首先是被前台进程收到的!
ctrl + c
本质是被进程解释为收到了2号信号。
进程收到2号信号的默认动作就是终止自己这个进程。
验证:
signal
函数就是修改特定进程对信号的处理动作的!
mysignal.cc
#include<iostream>
#include<unistd.h>
#include<signal.h>using namespace std;//int signum: 收到了哪个信号
void Handler(int signum)
{cout<<"process get a signal: "<<signum<<endl;
}int main()
{signal(SIGINT,Handler);//只需要设置一次,在进程生命周期内都有效。while(1){cout<<"I am a crazy process,pid: "<<getpid()<<endl;sleep(1);}return 0;
}
- 这个进程不退出了,默认处理动作是退出,我们已经捕捉了这个信号,
所以不再执行默认动作,转而执行自定义动作。(三个动作,三选一)
signal
函数调用Handler
方法是在收到信号的时候触发的!而不是调用signal函数时触发的。
如果后续没有收到这个信号,那么Handler
方法将永远不会被调用。
-
我们可以把所有的信号的处理方法都设置成同一个,
Handler
要知道是因为收到哪个信号而调用这个方法的。
键盘数据是如何输入给内核的,ctrl+c又是如何变成信号的–谈谈硬件
用户按下Ctrl-C ,这个键盘输入产生一个硬件中断,被OS获取,解释成信号,发送给目标前台进程。
前台进程因为收到信号,进而引起进程退出。
3. 注意
-
Ctrl-C 产生的信号只能发给前台进程。一个命令后面加个&可以放到后台运行,
这样Shell不必等待进程结束就可以接受新的命令,启动新的进程。
-
Shell可以同时运行一个前台进程和任意多个后台进程,
只有前台进程才能接到像 Ctrl-C 这种控制键产生的信号。
-
前台进程在运行过程中用户随时可能按下 Ctrl-C 而产生一个信号,
也就是说该进程的用户空间代码执行到任何地方都有可能收到 SIGINT 信号而终止
所以信号相对于进程的控制流程来说是异步(Asynchronous)的。
4. 信号概念
信号是进程之间事件异步通知的一种方式,属于软中断。
5.用kill -l命令可以察看系统定义的信号列表
一共62种,1-32号是普通信号,34-64是实时信号。
普通信号:信号产生了可以不立即处理。
实时信号:一旦产生必须立即(尽快)处理。
-
每个信号都有一个编号和一个宏定义名称,这些宏定义可以在
signal.h
中找到,例如其中有定 义
#define SIGINT 2
-
编号34以上的是实时信号,暂不讨论实时信号。
普通信号:进程收到了n次普通信号,但是来不及处理信号,所以最后只会当作一次信号来处理。
实时信号:
a. 实时信号一到来,那么进程必须立即处理!
b. 进程收到10次实时信号,就要执行10次实时信号。(不能丢失)
管理实时信号使用队列。
-
这些信号各自在什么条件下产生,默认的处理动作是什么
在signal(7)中都有详细说明: man 7 signal
6. 信号处理常见方式
(sigaction
函数),可选的处理动作有以下三种:
- 忽略此信号。(忽略)
- 执行该信号的默认处理动作。(默认动作)
- 提供一个信号处理函数。(自定义动作)
要求内核在处理该信号时切换到用户态执行这个处理函数(自定义动作),
这种方式称为捕捉(Catch
)一个信号。