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

文章目录

  • 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总结

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

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

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

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

相关文章

国内智能搜索工具实战教程

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…

分享10类正规的网上赚钱平台,让你摆脱单一收入

在这个互联网飞速发展的时代&#xff0c;你是否还在为单一的收入来源而焦虑&#xff1f;别担心&#xff0c;今天带你解锁10种网上赚钱的新姿势&#xff0c;让你的收入不再单一&#xff0c;甚至可能翻倍&#xff01; 1. 文库类&#xff1a;知识的变现 你知道吗&#xff1f;你的…

k8s 数据流向 与 核心概念详细介绍

目录 一 k8s 数据流向 1&#xff0c;超级详细版 2&#xff0c;核心主键及含义 3&#xff0c;K8S 创建Pod 流程 4&#xff0c;用户访问流程 二 Kubernetes 核心概念 1&#xff0c;Pod 1.1 Pod 是什么 1.2 pod 与容器的关系 1.3 pod中容器 的通信 2&#xff0c; …

imx91的uboot编译

一、准备操作 下载半导体厂家的uboot源码 如这里我要下载的是imx91的恩智浦linux芯片bootloader 进入半导体厂家官网 下载源码&#xff0c;略 更新linux源&#xff0c;这里我是替换成清华源 vi /etc/apt/sources.list deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ fo…

【江科大STM32学习笔记】新建工程

1.建立工程文件夹&#xff0c;Keil中新建工程&#xff0c;选择型号 2.工程文件夹里建立Start、Library、User等文件夹&#xff0c;复制固件库里面的文件到工程文件夹 为添加工程文件准备&#xff0c;建文件夹是因为文件比较多需要分类管理&#xff0c;需要用到的文件一定要复…

Web UI自动化测试--PO模式

没有PO实现的测试用例的问题: 重用性低:登录功能重复可维护性差:数据和代码混合可读性差:元素定位方法杂乱(id、xpath、css混杂)可读性差:不易识别操作的含义(特别是css和xpath语法)可维护性差:如果某个元素的属性改了,你要更改多次PO(Page Object Model)页面对象模型…

完全背包问题(c++)

完全背包问题 当前有 N 种物品&#xff0c;第 i 种物品的体积是 ci​&#xff0c;价值是 wi​。 每种物品的数量都是无限的&#xff0c;可以选择任意数量放入背包。 现有容量为 V 的背包&#xff0c;请你放入若干物品&#xff0c;使总体积不超过 V&#xff0c;并且总价值尽可…

YOLOv8+CLIP实现图文特征匹配

本文通过结合YOLOv8s的高效物体检测能力与CLIP的先进图像-文本匹配技术&#xff0c;展示了深度学习在处理和分析复杂多模态数据中的潜力。这种技术的应用不仅限于学术研究&#xff0c;还能广泛应用于工业、商业和日常技术产品中&#xff0c;以实现更智能的人机交互和信息处理。…

新年首站 | 宝兰德教育行业信创新动力发展研讨会顺利召开

近日&#xff0c;宝兰德携手慧点数码、安超云共同举办了教育行业信创新动力发展研讨会。会议邀请了中国人民公安大学、中国戏曲学院、北京航空航天大学、北京理工大学、华北电力大学、中国矿业大学、北京服装学院、北京城市学院等数十所高校信息中心负责人、专家出席了本次会议…

LeetCode 题目 120:三角形最小路径和

作者介绍&#xff1a;10年大厂数据\经营分析经验&#xff0c;现任字节跳动数据部门负责人。 会一些的技术&#xff1a;数据分析、算法、SQL、大数据相关、python&#xff0c;欢迎探讨交流 欢迎加入社区&#xff1a;码上找工作 作者专栏每日更新&#xff1a; LeetCode解锁1000题…

WEB后端复习——javabean与会话cookie、session

JavaBean 是一种符合特定命名约定的 Java 类&#xff0c;它通常用于封装数据。 JavaBean 的主要特点是&#xff1a; 1. 无参构造器&#xff1a;JavaBean 必须有一个公共的&#xff08;public&#xff09;无参构造方法&#xff0c;以便于反射时能够创建对象实例。 2. 属性&…

Android的视图显示和管理机制:layout view window WindowManager Canvas Surface

在Android系统中&#xff0c;Layout view window WindowManager Canvas Surface SurfaceFlinger这些组件协同工作&#xff0c;以实现图形的绘制和显示。需要搞明白这些组件是什么时候创建的以及他们之间的结构关系。 从上到下的层级关系&#xff1a;用户在View上进行操作&…

考研踩坑经验分享

文章目录 写在前面自身情况简介自身学习路线优点坑点 学习路线建议1、2和3月份3、4和5月份6、7和8月份9、10月份11、12月份 一些私货建议结尾 写在前面 考研是一件非常有盼头的事&#xff0c;但绝对不是一件容易的事。 如果你不能做好来年三月份出成绩时&#xff0c;坦然接受…

Ubuntu 下使用 Scons 交叉编译嘉楠堪智 CanMV K230 大小核 Coremark 程序

在 Ubuntu 下使用 SCons 进行交叉编译嘉楠堪智 CanMV K230 大小核&#xff08;不同的玄铁 C908 核心&#xff09;的 C 程序&#xff0c;以 Coremark 程序为例&#xff0c;顺便测试一下大小核和编译器的性能。 2024年3月14日&#xff0c;嘉楠科技宣布推出了全球首款支持 RISC-V…

# 从浅入深 学习 SpringCloud 微服务架构(十七)--Spring Cloud config(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;十七&#xff09;–Spring Cloud config&#xff08;1&#xff09; 一、配置中心的 概述 1、配置中心概述 对于传统的单体应用而言&#xff0c;常使用配置文件来管理所有配置&#xff0c;比如 SpringBoot 的 application.y…

消费金融平台公司如何做大做强自营产品

本文来自于2019年的某次内部分享沟通会&#xff0c;部分敏感内容已做删减。

油泼辣子在食品类别可以申请成商标不!

前阵韩国人在美国申请“chili crunch”油泼辣子作为商标&#xff0c;还准备禁止华人餐馆使用投诉侵权并索赔&#xff0c;普推知产老杨在USPTO上面检索发现&#xff0c;这个人申请的主要是30类方便食品的调味品&#xff0c;商标分类是全球通用的。 商标名称不能申请本类所属的通…

C/C++常用的内置的宏定义

常用的C/C 内置宏 这是我在VS2015下运行的 cout << "file " << __FILE__ << endl;cout << "line " << __LINE__ << endl;cout << "date " << __DATE__ << endl;cout << "…

力扣HOT100 - 55. 跳跃游戏

解题思路&#xff1a; class Solution {public boolean canJump(int[] nums) {int n nums.length;int maxReach 0;// 正常来说每次至少跳一格&#xff0c;所以最多循环n次for (int i 0; i < n; i) {if (i > maxReach) return false;// 这种情况代表遇到了0&#xff0…

机器学习周报第三十八周 iTransformer

文章目录 week38 iTransformer摘要Abstract一、文献阅读1. 题目2. abstract3. 网络架构**转置Embedding&#xff1a;****LayerNorm&#xff08;层归一化&#xff09;****Feed-forward network&#xff08;前馈网络&#xff09;****Multivariate-Attention&#xff08;多变量注意…