Linux学习条记:
https://blog.csdn.net/2301_80220607/category_12805278.html?spm=1001.2014.3001.5482
前言:
前面我们已经将历程通讯部分讲完了,现在我们来讲一个历程部分也非常告急的知识点——信号,信号也是历程间通讯的一种,本篇重要解说信号的概念和信号的几种产生方法及对应的场景
目次
一、弁言
二、信号的概念
2.1 什么是信号
2.2 信号的作用
2.3 信号的特点
2.4 常见信号列表
编辑
三、信号的产生
3.1 前台历程和配景历程
3.2 用户产生信号
3.3 体系产生信号
3.4 软件产生信号
四、信号的处置惩罚
4.1 默认处置惩罚方式
4.2 自界说信号处置惩罚函数
五、总结
一、弁言
在 Linux 利用体系中,信号(Signal)是一种历程间通讯(IPC,Inter - Process Communication)的机制,它用于关照历程发生了某种异步变乱。信号可以来自内核,也可以来自其他历程。历程吸收到信号后,会根据信号的范例以及自身的处置惩罚方式做出相应的反应。明白信号对于编写结实的 Linux 步伐以及深入明白 Linux 利用体系的运行机制至关告急。
二、信号的概念
2.1 什么是信号
信号是一种软停止,它是一种异步关照机制。当某个特定变乱发生时,如用户按下特定组合键、体系资源耗尽、历程非常停止等,体系会向干系历程发送一个信号。每个信号都有一个对应的编号和名称,比方信号 1 表现 SIGHUP(挂起信号),信号 9 表现 SIGKILL(逼迫停止信号)。
2.2 信号的作用
信号的重要作用是让历程可以或许对异步变乱做出相应。比方,当用户在终端中按下 Ctrl + C 组合键时,体系会向当前前台历程发送 SIGINT 信号,通常历程会吸收到这个信号后克制当前正在实行的使命并退出。信号还可以用于历程间的通讯,一个历程可以向另一个历程发送信号来关照实在行某些利用。
联合2.1和2.2我们来解说一个概念:信号是一种软停止,是什么意思呢?当我们往键盘中输入内容时是怎样告诉给内核的?ctrl+c又是怎样被表明为指令的呢?
我们先来看下面这张图:
键盘现实上是通过停止来让利用体系知道本身要写入内容的,键盘被按下时,就会触发硬件停止,差别的硬件对应着差别的停止号,停止单位就可以通过它们的停止号将它们与CPU中差别的键位相连,从而使CPU中这个方向的寄存器(32位)特定位置产生电信号,利用体系中有一个叫停止向量表的类似于函数指针布局体的布局,内里生存着访问各种外设的方法,利用体系通过CPU产生的电信号就辨别出要获取哪种硬件的信息,从而通过停止向量表中的方法,将硬件中的信息拷贝到利用体系的文件缓冲区中(利用体系下齐备皆文件,且每一个文件都有本身的文件缓冲中区),然后再拷贝到用户缓冲区
同时比如键盘等外键,利用体系在获取键盘上的信息时会先举行辨认,会对数据举行判定,假如是控制历程的比如ctrl+c等组合键就不会往缓冲区中拷贝,我们可以发现我们学习的信号与上面的停止过程很像,实在信号,就是用软件方式,模拟的对讲程的硬件停止,以是信号也被叫做软停止
2.3 信号的特点
- 异步性:信号的产生是异步的,与历程的实行序次无关。历程在运行过程中大概随时收到信号。
- 简朴性:信号机制相对简朴,只必要一个信号编号就可以标识差别的信号。
- 有限性:Linux 体系中界说的信号数目是有限的,差别的体系大概略有差异,但通常在几十种左右。
2.4 常见信号列表
信号编号
| 信号名称
| 寄义
| 默认处置惩罚方式
| 1
| SIGHUP
| 挂起信号,通常在终端关闭时发送给干系历程
| 停止历程
| 2
| SIGINT
| 停止信号,由用户按下 Ctrl + C 组合键产生
| 停止历程
| 3
| SIGQUIT
| 退出信号,由用户按下 Ctrl + \ 组合键产生
| 停止历程并天生核心转储文件
| 9
| SIGKILL
| 逼迫停止信号,不能被捕捉、壅闭或忽略
| 立即停止历程
| 15
| SIGTERM
| 停止信号,通常用于正常停止历程
| 停止历程
| 18
| SIGCONT
| 继承信号,用于规复被停息的历程
| 继承实行历程
| 19
| SIGSTOP
| 克制信号,用于停息历程,不能被捕捉、壅闭或忽略
| 停息历程
| 可以通过kill -l指令查察全部信号
三、信号的产生
3.1 前台历程和配景历程
先来科普一个小知识点:前台历程和配景历程,来看下面一个步伐
- #include<iostream>
- #include<unistd.h>
- using namespace std;
- int main()
- {
- while(true)
- {
- cout<<"I am a crazy process"<<endl;
- sleep(1);
- }
- return 0;
- }
复制代码 我们举行编译后会得到一个可实行步伐
我们如许实行时我们会发现在步伐运行的时间,我们输入别的指令比如Is,pwd等都不会有用果,历程还在继承运行,除非用ctrl+c停止掉历程,如许的历程称为前台历程
这种的背面加上地点符的叫做配景历程,配景历程可以被别的历程下令暂时打断并实行这个下令,比如我们输入ls指令,历程就会停息而且输出Is的结果,但是末了必要本身把历程竣事掉
Linux中,一次登岸中, 一个终端,一样寻常会配上一个bash,每一个登岸,只允许一个历程是前台历程,可以允许多个历程是配景历程
当./process运行时,输入指令之以是不能运行就是由于此时的前台历程由bash变革为了process
- 终端占用环境
- 前台历程:会独占终端,直到历程实行完成大概被挂起,在这期间终端无法担当其他下令输入,用户只能与该历程举行交互。
- 配景历程:不会占用终端,终端可以继承担当用户输入的其他下令,用户可以在同一个终端中同时启动多个配景历程,并随时切换到其他使命。
- 运行特性
- 前台历程:实在行过程会受到用户利用的直接影响,比如用户可以通过键盘输入来停止或停息历程。假如终端关闭,前台历程通常会被停止,除非举行了特殊的设置。
- 配景历程:通常是长时间运行的,不受终端关闭的影响,除非明白地对其举行克制或重启利用。它按照自身的逻辑和使命需求在配景连续运行,不会由于用户的一些通例利用而停止。
3.2 用户产生信号
- 键盘输入:用户可以通过在终端中按下特定的组合键来产生信号。比方:
- Ctrl + C:产生 SIGINT 信号,用于停止当前正在运行的历程。比如,我们在终端中运行一个长时间运行的下令while true; do echo "Hello"; sleep 1; done,按下 Ctrl + C 后,该下令对应的历程会吸收到 SIGINT 信号并停止。
- Ctrl + \:产生 SIGQUIT 信号,不光会停止历程,还会天生核心转储文件(假如体系设置允许,一样寻常在云服务器上是默认关闭的,捏造机上大概是开启的)。比方,运行一个简朴的 C 步伐#include <stdio.h> int main() { while(1); return 0; },编译运行后,按下 Ctrl + \,历程会停止并天生核心转储文件(在当前目次下,文件名为 core,详细名称和位置大概因体系设置而异)。(相识即可,这个天生core文件的内容与历程退出部分也有接洽,有想相识的可以单独去搜索一下)
- 使用 kill 下令:用户可以使用 kill 下令向指定历程发送信号。kill 下令的根本语法是kill [信号编号] 历程ID。比方,要向历程 ID 为 1234 的历程发送 SIGTERM 信号(信号编号为 15),可以在终端中输入kill -15 1234,也可以使用信号名称kill -SIGTERM 1234。假如省略信号编号或名称,默认发送 SIGTERM 信号。
3.3 体系产生信号
- 历程非常:当历程发生非常时,如段错误(访问非法内存地点)、除零错误等,体系会向该历程发送相应的信号。
- 段错误(Segmentation Fault):当历程访问了不属于它的内存地区时,会产生段错误,一样寻常都是野指针题目,体系会向该历程发送 SIGSEGV 信号。比方,下面的 C 代码会导致段错误:
- #include <stdio.h>
- int main() {
- int *ptr = NULL;
- *ptr = 10; // 试图向空指针指向的地址写入数据,会引发段错误
- return 0;
- }
复制代码 编译运行这段代码,步伐会瓦解,并提示 “Segmentation fault”,这是由于历程吸收到了 SIGSEGV 信号。
- 除零错误(Division by Zero):当历程实行除法运算时,假如除数为零,会产生除零错误,体系会向该历程发送 SIGFPE 信号。比方:
- #include <stdio.h>
- int main()
- {
- int a = 10;
- int b = 0;
- int c = a / b; // 除零操作,会引发除零错误
- return 0;
- }
复制代码 运行这段代码,步伐会瓦解,并提示 “Floating point exception”,这是由于历程吸收到了 SIGFPE 信号。
2. 体系资源干系:当体系资源到达肯定阈值时,也大概产生信号。比方,当历程使用的内存凌驾了体系限定时,体系大概会发送 SIGKILL 信号来停止该历程,以防止体系内存耗尽。不外,这种环境通常必要体系举行干系的设置和监控 。
3.4 软件产生信号
- 使用 kill 函数:在 C 语言编程中,可以使用 kill 函数向指定历程发送信号。kill 函数的原型可以用man手册查察,如下:
此中,pid 是目标历程的 ID,sig 是要发送的信号编号。比方,下面的代码演示了怎样使用 kill 函数向另一个历程发送 SIGTERM 信号:
- #include <stdio.h>
- #include <sys/types.h>
- #include <signal.h>
- #include <unistd.h>
- int main()
- {
- pid_t target_pid = 1234; // 假设目标进程ID为1234
- int result = kill(target_pid, SIGTERM);
- if (result == -1)
- {
- perror("kill failed");
- }
- else
- {
- printf("SIGTERM sent to process %d\n", target_pid);
- }
- return 0;
- }
复制代码 在现实使用中,必要将target_pid更换为真实的目标历程 ID。
2. 使用 raise 函数:历程可以使用 raise 函数向自身发送信号。raise 函数的原型也可以通过man手册来查察,如下:
此中,sig 是要发送的信号编号。比方,下面的代码演示了怎样使用 raise 函数向自身发送 SIGINT 信号:
- #include <stdio.h>
- #include <signal.h>
- int main()
- {
- int result = raise(SIGINT);
- if (result != 0)
- {
- perror("raise failed");
- }
- else
- {
- printf("SIGINT sent to self\n");
- }
- return 0;
- }
复制代码 运行这段代码,历程会吸收到本身发送的 SIGINT 信号并停止。
四、信号的处置惩罚
4.1 默认处置惩罚方式
每个信号都有一个默认的处置惩罚方式,常见的默认处置惩罚方式包罗:
- 停止历程:如 SIGINT、SIGTERM 等信号的默认处置惩罚方式是停止历程。
- 天生核心转储文件并停止历程:比方 SIGQUIT 信号,在停止历程的同时会天生核心转储文件,该文件包罗了历程在收到信号时的内存状态等信息,可用于调试步伐。
- 忽略信号:有些信号(如 SIGCHLD,子历程状态改变时发送给父历程的信号)的默认处置惩罚方式是忽略。
4.2 自界说信号处置惩罚函数
历程可以通过调用 signal 函数或 sigaction 函数来设置自界说的信号处置惩罚函数。
- signal 函数:signal 函数的原型如下:
此中,signum 是信号编号,handler 是指向信号处置惩罚函数的指针。比方,下面的代码演示了怎样使用 signal 函数设置 SIGINT 信号的自界说处置惩罚函数:
- #include <stdio.h>
- #include <signal.h>
- #include <unistd.h>
- void signal_handler(int signum)
- {
- printf("Received SIGINT. Cleaning up...\n");
- // 在这里进行一些清理工作,如关闭文件、释放资源等
- _exit(0); // 退出进程
- }
- int main()
- {
- signal(SIGINT, signal_handler);
- while (1)
- {
- printf("Running...\n");
- sleep(1);
- }
- return 0;
- }
复制代码 在这个例子中,当历程吸收到 SIGINT 信号时,会调用signal_handler函数,而不是默认的停止历程利用。
2. sigaction 函数:sigaction 函数比 signal 函数提供了更丰富的功能,它可以设置信号处置惩罚函数、处置惩罚信号时的掩码、信号的标记等。sigaction 函数的原型如下:
- #include <signal.h>
- int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
- struct sigaction {
- void (*sa_handler)(int);
- void (*sa_sigaction)(int, siginfo_t *, void *);
- sigset_t sa_mask;
- int sa_flags;
- void (*sa_restorer)(void);
- };
复制代码 此中,signum 是信号编号,act 是指向新的信号处置惩罚动作的布局体指针,oldact 是指向旧的信号处置惩罚动作的布局体指针(假如不必要获取旧的处置惩罚动作,可以设为 NULL)。比方,下面的代码演示了怎样使用 sigaction 函数设置 SIGINT 信号的自界说处置惩罚函数:
- #include <stdio.h>
- #include <signal.h>
- #include <unistd.h>
- void signal_handler(int signum)
- {
- printf("Received SIGINT. Cleaning up...\n");
- // 在这里进行一些清理工作,如关闭文件、释放资源等
- _exit(0); // 退出进程
- }
- int main()
- {
- struct sigaction new_action, old_action;
- new_action.sa_handler = signal_handler;
- sigemptyset(&new_action.sa_mask);
- new_action.sa_flags = 0;
- sigaction(SIGINT, &new_action, &old_action);
- while (1)
- {
- printf("Running...\n");
- sleep(1);
- }
- return 0;
- }
复制代码 这段代码与使用 signal 函数的例子功能类似,但使用 sigaction 函数可以更机动地设置信号处置惩罚方式。
五、总结
信号是 Linux 体系中一种告急的历程间通讯和异步变乱关照机制。通过本文,我们详细相识了信号的概念,信号的产生和部分信号的处置惩罚工作,背面我们还会解说信号的捕捉等处置惩罚工作,学习信号可以资助我们更好的实现历程通讯和异步处置惩罚等诸多利用
本篇条记:
感谢各位大佬观看,创作不易,还请各位大佬点赞支持!!!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |