【Linux】—— 信号的产生

news2024/9/24 7:14:31

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

目录

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

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

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

(四)硬件异常产生信号

(五)小结


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

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.coloradmin.cn/o/1420816.html

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

相关文章

【学习笔记】Vue3源码解析:第一部分-实现vue3环境搭建

课程地址&#xff1a;【已完结】全网最详细Vue3源码解析&#xff01;&#xff08;一行行带你手写Vue3源码&#xff09; 第一部分&#xff1a;实现vue3环境搭建&#xff08;对应课程的第1-3节&#xff09; VUE2与VUE3的对比&#xff1a; 也即vue2的痛点&#xff1a; 对TypeSc…

vue3前端开发框架的安全特性,非常适合现在的市场需求

vue3前端开发框架的安全特性,非常适合现在的市场需求&#xff01;现在几乎所有的前端开发&#xff0c;都是使用的vue3做了开发。下面给大家展示一下。为什么说vue3框架自带安全特性呢。 如图&#xff0c;这个是我们在浏览器内看见的&#xff0c;渲染后的数据页面信息。很齐全。…

超越人类上限的策划:百度输入法在候选词区域植入广告

一位 V2EX 用户最新发帖称&#xff0c;百度输入法的最新版本中引入了一个新功能&#xff0c;将广告直接植入到候选词区域。 具体表现为&#xff0c;当用户输入某些关键词时&#xff0c;候选词区域会显示与输入内容相关的广告链接。例如&#xff0c;用户输入“招商”时&#xf…

【C++】类与对象(二)特殊成员函数

前言 类与对象&#xff08;二&#xff09; 文章目录 一、特殊成员函数二、构造函数三、析构函数四、拷贝构造函数五、拷贝赋值运算符 一、特殊成员函数 如果在类的声明中未显式提供某个成员函数的定义&#xff0c;编译器会自动生成一个默认实现。 这包括默认构造函数、默认析构…

Flutter 开发3:创建第一个Flutter应用

Step 1: 安装Flutter 1.1 下载Flutter SDK 首先&#xff0c;你需要访问Flutter官方网站下载最新的Flutter SDK。选择适合你操作系统的安装包。 $ cd ~/development $ unzip ~/Downloads/flutter_macos_2.2.3-stable.zip1.2 更新环境变量 接下来&#xff0c;你需要将Flutter…

笔记本电脑系统Win10重装教程

当前很多用户都会使用笔记本电脑办公&#xff0c;如果笔记本电脑携带的操作系统不好用&#xff0c;就会影响到用户的办公效率&#xff0c;这时候可以给笔记本电脑重新安装一款好用的系统。以下小编带来笔记本电脑系统Win10重装教程&#xff0c;让用户们轻松给笔记本电脑重新安装…

张维迎《博弈与社会》纳什均衡与囚徒困境博弈(2)囚徒困境博弈

囚徒困境大家应该都比较熟悉了&#xff0c;我觉得这篇的意义大概在与&#xff0c;经济学术语的运用&#xff1f; 囚徒困境&#xff1a;个人理性与集体理性的矛盾 假定有两个犯罪嫌疑人共同作案。警察抓住他们以后&#xff0c;分开拘押&#xff0c;并告诉他们&#xff1a;可以选…

GWIT 和GWFI

关于燃烧的历史&#xff1a; -UL request needle flame (open fire) test to rate flammability per UL-94 Vxx UL 要求针焰&#xff08;明火&#xff09;试验以评定UL-94的易燃性。 - industry recognized that glowing wires ( caused by electrical overload) may put …

《幻兽帕鲁》游戏公司如何打造全球爆款 《幻兽帕鲁Palworld》怎么在Mac上玩?

玩法融合之外&#xff0c;《幻兽帕鲁》设计的成功和难点其实是把大部分系统及玩法结合得更紧密&#xff0c;做到多个系统之间互相强化。 “下班&#xff0c;该当帕鲁训练家了。”近日&#xff0c;记者从多个游戏群中看到&#xff0c;《幻兽帕鲁》正在取代其他游戏&#xff0c;成…

蓝桥杯-常用STL(一)

常用STL &#x1f388;1.动态数组&#x1f388;2.vector的基础使用&#x1f52d;2.1引入库&#x1f52d;2.2构造一个动态数组&#x1f52d;2.3插入元素&#x1f52d;2.4获取长度并且访问元素&#x1f52d;2.5修改元素&#x1f52d;2.6删除元素&#x1f52d;2.7清空 &#x1f38…

抽象类(Java)、模板方法设计模式

一、概念 在Java中有abstract关键字&#xff0c;就是抽象的意思&#xff0c;可用来修饰类和成员方法。 用abstract来修饰类&#xff0c;那这个类就是抽象类&#xff1b;修饰方法&#xff0c;那这个方法就是抽象方法。 修饰符 abstract class 类名{修饰符 abstract 返回值类型…

知识库是什么 产品经理必须知道的行业知识

现如今&#xff0c;我们生活在一个知识爆炸的时代。对于产品经理来说&#xff0c;信息不再是稀缺资源&#xff0c;如何高效地管理和利用这些信息&#xff0c;是他们面临的重要问题。这时&#xff0c;知识库便悄然成为产品经理必备的工具。所以&#xff0c;什么是知识库呢&#…

Python网络拓扑库之mininet使用详解

概要 网络工程师、研究人员和开发人员需要进行各种网络实验和测试&#xff0c;以评估网络应用和协议的性能&#xff0c;以及解决网络问题。Python Mininet是一个功能强大的工具&#xff0c;它允许用户创建、配置和仿真复杂的网络拓扑&#xff0c;以满足各种实际应用场景。本文…

2024美赛备战--六大题型常用模型简要分析

美国大学生数学建模竞赛&#xff08;MCM&#xff09;是全球知名的数学建模比赛之一&#xff0c;每年都吸引了来自世界各地的学生参加。在这场充满挑战的竞赛中&#xff0c;参赛者将面对多种题目&#xff0c;需要利用他们的数学建模技能来解决实际问题。下面&#xff0c;建模忠哥…

深入了解Yum:Linux系统的软件包管理利器

目录 软件包 软件包的来源 关于yum yum是什么 yum的相关操作 介绍rzsz rz&#xff08;从Windows本地传到Linux服务器&#xff09; sz&#xff08;从Linux服务器传到Windows本地&#xff09; 注意事项 查看软件包 安装软件 卸载软件 yum的本地配置 为什么要进行配置…

Redis学习——高级篇④

Redis学习——高级篇④ Redis7高级之Redis与Mysql数据双写一致性工程案例&#xff08;四&#xff09; 4.1 MySQL主从复制原理4.2 canal 工作原理4.3 mySQL->canal->redis 双写一致性1.环境2.配置Mysql3.配置canal4. Canal客户端&#xff08;Java编写&#xff0…

03:华为云管理|云主机管理|云项目实战

华为云管理&#xff5c;云主机管理&#xff5c;云项目实战 安全组配置部署跳板机配置yum源&#xff0c;安装软件包优化系统服务安装配置ansible管理主机 模版镜像配置配置yum源&#xff0c;安装软件包优化系统 网站云平台部署实战华为云的负载均衡 安全组配置 设置安全组 云…

Whatsapp 相关(七) -网络请求

本篇主要用来完善上篇文章 frida 监测网络请求的. whatsapp相关(五)- frida监测网络请求 1: 脚本 本次的脚本与上次的区别是,之前只能输出请求的地址,本次优化后,可输出请求参数,结果等. 代码如下: Java.perform(function () {var HttpURLConnection Java.use(java.net.H…

PyTorch][chapter 12][李宏毅深度学习][Semi-supervised Linear Methods-1]

这里面介绍半监督学习里面一些常用的方案&#xff1a; K-means ,HAC, PCA 等 目录&#xff1a; K-means HAC PCA 一 K-means 【预置条件】 N 个样本分成k 个 簇 step1: 初始化簇中心点 (随机从X中抽取k个样本点作为&#xff09; Repeat: For all in X: 根据其到 &…

MP4格式视频怎么提取gif?一招教你在线做

MP4是一种常见的数字多媒体容器格式&#xff0c;它是一种使用最广泛的视频文件格式之一。MP4文件可以包含音频、视频和字幕等多种媒体数据&#xff0c;并且可以通过各种播放器和设备进行播放和共享。它是一种压缩格式&#xff0c;可以在保持相对较小文件大小的同时提供较高的视…