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

进程间信号

进程间信号

  • 信号的认识
  • 信号的产生
  • 进程对信号的处理机制
    • 普通信号的处理机制
    • 实时信号的处理机制
  • 信号集操作函数
  • 信号的捕捉

信号的认识

信号的概念:
 信号是一种软件中断,它用于通知进程一个异步事件的发生。
 这些事件可能来自系统内部(如硬件异常、定时器到期等),也可能来自其他进程或用户操作(如通过键盘输入Ctrl+C发送中断信号)。
 每个信号都有一个特定的编号和名称,例如SIGINT(中断信号)和SIGTERM(终止请求信号)。

信号总数:
标准信号(非实时信号):编号范围为 1 到 31(如 SIGINT、SIGTERM)。
实时信号(POSIX 标准扩展):编号从 SIGRTMIN(通常 34)到 SIGRTMAX(通常 64)。
总计约 64 个信号。
在这里插入图片描述


进程对信号的响应:
当一个进程接收到信号时,它可以采取以下三种行动之一:
 执行默认动作:大多数信号都有默认的动作,可能是忽略信号、暂停进程、终止进程或者产生核心转储文件。

 忽略信号:进程可以选择忽略某些信号,除了SIGKILL(9号信号)和SIGSTOP(19号信号),这两个信号不能被忽略。

 捕获并处理信号:进程可以定义自己的信号处理函数来捕获信号,并在接收到信号时执行自定义的行为。

系统调用signal(): 允许进程指定一个信号处理函数,当进程接收到特定类型的信号时,操作系统将调用这个函数而不是执行该信号的默认行为。

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int sig, sighandler_t handler);参数:
sig:需处理的信号编号(如 SIGINT、SIGTERM)。
handler:信号处理方式,可为:1.用户自定义函数:信号触发时执行;2.SIG_IGN:忽略该信号;3.SIG_DFL:恢复系统默认行为。返回值:成功时返回旧的信号处理函数指针,失败返回 SIG_ERR。

进程的整个生命周期里只需要设置一次,往后都有效。
不是所有的信号都能被自定义捕捉, 9号(杀死进程)和19号(暂停进程)信号不能被捕捉。(只看1~31号普通信号)

示例:

#include <stdio.h>
#include <signal.h>
#include <stdlib.h>// 信号处理函数
void handle_sigint(int sig) {printf("Caught signal %d (SIGINT), exiting...\n", sig);exit(0); // 优雅地退出程序
}int main() {// 设置 SIGINT 信号的处理函数if (signal(SIGINT, handle_sigint) == SIG_ERR) {perror("signal");exit(EXIT_FAILURE);}// 无限循环,等待信号while (1) {// 通常,这里会执行一些有用的工作}return 0;
}

信号的产生

1. 键盘组合键(终端驱动触发)
用户通过终端输入特定组合键时,终端驱动程序会检测并生成信号,发送给当前前台进程组

  • Ctrl + C
    • 产生 SIGINT(2号信号),默认行为是终止进程。
    • 用于用户主动中断进程(如停止卡死的程序)。
  • Ctrl + \
    • 产生 SIGQUIT(3号信号),默认行为是终止进程并生成核心转储(core dump)。
    • 用于强制终止进程并调试(如定位程序崩溃点)。

2. kill 命令(用户主动发送)
通过命令行工具 kill 显式向指定进程发送信号,本质是调用 kill() 系统调用的封装。

  • 语法kill -信号编号 PID
    • 示例:
      kill -9 1234      # 发送 SIGKILL(9号信号)强制终止 PID=1234 的进程
      kill -TERM 5678   # 发送 SIGTERM(15号信号)请求优雅终止进程
      
  • 关键信号
    • SIGTERM(15号):默认终止进程,允许进程清理资源(友好终止)。
    • SIGKILL(9号):强制立即终止进程,不可被捕获或忽略(终极手段)。

3. 库函数(程序内部触发)
通过编程调用库函数或系统调用,由进程主动生成信号。
(1) kill() 系统调用

  • 功能:向指定进程(或进程组)发送信号。
  • 原型
    #include <sys/types.h>
    #include <signal.h>
    int kill(pid_t pid, int sig);  // 成功返回 0,失败返回 -1
    
  • 示例
    kill(1234, SIGTERM);  // 向 PID=1234 发送终止信号
    kill(-1, SIGKILL);    // 向所有进程广播 SIGKILL(需 root 权限)
    

(2) raise() 系统调用

  • 功能:向当前进程自身发送信号(等价于 kill(getpid(), sig))。
  • 原型
    int raise(int sig);  // 成功返回 0,失败返回非 0
    
  • 示例
    raise(SIGABRT);      // 主动触发异常终止(生成 core dump)
    

(3) abort() 库函数

  • 功能:触发 SIGABRT(6号信号),强制终止当前进程并生成核心转储。
  • 特点
    • 即使进程捕获 SIGABRT 并定义处理函数,处理完毕后仍会终止进程(除非处理函数内调用 longjmp 等跳转函数)。
    • 用于断言失败(如 assert() 宏底层调用 abort())。
  • 示例
    if (error) abort();  // 检测到致命错误时终止程序
    

4. 硬件异常或软件条件(操作系统触发)
由硬件错误或系统内部条件触发的信号,由内核直接生成并发送给进程。

(1) 硬件异常

  • 场景
    • 除零错误:CPU 检测到除零操作 → 内核发送 SIGFPE(8号信号)。
    • 非法内存访问:MMU 检测到野指针访问 → 内核发送 SIGSEGV(11号信号)。
    • 总线错误:未对齐内存访问 → 内核发送 SIGBUS(7号信号)。

(2) 软件条件

  • 场景
    • 定时器到期alarm(seconds) 设置定时器 → 到期后触发 SIGALRM(14号信号)。
    • 子进程终止:子进程退出时,内核向父进程发送 SIGCHLD(17号信号)。
    • 管道破裂:向已关闭的管道写数据 → 触发 SIGPIPE(13号信号)。
  • 示例
    alarm(5);  // 5秒后触发 SIGALRM
    

信号产生的本质
所有信号最终由操作系统内核生成并传递给目标进程:

  • 异步事件:信号可能在任何时间点中断进程的执行流程。
  • 统一管理:无论信号来源(用户、硬件、程序自身),均由内核统一调度和处理。
信号产生方式典型场景核心机制
键盘组合键用户中断前台进程终端驱动调用 kill()
kill 命令管理进程生命周期封装 kill() 系统调用
库函数/系统调用程序主动触发异常或终止调用 kill()/raise()
硬件异常或软件条件CPU/MMU 异常或系统事件内核直接生成信号

进程对信号的处理机制

普通信号的处理机制

进程收到信号后,不一定会立即处理,而是等待特定时机(如从内核态返回用户态时),这种延迟形成一个时间窗口,信号三表来统筹完成这个过程。

信号传递过程相关名词:
信号递达:实际执行信号的处理动作。

信号未决:信号从产生到递达之间的状态。

进程可以选择阻塞 (Block )某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

信号三表:
blocked位图: 用来标记当前哪些信号被该进程所屏蔽(阻塞),即使这些信号已经到达也不会立即触发处理函数,而是保持在未决状态直到解除屏蔽。
(位值为 1 表示信号被阻塞,被阻塞的信号会停留在 pending 中,直到解除阻塞。)

pending位图: 用于记录所有已经到达但是还没有被递达给进程的信号。
(位值为 1 表示信号已到达但未处理,信号递送时,内核清除对应位)

handler表: 实际上是一个函数指针数组,用于定义每种信号的具体处理动作。当信号递达时,根据信号编号找到对应的处理函数指针,并执行相应的处理逻辑。如果信号没有特定的处理函数,则按照默认行为处理或者忽略该信号。

在这里插入图片描述
【注】位图中的不同位代表不同的信号是否被接收到(不同类型的信号会分别占据pending位图中的不同比特位)。
  普通信号不支持排队,多个相同类型的信号在它们被处理之前到达,只会有一个实例被记录下来。

实时信号的处理机制

(1) 可靠排队传递
队列化存储:当多个相同的实时信号到达时,内核会将其按接收顺序加入等待队列,确保每次触发均被处理。

(2) 优先级与顺序控制
按编号区分优先级:实时信号的编号越大,优先级越高。
同种信号按到达顺序处理:若多次发送同一实时信号(如多次 SIGRTMIN+5),则按 先进先出(FIFO) 顺序处理。

(3) 支持携带数据
通过 sigqueue() 发送实时信号时,可附加用户自定义数据(整数或指针),由内核传递给信号处理函数。

信号集操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);
//初始化信号集 set,清空所有信号(所有位设为0)int sigfillset(sigset_t *set);
//初始化信号集 set,包含所有信号(所有位设为1)int sigaddset (sigset_t *set, int signo);
//将信号 signo 添加到信号集 set 中int sigdelset(sigset_t *set, int signo);
//从信号集 set 中删除信号 signoint sigismember (const sigset_t *set, int signo);
//检查信号 signo 是否在信号集 set 中

信号屏蔽控制函数:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)
//修改或获取进程的信号屏蔽字(blocked信号集)
int sigpending(sigset_t *set)
//获取当前进程的未决信号集(pending信号集,(已到达但被阻塞的信号))

信号的捕捉

在这里插入图片描述
信号捕捉在进程从内核态返回用户态时触发,如果自定义信号处理函数会返回用户态执行函数,函数返回时执行特殊系统调用再次进入内核态,内核态执行返回系统调用,返回用户态中断的地方继续向下执行。

系统调用sigaction(): 更灵活更安全的signal()。

#include <signal.h>int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

参数说明:
signum:目标信号的编号(如 SIGINT、SIGTERM)。
act:指向 struct sigaction 的指针,用于设置新行为。
oldact:若不为 NULL,则保存之前的信号处理配置。
返回值:成功返回 0,失败返回 -1 并设置 errno。

struct sigaction 结构体

struct sigaction {void     (*sa_handler)(int);          // 信号处理函数(和signal()类似的处理函数,无法携带附加信息)void     (*sa_sigaction)(int, siginfo_t *, void *); // 信号处理函数(可带附加信息)//两个信号处理函数选择其中一个填写使用sigset_t  sa_mask;                    // 信号掩码,在处理期间临时屏蔽的信号集int       sa_flags;                   // 信号处理标志
};

示例:

//无法携带附加信息的信号处理函数版本
#include <signal.h>
#include <stdio.h>void handler(int sig) {printf("Caught signal %d\n", sig);
}int main() {struct sigaction act;sigemptyset(&act.sa_mask);  // 清空信号掩码act.sa_flags = 0;act.sa_handler = handler;   // 设置处理函数if (sigaction(SIGINT, &act, NULL) == -1) {perror("sigaction");return 1;}while(1) pause();  // 等待信号return 0;
}//使用携带附加信息处理函数的版本
#include <signal.h>
#include <stdio.h>void handler(int sig, siginfo_t *info, void *context) {printf("Caught signal %d from pid %d\n", sig, info->si_pid);
}int main() {struct sigaction act;sigemptyset(&act.sa_mask);act.sa_flags = SA_SIGINFO;  // 启用 sa_sigactionact.sa_sigaction = handler; // 设置带附加信息的处理函数if (sigaction(SIGUSR1, &act, NULL) == -1) {perror("sigaction");return 1;}while(1) pause();return 0;
}

  
信号处理时的机制:
  接收到信号后,pending位图上的对应位置由0->1;信号在递达时,位图对应位置上的1会在执行信号捕捉方法之前被清0。
  当某个信号的处理函数被调用时,内核自动将当前信号加入进程的信号屏蔽字,当信号处理函数返回时自动恢复原来的信号屏蔽字,这样就保证了在处理某个信号时,如果这种信号再次产生,那么它会被阻塞到当前处理结束为止,不允许嵌套捕捉。
(sa_mask屏蔽的信号,在信号处理函数执行时也会被屏蔽,当信号处理函数返回时自动恢复原来的信号屏蔽字。)

相关文章:

进程间信号

进程间信号 信号的认识信号的产生进程对信号的处理机制普通信号的处理机制实时信号的处理机制 信号集操作函数信号的捕捉 信号的认识 信号的概念&#xff1a;  信号是一种软件中断&#xff0c;它用于通知进程一个异步事件的发生。  这些事件可能来自系统内部&#xff08;如硬…...

2011-2019年各省地方财政国债还本付息支出数据

2011-2019年各省地方财政国债还本付息支出数据 1、时间&#xff1a;2007-2019年 2、来源&#xff1a;国家统计局、统计年鉴 3、指标&#xff1a;行政区划代码、地区、年份、地方财政粮油物资储备管理等事务 4、范围&#xff1a;31省 5、指标说明&#xff1a;地方财政的国债…...

2025华为软件精英挑战赛2600w思路分享

这里写自定义目录标题 得分展示对象定义请求价值计算时间同步删除操作完整思路 得分展示 对象定义 // 将一个磁盘划分为多个基于标签聚合的区块 class Block{ public:int tag 0; // 区块标签int start_pos;int end_pos;int id;int use_size 0;int v;// 为区块确定范围Bloc…...

WEB安全-CTF中的PHP反序列化漏洞

什么是序列化&#xff1f; 简单来说序列化是将数组或对象转换成字符串的过程&#xff0c;这样的好处是利于对象存储与传输&#xff0c;在PHP中&#xff0c;序列化函数是serialize()&#xff0c;反序列化是unserialize() 无类序列化 无类序列化顾名思义就是不包含class的序列…...

【论文阅读】Co2l: Contrastive continual learning

原文链接&#xff1a;[2106.14413] Co$^2$L: Contrastive Continual Learning 阅读本文前&#xff0c;需要对持续学习的基本概念以及面临的问题有大致了解&#xff0c;可参考综述&#xff1a; Wang L, Zhang X, Su H, et al. A comprehensive survey of continual learning: …...

OpenMCU(五):STM32F103时钟树初始化分析

概述 本文主要描述了STM32F103初始化过程系统时钟的初始化,主要描述了系统时钟的初始化&#xff0c;AHB总线时钟&#xff0c;APB总线时钟等的初始化。 硬件板卡3d图 时钟树 STM32F103的时钟树&#xff0c;如下所示: 时钟源选择 从STM32F103的时钟树框图&#xff0c;我们可以…...

ISIS报文

IS-IS 报文 目录 IS-IS 报文 一、报文类型与功能 二、报文结构解析 三、核心功能特性 四、典型应用场景 五、抓包数据分析 六、总结 IS-IS&#xff08;中间系统到中间系统&#xff09;协议报文是用于链路状态路由协议中网络设备间交换路由信息的关键载体&#xff0c;其设…...

【Android】BluetoothSocket.connect () 的实现与协议栈交互源码解析

本文以 Android 蓝牙框架中的BluetoothSocket.connect()方法为切入点,深入剖析 Android 设备与远程蓝牙设备建立连接的全流程。从 Java 层的 API 调用出发,逐步追踪至 JNI 层的接口转发,最终进入 Buedroid 协议栈(RFCOMM/L2CAP 层),揭示蓝牙连接的核心机制。重点解析了权…...

首屏加载时间优化解决

&#x1f916; 作者简介&#xff1a;水煮白菜王&#xff08;juejin/csdn同名&#xff09; &#xff0c;一位前端劝退师 &#x1f47b; &#x1f440; 文章专栏&#xff1a; 高德AMap专栏 &#xff0c;记录一下平时学习在博客写作中记录&#xff0c;总结出的一些开发技巧✍。 感…...

RabbitMQ--延迟队列事务消息分发

目录 1.延迟队列 1.1应用场景 1.2利用TTL死信队列模拟延迟队列存在的问题 1.3延迟队列插件 1.4常见面试题 2.事务 2.1配置事务管理器 3.消息分发 3.1概念 3.2应用场景 3.2.1限流 3.2.2负载均衡 1.延迟队列 延迟队列(Delayed Queue)&#xff0c;即消息被发送以后, 并…...

Linux服务器组建与管理

#!/bin/bash #判断是否是root用户if [ "$USER" ! "root" ]; then echo "不是root用户&#xff0c;无法进行安装操作" exit 1 fi#关闭防火墙systemctl stop firewalld && systemctl disable firewalld && echo "防火墙已经关…...

程序化广告行业(48/89):DSP与外部平台对接的关键要点解析

程序化广告行业&#xff08;48/89&#xff09;&#xff1a;DSP与外部平台对接的关键要点解析 大家好&#xff01;在之前的博客中&#xff0c;我们逐步深入了解了程序化广告行业的诸多知识。一直以来&#xff0c;我都希望能和大家一起在这个领域探索&#xff0c;不断进步&#…...

设计模式 Day 2:工厂方法模式(Factory Method Pattern)详解

继 Day 1 学习了单例模式之后&#xff0c;今天我们继续深入对象创建型设计模式——工厂方法模式&#xff08;Factory Method&#xff09;。工厂方法模式为对象创建提供了更大的灵活性和扩展性&#xff0c;是实际开发中使用频率极高的一种设计模式。 一方面&#xff0c;我们将简…...

自动驾驶浪潮下,HMI 设计如何保障安全与便捷?

自动驾驶系统与 HMI 设计的关联性 自动驾驶系统涵盖了一系列复杂的传感器技术、算法以及执行机构。从激光雷达、摄像头等环境感知传感器&#xff0c;到用于处理海量数据的人工智能算法&#xff0c;再到控制车辆行驶的动力与转向执行系统&#xff0c;各部分协同工作&#xff0c…...

瑞昱RTD2556QR显示器驱动芯片

一、概述 RTD2556QR芯片是由Realtek公司精心研发的一款高性能显示驱动芯片&#xff0c;专为满足现代显示设备对高分辨率、多功能接口及稳定性能的需求而设计。该芯片凭借其卓越的技术特性和广泛的应用领域&#xff0c;在显示驱动市场中占据重要地位。它集成了多种先进的功能模…...

复合缩放EfficientNet原理详解

1. 为什么复合缩放更高效&#xff1f; &#xff08;1&#xff09;单维度缩放的瓶颈 增加深度&#xff08;层数&#xff09;&#xff1a; 更深的网络可以学习更复杂特征&#xff0c;但容易导致梯度消失/爆炸问题&#xff0c;且计算量随深度线性增长。 问题&#xff1a;深层网络…...

线程等待与唤醒的几种方法与注意事项

写在前面&#xff1a;无论是调用哪种等待和唤醒的方法&#xff0c;都必须是当前线程所持有的对象&#xff0c;否则会导致 java.lang.IllegalMonitorStateException 等并发安全问题。 以三个线程循环打印 XYZ 为例。 一、方法 1.1 Object 对象锁 可以通过 synchronized 对方…...

rustdesk 客户端使用

配置中继服务器 RustDesk 搭建-CSDN博客 配置客户端&#xff0c;服务端&#xff08;控制方&#xff0c;被控方&#xff09; 1.下载rustdesk.exe(windows为例) 2.完成后如下 3.配置...

react+antd封装一个可回车自定义option的select并且与某些内容相互禁用

需求背景 一个select框 现在要求可多选 并且原有一个any的选项 其他选项为输入后回车自己增加 若选择了any 则其他选项不可选择反之选择其他选项any不可选择 并且回车新增时也不可直接加入到选中数组只加入到option内 并且不可重复添加新内容 实现过程 <Form.Item …...

碳化硅 MOSFET三相逆变电路损耗新算法

基 于 碳 化 硅 MOSFET三相逆变电路损耗新算法 摘 要 提出了一种三相逆变电路功率开关器件损耗计算的新方法.为了达到将高频电力电子电路和实时仿真算 法 相 结 合 应 用 于 嵌 入 式 实 时 仿 真 平 台 的 目 的 &#xff0c;针 对 工 程 应 用 中 逆 变 器 损 耗 计 算 的 实…...

增加等IO状态的唤醒堆栈打印及缺页异常导致iowait分析

一、背景 在之前的博客 在计算进程D状态持续时间及等IO的时间遇到的一处问题-CSDN博客 里&#xff0c;我们修复了一处在抓取D状态及等IO状态堆栈的监控程序的一处时间计算bug&#xff0c;在这篇博客里&#xff0c;我们进一步丰富监控程序&#xff0c;在进程iodelay被唤醒时&am…...

nodejs:midi-writer-js 将基金净值数据转换为 midi 文件

开放式基金是没有公布每日交易量的。 /funds/data/660008.csv 文件开头&#xff1a; date,jz,ljjz 2016-01-04,1.1141,1.1141 2016-01-05,1.1161,1.1161 2016-01-06,1.1350,1.1350 这是一个将开放式基金数据转换为 MIDI音乐的 js 程序示例。该程序将基金净值映射为 MIDI音符的…...

新能源汽车空调系统(R134A)性能评估(一)

国内外主流空调系统厂家&#xff1a;贝尔、德尔福、空调国际、法雷奥、电装、松芝、杰信、新电、豫新等 泛亚汽车的空调电子部是比较优秀的整车空调研发团队。 空调系统综合试验台架是一套由试验室、风量测定装置、空气调和器、空气温度测定装置、湿度测定装置、加热器试验辅助…...

Oracle 数据库中优化 INSERT INTO 操作的性能

在 Oracle 数据库中优化 INSERT INTO 操作的性能&#xff0c;尤其是在处理大批量数据时&#xff0c;可以通过以下方法显著提升效率。 使用直接路径插入&#xff08;Direct-Path Insert&#xff09; 通过 APPEND 提示绕过缓冲区缓存&#xff0c;直接写入数据文件&#xff0c;减…...

Ubuntu 22.04安装MongoDB:GLM4模型对话数据收集与微调教程

在Ubuntu 22.04安装MongoDB Community Edition的教程请点击下方链接进行参考&#xff1a; 点击这里获取MongoDB Community Edition安装教程 今天将为大家带来如何微调GLM4模型并连接数据库进行对话的教程。快跟着小编一起试试吧~ 1. 大模型 ChatGLM4 微调步骤 1.1 从 github…...

Java 中的继承与多态:面向对象编程的核心特性

继承和多态是面向对象编程中最重要的两个概念&#xff0c;它们使代码结构更加清晰、灵活&#xff0c;并极大地提高了代码复用性。本文将深入探讨 Java 中的继承与多态&#xff0c;帮助你更好地理解这些核心概念。 1. 继承 1.1 为什么需要继承 在实际编程中&#xff0c;我们经…...

可编程增益放大器(PGA)在智能传感器自调节系统中的角色

在电子电路设计中&#xff0c;放大器芯片作为信号处理的核心器件&#xff0c;其性能直接影响系统整体表现。然而面对运算放大器、功率放大器、仪表放大器等众多类型&#xff0c;工程师常陷入选型困惑。作为国内领先的半导体解决方案提供商&#xff0c;华芯邦深耕放大器芯片领域…...

微信登录、商品浏览前瞻

一.业务效果 二.所需技术...

浙大研究团队揭示电场调控5-HT1AR的分子机制

本期介绍的文章题为 “Structural Insight into the Inactive/Active States of 5‑HT1AR and Molecular Mechanisms of Electric Fields in Modulating 5‑HT1AR” 。近期发表于JCIM。通过分子动力学模拟&#xff0c;探究 5-羟色胺 1A 受体(5-HT1AR) 在非活性 / 活性状态的构象…...

RoboOS与RoboBrain:引领具身智能新时代的跨本体协作框架

摘要 2025年3月29日&#xff0c;智源研究院在中关村论坛的“未来人工智能先锋论坛”上发布了两项重要成果&#xff1a;跨本体具身大小脑协作框架RoboOS与开源具身大脑RoboBrain。这一创新技术使机器人能够实现跨场景多任务部署及跨本体协作&#xff0c;推动单机智能向群体智能…...