Linux系列
文章目录
- Linux系列
- 前言
- 一、背景知识铺垫
- 1.1 信号的基本概念
- 1.2 进程对信号的处理
- 二、信号的产生
- 2.1 前台进程和后台进程
- 2.2 键盘组合键
- 2.3 kill 命令
- 2.4 系统调用
- 2.4.1 signal()接口
前言
Linux中,信号(Signal)是一种进程间通信(IPC)的机制,它用来通知进程发生了特定的事件。进程接受到信号后会根据信号的类型结合自己的处理方式做出相应的处理。
一、背景知识铺垫
1.1 信号的基本概念
Linux信号是一种异步通信机制,用于在进程之间传递事件或在系统于进程之间进行交互。当发生某个特定事件时,如:用户输入特定组合建(Ctrl+c等)、进程异常终止,系统就会向相关进程发送信号。
1.2 进程对信号的处理
进程在被设计时,就内置了识别信号的方法以及默认处理不同信号方式,当进程接收到信号时,并不一定会立即处理,这也就要求进程需要具有保存信号的能力,当等到合适的时候,进程会根据信号的类型结合自己的处理方式法,做出处理。
进程在处理信号的方式:
- 默认处理方式(进程内置的)
- 忽略信号
- 自定义处理方式(捕捉信号后,使用用户设定的方法)
二、信号的产生
穿插了一部分拓展知识
2.1 前台进程和后台进程
首先我们先来看下面这个程序:
#include<iostream>#include<unistd.h>using namespace std;intmain(){while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;sleep(1);}return0;}
当我们执行该程序后,再输入ls、pwd,可以看到指令并不会执行,进程则一直运行,当我们使用Ctrl+c就可以将进程终止,这样的进程就是前台进程。再次执行该程序:
这次我们以后台进程的形式执行该进程./可执行程序 &,可以发现,当进程执行后,我们再输入指令,此时指令是可以成功执行的,当我们使用Ctrl+c时无法终止进程。这种进程为后台进程。
Linux中,一次登录,一个终端,一般配有一个bash,而每个终端只允许有一个前台进程,可有多个后台进程,当我们执行./process时,前台进程就由bash变为了./process而键盘输入是优先被前台进程获取的,所以指令无法被执行,但前台进程./peocess接收到Ctrl+c信号时就会终止。这样我们再来理解第二个现象,当我们以后台进程运行./process是,此时bash仍被视为前台进程,当用户输入指令是仍可被接收并执行,此时再输入Ctrl+c信号./process进程并没有接收,所以没有终止。
前台进程:会独占终端,直到进程执行完成或者被挂起,在这期间终端无法接受其他命令输入,用户只能与该进程进行交互。
后台进程:不会占用终端,终端可以继续接受用户输入的其他命令,用户可以在同一个终端中同时启动多个后台进程。
前台进程:其执行过程会受到用户操作的直接影响,比如用户可以通过键盘输入来中断或暂停进程。如果终端关闭,前台进程通常会被终止,除非进行了特殊的设置。
后台进程:通常是长时间运行的,不受终端关闭的影响,除非明确地对其进行停止或重启操作。它按照自身的逻辑和任务需求在后台持续运行,不会因为用户的一些常规操作而中断
Ctrl+c本质会解释为2号信号,后面我们会验证
2.2 键盘组合键
这是我们在学习Linux过程中,比较常用的一种向进程发送信号的方式,它通过一些特定的键盘组合键,来发送一些特殊的信号,如Ctrl+c终止进程。组合有很多种,都比较简单,这里我们想要介绍的是,Ctrl+c这类组合键是如何被转换为信号,又是如何被进程接收的?
我们可以确定的意见事是,CPU不能直接从键盘读取数据(冯诺依曼体系结构),那么这个工作只能交由操作系统来完成,操作系统又是如何得知键盘有数据了呢?我们根据下图来回答:
CPU上有很多针脚,每个针脚对应一个硬件设备(键盘、网卡),当用户按下Ctrl+c组合键时,键盘发生硬件中断,产生中断号,通过对于针脚发送(充放电)给CPU,通知CPU进行相关处理,操作系统从CPU读取到中断号,通过中断号在自己的中断向量表中索引到对应方法地址,执行该方法(读取键盘),操作系统识别如果是数据直接读取,如果是组合键就将他解释成对应信号,如ctrl+c解释为2号信号,并将它读入键盘缓冲区(一切皆文件),再拷贝至用户缓冲区,被进程接收,进程执行对应处理方法。我们的信号处理方式(异步通知、事件驱动等)就是模拟的硬件中断,因此信号又被叫做软件中断。
2.3 kill 命令
对于上面的后台进程,我们可以通过kill指令的形式,给它发送信号,终止它,可以通过kill -l查看信号种类:
0~31为普通信号,34~64为实时信号(我们不研究),这里有多种信号来都能达到终止,后台进程的要求,如:2号终止进程,9号杀掉进程。
使用格式:kill -信号编号 进程PID.
常用信号
| 信号编号 | 信号名称 | 触发方式 | 作用 |
|---|---|---|---|
| 2 | SIGINT | Ctrl+C | 终止前台进程 |
| 3 | SIGQUIT | Ctrl+\ | 终止进程并生成core文件 |
| 9 | SIGKILL | kill -9 pid | 强制终止进程,不可被捕获或忽略 |
| 15 | SIGTERM | kill -15 pid | 正常终止进程,进程可捕获并进行清理 |
| 1 | SIGHUP | 终端连接断开等 | 让进程重新初始化或终止 |
| 18 | SIGCONT | kill -18 pid | 继续执行暂停的进程 |
| 19 | SIGSTOP | kill -19 pid | 暂停进程,不可被捕获或忽略 |
| 20 | SIGTSTP | Ctrl+Z | 暂停前台进程,将其放入后台 |
| 10 | SIGUSR1 | kill -10 pid | 用户自定义信号,用于特定程序逻辑 |
| 12 | SIGUSR2 | kill -12 pid | 用户自定义信号,用于特定程序逻辑 |
2.4 系统调用
2.4.1 signal()接口
#include<signal.h>typedefvoid(*sighandler_t)(int);sighandler_tsignal(intsignum,sighandler_thandler);功能:捕捉指定信号,执行自定义功能
参数
- signum: 要捕捉信号编号
- handler:函数指针,用互自定义的方法
下面的程序我们使用signal函数捕捉二号信号,执行我们自定义的方法。
#include<iostream>#include<unistd.h>#include<signal.h>using namespace std;voidhandler(intnum){cout<<"I captured Signal No."<<num<<endl;return;}intmain(){sighandler_t_handler=signal(2,handler);while(true){cout<<"I'm a crazy process,PID:"<<getpid()<<endl;sleep(1);}return0;}
可以看到此时我们再使用Ctrl+c,信号程序就不会终止,而是执行我们的自定义方法。上面的场景也完美的呈现了,信号的异步性(程序先调用singnl,但是此时进程没有收到信号,所以这个函数不会执行,当进程接收到信号后,就会执行对于方法)。
需要注意的是,并不是所有信号,都可以被捕捉的,我们可以通过下面的方式来验证: