当前位置: 首页 > news >正文

【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. 软件条件满足: 在程序中,当特定的条件满足时,可以使用信号来通知其他部分程序执行某些动作,这通常需要由程序本身显示地触发。

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

 

相关文章:

【Linux】—— 信号的产生

本期&#xff0c;我们今天要将的是信号的第二个知识&#xff0c;即信号的产生。 目录 &#xff08;一&#xff09;通过终端按键产生信号 &#xff08;二&#xff09;调用系统函数向进程发信号 &#xff08;三&#xff09;由软件条件产生信号 &#xff08;四&#xff09;硬件…...

【算法】Hash 算法-关注优化细节

//给定一个未排序的整数数组 nums &#xff0c;找出数字连续的最长序列&#xff08;不要求序列元素在原数组中连续&#xff09;的长度。 // // 请你设计并实现时间复杂度为 O(n) 的算法解决此问题。 // // // // 示例 1&#xff1a; // // //输入&#xff1a;nums [100,4…...

回归预测 | Matlab实现CPO-SVR冠豪猪优化支持向量机的数据多输入单输出回归预测

回归预测 | Matlab实现CPO-SVR冠豪猪优化支持向量机的数据多输入单输出回归预测 目录 回归预测 | Matlab实现CPO-SVR冠豪猪优化支持向量机的数据多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现CPO-SVR冠豪猪优化支持向量机的数据多输入…...

Idea设置代理后无法clone git项目

背景 对于我们程序员来说&#xff0c;经常上github找项目、找资料是必不可少的&#xff0c;但是一些原因&#xff0c;我们访问的时候速度特别的慢&#xff0c;需要有个代理&#xff0c;才能正常的访问。 今天碰到个问题&#xff0c;使用idea工具 clone项目&#xff0c;速度特…...

tkMapper 通用mapper的批量更新 批量新增 官方实现 springboot项目 依赖引入

文章目录 场景官方插件源码解析项目细节小结 场景 在许多业务场景下&#xff0c;需要对tkMapper的功能进行增强&#xff0c;需要用到批量新增和批量更新&#xff08;这里是唯一主键去更新的&#xff09;&#xff0c;许多论文博客自己写的看起来并不行&#xff0c;我们这里就采…...

【leetcode刷刷】回溯:77.组合

77. 组合 第一次专门做回溯&#xff0c;有点难理解。首先可以理解回溯可以可视化为树的搜索&#xff0c;因此这道题&#xff0c;树的宽度为n&#xff0c;树的深度为kpath作为一个参数传入有点难想回溯没有返回值剪纸更难想&#xff0c;通过列算式可以勉强得到for的表达式&…...

【OOP】Python的OOP编程笔记

1.类变量和实例变量 类变量&#xff1a;变量属于类&#xff0c;在对象中是共用的。访问方式为类名.变量名&#xff0c;或对象名.__class__.变量名 实例变量&#xff1a;定义在方法中的变量&#xff0c;属于具体对象。访问方式为对象名.变量名 类变量访问方式 class Car:# nu…...

一进一出模拟量信号隔离变送器

一进一出模拟量信号隔离变送器 捷晟达科技推出一进一出模拟量信号隔离变送器 深圳捷晟达科技推出一款具有隔离,放大,转换保护功能的一进一出的小型隔离变送器设备,该设备可以把模拟量(4-20mA/0-10V等)标准信号转换用户需要的信号,该产品具有抗EMC干扰,可以有效的保护后级设备安…...

Mybatis-plus原生pages分页未生效的解决方案

文章目录 前言原因1、Mybatis Plus版本的问题2、Mapper.xml文件中SQL语句格式问题3、Mybatis Plus默认分页拦截器问题4、分页参数传参问题5、分页配置的问题 解决方案1、升级对应的Mybatis-plus版本分页插件配置问题3、自定义分页拦截器4、正确的参数5、不同版本的配置文件3.4.…...

【linux】-centos7版本前后-变化篇

1.centos7版本前后区别 首先文件系统变化&#xff0c;由EXT4&#xff0c;变为XFS格式。可支持容量500TB的文件&#xff0c;而6代仅能支持16TB。首个进程变为systemd, 替换了熟悉的init进程。它的特点是功能强大&#xff0c;体积也很强大。 systemd给我们带来了一个全家桶命令&…...

001集—shapefile(.shp)格式详解——arcgis

一、什么是shapefile Shapefile 是一种用于存储地理要素的几何位置和属性信息的非拓扑简单格式。shapefile 中的地理要素可通过点、线或面&#xff08;区域&#xff09;来表示。包含 shapefile 的工作空间还可以包含 dBASE 表&#xff0c;它们用于存储可连接到 shapefile 的要…...

ssrf服务器请求伪造漏洞(个人学习)

SSRF前置学习须了解net工作原理 计算机网络 网络地址转换NAT_内部本地地址-CSDN博客 可以看这个来了解 SSRF 攻击的目标&#xff1a;从外网无法访问的内部网络 形成原因&#xff1a;大部分服务器提供了从外部应用获取数据的功能&#xff0c;但是对目标地址没有做过滤和限制…...

【前端web入门第二天】03 表单-下拉菜单 文本域 label标签 按钮 【附注册信息综合案例】

文章目录: 1. 下拉菜单 2. 文本域3.label标签 4.按钮- button 4.1 reset重置按钮结合form表单区域使用 5.无语义的布局标签 6.字符实体 注册信息综合案例 表单第二节 1. 下拉菜单 标签: select嵌套option,select是下拉菜单整体&#xff0c;option是下拉菜单的每一项。 代码…...

回响科技二面面试题解答

面试题 1、你们的数仓中DWD层为什么要划分数据域&#xff1f;划分数据域之后会对ADS层造成什么影响&#xff1f;是可以提效还是可扩展性强&#xff1f;你们是如何考虑的呢&#xff1f; 2、AZkaban和dolphinScheduler的区别是什么&#xff1f;如果选型会从哪几个方面来考虑呢&a…...

node学习过程中的终端命令

冷的哥们手真tm冷&#xff0c;打字都是僵的&#xff0c;屮 目录 一、在学习nodejs过程中用到的终端命令总结 一、在学习nodejs过程中用到的终端命令 node -v nvm install 20.11.0 nvm list nvm list available nvm on nvm -v nvm use 20.11.0 node加要运行的js文件路径 ps&a…...

oracle版本号中的i,G,C代表什么含义

大家都熟悉的 Oracle 版本号有 9i、10G、11G、12C、19C 等&#xff0c;但在早期&#xff0c;Oracle 的版本号并不包含这些字母。 最初&#xff0c;Oracle 的版本号简单地是 1、2、3、4 等&#xff0c;一直发展到 1999 年发布的 8i 版本。20 世纪末是互联网爆发式发展的时代。 …...

Unity2D_角色移动跳跃

水平移动 Rigidbody2D 使用Unity自带的刚体组件&#xff0c;通过修改刚体物品中Rigidbody2D的属性velocity速度来实现物体移动 声明Rigidbody2D属性&#xff0c;以及角色移动速度 public float playerSpeed 5f; private Rigidbody2D rigidbody2D;在Start方法中将rigidbody2D…...

23-GPTs Actions详细解析:如何查找、对接API,及如何编写Schema

目录 1. 挖掘一个API接口,并将其对接到GPTs中1.1 如何寻找API1.2 把API对接到Schema中1.3 Schema中的参数结构本节详细介绍GPTs Actions 中的Schema。如何写、它里面的结构是什么、里面参数的含义。 后续使用GPTs过程中,如何找到自己想要的API,以及如何把找到的API对接到GPT…...

微信小程序canvas画布实现椭圆元素自由缩放、移动功能

目录 实现效果 ​编辑 一、获取画布信息并绘制背景 二、绘制椭圆...

使用Excel计算--任务完成总工作日时长

(Owed by: 春夜喜雨 http://blog.csdn.net/chunyexiyu) 引言 计算任务完成时间周期&#xff0c;和计算金钱一样&#xff0c;是一个比较细致严谨的工作。 通常&#xff0c;我们可能以为&#xff0c;完成周期形如&#xff1a; 任务完成周期 任务结束时间 - 任务开始时间 但是…...

Leetcode 3576. Transform Array to All Equal Elements

Leetcode 3576. Transform Array to All Equal Elements 1. 解题思路2. 代码实现 题目链接&#xff1a;3576. Transform Array to All Equal Elements 1. 解题思路 这一题思路上就是分别考察一下是否能将其转化为全1或者全-1数组即可。 至于每一种情况是否可以达到&#xf…...

前端倒计时误差!

提示:记录工作中遇到的需求及解决办法 文章目录 前言一、误差从何而来?二、五大解决方案1. 动态校准法(基础版)2. Web Worker 计时3. 服务器时间同步4. Performance API 高精度计时5. 页面可见性API优化三、生产环境最佳实践四、终极解决方案架构前言 前几天听说公司某个项…...

条件运算符

C中的三目运算符&#xff08;也称条件运算符&#xff0c;英文&#xff1a;ternary operator&#xff09;是一种简洁的条件选择语句&#xff0c;语法如下&#xff1a; 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true&#xff0c;则整个表达式的结果为“表达式1”…...

Frozen-Flask :将 Flask 应用“冻结”为静态文件

Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是&#xff1a;将一个 Flask Web 应用生成成纯静态 HTML 文件&#xff0c;从而可以部署到静态网站托管服务上&#xff0c;如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分

一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计&#xff0c;提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合&#xff1a;各模块职责清晰&#xff0c;便于独立开发…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...