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

【Linux系统编程】:信号(2)——信号的产生

1.前言

我们会讲解五种信号产生的方式:

  • 通过终端按键产生信号,比如键盘上的Ctrl+C。
  • kill命令。本质上是调用kill()
  • 调用函数接口产生信号
  • 硬件异常产生信号
  • 软件条件产生信号
    前两种在前一篇文章中做了介绍,本文介绍下面三种.

2. 调用函数产生信号

2.1 kill()在这里插入图片描述

sig是信号编码,pid是捕获信号的进程pid。
我们编写一个程序proc.c,

#include <stdio.h>
#include <unistd.h>int main()
{while(1){printf("I am a process, pid: %d\n", getpid());sleep(1);}
}

利用mykill中的kill()杀掉它,

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
using namespace std;
//argv*[]:mykill pid signal
int main(int argc, char const *argv[])
{if(argc != 3){cout << "usage: ./mykill signal pid" << endl;//告诉用户用法exit(1);}int signo= atoi(argv[1]);int pid = atoi(argv[2]);int ret = kill(pid, signo);if(ret == -1){perror("kill");exit(2);}//kill函数返回值:成功返回0,失败返回-1return 0;
}

在这里插入图片描述

2.2 raise()

在这里插入图片描述
raise(sig)是对kill(getpid(),sig)的封装。

2.3 abort()

在这里插入图片描述
我们编写代码来测试一下abort函数,

#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
using namespace std;void myhandler(int signo)
{cout << "process get a signal: " << signo <<endl;// exit(1);
}
int main(int argc, char *argv[])
{  int cnt = 0;while (true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt++;if(cnt % 2 == 0) {abort();}}
}

重新编译并运行,在这里插入图片描述
我们怎么确定abort()调用的是6号信号呢?我们可以捕捉6号信号,修改代码为:

//头文件等略
void myhandler(int signo)
{cout << "process get a signal: " << signo <<endl;
}
int main(int argc, char *argv[])
{  signal(SIGABRT, myhandler);int cnt = 0;while (true){cout << "I am a process, pid: " << getpid() << endl;sleep(1);cnt++;if(cnt % 2 == 0) {abort();}}
}

在这里插入图片描述

SIGABRT确实被捕获到了,可为什么最后还是调用了abort()呢?不是应该一直循环下去吗?
我们将abort()注释掉,换成“kill(getpid(), 6);”,在这里插入图片描述
重新编译运行,在这里插入图片描述
发现程序没有推掉,说明abort()虽然是对SIGABORT的封装,但后面还增加了自己的细节,致使所在进程退出,而SIGABORT不会终止进程,它表示程序出现异常。

3. 硬件异常产生信号

3.1 “除0代码”

我们编写一段“除0代码”

#include <iostream>
#include <unistd.h>using namespace std;int main()
{   cout << "div before" << endl;sleep(5);int a = 10;a /= 0;//异常cout << "div after" << endl;sleep(1);return 0;
}

编译运行,
在这里插入图片描述
输入指令“man 7 signal”,查阅信号对应的注释,在这里插入图片描述
找到注释对应的信号SIGFPE,在这里插入图片描述
是8号信号中断了该进程。我们尝试捕获*号信号,

#include <iostream>
#include <unistd.h>
#include <signal.h>using namespace std;void handler(int signo)
{sleep(1);cout << "catch signal" << signo << endl;
}
int main()
{   signal(SIGFPE,handler);cout << "div before" << endl;sleep(5);int a = 10;a /= 0;//异常cout << "div after" << endl;sleep(1);return 0;
}

重新编译运行,并监视
在这里插入图片描述
我们发现,当SIGFPE被捕获后,进程不会退出,并且一直执行“自定义行为”(也就是一直打印)。

3.2 “野指针代码”

我们编写一段“野指针代码”,

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
int main()
{   cout << "point error before" << endl;sleep(3);int *p = nullptr;*p = 10;cout << "point error after" << endl;sleep(1);return 0;
}

在这里插入图片描述
段错误是11号信号,也就是内存错误,在这里插入图片描述
我们捕捉该信号,

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void handler(int signo)
{cout << "catch signal:" << signo << endl;sleep(1);
}
int main()
{   signal(SIGSEGV,handler);cout << "point error before" << endl;sleep(3);int *p = nullptr;*p = 10;cout << "point error after" << endl;sleep(1);return 0;
}

在这里插入图片描述
同样,11号信号被捕捉后,段错误异常就不会终止进程。
所以程序出现异常,进程不一定会被终止,当然,这是因为我们自定义了进程接收到信号后的处理行为。所以一般情况下,进程出现异常了,都会终止。

3.3 为什么“除0、野指针”会让进程终止呢?

这是因为操作系统遇到“除0、野指针”问题,会发送信号给进程,进程处理信号会终止自己。这也说明,不论产生信号的方式是什么,最终都是由操作系统发送信号给进程。
但这不是关键,关键是操作系统怎么知道代码中的“除0、野指针”问题,

  • 对于除0错误:当CPU从上到下执行程序的代码时,如果遇到了除0,CPU中的状态寄存器的溢出标志位就会由0变为1,操作系统就知道CPU当前调度的进程出现了异常(操作系统是硬件的管理者)。注意:寄存器信息是进程的上下文,进程之间是独立的,所以上个进程的溢出标识符为1,并不会影响到下一个进程,更不会让操作系统出错。
    总结:除0问题会被转换成硬件问题,表现在硬件上,从而被操纵系统识别到,操作系统就会处理该问题,该问题并不会影响到操作系统的稳定性,只会影响到当前进程(异常的进程)。
    在这里插入图片描述
    那么我们捕获信号后为什么程序会一直打印而不崩溃呢?
    这是因为问题一直没有被修复,当进程被调度进CPU,状态寄存器"出错",操作系统向当前进程发送信号,进程执行信号打印,打印完后上下文中的错误又没被修复,进程还一直在调度运行中,状态寄存器一直”出错“,操作系统一直发送信号,所以程序一直打印。
    那么捕捉信号不修正问题,为什么还要有“自定义信号处理”的方法呢?
    自定义信号捕捉是为了让用户知道程序为什么崩溃,便于打印日志,以及保存崩溃前的信息。而不是为了让用户直接解决当前的进程异常问题。

  • 对于“野指针”问题,是因为虚拟地址无法经过页表转换为物理内存地址(可能溢出或者没有访问权限),而页表是由MMU维护的,MMU会发送对应的信号被操作系统识别。

4.软件条件产生异常

处理硬件可能产生异常,软件也可能产生异常。比如我们在匿名管道一章讲解的管道四大特征之一:当管道的写端被关闭后,读端的进程会自动退出。这是13号信号SIGPIPE造成的。
软件运行中,可能会出现一些特殊事项,致使软件的一些条件没有被满足,就可能产生异常。
我们拿alarm()举例,

4.1 alarm

alarm() 函数是 Unix 和类 Unix 系统编程中的一个标准函数,它用于设置一个定时器,当定时器到达指定时间后,会向进程发送一个 SIGALRM 信号。这个函数通常用于实现定时任务或超时处理。

函数原型

#include <unistd.h>
unsigned int alarm(unsigned int seconds);

参数

  • seconds:定时器的秒数。如果设置为 0,则会关闭之前设置的定时器。

返回值

  • 返回值是之前定时器剩余的时间(秒),也就是前一个闹钟要响起的剩余时间,防止多个闹钟在同一时间响起。如果之前没有设置定时器,则返回 0。

使用示例

以下是一个简单的 C 程序示例,演示如何使用 alarm() 函数:

#include <iostream>
#include <unistd.h>
using namespace std;int main()
{   int n = alarm(5);//设置一个5秒的闹钟while(1){cout << "the proc is running" << endl;sleep(1);}return 0;
}

在这里插入图片描述
我们在查一下信号表,在这里插入图片描述
这样我们还不确信,可以捕获该信号测试一下,

#include <iostream>
#include <unistd.h>
#include <signal.h>
using namespace std;
void handler(int signo)
{cout << "catch a signal,the number:" << signo << endl;sleep(1);
}
int main()
{   int n = alarm(5);//设置一个5秒的闹钟signal(SIGALRM,handler);while(1){cout << "the proc is running" << endl;sleep(1);}}

在这里插入图片描述
这个闹钟为什么只响一次呢?我们之前的“野指针”和“除0”都不断的打印自定义行为,这个却打印一次,因为闹钟不是异常。
如果我们要让闹钟每隔5秒打印一次,可以在handler()修改为,

void handler(int signo)
{cout << "catch a signal,the number:" << signo << endl;alarm(5);
}

在这里插入图片描述
我们利用这个原理,可以让进程每隔一段时间执行特定的工作,比如打印日志。

void work()
{cout << "print log..." << endl;
}
void handler(int signo)
{work();cout << "catch a signal,the number:" << signo << endl;alarm(5);
}

注意事项

  • alarm() 只能设置以秒为单位的定时器,如果需要更精确的时间控制,可以考虑使用 setitimer()timer_create() 等函数。
  • alarm() 设置的定时器是单次的,如果需要重复触发,需要在信号处理函数中再次调用 alarm()
  • 在多线程程序中使用 alarm() 时要特别小心,因为它是针对整个进程的,可能会影响其他线程的行为。

5. Core dump

在这里插入图片描述
SIGINT的默认处理动作是终止进程,SIGQUIT的默认处理动作是终止进程并且Core Dump,Core Dump是什么意思呢?
我们在进程等待中也提到过Core Dump,在这里插入图片描述
我们编写一段父进程回收子进程的代码,分别用8号信号和2号信号终止子进程,获取子进程的core dump标志,
在这里插入图片描述
2号信号和8号信号杀死进程的core dump标志确实不一样,那么这个表示到底是什么意思呢?
由于云服务器一般把core file文件的大小设为0(相当于关闭了core dump的功能),或者操作系统重新配置了core文件生成的目录,所以我们用ll查看当前目录,不会看到相关文件,我们可以用ulimit -a查看系统资源的限制信息,其中就包括core文件的大小,然后用“ulimit -c 10240",将core file 的大小设置为10240K,
在这里插入图片描述
然后重新运行程序,再用8号信号杀死,此时如果还看不到相关的core文件,可在命令行输入“sudo bash -c "echo core.%p > /proc/sys/kernel/core_pattern”,core文件不存在的原因
在这里插入图片描述
重新编译再杀死进程,就有对应的core文件了。
所以,一旦打开了系统的core dump功能,某个进程因异常而被Action为core的信号终止时,操作系统就会将进程在内存中的运行信息,dump(转储)到进程的工作目录下(磁盘中),形成core.pid文件。
那么core.pid文件有什么用呢?
该文件保存了程序中断的原因,可以帮助我们更好的识别、修改bug。
在这里插入图片描述

为什么core dump默认是关闭的呢?

在 Linux 系统中,core dump 默认是关闭的,主要原因有以下几点:

  1. 磁盘空间占用:core dump 文件会包含程序在崩溃时的内存映像,包括代码段、数据段、堆、栈等信息,其大小可能非常大,尤其是对于大型应用程序。如果系统中多个程序频繁崩溃并生成 core dump 文件,会占用大量的磁盘空间,影响系统的正常运行和存储资源的使用效率。
  2. 性能影响:生成 core dump 文件需要将大量内存数据写入磁盘,这个过程可能会消耗较多的 I/O 资源,导致系统性能下降。对于一些对性能要求较高的系统或应用程序,这种性能损失是不可接受的。
  3. 安全性考虑:core dump 文件可能包含程序运行时的敏感信息,如用户数据、加密密钥、系统配置等。如果这些文件被未授权的用户访问,可能会导致信息泄露,带来安全隐患。因此,默认关闭 core dump 功能可以在一定程度上保护系统的安全性。
  4. 管理复杂性:如果系统中所有程序都默认开启 core dump 功能,可能会导致生成大量的 core dump 文件,增加了系统管理员管理和分析这些文件的复杂性。管理员需要定期清理这些文件,以避免磁盘空间被占用,同时还需要对每个文件进行分析,以确定程序崩溃的原因,这会消耗大量的时间和精力。

当然,core dump 文件对于程序开发和故障排查是非常有用的,它可以帮助开发者快速定位程序崩溃的原因,提高程序的稳定性和可靠性。因此,在需要调试程序或分析程序崩溃原因时,可以手动启用 core dump 功能,并根据实际情况设置合适的文件大小限制和保存路径。

来源:https://kimi.moonshot.cn/chat/

相关文章:

【Linux系统编程】:信号(2)——信号的产生

1.前言 我们会讲解五种信号产生的方式: 通过终端按键产生信号&#xff0c;比如键盘上的CtrlC。kill命令。本质上是调用kill()调用函数接口产生信号硬件异常产生信号软件条件产生信号 前两种在前一篇文章中做了介绍&#xff0c;本文介绍下面三种. 2. 调用函数产生信号 2.1 k…...

Android Studio AI助手---Gemini

从金丝雀频道下载最新版 Android Studio&#xff0c;以利用所有这些新功能&#xff0c;并继续阅读以了解新增内容。 Gemini 现在可以编写、重构和记录 Android 代码 Gemini 不仅仅是提供指导。它可以编辑您的代码&#xff0c;帮助您快速从原型转向实现&#xff0c;实现常见的…...

【day09】面向对象——静态成员和可变参数

【day08】面向对象——封装重点:1.封装:a.将细节隐藏起来,不让外界直接调用,再提供公共接口,供外界通过公共接口间接使用隐藏起来的细节b.代表性的:将一段代码放到一个方法中(隐藏细节),通过方法名(提供的公共接口)去调用private关键字 -> 私有的,被private修饰之后别的类不…...

Android学习(七)-Kotlin编程语言-Lambda 编程

Lambda 编程 而 Kotlin 从第一个版本开始就支持了 Lambda 编程&#xff0c;并且 Kotlin 中的 Lambda 功能极为强大。Lambda 表达式使得代码更加简洁和易读。 2.6.1 集合的创建与遍历 集合的函数式 API 是入门 Lambda 编程的绝佳示例&#xff0c;但在开始之前&#xff0c;我们…...

彻底认识和理解探索分布式网络编程中的SSL安全通信机制

探索分布式网络编程中的SSL安全通信机制 SSL的前提介绍SSL/TLS协议概述SSL和TLS建立在TCP/IP协议的基础上分析一个日常购物的安全问题 基于SSL的加密通信SSL的安全证书SSL的证书的实现安全认证获取对应的SSL证书方式权威机构获得证书创建自我签名证书 SSL握手通信机制公私钥传输…...

【libuv】Fargo信令2:【深入】client为什么收不到服务端响应的ack消息

客户端处理server的ack回复,判断链接连接建立 【Fargo】28:字节序列【libuv】Fargo信令1:client发connect消息给到server客户端启动后理解监听read消息 但是,这个代码似乎没有触发ack消息的接收: // 客户端初始化 void start_client(uv_loop_t...

Vue3自定义事件

自定义事件是一种组件间通信的方式&#xff0c;它允许子组件向父组件发送信息。子组件可以通过自定义事件向父组件传递数据以及事件&#xff0c;当自定义事件触发时&#xff0c;子组件可以借此将子组件的数据传递给父组件并使父组件对此做出相应的操作。 1.声明自定义事件 使…...

BeautifulSoup 与 XPath 用法详解与对比

BeautifulSoup&#xff08;bs4&#xff09; 和 XPath 是学习python爬虫过程中常常用到的库&#xff0c;本文将详细介绍它们的功能、使用方法、优缺点以及实际应用中的区别和选择建议。 1. BeautifulSoup 用法详解 1.1 什么是 BeautifulSoup&#xff1f; BeautifulSoup 是 Pyt…...

Emacs 折腾日记(五)——elisp 数字类型

本文是参考 emacs lisp 简明教程 写的&#xff0c;很多东西都是照搬里面的内容&#xff0c;如果各位读者觉得本文没有这篇教程优秀或者有抄袭嫌疑、又或者觉得我更新比较慢、再或者其他什么原因&#xff0c;请直接阅读上述链接中的教程。 上一篇我们讲了elisp中的流程控制结构相…...

重拾设计模式--外观模式

文章目录 外观模式&#xff08;Facade Pattern&#xff09;概述定义 外观模式UML图作用 外观模式的结构C 代码示例1C代码示例2总结 外观模式&#xff08;Facade Pattern&#xff09;概述 定义 外观模式是一种结构型设计模式&#xff0c;它为子系统中的一组接口提供了一个统一…...

源码编译llama.cpp for android

源码编译llama.cpp for android 我这有已经编译好的版本&#xff0c;直接下载使用&#xff1a; https://github.com/turingevo/llama.cpp-build/releases/tag/b4331 准备 android-ndk 已下载&#xff1a; /media/wmx/ws1/software/qtAndroid/Sdk/ndk/23.1.7779620版本 &am…...

StarRocks 排查单副本表

文章目录 StarRocks 排查单副本表方式1 查询元数据&#xff0c;检查分区级的副本数方式2 SHOW PARTITIONS命令查看 ReplicationNum修改副本数命令 StarRocks 排查单副本表 方式1 查询元数据&#xff0c;检查分区级的副本数 # 方式一 查询元数据&#xff0c;检查分区级的副本数…...

Windows11 家庭版安装配置 Docker

1. 安装WSL WSL 是什么&#xff1a; WSL 是一个在 Windows 上运行 Linux 环境的轻量级工具&#xff0c;它可以让用户在 Windows 系统中运行 Linux 工具和应用程序。Docker 为什么需要 WSL&#xff1a; Docker 依赖 Linux 内核功能&#xff0c;WSL 2 提供了一个高性能、轻量级的…...

线程知识总结(二)

本篇文章以线程同步的相关内容为主。线程的同步机制主要用来解决线程安全问题&#xff0c;主要方式有同步代码块、同步方法等。首先来了解何为线程安全问题。 1、线程安全问题 卖票示例&#xff0c;4 个窗口卖 100 张票&#xff1a; class Ticket implements Runnable {priv…...

解决vscode ssh远程连接服务器一直卡在下载 vscode server问题

目录 方法1&#xff1a;使用科学上网 方法2&#xff1a;手动下载 方法3 在使用vscode使用ssh远程连接服务器时&#xff0c;一直卡在下载"vscode 服务器"阶段&#xff0c;但MobaXterm可以正常连接服务器&#xff0c;大概率是网络问题&#xff0c;解决方法如下: 方…...

【Cadence射频仿真学习笔记】IC设计中电感的分析、建模与绘制(EMX电磁仿真,RFIC-GPT生成无源器件及与cadence的交互)

一、理论讲解 1. 电感设计的两个角度 电感的设计可以从两个角度考虑&#xff0c;一个是外部特性&#xff0c;一个是内部特性。外部特性就是把电感视为一个黑盒子&#xff0c;带有两个端子&#xff0c;如果带有抽头的电感就有三个端子&#xff0c;需要去考虑其电感值、Q值和自…...

Definition of Done

Definition of Done English Version The team agrees on, a checklist of criteria which must be met before a product increment “often a user story” is considered “done”. Failure to meet these criteria at the end of a sprint normally implies that the work …...

【漏洞复现】CVE-2023-37461 Arbitrary File Writing

漏洞信息 NVD - cve-2023-37461 Metersphere is an opensource testing framework. Files uploaded to Metersphere may define a belongType value with a relative path like ../../../../ which may cause metersphere to attempt to overwrite an existing file in the d…...

简单工厂模式和策略模式的异同

文章目录 简单工厂模式和策略模式的异同相同点&#xff1a;不同点&#xff1a;目的&#xff1a;结构&#xff1a; C 代码示例简单工厂模式示例&#xff08;以创建图形对象为例&#xff09;策略模式示例&#xff08;以计算价格折扣策略为例&#xff09;UML区别 简单工厂模式和策…...

HuggingFace datasets - 下载数据

文章目录 下载数据修改默认保存地址 TRANSFORMERS_CACHE保存到本地 & 本地加载保存加载 读取 .arrow 数据 下载数据 1、Python 代码下载 from datasets import load_dataset imdb load_dataset("imdb") # name参数为full或mini&#xff0c;full表示下载全部数…...

MPNet:旋转机械轻量化故障诊断模型详解python代码复现

目录 一、问题背景与挑战 二、MPNet核心架构 2.1 多分支特征融合模块(MBFM) 2.2 残差注意力金字塔模块(RAPM) 2.2.1 空间金字塔注意力(SPA) 2.2.2 金字塔残差块(PRBlock) 2.3 分类器设计 三、关键技术突破 3.1 多尺度特征融合 3.2 轻量化设计策略 3.3 抗噪声…...

React 第五十五节 Router 中 useAsyncError的使用详解

前言 useAsyncError 是 React Router v6.4 引入的一个钩子&#xff0c;用于处理异步操作&#xff08;如数据加载&#xff09;中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误&#xff1a;捕获在 loader 或 action 中发生的异步错误替…...

C++:std::is_convertible

C++标志库中提供is_convertible,可以测试一种类型是否可以转换为另一只类型: template <class From, class To> struct is_convertible; 使用举例: #include <iostream> #include <string>using namespace std;struct A { }; struct B : A { };int main…...

【Linux】C语言执行shell指令

在C语言中执行Shell指令 在C语言中&#xff0c;有几种方法可以执行Shell指令&#xff1a; 1. 使用system()函数 这是最简单的方法&#xff0c;包含在stdlib.h头文件中&#xff1a; #include <stdlib.h>int main() {system("ls -l"); // 执行ls -l命令retu…...

Python爬虫实战:研究feedparser库相关技术

1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的信息资源。RSS(Really Simple Syndication)作为一种标准化的信息聚合技术,被广泛用于网站内容的发布和订阅。通过 RSS,用户可以方便地获取网站更新的内容,而无需频繁访问各个网站。 然而,互联网…...

使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台

🎯 使用 Streamlit 构建支持主流大模型与 Ollama 的轻量级统一平台 📌 项目背景 随着大语言模型(LLM)的广泛应用,开发者常面临多个挑战: 各大模型(OpenAI、Claude、Gemini、Ollama)接口风格不统一;缺乏一个统一平台进行模型调用与测试;本地模型 Ollama 的集成与前…...

用机器学习破解新能源领域的“弃风”难题

音乐发烧友深有体会&#xff0c;玩音乐的本质就是玩电网。火电声音偏暖&#xff0c;水电偏冷&#xff0c;风电偏空旷。至于太阳能发的电&#xff0c;则略显朦胧和单薄。 不知你是否有感觉&#xff0c;近两年家里的音响声音越来越冷&#xff0c;听起来越来越单薄&#xff1f; —…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...

jmeter聚合报告中参数详解

sample、average、min、max、90%line、95%line,99%line、Error错误率、吞吐量Thoughput、KB/sec每秒传输的数据量 sample&#xff08;样本数&#xff09; 表示测试中发送的请求数量&#xff0c;即测试执行了多少次请求。 单位&#xff0c;以个或者次数表示。 示例&#xff1a;…...

前端中slice和splic的区别

1. slice slice 用于从数组中提取一部分元素&#xff0c;返回一个新的数组。 特点&#xff1a; 不修改原数组&#xff1a;slice 不会改变原数组&#xff0c;而是返回一个新的数组。提取数组的部分&#xff1a;slice 会根据指定的开始索引和结束索引提取数组的一部分。不包含…...