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

进程信号简述

01. 信号产生

生活中的信号类比(交通信号灯、警报),当产生这些信号时,我们会立马想到对应的动作。在Linux中,信号是事件发生对进程的通知机制亦称软件中断,由操作系统内核、进程本身或者其他进程向目标进程异步事件发送机制(即收到某种信号,并不会立马去执行)。通知进程发生某种预定义的时间,要求进程作出相应响应。信号与硬件中断相似之处在于会打断程序执行流程。

1.1 认识常见信号

45% 20% 15% 20% 常见信号类型占比 终止进程 暂停进程 核心转储 其他功能
  • 硬件异常
    • 如内存越界(SIGSEGV
    • 除零错误(SIGFPE
    • 由内核自动生成
  • 系统调用:
    • kill():向指定进程发送信号
    • raise():进程向自身发送信号
    • alarm():设置定时器,超时后发送 SIGALRM
  • 终端输入:
    • Ctrl+C:SIGINT(终止进程)
    • Ctrl+Z:SIGTSTP(暂停进程)
    • Ctrl+\:
  • 软件条件:
    • 子进程退出时,父进程收到 SIGCHLD
    • 定时器到期(如 alarm())触发信号
  • 核心转储 :

1.2 信号的分类

信号分为两大类。(编号1-31)为传统信号信号,内核向进程通知且递送一次,(编号34-64)为实时信号使信号按序递送。


1.3 信号处理方式

  • 默认动作: 部分是终止自己,暂停等
  • 忽略动作: 是一种信号处理的方式,只不过动作就是什么都不干
  • 自定义动作: 使用signal方法修改信号的处理动作。(即用户程序员编写的函数:将默认动作转化为自定义动作)

1.4 改变信号处理方式

在这里插入图片描述


02. 信号阻塞

2.1 概念悉知

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.
  • 阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
信号被阻塞
信号未阻塞
解除阻塞后
信号产生
未决集
递达

信号产生后会稍后递达给某进程,信号在产生和到达期间会一直处于pending等待状态。


2.2 信号在内核中的表示

每个进程都有一个信号屏蔽字亦称阻塞信号集,一个 sigset_t 类型的位图,每一位对应一个信号(如 SIGINTSIGQUIT 等)。用来标记哪些信号当前被阻塞。被阻塞的信号若已产生,会进入未决状态,直到阻塞被解除才会触发处理。位为1生效,0不生效。


2.2.1 block与pending之间联系

pending集是信号产生后的暂存区,block是控制信号是否能从pending暂存区递交给进程的开关

  • block:决定哪些信号会被阻塞,近而将其放入未决信号集
  • pending:记录已产生但未被处理的信号(因被阻塞或正在处理其他信号),本质是这个信号被暂存task_struct信号为途中?

其中block位图用于表示进程是否阻塞。pending位图用于是否有信号写入(信号是否产生,信号产生时,内核在进程控制块中设置该信号的pending位图,直到信号抵达才消失)。handler函数指针数组用于进程执行何种动作(其中存放的是动作函数指针,每个信号的编号就是其数组下标)。

在这里插入图片描述


位=1
位=0
解除阻塞
执行期间
信号产生
blocked位图
pending位图
handler函数
临时sa_mask

2.3 关键系统调用

2.3.1 信号集操作函数

sigset_t 本质上是一个位图,每一位代表一个信号。当某一位被设置为 1 时,表示对应的信号被包含在信号集中;为 0 则表示不包含。使用者只能调用以下函数来操作sigset_t变量,不用关注内部数据。

在使用sigset_t类型的变量之前,一定要调用sigemptyset()函数初始化一个未包含任何成员的信号集或者sigfillset()函数则初始化一个信号集,使其包含所有信号(包括所有实时信号)。

#include <signal.h>
int sigemptyset(sigset_t *set);    // 清空信号集,置0
int sigfillset(sigset_t *set);     // 填充所有信号, 置有效状态1?

信号集初始化后,可以分别使用 sigaddset()sigdelset()函数向一个集合中添加或者移除单个信号。使用sigismember()测试信号是否是信号集set的成员。

#include <signal.h>
int sigaddset(sigset_t *set, int signo); // 添加单个信号
int sigdelset(sigset_t *set, int signo); // 删除单个信号
int sigismember(const sigset_t *set, int signo); // 判断信号是否存在

2.3.2 信号掩码(阻塞信号传递)

内核会为每个进程维护一个信号掩码,即一组信号,并将阻塞其针对该进程的传递。如果将遭阻塞的信号发送给某进程,那么对该信号的传递将延后,直至从进程信号掩码中移除该信号,从而解除阻塞为止。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数 :

  • how:指定了sigprocmask()函数想给信号掩码带来的变化。
    • SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字的信号(逻辑或)
    • SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号(逻辑与非)
    • SIG_SETMASK:设置当前信号屏蔽字为set所指向的值(直接赋值)
  • set
    • 要操作的信号集,(为NULL则忽略)
  • oldset
    • 保存旧的信号掩码(可为NULL)

返回值:成功返回 0,失败返回 -1(设置 errno

  • 阻塞:信号暂存到未决信号集直到解除阻塞
  • 忽略:直接丢弃

2.3.3 sigpending获取未决信号集

如果某进程接受了一个该进程正在阻塞的信号,那么会将该信号填加到进程的等待信号集中。当之后解除了对该信号的锁定时,会随之将信号传递给此进程。

#include <signal.h>
int sigpending(sigset_t *set);

作用: 返回处于等待状态的信号集,并将其置于 set指向的sigset_t 结构中。


2.3.4 sigaction() - 设置信号处理
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum : 目标信号

  • act 新的信号处理方式

    • sa_handler:信号处理函数指针(或 SIG_IGNSIG_DFL)。
    • sa_mask:处理该信号时额外阻塞的信号集。
    • sa_flags:控制选项(如 SA_RESTARTSA_NODEFER
    • NULL
  • oldact

    • 保存旧的处理配置(可为NULL)

返回值:成功返回 0,失败返回 -1(设置 errno

struct sigaction {void     (*sa_handler)(int);          // 简单处理函数void     (*sa_sigaction)(int, siginfo_t *, void *); // 高级处理函数sigset_t sa_mask;                     // 处理期间屏蔽的信号集int      sa_flags;                    // 控制标志void     (*sa_restorer)(void);        // 内部使用(已废弃)
};

现在我们使用sigprocmask函数和sigpending函数完成一个函数。思路是

  1. 对2号信号写入信号集
  2. 将该信号集使用sigprocmask函数将2号信号block住
  3. 我们键盘输入ctrl+c,产生2号信号,发送至该进程。
  4. 使用sigpending函数输出pending位图,
    因为我们将2号进程block住了,该信号并不能抵达。我们再向其发送2号信号,信号被block住了并不会影响信号的写入。因此我们再发送2号进程之前pending位图应该全为0,发送2号进程之后会看见pending位图发送由01的变化。

执行流程:

  1. 前 5 秒内,SIGINT(Ctrl+C)被阻塞,信号进入未决状态但不触发处理。
  2. 5 秒后解除阻塞,若之前有未决的 SIGINT,会立即触发 handle 函数。
#include <iostream>
#include <cassert>
#include <unistd.h>
#include <signal.h>
using namespace std;
static void handler(int signo){cout << signo << " 号信号确实递达了" << endl;//最终不退出进程
}void DisplayPending(const sigset_t pending){// 打印 pending 表int i = 1;while (i < 32){if (sigismember(&pending, i))  cout << "1";else cout << "0";i++;} cout << endl;
}
int main(){// 更改 2 号信号的执行动作signal(2, handler);// 创建信号集sigset_t set, oset;// 信号集清空  0sigemptyset(&set);sigemptyset(&oset);sigaddset(&set, 2);	//将2号信号写入set// 设置当前进程的屏蔽信号集sigprocmask(SIG_BLOCK, &set, &oset);//0给进程// 死循环int n = 0;while (true){if (n == 5){// 采用 SIG_SETMASK 的方式,覆盖进程的 block 表sigprocmask(SIG_SETMASK, &oset, NULL); // 不接收进程的 block 表}// 获取进程的 未决信号集sigset_t pending;sigemptyset(&pending);int ret = sigpending(&pending);assert(ret == 0);(void)ret; // 避免 release 模式中出错DisplayPending(pending);n++;sleep(1);}return 0;
}

03. 信号处理

loading…

相关文章:

  • 6.01打卡
  • DDD架构
  • 【RocketMQ 生产者和消费者】- 生产者发送同步、异步、单向消息源码分析(1)
  • 2025——》NumPy中的np.random.randn使用/在什么场景下适合使用np.random.randn?NumPy标准正态分布生成全解析
  • 平移坐标轴 +奇偶性 简化二重积分
  • ​​技术深度解析:《鸿蒙5.0+:AI驱动的全场景功耗革命》​
  • 微软常用运行库合集(VisualC++)2025.04.22
  • Json详解
  • MyBatis-Plus高级用法:最优化持久层开发
  • 6.1 数学复习笔记 23
  • 工作流引擎-09-XState 是一个 JavaScript 和 TypeScript 的状态管理库,它使用状态机和状态图来建模逻辑。
  • QT中子线程触发主线程弹窗并阻塞等待用户响应
  • Spring是如何实现属性占位符解析
  • 《汇编语言》第13章 int指令
  • 6个月Python学习计划 Day 11 - 列表推导式、内置函数进阶、模块封装实战
  • vscode 连接远程服务器
  • leetcode0404. 左叶子之和-easy
  • ROS仓库GPG签名密钥过期问题
  • DAY 36 超大力王爱学Python
  • 车辆检测算法在爆炸事故应急响应中的优化路径
  • 现在给别人做网站还赚钱吗/产品如何做网络推广
  • 微信公众号怎么创建要多少钱/宁波网站优化公司价格
  • 北京做网站设计招聘/网络营销有哪些特点
  • 新闻网站怎么做缓存/廊坊网站推广公司
  • 系统管理下载/百度seo推广免费
  • 做恋足的网站能赚钱吗/友链出售