【Linux系统化学习】信号概念和信号的产生

2024-02-29 03:04

本文主要是介绍【Linux系统化学习】信号概念和信号的产生,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

目录

信号的概念

从生活中的例子中感知信号

前台进程和后台进程

前台进程

后台进程

操作系统如何知道用户向键盘写入数据了?

进程如何得知自己收到了信号?

信号捕捉

signal函数

Core Dump(核心转储)

信号产生的方式

通过键盘按键产生信号

kill函数

raise函数

硬件异常产生信号

浮点数异常

非法地址异常 

由软件条件产生信号

alarm函数


信号的概念

从生活中的例子中感知信号

  1. 你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”
  2. 当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”。
  3. 在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”
  4. 当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:1. 执行默认动作(幸福的打开快递,使用商品)2. 执行自定义动作(快递是零食,你要送给你你的女朋友)3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)
  5. 快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

通过上面的例子我们可以总结出来一下四点:

  • 信号没有产生的时候,其实我们已经直到怎么处理这个信号了。
  • 信号的到来我们并不清楚具体什么时候,信号的到来对于我正在做的工作是异步产生的。
  • 信号产生了,我们不一定要立即处理它,而是我们在合适的时候处理。
  • 我们有一种能力,可以将已经到来的信号进行暂时保存。

让我们转换视角,将我们看成操作系统中被操作系统管理的进程;在进程运行期间根本不知道操作系统什么时候会给这个进程传递一种信息,当操作系统的信息来临时进程可以选择直接处理这个信息,也可以将信息暂时保存下来。

到此我们可以给信号下个定义信号是一种向目标进程发送通知消息的一种机制

在了解信号的产生之前我们还需要一点预备知识让我们对操作系统的信号机制了解的更全面一点。


前台进程和后台进程

前台进程

当我们循环执行一条输出语句时,执行我们的Linux指令操作系统是没有任何相应的;也就是操作系统没有能力接受用户的输入

后台进程

只需要在运行可执行程序时指令的后面添加一个按位与的位运算符即可成为后台进程。后台进程有能力可以接受用户的输入,当我么输入指令后shell会执行我们的命令。

查看后台进程:

jobs

将后台进程调整为前台进程:

fg number(后台每个进程的任务号)

将前台进程调整为后台进程

ctrl + z
//先对前台进程进行暂停
bg number
//然后在进行调整

总结: 

  1. 前台进程有且只有一个,一般情况下为shell。
  2. 操作系统会自动将shell提到前台或者后台。
  3. 前台进程可以被ctrl+c终止掉。
  4. 前台进程不可以被暂停,如果被暂停该前台进程必须被放到后台。 

操作系统如何知道用户向键盘写入数据了?

操作系统是管理者,键盘、显示器、网卡等这些硬件也是要被操作系统管理起来的。当我们使用键盘进行写入操作时,键盘文件会发生变化;操作系统会将这些输入的数据进行各种处理。但是根据冯诺依曼体系结构外设是不可以和CPU进行打交道的,可以通过其他硬件间接联系。CPU中含有很多针脚,外设通过8259主板和CPU上的针脚关联。每个针脚对应一个外设,当我们键盘进行写入时,高低电平会通过主板让针脚发生“变化”。这样CPU就可以知道键盘进行写入了然后读取数据,对数据进行处理。这样的方式称作中断,引起中断的原因称作中断源。对于CPU上的针脚,操作系统也是需要进行管理的,使用一个函数指针数组;数组中的指针指向该针脚对应外设的读写操作函数。这个函数指针数组被称为断向量表。

因此信号的本质就是使用软件来模拟中断的行为。


进程如何得知自己收到了信号?

前面的文章中我们简单提到过信号系统中总共含有62中信号;其中 1-31为普通信号,34-64为实时信号,且没有0号信号。这篇文章我们只探讨普通信号,不对实时信号做任何解释和介绍。

可以使用kill -l查看系统信号

每个信号都含有一个数字编号和一个宏定义名称,这些宏定义可以在signal.h中找到,例如其中有定 义 #define SIGINT 2。

前面的文章我们介绍了每个进程都会被操作系统管理起来,形成一个结构体(PCB)其中含有进程的属性和信息;在这个结构体中就含有一个信号位图,每个比特位代表对应的信号每个比特位对应的内容表示是否收到了信号。当进程收到信号之后,会修改这个信号位图表示收到了某中信号。


信号捕捉

上面提到每个信号都有编号和宏定义,当进程收到某种信号时会调用系统默认的方法;Linux操作系统支持我们使用函数将默认方法改写。

signal函数

//头文件
#include<signal.h>
//函数名称
signal(signo,(void)(*)(int))
  • 第一个参数表示我们要对哪个信号进行捕捉。
  • 第二个参数是一个参数为整形的函数指针,指向我们重写的函数;这个函数的参数也为我们的第一个参数。

注意:对于9号进程来说是不可以被重写函数的。


Core Dump(核心转储)

首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部 保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c 1024。

#include<iostream>
using namespace std;
int main()
{int a=10;a/=0;return 0;
}

使用指令修改限制,允许产生core文件;专门制造一个浮点数异常编译并运行后会产生一个和本进程id相关的core文件;我们可以发现这个文件是非常大的,程序运行时如果出现这类异常会将我们的磁盘写满,造成无法进行修改这也是为什么系统默认不允许产生core文件。


信号产生的方式

通过键盘按键产生信号

通过键盘进行信号的产生就像我们上面使用键盘输入crtl +c可以终止一个进程,当这个键盘输入组合键时会产生一个硬件中断,被操作系统获取,解释成为2号信号,发送给目标前台进程。进程会修改自己PCB中的信号位图,表示已经收到信号。

前台进程因为接收到引号,直接对信号进行处理,进而终止进程。

此外常用的组合键含有:ctrl + z 默认暂停进程 ctrl + \ 默认退出进程

通过系统调用/指令产生信号

kill函数

//头文件
#include<sys/types.h>
#include<signal.h>
//函数
int kill(pid_t pid, int sig)

参数说明:

  • pid:表示进程的pid,代表向哪一个进程发送。
  • sig:表示发送信号的编号。 
#include<iostream>2 #include<unistd.h>3 #include<signal.h>4 #include<sys/types.h>                                              5 using namespace std;                                                  10 int main()                           11 {                                    12     cout<<"wait 2 signal"<<endl;     13     sleep(2);                        14     kill(getpid(),9);                15     cout<<"WTF"<<endl; return 0;}

上面的代码演示了使用kill函数给自身进程产生了9号信号,并由操作系统发送成功;也可以通过另一个进程给一个指定的进程发送指定的信号。

raise函数

//头文件
#include<signal.h>
//函数
int raise(int sig);

参数说明:

  • sig:代表信号编号。
  • raise专门用来给自己发送指定的信号的。 
 #include<iostream>2 #include<unistd.h>3 #include<signal.h>4 #include<sys/types.h>5 using namespace std;10 int main()11 {12     cout<<"wait signal"<<endl;13     raise(9);    reutrn 0;}                                                  

硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释 为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,,MMU会产生异常,内核将这个异常解释SIGSEGV信号发送给进程。

浮点数异常

#include<iostream>
using namespace std;
int main()
{int a=10;a/=0;return 0;
}

非法地址异常 

#include<iostream>
using namespace std;
int main()
{int* a=NULL;*a=100;return 0;
}

 

由软件条件产生信号

在之前管道的文章,匿名管道如果读端关闭,写端一直写入;操作系统就会发送SIGPIPE(13)号信号终止目标进程。SIGPIPE就是一种由软件条件产生的信号。

alarm函数

//头文件
#include <unistd.h>
//函数
unsigned int alarm(unsigned int seconds);

调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。

这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。打个比方,某人要小睡一觉,设定闹钟为30分钟之后响,20分钟后被人吵醒了,还想多睡一会儿,于是重新设定闹钟为15分钟之后响,“以前设定的闹钟时间还余下的时间”就是10分钟。如果seconds值为0,表示取消以前设定的闹钟,函数的返回值仍然是以前设定的闹钟时间还余下的秒数。

#include<iostream>
#include<unistd.h>
int cnt = 0;
int main()
{alarm(1);while(true){cnt++;cout<<cnt<<endl;}return 0;
}


今天对Linux下信号的产生的分享到这就结束了,希望大家读完后有很大的收获,也可以在评论区点评文章中的内容和分享自己的看法;个人主页还有很多精彩的内容。您三连的支持就是我前进的动力,感谢大家的支持!!!  

这篇关于【Linux系统化学习】信号概念和信号的产生的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/757384

相关文章

Linux进程CPU绑定优化与实践过程

《Linux进程CPU绑定优化与实践过程》Linux支持进程绑定至特定CPU核心,通过sched_setaffinity系统调用和taskset工具实现,优化缓存效率与上下文切换,提升多核计算性能,适... 目录1. 多核处理器及并行计算概念1.1 多核处理器架构概述1.2 并行计算的含义及重要性1.3 并

Linux线程之线程的创建、属性、回收、退出、取消方式

《Linux线程之线程的创建、属性、回收、退出、取消方式》文章总结了线程管理核心知识:线程号唯一、创建方式、属性设置(如分离状态与栈大小)、回收机制(join/detach)、退出方法(返回/pthr... 目录1. 线程号2. 线程的创建3. 线程属性4. 线程的回收5. 线程的退出6. 线程的取消7.

Linux下进程的CPU配置与线程绑定过程

《Linux下进程的CPU配置与线程绑定过程》本文介绍Linux系统中基于进程和线程的CPU配置方法,通过taskset命令和pthread库调整亲和力,将进程/线程绑定到特定CPU核心以优化资源分配... 目录1 基于进程的CPU配置1.1 对CPU亲和力的配置1.2 绑定进程到指定CPU核上运行2 基于

golang程序打包成脚本部署到Linux系统方式

《golang程序打包成脚本部署到Linux系统方式》Golang程序通过本地编译(设置GOOS为linux生成无后缀二进制文件),上传至Linux服务器后赋权执行,使用nohup命令实现后台运行,完... 目录本地编译golang程序上传Golang二进制文件到linux服务器总结本地编译Golang程序

Linux下删除乱码文件和目录的实现方式

《Linux下删除乱码文件和目录的实现方式》:本文主要介绍Linux下删除乱码文件和目录的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux下删除乱码文件和目录方法1方法2总结Linux下删除乱码文件和目录方法1使用ls -i命令找到文件或目录

Linux在线解压jar包的实现方式

《Linux在线解压jar包的实现方式》:本文主要介绍Linux在线解压jar包的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux在线解压jar包解压 jar包的步骤总结Linux在线解压jar包在 Centos 中解压 jar 包可以使用 u

linux解压缩 xxx.jar文件进行内部操作过程

《linux解压缩xxx.jar文件进行内部操作过程》:本文主要介绍linux解压缩xxx.jar文件进行内部操作,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、解压文件二、压缩文件总结一、解压文件1、把 xxx.jar 文件放在服务器上,并进入当前目录#

Linux系统性能检测命令详解

《Linux系统性能检测命令详解》本文介绍了Linux系统常用的监控命令(如top、vmstat、iostat、htop等)及其参数功能,涵盖进程状态、内存使用、磁盘I/O、系统负载等多维度资源监控,... 目录toppsuptimevmstatIOStatiotopslabtophtopdstatnmon

在Linux中改变echo输出颜色的实现方法

《在Linux中改变echo输出颜色的实现方法》在Linux系统的命令行环境下,为了使输出信息更加清晰、突出,便于用户快速识别和区分不同类型的信息,常常需要改变echo命令的输出颜色,所以本文给大家介... 目python录在linux中改变echo输出颜色的方法技术背景实现步骤使用ANSI转义码使用tpu

linux hostname设置全过程

《linuxhostname设置全过程》:本文主要介绍linuxhostname设置全过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录查询hostname设置步骤其它相关点hostid/etc/hostsEDChina编程A工具license破解注意事项总结以RHE