【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/

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

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

相关文章

专业电脑数据恢复软件 iFind Data Recovery v9.2.3 绿色便携版

前言 iFinD Data Recovery一款特别实用的数据找回工具&#xff0c;它很厉害&#xff0c;能帮你在SSD硬盘和Windows10系统上找回丢失的数据。而且&#xff0c;它还能深度扫描并恢复各种主流数码相机里的RAW格式照片&#xff0c;速度超快&#xff0c;用起来也很稳定顺畅&#xf…

QT:Widgets中的数据库应用

SQL数据库驱动 pro文件中添加如下一行代码 QT sql widgetsmain.cpp #include <QApplication> #include <QSqlDatabase> #include <QStringList> int main(int argc, char *argv[]) {QApplication a(argc, argv);qDebug() << "Available driver…

AI的进阶之路:从机器学习到深度学习的演变(三)

&#xff08;承接上集&#xff1a;AI的进阶之路&#xff1a;从机器学习到深度学习的演变&#xff08;二&#xff09;&#xff09; 四、深度学习&#xff08;DL&#xff09;&#xff1a;机器学习的革命性突破 深度学习&#xff08;DL&#xff09;作为机器学习的一个重要分支&am…

数据集-目标检测系列 车牌检测识别 数据集 CCPD2019

车牌检测&识别 数据集 CCPD2019 DataBall 助力快速掌握数据集的信息和使用方式&#xff0c;会员享有 百种数据集&#xff0c;持续增加中。 需要更多数据资源和技术解决方案&#xff0c;知识星球&#xff1a; “DataBall - X 数据球(free)” 贵在坚持&#xff01; 数据样…

(2024.12)Ubuntu20.04安装ZED-SDK

一.官网地址 ZED SDK 4.2 - Download | Stereolabs 选择适配版本进行下载 二.安装程序 下载完成后&#xff0c;进入文件目录&#xff0c;打开终端&#xff0c;输入&#xff1a; chmod x ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zstd.run ./ZED_SDK_Ubuntu20_cuda11.8_v4.2.2.zst…

python coding(二) Pandas 、PIL、cv2

Pandas 一个分析结构化数据的工具集。Pandas 以 NumPy 为基础&#xff08;实现数据存储和运算&#xff09;&#xff0c;提供了专门用于数据分析的类型、方法和函数&#xff0c;对数据分析和数据挖掘提供了很好的支持&#xff1b;同时 pandas 还可以跟数据可视化工具 matplotli…

第十五届蓝桥杯Scratch01月stema选拔赛—排序

排序 具体要求&#xff1a; 1). 点击绿旗&#xff0c;在舞台上出现4张点数不同的扑克牌&#xff0c;牌上的点数是随机的&#xff08;4-9点&#xff09;&#xff0c;如图所示&#xff1b; 完整题目可点击下方链接查看&#xff1a; 排序_scratch_嗨信奥-玩嗨信息奥林匹克竞赛-…

图形 3.4 延迟渲染管线介绍

延迟渲染管线介绍 B站视频&#xff1a;图形 3.4 延迟渲染管线介绍 文章目录 延迟渲染管线介绍渲染路径前向渲染渲染流程光照规则 延迟渲染渲染流程几何缓冲区 G-buffer 不同渲染路径的优劣以及特性优劣 Unity中渲染路径设置移动端优化分块延迟渲染 其他渲染路径不同路径下光源…

Qt之串口设计-线程实现(十二)

Qt开发 系列文章 - Serial-port&#xff08;十二&#xff09; 目录 前言 一、SerialPort 二、实现方式 1.创建类 2.相关功能函数 3.用户使用 4.效果演示 5.拓展应用-实时刷新 总结 前言 Qt作为一个跨平台的应用程序开发框架&#xff0c;在串口编程方面提供了方便易用…

1.gitlab 服务器搭建流程

前提条件&#xff1a; 一、服务器硬件水平 搭建gitlab服务器最低配置要求2核4G,低于这个配置的服务器运行效果很差。 gitlab官网&#xff1a;https://about.gitlab.com/ 下载地址&#xff1a;gitlab/gitlab-ce - Packages packages.gitlab.com 本机ubuntu 二、安装依赖 su…

powerhsell 初认识

免责声明 本文是学习与泷羽Sec B站课程的课程笔记内容&#xff0c;仅作学习使用&#xff0c;如有破坏网络安全的行为&#xff0c;本人概不负责 B站链接&#xff1a;https://space.bilibili.com/350329294 资源自取&#xff1a;https://pan.quark.cn/s/b2718e905db8 powerhsel…

自我维护和保养

学习运动两不误&#xff01; 本人学习过程中&#xff0c;长期久坐导致各种身体问题&#xff08;特别是腰间盘突出&#xff0c;右侧肩胛骨翘等问题&#xff01;&#xff09;&#xff0c;希望给有类似烦恼的人们带去福音&#xff01;&#xff01;&#xff01; 我的椎间盘损伤历…

详解磁盘IO、网络IO、零拷贝IO、BIO、NIO、AIO、IO多路复用(select、poll、epoll)

1、什么是I/O 在计算机操作系统中&#xff0c;所谓的I/O就是输入&#xff08;Input&#xff09;和输出&#xff08;Output&#xff09;&#xff0c;也可以理解为读&#xff08;Read&#xff09;和写&#xff08;Write)&#xff0c;针对不同的对象&#xff0c;I/O模式可以划分为…

Cursor的重磅功能Agent登场

今天看了一些介绍&#xff0c;cursor有一个新功能agent ,试用了一下非常好用。再也不用头痛地选择相关的上下文&#xff0c;真是懒人利器。 Agant特性&#xff1a; 可以自主选择上下文能够使用终端可以独立完成整个任务 赶紧介绍给大家&#xff0c;使用时&#xff0c;需要在c…

5G -- 空口关键技术

前言&#xff1a; 手机(UE)和5G基站(gNodeB)之间的空中接口 新技术的特点&#xff1a; 1、提升速率&#xff1a;大带宽、新编码、高阶调制、F-OFDM、M-MIMO 2、降低时延&#xff1a;灵活帧结构、自包含时隙、免授权调度、D2D 3、提升覆盖&#xff1a;上下行解耦、EN-DC(双连…

常用Python自动化测试框架有哪些?

随着技术的进步和自动化技术的出现&#xff0c;市面上出现了一些自动化测试框架。只需要进行一些适用性和效率参数的调整&#xff0c;这些自动化测试框架就能够开箱即用&#xff0c;大大节省了测试时间。而且由于这些框架被广泛使用&#xff0c;他们具有很好的健壮性&#xff0…

【自用】通信内网部署rzgxxt项目_01,后端pipeDemo部署(使用nssm.exe仿照nohup)

做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要忘记打开 Windows Server 的防火墙端口&#xff0c;8181、8081、8080、22、443、1521 做完这些工作之后&#xff0c;不要…

jmeter 接口性能测试 学习笔记

目录 说明工具准备工具配置jmeter 界面汉化配置汉化步骤汉化结果图 案例1&#xff1a;测试接口接口准备线程组添加线程组配置线程组值线程数&#xff08;Number of Threads&#xff09;Ramp-Up 时间&#xff08;Ramp-Up Period&#xff09;循环次数&#xff08;Loop Count&…

Iris简单实现Go web服务器

package mainimport ("github.com/kataras/iris" )func main() {app : iris.New() // 实例一个iris对象//配置路由app.Get("/", func(ctx iris.Context) {ctx.WriteString("Hello Iris")})app.Get("/aa", func(ctx iris.Context) {ct…

tryhackme-Pre Security-HTTP in Detail(HTTP的详细内容)

任务一&#xff1a;What is HTTP(S)?&#xff08;什么是http&#xff08;s&#xff09;&#xff09; 1.What is HTTP? (HyperText Transfer Protocol)&#xff08;什么是 HTTP&#xff1f;&#xff08;超文本传输协议&#xff09;&#xff09; http是你查看网站的时候遵循的…