【Linux】—— 信号的产生

本期,我们今天要将的是信号的第二个知识,即信号的产生。

目录

(一)通过终端按键产生信号

(二)调用系统函数向进程发信号

(三)由软件条件产生信号

(四)硬件异常产生信号

(五)小结


(一)通过终端按键产生信号

SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,现在我们来验证一下。

我们之前学习进程等待的时候,给大家介绍了以下这张图片,其中【core dump】没有讲,今天我将给大家解释这个词的含义。

 【解释说明】

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

命令用于显示有关用户当前资源限制(ulimits)的信息。此命令显示各种系统资源的硬限制和软限制,以下是一个示例输出: 

【解释说明】

  • core文件大小: core转储的最大大小(以块为单位)。
  • 数据段大小: 进程的数据区大小(以千字节为单位)。
  • 文件大小: 文件的最大大小(以块为单位)。
  • 最大锁定内存: 锁定在内存中的地址空间的最大大小(以千字节为单位)。
  • 最大内存大小: 进程可以拥有的最大数据大小(以千字节为单位)。
  • 打开文件数: 进程可以拥有的最大文件描述符数量。
  • 堆栈大小: 进程的堆栈大小(以千字节为单位)。
  • CPU时间: 进程可以消耗的最大CPU时间(以秒为单位)。
  • 最大用户进程数: 用户可以创建的最大进程数。
  • 虚拟内存大小: 进程可用的虚拟内存大小(以千字节为单位)。

【注意】

  • 值为“无限制”表示该特定资源没有设置特定限制;
  • 可以使用 ulimit 命令或通过修改配置文件来调整这些限制。请注意,特定资源及其限制可能因操作系统和系统配置而异。

接下来,我们手动设置核心转储文件的大小。具体如下:

【解释说明】

  • 上述命令用于设置核心文件的最大大小为10240个块(通常每个块大小为字节,具体大小取决于系统设置);
  • -c: 表示设置或显示核心文件的最大大小限制。
  • 10240: 表示核心文件的最大大小限制为10240个块。如果以字节计算,这意味着核心文件的最大大小为10240个块乘以每个块的字节数。

  • 接下来,我们查询 signal信号。发现信号后面的有的是【term】,有的是【core】。具体如下:

【解释说明】

  • 【term】 是一个信号名称,它代表着进程正常终止信号,无其他操作;
  • 【core】通常指的是在进程异常终止(如段错误)时生成的核心转储文件。这个文件包含了进程在崩溃时的内存映像,可以用于调试和分析问题。当一个进程崩溃时,操作系统通常会生成一个名为 core 的文件,其中包含了进程在崩溃瞬间的内存状态。

接下来,我们就去验证上述结论: 

  • 首先,这里有几行简单的代码: 
int main(int argc, char *argv[])
{while (true){cout << "我是一个正常运行的进程:" << getpid() << endl; sleep(1);}return 0;
}
  • 紧接着,我们先让程序正常的跑起来:

  •  程序可以正常运行之后,接下来,我先测试上述信号中后面标志为【term】的,看测试效果:

  •  接下来,我先测试上述信号中后面标志为【core】的,看测试效果:

  • 当我们打开这个文件时,发现全是乱码(因为这是给OS看的,不是给用户看的):

ulimit命令改变了Shell进程的Resource Limit,test进程的PCB由Shell进程复制而来,所以也具 有和Shell进程相同的Resource Limit值,这样就可以产生Core Dump了。 使用core文件:


(二)调用系统函数向进程发信号

首先在后台执行死循环程序,然后用kill命令给它发SIGSEGV信号

【解释说明】

  • 3995是test进程的id。之所以要再次回车才显示 Segmentation fault ,是因为在3995进程终止掉 之前已经回到了Shell提示符等待用户输入下一条命令,Shell不希望Segmentation fault信息和用 户的输入交错在一起,所以等用户输入命令之后才显示。
  • 指定发送某种信号的kill命令可以有多种写法,上面的命令还可以写成 kill -SIGSEGV 3995或 kill -11 3995, 11是信号SIGSEGV的编号。以往遇 到的段错误都是由非法内存访问产生的,而这个程序本身没错,给它发SIGSEGV也能产生段错误

kill命令是调用kill函数实现的。kill函数可以给一个指定的进程发送指定的信号。

  • 函数的原型如下:
#include <signal.h>int kill(pid_t pid, int sig);
  • pid_t pid: 要发送信号的进程的进程ID。如果 pid 为正数,信号将发送到具有该进程ID的进程。如果 pid 为0,信号将发送到与调用进程属于同一进程组的所有进程。如果 pid 为-1,信号将发送到调用进程有权限发送信号的所有进程。
  • int sig: 要发送的信号的编号。

而raise函数可以给当前进程发送指定的信号(自己给自己发信号)

  • 函数的原型如下:
#include <signal.h>int raise(int sig);
  • int signo:要发送的信号的编号。

abort函数使当前进程接收到信号而异常终止

  • 函数的原型如下:
#include <cstdlib>void abort(void);

使用abort函数非常简单,只需在需要终止进程的地方调用它即可。当调用abort函数时,以下操作将被执行:

  1. 向当前进程发送SIGABRT信号。
  2. 默认情况下,SIGABRT信号会导致进程终止,并生成一个核心转储文件。
  3. 终止处理程序会被启动,这是一个特殊的信号处理程序,可以用来执行一些清理工作或记录错误信息。
  4. 如果没有安装终止处理程序,或者终止处理程序调用了_Exit函数或返回,则进程会异常终止,并打印一条错误消息到标准错误流(stderr)。

代码展示: 

void cleanup() {std::cout << "Performing cleanup before aborting..." << std::endl;// 执行一些清理工作
}void handler(int signo) {std::cout << "Received signal " << signo << std::endl;// 自定义信号处理逻辑exit(signo);
}int main() 
{// 注册终止处理程序if (atexit(cleanup) != 0) {std::cerr << "Failed to register cleanup function" << std::endl;exit(EXIT_FAILURE);}// 注册信号处理函数if (signal(SIGABRT, handler) == SIG_ERR) {std::cerr << "Failed to register signal handler" << std::endl;exit(EXIT_FAILURE);}std::cout << "Starting program..." << std::endl;std::cout << "Triggering abort..." << std::endl;// 调用abort函数,触发进程终止abort();std::cout << "This line will not be reached" << std::endl;return 0;
}

输出展示:

【解释说明】

  • 在调用abort函数后,程序收到了SIGABRT信号,并执行了注册的终止处理程序和信号处理函数;
  • 就像exit函数一样,abort函数总是会成功的,所以没有返回值
     

(三)由软件条件产生信号

SIGPIPE是一种由软件条件产生的信号,在“管道”中已经介绍过了。本节主要介绍alarm函数 和SIGALRM信号。

#include <unistd.h>
unsigned int alarm(unsigned int seconds);调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
  • 接下来,我简单的演示一下这个函数:
//io的效率低下
int main()
{alarm(1);int count = 0;while (true) {// 打印,显示器打印网络std::cout << "count : " << count++ << std::endl; //1s之内计算机将一个正数累计到多少}return 0;
}
  • 多跑几次程序我们可以发现打印出来的结果都是不同的: 

  • 当我们此时真正想测试计算机的算力时,我们可以像如下这样: 
int count = 0;void myhandler(int signo)
{std::cout << "get a signal: " << signo << " count: " << count << std::endl;exit(0);
}int main(int argc, char *argv[])
{signal(SIGALRM,myhandler);alarm(1);while (true) count++;return 0;
}
  • 多跑几次程序我们可以发现打印出来的结果跟上完全是天差地别:


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

接下来,我们代码简单的演示一下:

void myhandler(int signo)
{std::cout << "get a signal: " << signo << " count: " << count << std::endl;int n = alarm(10);std::cout << "return: " << n << std::endl;
}int main(int argc, char *argv[])
{std::cout << "pid: " << getpid() << std::endl;signal(SIGALRM,myhandler);alarm(10);while(true){sleep(1);}return 0;
}

输出展示: 


(四)硬件异常产生信号

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


(五)小结

以上便是本文的主要内容,接下来简单小结本文都讲了些什么!!!

在Linux中,信号可以通过多种方式产生,包括:

  1. 硬件异常: 这些是由硬件引起的异常事件,例如:

    • 除零错误(SIGFPE): 当程序尝试执行除以零的操作时,会产生 SIGFPE 信号。
    • 无效内存访问(SIGSEGV): 当程序尝试访问未分配给它的内存地址时,会产生 SIGSEGV 信号。
    • 内存访问越界(SIGBUS): 当程序尝试访问无效的内存地址时,会产生 SIGBUS 信号。
  2. 软件中断: 这些是由软件引发的事件,通常是为了通知进程已经达到了某个预定条件,例如:

    • 定时器超时(SIGALRM): 当一个定时器达到设定的时间时,会产生 SIGALRM 信号。
  3. 其他进程发送信号: 一个进程可以通过系统调用向另一个进程发送信号,例如:

    • kill 命令: 通过命令行的 kill 命令或者在程序中使用 kill 函数可以向指定的进程发送信号。
    • 终端操作: 用户在终端中执行特定的操作,例如按下 Ctrl+C 组合键,会向当前前台进程发送 SIGINT 信号。
  4. 进程自身发送信号: 进程可以通过调用 raise 函数或者 kill 函数向自身发送信号。

    • 调用 kill 函数: 进程可以使用 kill 函数向自身发送信号,使用进程ID为 getpid()
  5. 软件条件满足: 在程序中,当特定的条件满足时,可以使用信号来通知其他部分程序执行某些动作,这通常需要由程序本身显示地触发。

以上便是本文的全部内容了,感谢大家的观看和支持!!!

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/655756.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

会计分录的概念和应用

目录 一. 会计分录的概念二. 会计分录的分类三. 会计分录的应用 \quad 一. 会计分录的概念 \quad 会计分录是指对每笔经济业务列示其应借记和应贷记账户及其金额的一种记录。 会计分录的基本要素 ( 1 )账户及其所属明细账户名称(或会计科目及其所属明细科目名称) (2 )记账方向…

Linux系统——正则表达式

有一段时间本机访问量过高&#xff0c;如何查看日志提取出访问量前十的信息 1.使用提取命令&#xff08;cut、awk、sed&#xff09;提取出ip地址的那一列 2.使用sort按数字排序&#xff0c;将相同的地址整合到一起 3.使用uniq -c统计出数量 4.使用sort 数字 数字倒序排序 5.最…

【React教程】(2) React之JSX入门与列表渲染、条件渲染详细代码示例

目录 JSX环境配置基本语法规则在 JSX 中嵌入 JavaScript 表达式在 JavaScript 表达式中嵌入 JSXJSX 中的节点属性声明子节点JSX 自动阻止注入攻击在 JSX 中使用注释JSX 原理列表循环DOM Elements 列表渲染语法高亮 条件渲染示例1&#xff1a;示例2&#xff1a;示例3&#xff08…

Learn to Earn,Move星航计划第三期诚邀您探索编程和区块链的乐趣

*以下文章来源于MoveFuns &#xff0c;作者MoveFuns DAO 星航计划是一个 Web3 技术的公益计划,旨在引导更多的人加入开源社区,学习Move语言&#xff0c;了解Web3。本期星航计划由 MoveFuns Dao 发起&#xff0c;由Sui官方基金会支持&#xff0c;汇集了 Web3开发领域内的专业导…

FullStack之Django(1)开发环境配置

FullStack之Django(1)开发环境配置 author: Once Day date&#xff1a;2022年2月11日/2024年1月27日 漫漫长路&#xff0c;才刚刚开始… 全系列文档请查看专栏: FullStack开发_Once_day的博客-CSDN博客Django开发_Once_day的博客-CSDN博客 具体参考文档: The web framewor…

图灵之旅--ArrayList顺序表LinkedList链表栈Stack队列Queue

目录 线性表顺序表ArrayList简介ArrayList使用ArrayList的构造ArrayList常见操作ArrayList的遍历ArrayList的扩容机制利用ArrayList洗牌ArrayList的优缺点 链表链表的实现双向链表的实现 LinkedListLinkedList引入LinkedList的使用LinkedList的构造LinkedList的常用方法介绍Lin…

ArcGIS Pro如何新建字段

无论是地图制作还是数据分析&#xff0c;字段的操作是必不可少的&#xff0c;在某些时候现有的字段不能满足需求还需要新建字段&#xff0c;这里为大家讲解一下在ArcGIS Pro中怎么新建字段&#xff0c;希望能对你有所帮助。 数据来源 教程所使用的数据是从水经微图中下载的水…

pytorch安装教程(Anaconda + GPU)

可以去nvidia官网更新驱动 获取下载pytorch的命令地址&#xff1a;Start Locally | PyTorch 在这里可以找到旧版本的cuda的命令&#xff1a;Previous PyTorch Versions | PyTorch 如果使用conda没有安装成功的话&#xff0c;就使用pip&#xff1a;

ToF传感器在移动机器人中的作用

原创 | 文 BFT机器人 在日新月异的机器人技术领域&#xff0c;技术的无缝整合正引领着人类与机器交互方式的革新潮流。ToF传感器作为变革性创新的一个例子&#xff0c;对移动机器人更好地感知周围环境起到了决定性的作用。 ToF传感器与激光雷达技术在创建深度图方面有着异曲同…

大模型视觉理解能力更进一步,谷歌提出全新像素级对齐模型PixelLLM

论文题目&#xff1a;Pixel Aligned Language Models 论文链接&#xff1a;https://arxiv.org/abs/2312.09237 项目主页&#xff1a;Pixel Aligned Language Models 近一段时间以来&#xff0c;大型语言模型&#xff08;LLM&#xff09;在计算机视觉领域中也取得了巨大的成功&a…

Unity 观察者模式(实例详解)

文章目录 简介示例1 - 简单的文本更新通知示例2 - 多观察者监听游戏分数变化示例3 - 事件系统实现观察者模式示例4 - 泛型观察者和可序列化的事件系统示例5 - 使用C#委托简化版 简介 在Unity中实现观察者模式&#xff0c;我们可以创建一个Subject&#xff08;目标/主题&#x…

前端面试题-js部分-数组去重-数组扁平化-伪数组转数组-面向对象的继承方式(ES5)

前端面试题-js部分-数组去重-数组扁平化-伪数组转数组-面向对象的继承方式ES5 数组去重数组扁平化伪数组转换为数组面向对象的继承方式&#xff08;ES5&#xff09; 数组去重 1.利用es6 set 去重 Set 类型不允许有值重复 let arr1 [1, 2, 4, 3, 5, 7, 1, 8, 2, 4, 9]console.…

【郑益慧】模拟电子技术:7.Mos管的工作原理

Mos管的工作原理 Mos管的出现&#xff0c;几乎不怎么耗电。因此在集成电路中起了非常大的作用 在某些方面确实比晶体三极管强。 基本原理&#xff1a;依靠电场效应来控制。 电场效应几乎是没有电流的&#xff0c;没有电流几乎是没有功率的。 从控制上来说&#xff0c;消耗…

华为——NGFW Module安装在集群交换机上,二层双机负载分担部署,交换机重定向引流

NGFW Module安装在集群交换机上&#xff0c;二层双机负载分担部署&#xff0c;交换机重定向引流 业务需求 如图1所示&#xff0c;两台交换机集群组网&#xff0c;两块NGFW Module分别安装在两台交换机的1号槽位组成双机负载分担组网。NGFW Module工作在二层&#xff0c;也就是…

Stable Diffusion结构解析-以图像生成图像!

手把手教你入门绘图超强的AI绘画&#xff0c;用户只需要输入一段图片的文字描述&#xff0c;即可生成精美的绘画。给大家带来了全新保姆级教程资料包 &#xff08;文末可获取&#xff09; AIGC专栏3——Stable Diffusion结构解析-以图像生成图像&#xff08;图生图&#xff0c…

【活动回顾】CSDN 1024 程序员节城市站系列活动·成都站 - 圆满结束!

文章目录 前言一、活动介绍二、精彩分享内容及活动议程2.1、1024 活动限量周边大放送2.2、《COC 成都社区情况和活动介绍》2.3、CSDN 创始人蒋涛寄语2.4、《AI 重构世界》2.5、《新技术助力企业降本增效》2.6、现场互动情况2.7、《探索开源世界&#xff0c;开拓创新思路》2.8、…

用C#实现最小二乘法(用OxyPlot绘图)

最小二乘法介绍✨ 最小二乘法&#xff08;Least Squares Method&#xff09;是一种常见的数学优化技术&#xff0c;广泛应用于数据拟合、回归分析和参数估计等领域。其目标是通过最小化残差平方和来找到一组参数&#xff0c;使得模型预测值与观测值之间的差异最小化。 最小二…

电商API接口的应用|电商跨境电商商品采集高效解决方案

电商API接口的应用|电商跨境电商商品采集高效解决方案 面对数十万亿元的跨境电商市场&#xff0c;以阿里巴巴国际站为代表的跨境电商数字平台&#xff0c;在政策、需求以及供应链的驱动下&#xff0c;为中小企业提供了全产业链、全供应链一体化综合服务&#xff0c;让越来越多…

使用plotly dash 画3d圆柱(Python)

plotly3D &#xff08;3d charts in Python&#xff09;可以画3维图形 在做圆柱的3D装箱项目&#xff0c;需要装箱的可视化&#xff0c;但是Mesh &#xff08;3d mesh plots in Python&#xff09;只能画三角形&#xff0c;所以需要用多个三角形拼成一个圆柱&#xff08;想做立…

Python qt.qpa.xcb: could not connect to display解决办法

遇到问题&#xff1a;qt.qpa.xcb: could not connect to display 解决办法&#xff0c;在命令行输入&#xff1a; export DISPLAY:0 然后重新跑python程序&#xff0c;解决&#xff01; 参考博客&#xff1a;qt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could …