【进程通信】了解信号以及信号的产生

文章目录

  • 0.前言
  • 1.信号的基本概念
    • 1.1中断
      • 1.1.1 软中断
      • 1.1.2硬中断
    • 1.2异步
      • 1.2.1异步和同步的比较
  • 2.信号的主要用途
  • 3.信号的特点
  • 4.查看信号
    • 4.1Core和Term的区别
    • 4.2生成Core文件
  • 5.初识捕捉信号
    • 5.1signal函数
  • 6.产生信号的方式
    • 6.1.通过终端按键产生信号
    • 6.2.调用系统函数向进程发送信号
      • kill函数
      • raise函数
      • abort函数
    • 6.3.由软件条件产生信号
      • 6.3.1alarm函数
    • 6.4硬件异常产生信号
  • 7总结

0.前言

信号是进程通信的一种方式。如同我们按下ctrl+c就能终止一个进程,实际上就
是bash进程向子进程发送了一个终止信号。和手机信号,wifi信号等类似,进程之间也可以通过信号来传递某种信息。不同的是,进程之间的信号本身就是一种共享资源信号是如何产生的?被谁产生的?又是怎么获取信号的? 本篇文章将从这几个问题展开叙述,详细讲解信号产生的原理。

1.信号的基本概念

在前言中我们只是知道了信号是实现进程通信的一种机制,那么于其它通信方式来说,信号又有着什么样的特性呢?

在Linux操作系统中,信号是一种软件中断机制,用于通知进程某些事件的发生。信号通常用于处理异步事件。信号可以由操作系统、其它进程、或者同一进程内的其它线程发送。

根据上面的概述给出以下知识扩展:

1.1中断

中断是指处理器响应硬件或软件事件的机制(其本身就是一个信号)。中断分为软件中断和硬件中断,这两者在来源处理方式上有所不同。

1.1.1 软中断

软件中断又叫软中断,是操作系统生成的中断,用于处理进程间通信或者操作系统内部的事件,比如调度、资源管理等。软中断可以通过执行特定的指令来认为生成(如kill指令),或者由操作系统内部事件触发不依赖于硬件

软中断的特性

  • 处理延迟性:软中断不需要立即被响应,可以按照系统的调度策略进行处理
  • 上下文:软中断通常在操作系统系统的上下文中执行,不会直接与硬件交互
  • 灵活性:软中断可以由操作系统根据需求灵活生成

1.1.2硬中断

硬中断是由硬件设备产生的中断,用于通知处理器一些外部事件的发生。比如键盘在输入消息时会向cpu发送一个硬件中断,告诉cpu键盘要输入了。此外鼠标、网卡等都可以通过中断请求线(IRQ)发送到cpu。硬中断依赖硬件。

硬中断特性:

  • 即时性:硬件中断通常需要cpu立即响应
  • 优先级:不同硬件中断有不同的优先级,处理器根据优先级来决定响应哪一个中断,通常硬盘中断优先级是最高的
  • 中断处理程序:每个硬中断都有一个专门的中断处理程序。

1.2异步

异步是指一个过程不需要等待其它任务完成的情况下继续进行的能力。不同于依赖同步机制的管道通信,必须要有数据写入,读端进程才会去读。发送信号的一端并不关心接收端此刻有没有做好接收的准备,也不关心信号发送之后会不会被处理。异步处理是提高程序效率和响应性的一种常用方法

1.2.1异步和同步的比较

特性异步同步
执行流非阻塞,允许并发执行阻塞,按照顺序执行
资源使用高效,可以等待时执行其他任务效率较低,cpu可能在等待时闲置
复杂度,需要处理竞态条件、死锁等问题,因为操作按顺序执行,易于理解和实现

根据以上信息得出信号的以下几种用途

2.信号的主要用途

  1. 异常处理:如当程序执行除0操作时,系统会发送一个SIGFPE浮点异常信号给进程
  2. 外部中断:比如键盘发送的中断信号例如ctrl+c,进程会收到一个SIGINT信号。
  3. 进程控制:比如有些信号可以暂停进程,有些信号用来终止进程。

同样根据信号的定义,我们能得到以下关于信号的特点:

3.信号的特点

  • 异步性:信号可以在进程的任何石刻发送或者接收。
  • 通知:信号只起到通知作用,接收信号之后怎么处理跟信号本身无关。
  • 自定义处理方式:对于一个进程来说,对待一个信号有三种处理方式:
    1.忽略信号不做处理 2.默认处理方式,即让操作系统自己去处理 3.自已定义信号的处理方式(自定义函数)

4.查看信号

在linux中我们可以使用kill -l指令来查看系统定义的信号列表
在这里插入图片描述
对于以上信号列表:

  • 每个信号都有一个编号和一个宏定义名称,这些宏定义可以在signal.h里面找到,例如其中有定义 #define SIGINT 2
  • 常见的信号是1-31,其中34-64都是实时信号,实时信号不在本文讨论范围内。每个信号都有自己默认的处理动作,在signal(7)中都有详细说明,指令man 7 signal可以查看:
    在这里插入图片描述

在谈信号的产生之前,我们可以先学习如何捕捉信号,因为捕捉信号能让我们更好的理解信号的产生。

4.1Core和Term的区别

查看信号的默认响应行为我们可以发现发现,大多信号都是Core或者Term,且这两种信号都表示终止进程。那这两种终止进程的方式有什么区别呢?
其中Term就是普通的终止进程,之后没有其他动作。而Core不仅会终止进程,还会生成一个核心转储文件

核心转储(Core Dump)文件包含了进程终止时的内存映像,和关于进程状态的详细信息,也就意味着,通过这个核心转储文件我们就能知道这个进程在终止之前发生了什么。这对于开发者来说是非常宝贵的调试信息(可以借助gdb调试器加载其中的信息并调试)。

4.2生成Core文件

系统默认进程终止时不生产Core文件,因为core文件中可能包含用户密码等隐私信息,不安全。也就意味着,即使某个进程收到SIGQUIT信号(默认产生core文件)也不会生成core文件。但是在开发调试阶段可以使用ulimit指令改变这个限制,允许产生core文件。此外core文件的大小取决于进程的Resource Limit(这个信息保存 在PCB中,默认是0)。

此外,如果子进程终止之后生成了core文件,那么子进程的退出码中的core_dump标记位就会置为1。

  1. 首先使用ulimit -c 1024指令改变core文件的大小为1024字节,再使用-a选项查看
    在这里插入图片描述

  2. 写一个死循环的程序:

#include <iostream>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
using namespace std;
int main()
{
    while (true)
    {
        cout << "pid: " << getpid() << endl;
        sleep(1);
    }
    return 0;
}
  1. 运行之后在终端按下Ctrl+\(SIGQUIT信号):
    在这里插入图片描述
    在这里插入图片描述
    如果你发现没有看到这个core文件,那可能是默认的核心转储的位置不在当前目录,可以使用以下指令修改:
    echo 'core.%e.%p.%t' | sudo tee /proc/sys/kernel/core_pattern
    %e表示程序名,%p表示进程pid,%t表示时间戳。生成的core文件后缀就是.%e.%p.%t格式。可自行修改。

我们可以用gdb加载core文件信息并调试,在gdb中使用core-file指令可以加载core文件:在这里插入图片描述

5.初识捕捉信号

5.1signal函数

signal函数(库函数)是用于设置处理某个信号时所调用的处理函数,也被称为信号处理器。由头文件signal.h提供,这个头文件属于c标准库的一部分。该函数允许程序自定义特定信号的响应行为。这一点也证实了信号的特征。

 #include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
  • sighandler_t是一个函数指针(参数为int),作为signal的参数
  • signum表示的就是要捕捉的信号
  • 返回值是一个指向之前处理该信号的函数的指针,或者在错误情况下返回 SIG_ERR。
  • signal函数并不是系统调用,但是其内部封装了系统调用sigaction。一旦我们使用signal函数捕捉了某个信号,该进程响应该信号的方式就可以自己决定了。但是值得注意的是,有一些特殊的信号的响应方式并不会完全被sighandler替代。

6.产生信号的方式

6.1.通过终端按键产生信号

ctrl+c表示一个终止信号这个好理解,但是如何证明就是产生了信号SIGINT(2)呢?我们可以用signal函数捕捉SIGINT函数,然后在自定义该信号的响应方式,最后在进程运行时按下ctrl+c观察。给出实验代码:

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
using namespace std;

void myhandler(int sig)
{
    cout <<"进程"<<getpid()<< " 接收到了sig信号: " << sig << endl;
}

int main()
{
    signal(2, myhandler);
    while (true)
    {
        cout << "hello" << endl;
        sleep(1);
    }

    return 0;
}

观察运行结果:
在这里插入图片描述
当我们按下ctrl+c之后发现并没有终止进程,而是执行了自定义的myhandler函数。

6.2.调用系统函数向进程发送信号

其实就是我们常用的kill指令发送信号给指定进程。kill指令本质上是调用了系统调用kill函数。kill系统调用可以发信号到某一个进程,也可以发送到某一组进程

kill函数

#include <sys/types.h>
#include <signal.h>

int kill(pid_t pid, int sig);
  • pid表示要信号发送到哪一个进程
  • sig表示发送信号的类型
  • 发送成功返回0,否则-1.

于是,我们可以在代码中使用kill函数发送信号了。此外再介绍一个raise函数,raise函数可以给当前进程发送指定的信号(自己给自己发信号)。

raise函数

#include<signal.h>
int raise(int sig);
  • sig表示发送信号的类型
  • 发送成功返回0,否则-1。

abort函数

#include <stdlib.h>
void abort(void);

这个函数没有参数,并且它也没有返回值。调用 abort 函数后,程序会立即异常终止。本质上这个函数调用之后,操作系统就会先该进程发送一个SIGABRT信号,这个信号会终止当前进程,且通常生成core文件。(其实就是调用raise发送一个SIGABRT信号

6.3.由软件条件产生信号

软件条件产生信号其实就是软中断的一种,就是因为某种软事件或程序内部逻辑触发的信号。比如管道读端关闭之后,写端就会收到一个SIGPIPE信号进而终止。 下面介绍alarm函数和SIGALRM信号。

6.3.1alarm函数

alarm函数是一个定时器,可以设置一个时间,这个定时器会在未来的一个时刻发送一个SIGALRM信号给当前进程(该信号默认处理动作是终止进程)。alarm函数原型具体如下:

#include<unistd.h>
unsigned int alarm(unsigned int seconds);
  • seconds表示的是一个时间,单位为秒。如果seconds为0,表示取消以前设置的定时器。
  • 返回值是0或者是以前设定的闹钟时间还余下的秒数

6.4硬件异常产生信号

硬件异常被硬件以某种方式被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如当前进程执行了除以0的指令,CPU的运算单元会产生异常,内核将这个异常解释为SIGFPE信号发送给进程。再比如当前进程访问了非法内存地址,MMU会产生异常,内核将这个异常解释为SIGSEGV信号发送给进程。具体这个异常是怎么被检测出来的我们无需关心,我们只要知道发生硬件异常之后操作系统会向该进程发送一个信号来终止进程就行了。

为什么说除0是硬件异常?因为除0这个操作是CPU在执行的,除0之后会CPU触发异常信号。访问野指针也是类似。

下面来模拟一下野指针异常,给出实验代码:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>

void handler(int sig)
{
    printf("catch a sig : %d\n", sig);
    sleep(1);
}

int main()
{
    signal(SIGSEGV, handler);
    sleep(1);
    int *p = NULL;
    *p = 100;
    return 0;
}

观察以上实验代码我们就会发现,我们访问了野指针之后会触发一个信号,这个信号是什么类型,我们可以通过hanler函数来显示。
在这里插入图片描述
信号11就是SIGSEGV信号。但是我们发现一个很奇怪的现象:为什么这个handler函数会一直执行下去呢?
按理来说,调用了一次signal函数捕捉了异常信号,hander应该只会执行一次。那为什么之前我们在捕捉Ctrl+c(信号2)时却只执行了一次呢?

代码int *p = NULL; *p = 100; 显然会引发一个 SIGSEGV 信号,因为它尝试向 NULL 指针所指向的内存地址写入一个值,这是非法的内存访问。之后CPU触发一个硬件异常信号,执行handler函数之前操作系统会保存触发信号的指令地址于上下文中,执行完handler之后,操作系统又会回到之前保存的地址中去,即又回到了信号发生时的状态,于是就又重新执行*p这个代码。于是就产生了死循环。

那这种问题该如何解决呢?
可以在在handler函数中调用exit()函数或者_exit()函数确保进程终止。

7总结

  • 所有的信号本质上都是又操作系统产生的 。只不过是进程委托操作系统将这个信号发送给另一个进程(也可以是自己)。

  • 所有的信号都是起到一个作用:告诉某个进程发生了什么。怎么做由开发者决定。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

浅谈如何做好软件项目

如何做好软件项目&#xff0c;这是摆在软件实施团队每个人面前的关键问题。笔者在此提出一些浅见&#xff0c;供大家参考。欢迎在评论区交流&#xff01; 目录 【摘要】 【正文】 一、树立正确的需求调研理念 二、谋定而后动的开发工作 三、大道至简的系统设计 四、专注项…

hcip实验6:BGP综合实验

实验拓扑&#xff1a; 实验配置&#xff1a; ip地址配置&#xff1a; #R1 [R1]int g0/0/0 [R1-GigabitEthernet0/0/0]ip add 12.1.1.1 24 [R1-GigabitEthernet0/0/0]int l0 [R1-LoopBack0]ip add 172.16.0.1 32 [R1-LoopBack0]int l1 [R1-LoopBack1]ip add 192.168.1.1 24#R2…

算法笔记——数位DP

一、前置知识 1.DP小知识 D P DP DP 是一种算法思想&#xff0c;用递推方程的方式解决问题。但是使用它要满足如下性质&#xff1a; 最优子结构&#xff1a; 子结构优秀&#xff0c;整个就优秀。无后效性&#xff1a;当前决策不会影响后面。 2.DP实现方法 众所周知&#xf…

环境变量(全)

概念 环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数 如&#xff1a;我们在编写C/C代码的时候&#xff0c;在链接的时候&#xff0c;从来不知道我们的所链接的动态静态库在哪里&#xff0c;但是照样可以链接成功&#xff0c;生成可执…

2024中国(重庆)机器人展览会8月举办

2024中国(重庆)机器人展览会8月举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 2024中国重庆机器人展会将汇聚机器人全产业链知名企业&#xff0c;世界科技领先的生产制造企业与来自多个国家和地区…

Python | Leetcode Python题解之第78题子集

题目&#xff1a; 题解&#xff1a; class Solution:def subsets(self, nums: List[int]) -> List[List[int]]:self.res []self.backtrack([], 0, nums)return self.resdef backtrack(self, sol, index, nums):self.res.append(sol)for i in range(index, len(nums)):self…

2024中国(重庆)无人机展览会8月在重庆举办

2024中国(重庆)无人机展览会8月在重庆举办 邀请函 主办单位&#xff1a; 中国航空学会 重庆市南岸区人民政府 招商执行单位&#xff1a; 重庆港华展览有限公司 报名&#xff1a;【交易会I 59交易会2351交易会9466】 展会背景&#xff1a; 为更好的培养航空航天产业和无人…

鸿蒙内核源码分析(Shell编辑篇) | 两个任务,三个阶段

系列篇从内核视角用一句话概括shell的底层实现为&#xff1a;两个任务&#xff0c;三个阶段。其本质是独立进程&#xff0c;因而划到进程管理模块。每次创建shell进程都会再创建两个任务。 客户端任务(ShellEntry)&#xff1a; 负责接受来自终端(控制台)敲入的一个个字符&…

OFDM802.11a的FPGA实现(十二)使用FFT IP核添加循环前缀

原文链接&#xff08;相关文章合集&#xff09;&#xff1a;OFDM 802.11a的xilinx FPGA实现 目录 1.前言2.循环前缀3.硬件实现4.ModelSim仿真 1.前言 为了能够消除传输过程当中的符号间干扰&#xff0c;在IFFT处理完毕之后还要加上循环前缀。 2.循环前缀 实际通信信道中,由于接…

HTML表单创建学习

文章目录 1、创建HTML框架2.body标签CSS3.表单创建3.1、添加fieldset与label标签3.2、为label标签添加css样式3.3、添加input标签3.4、添加提交按钮3.5、在input标签中添加required3.6、添加minlength属性3.7、pattern属性3.8、设置表单单选按钮无法同时选中3.9、添加链接3.10、…

机器人系统ros2-开发实践06-将静态坐标系广播到 tf2(Python)-定义机器人底座与其传感器或非移动部件之间的关系

发布静态变换对于定义机器人底座与其传感器或非移动部件之间的关系非常有用。例如&#xff0c;最容易推断激光扫描仪中心框架中的激光扫描测量结果。 1. 创建包 首先&#xff0c;我们将创建一个用于本教程和后续教程的包。调用的包learning_tf2_py将依赖于geometry_msgs、pyth…

简约在线生成短网址系统源码 短链防红域名系统 带后台

简约在线生成短网址系统源码 短链防红域名系统 带后台 安装教程&#xff1a;访问 http://你的域名/install 进行安装 源码免费下载地址抄笔记 (chaobiji.cn)https://chaobiji.cn/

刨析YOLOv8的改进模块

1、YOLOv5回顾 这里粗略回顾一下,这里直接提供YOLOv5的整理的结构图吧:Backbone:CSPDarkNet结构,主要结构思想的体现在C3模块,这里也是梯度分流的主要思想所在的地方;PAN-FPN:双流的FPN,必须香,也必须快,但是量化还是有些需要图优化才可以达到最优的性能,比如cat前后…

支持视频切片的开源物联网平台

软件介绍 MzMedia开源视频联动物联网平台是一个简单易用的系统,该平台支持主流短视频平台&#xff08;如抖音、快手、视频号&#xff09;的推流直播功能&#xff0c;同时提供视频切片等功能。系统后端采用Spring Boot&#xff0c;前端采用Vue3和Element Plus&#xff0c;消息服…

构建教育新未来:智慧校园平台的深度解读与全景呈现

引言 在全球数字化转型的大潮中&#xff0c;智慧校园平台作为教育信息化的重要载体&#xff0c;正以前所未有的姿态颠覆传统的教育模式&#xff0c;引领教育行业步入一个崭新的时代。这个融合了大数据、人工智能、云计算、物联网等一系列前沿科技的平台&#xff0c;以其强大的功…

python算法demo0512

最长回文数 代码 class Solution:def longestPalindrome(self, s: str) -> str:n len(s)if n < 2:return smax_len 1begin 0# dp[i][j] 表示 s[i..j] 是否是回文串dp [[False] * n for _ in range(n)]for i in range(n):dp[i][i] True# 递推开始# 先枚举子串长度fo…

pycharm本地文件更新至虚拟机

tools–>deployment–>configuration root path的路径要跟远程路径对齐&#xff0c;方便后续运行 mapping映射&#xff0c;本地路径和远程路径 点击Browse 可以在右侧同步查看更新情况

llama3 发布!大语言模型新选择 | 开源日报 No.251

meta-llama/llama Stars: 53.0k License: NOASSERTION llama 是用于 Llama 模型推理的代码。 提供了预训练和微调的 Llama 语言模型&#xff0c;参数范围从 7B 到 70B。可以通过下载脚本获取模型权重和 tokenizer。支持在本地快速运行推理&#xff0c;并提供不同规格的模型并…

Vue报错:TypeError: Cannot read property ‘upgrade‘ of undefined

Vue报错&#xff1a;TypeError: Cannot read property ‘upgrade’ of undefined 前言 最近打开一个很就之前的开发项目&#xff0c;因为扫描包&#xff0c;所以删除了部分代码&#xff0c;后来就一直报错&#xff0c;现在总结一下。 报错原因&#xff1a;vue.config.js中 d…

探索数据结构:树与二叉树

✨✨ 欢迎大家来到贝蒂大讲堂✨✨ &#x1f388;&#x1f388;养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; 所属专栏&#xff1a;数据结构与算法 贝蒂的主页&#xff1a;Betty’s blog 1. 树 1.1. 树的定义 树是一种非线性的数据结构&#xff0c;它是由n&a…