信号处理: Block Pending Handler 与 SIGKILL/SIGSTOP 实验

devtools/2024/10/18 14:14:55/
1. 信号处理机制的 “三张表”

kill -l :前 31 个信号为系统标准信号。

block pending handler 三张表保存在每个进程的进程控制块 —— pcb 中,它们分别对应了某一信号的阻塞状态、待处理状态以及处理方式。

  • block :通过 sigset_t 类型实现,是一个位字段集合,每一位对应一个信号;如果某一位被设置,则对应的信号会被阻塞;信号的阻塞状态 与 该信号是否被发送到进程 无关

  • pending :与 block 相同,通过 sigset_t 类型实现,用于记录 已经发送给进程但未被处理 的信号。

  • handler :函数指针数组,指向各个信号的处理方法(函数)。

2. 介绍几个信号处理的系统调用
  • sigemptyset :用于初始化一个信号集为空,即将信号集中的所有信号位都清零。

    创建一个 sigset_t 类型的变量,在对该变量进行各种信号处理操作之前,先要使用 sigemptyset 对该变量初始化。

	sigset_t sig;sigemptyset(&sig); // 对 sig 初始化, success -> 0, fail -> -1
  • sigaddset :用于在一个已存在的信号集中添加信号。
	sigaddset(&sig, SIGINT); // success return 0, fail return -1
  • sigprocmask :用于更改当前进程的信号掩码,简单来说即设置哪些信号被阻塞。
 sigprocmask(int how, const sigset_t* signal, sigset_t* old_signal);

how 的处理方式, SIG_BLOCK SIG_UNBLOCK SIG_SETMASK

SIG_BLOCK : 设置新的信号掩码,同时保留旧的信号掩码

SIG_UNBLOCK : 取笑新信号掩码中的信号阻塞,并保留旧的信号掩码

SIG_SETMASK : 设置新的信号掩码,并丢弃旧的信号掩码

	sigset_t oldsig;sigemptyset(&oldsig);sigprocmask(SIG_SETMASK, &sig, &oldsig);
  • sigpending :用于获取当前进程中待处理的信号集。
	sigset_t pending;sigemptyset(&pending);sigpending(&pending);
  • sigismember :用于测试一个信号是否在一个信号集中,存在返回 1,否则返回 0。
	sigismember(&pending, SIGINT);
3. demo —— 示范案例

demo 中主要完成以下四个动作:

  1. 屏蔽 2 号信号 —— SIGINT
  2. 获取当前进程的 pending
  3. 打印 pending 表
  4. 取消对 2 号信号的屏蔽

其中,2.3. 是 “同时” 且 重复 进行的,意味着将出现这样一个现象:当前进程在接收 2 号信号之前,pending 表 2 号信号对应位置为 0;向当前进程发生 2 号信号后,pending 表 2 号信号位置被设置。

以及,取消对 SIGINT 的屏蔽后,2 号信号会被立即递达,进程结束。

1) 屏蔽 2 号信号
#include <iostream>
using namespace std;
#include <signal.h>int main()
{// 1. 屏蔽 2 号信号sigset_t block;sigset_t old_block;sigemptyset(&block);sigemptyset(&old_block);sigaddset(&block, SIGINT);sigprocmask(SIG_SETMASK, &block, &old_block);cout << "process pid: " << getpid() << endl;cout << "block SIGINT success ..." << endl;return 0;
}
2) 获取当前进程的 pending 表 | 3) 打印 pending 表
#include <unistd.h>void PrintSig(sigset_t pending)
{cout << "process pending: ";for (int i = 31; i >= 1; i--){if (sigismember(&pending, i))cout << "1";else cout << "0";}cout << endl;
}int main()
{// ...while (1){// 2. 获取当前 pending 表sigset_t pending;sigemptyset(&pending);sigpending(&pending);// 3. 打印 pending 表PrintSig(pending);sleep(1);}}

将这部分代码先运行起来,并对该进程发送一个 SIGINT 信号,观察现象:
在这里插入图片描述

当前进程(3988092)pending 表的 2 号信号对应位置被设置,但进程没有退出!

4) 取消对 2 号信号的屏蔽
int main()
{while (1) {// ...   // 4. 取消对 2 号信号对屏蔽++cnt;if (cnt == 20){sigprocmask(SIG_UNBLOCK, &block, &old_block);}}
}

为了使 2 号信号恢复且被递达后的观察效果更明显,我们对 PrintSig 函数 做一下优化,再 对 2 号信号的处理程序自定义

介绍一个新的系统调用接口:signal

signal() 允许我们为特定信号指定一个处理函数 —— handler() 是一个参数类型为 int 返回值为 的函数, 当信号到达时,该函数将被调用。

#include <iostream>
using namespace std;
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>void PrintSig(sigset_t pending, int count)
{cout << "process pending: ";for (int i = 31; i >= 1; i--){if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << " , cnt: " << count << endl;
}void handler(int signal)
{cout << "2 号信号被递达" << endl;exit(1);
}int main()
{signal(SIGINT, handler); // 对 SIGINT 的处理方式重定义// 1. 屏蔽 2 号信号sigset_t block;sigset_t old_block;sigemptyset(&block);sigemptyset(&old_block);sigaddset(&block, SIGINT);sigprocmask(SIG_SETMASK, &block, &old_block);cout << "process pid: " << getpid() << endl;cout << "block SIGINT success ..." << endl;int cnt = 0;while (1){// 2. 获取当前 pending 表sigset_t pending;sigemptyset(&pending);sigpending(&pending);// 3. 打印 pending 表 PrintSig(pending, cnt);sleep(1);// 4. 取消对 2 号信号对屏蔽++cnt;if (cnt == 20){sigprocmask(SIG_UNBLOCK, &block, &old_block);}}return 0;
}

在这里插入图片描述

进程运行 20 秒后,SIGINT 的屏蔽被取消,SIGINT 被递达,进程终止!

4. 对 SIGKILL 和 SIGSTOP 的验证

pid 为 “当前进程” 的 pid

while :; do for i in {1..8}; do kill -$i pid; done; sleep 1; done    # 对进程发送 1~8 号信号
while :; do for i in {10..18}; do kill -$i pid; done; sleep 1; done  # 对进程发送 10~18 号信号
while :; do for i in {20..31}; do kill -$i pid; done; sleep 1; done  # 对进程发送 20~31 号信号

对原代码做出修改:使用 for 循环,让进程阻塞 1~31 号信号

随后,在另一窗口使用 bash 脚本,分别向当前进程发送 1~8 、 10~18 、 20~31 号信号,观察进程的 pending 表;

在过程中,对当前进程发送 9 号信号 和 19 号信号,观察现象。

#include <iostream>
using namespace std;
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>void PrintSig(sigset_t pending)
{cout << "process pending: ";for (int i = 31; i >= 1; i--){if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void handler(int signal)
{cout << "2 号信号被递达" << endl;exit(1);
}int main()
{// 1. 屏蔽 1~31 号信号sigset_t block;sigset_t old_block;sigemptyset(&block);sigemptyset(&old_block);for (int i = 1; i <= 31; i++){sigaddset(&block, i);}sigprocmask(SIG_SETMASK, &block, &old_block);cout << "process pid: " << getpid() << endl;cout << "block signal success ..." << endl;int cnt = 0;while (1){// 2. 获取当前 pending 表sigset_t pending;sigemptyset(&pending);sigpending(&pending);// 3. 打印 pending 表PrintSig(pending);sleep(2);}return 0;
}
  • 对当前进程发送 1~8 号信号 和 9 号信号

在这里插入图片描述

对当前进程发送 1~8 号信号,进程没有结束。

对当前进程发送 9 号信号,进程终止!

  • 对当前进程发送 10~18 号信号 和 19 号信号

在这里插入图片描述

对当前进程发送 10~18 号信号,进程没有结束。

对当前进程发送 19 号信号,进程终止!

  • 对当前进程发送 20~31 号信号

在这里插入图片描述

对当前进程发送 20~31 号信号,进程没有结束。

总结:9 号信号 — SIGKILL 及 19 号信号 — SIGSTOP 是特殊信号,不能被阻塞,因为它们具有重要作用!

http://www.ppmy.cn/devtools/121944.html

相关文章

【CCPC】The 2024 Shanghai Collegiate Programming Contest F

羁绊大师 #动态规划 #图论 #并查集 #背包 题目描述 在某一自走棋游戏中&#xff0c;胖胖龙此刻拥有 n n n 个英雄&#xff0c;其中第 i i i 个英雄拥有两种羁绊&#xff0c;分别为 a i , b i ( a i < b i ) a_i, b_i(a_i < b_i) ai​,bi​(ai​<bi​) 不存在两个…

使用rust写一个Web服务器——多线程版本

文章目录 模拟慢请求多线程Web服务器实现为每个请求单独生成一个线程限制创建线程的数量ThreadPool的初始化ThreadPool的存储ThreadPool的设计 关闭和资源清理为ThreadPool实现Drop停止工作线程测试 仓库地址&#xff1a; 1037827920/web-server: 使用rust编写的简单web服务器 …

拓扑排序简介

拓扑排序(Topological Sort)是一种重要的图算法,用于对有向无环图(DAG, Directed Acyclic Graph)中的节点进行排序。拓扑排序的结果是一种线性序列,使得对于图中的任意一条有向边(u, v),顶点u都在顶点v之前。这种排序常用于任务调度、编译器依赖关系分析等领域。 拓…

程序计数器(学习笔记)

程序计数器是一块较小的内存空间&#xff0c;它的作用可以看做是当前线程所执行的字节码的信号指示器&#xff08;偏移地址&#xff09;&#xff0c;Java编译过程中产生的字节码有点类似编译原理的指令&#xff0c;程序计数器的内存空间存储的是当前执行的字节码的偏移地址 因为…

linux网络编程实战

前言 之前找工作的之后写了一些网络编程的笔记和代码&#xff0c;然后现在放到csdn上保存一下。有几个版本的&#xff0c;看看就好。就是简单的实现一下服务端和客户端之间的交互的&#xff0c;还没有我之前上linux编程课写的代码复杂。 哦对了&#xff0c;这个网络编程的代码对…

Debezium日常分享系列之:Debezium 3.0.0.Final发布

Debezium日常分享系列之&#xff1a;Debezium 3.0.0.Final发布 Debezium 核心的变化需要 Java 17基于Kafka 3.8 构建废弃的增量信号字段的删除每个表的详细指标 MariaDB连接器的更改版本 11.4.3 支持 MongoDB连接器的更改MongoDB sink connector MySQL连接器的改变MySQL 9MySQL…

软考系统分析师知识点二:经济管理

前言 今年报考了11月份的软考高级&#xff1a;系统分析师。 考试时间为&#xff1a;11月9日。 倒计时&#xff1a;35天。 目标&#xff1a;优先应试&#xff0c;其次学习&#xff0c;再次实践。 复习计划第一阶段&#xff1a;扫平基础知识点&#xff0c;仅抽取有用信息&am…

企业必备:搭建大模型应用平台实操教程

最近AI智能体很火&#xff0c;AI智能体平台化产品肯定属于大公司的。但在一些场景下&#xff0c;尤其是对业务数据要求很高的公司&#xff0c;那就只能用私有大模型。不一定完全是为了对外提供服务&#xff0c;对内改造工作流也是需要的。所以 我感觉未来大部分企业都会搞一个…