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

server/2024/10/21 7:47:10/
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/server/128281.html

相关文章

数据库管理-第245期 主流国产数据库RAC架构概览(20240929)

数据库管理245期 2024-09-29 数据库管理-第245期 主流国产数据库RAC架构概览&#xff08;20240929&#xff09;1 DMDSC2 KingBaseES RAC3 PolarDB4 Cantian5 HaloDB DLB/Data Sharding总结 数据库管理-第245期 主流国产数据库RAC架构概览&#xff08;20240929&#xff09; 作者…

18732 最短路问题

### 思路 1. **建模问题**&#xff1a;将车站和公交线路建模为图&#xff0c;其中车站是节点&#xff0c;公交线路是带权边。 2. **选择算法**&#xff1a;使用Dijkstra算法求解从车站1到车站n的最短路径问题。 3. **初始化**&#xff1a;创建一个优先队列&#xff08;最小堆&…

FinalShell解决Docker日志中文乱码问题

在DockerFile文件末尾添加如下配置即可解决&#xff1a; #解决Docker容器中文显示乱码问题 ENV LANG C.UTF-8 ENV LC_ALL C.UTF-8

php常用的注释符号

如果没有安装vscode和小皮&#xff0c;请点击下方链接安装&#xff1a; Vscode、小皮面板安装-CSDN博客 在学习php过程中&#xff0c;肯定少不了注释&#xff0c;也可以理解为备注的信息&#xff0c;来提醒自己这段代码有什么用&#xff0c;是什么意思等&#xff0c;接下来就介…

平台数据分类与聚类实验报告

参考书籍&#xff1a;《数据流挖掘与在线学习算法》 李志杰 1.6.1 实验目的 本书内容以及课程实验主要涉及Java程序设计语言、数据挖掘工具Weka和数据流机器学习平台MOA&#xff0c;因此&#xff0c;需要安装、配置并熟悉实验环境。Java、Weka和MOA都是开源小软件&#xff0…

制作离线版Koczkatamas工具包

一、下载源码 从https://github.com/koczkatamas/koczkatamas.github.io下载koczkatamas.github.io-master.zip 二、解压 $ unzip koczkatamas.github.io-master.zip三、运行index.html 可以看到输入一个字符后&#xff0c;下面的各种编码都没有显示&#xff0c;则表示运行…

基于51单片机的多路电压测量proteus仿真

地址&#xff1a;https://pan.baidu.com/s/1cpgtfl571DcKfjhKvcKqSA 提取码&#xff1a;1234 仿真图&#xff1a; 芯片/模块的特点&#xff1a; AT89C52/AT89C51简介&#xff1a; AT89C52/AT89C51是一款经典的8位单片机&#xff0c;是意法半导体&#xff08;STMicroelectron…

生信机器学习入门4 - 构建决策树(Decision Tree)和随机森林(Random Forest)分类器

机器学习文章回顾 生信机器学习入门1 - 数据预处理与线性回归&#xff08;Linear regression&#xff09;预测 生信机器学习入门2 - 机器学习基本概念 生信机器学习入门3 - Scikit-Learn训练机器学习分类感知器 生信机器学习入门4 - scikit-learn训练逻辑回归&#xff08;L…