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

Linux信号——信号的保存(2)

关于core和term两种终止方式

core是什么?

将进程在内存中的核心数据(与调试有关)转存到磁盘中形成core,core.pid的文件。
core dump:核心转储。
core与term的区别: term只是普通的终止,而core终止方式还要形成与调试相关的文件。

为什么要有core?

一个进程异常退出了,我们想知道为什么退出,通过core 可以定位到进程为什么退出,以及执行到哪行代码退出的。总的来说协助我们进行调试

core的使用

如何打开Linux中的core功能?

开启Linux的 core dump 功能

ulimit [选项] [限制值]

ulimit -a 显示当前 Shell 的所有资源限制设置。
ulimit -c 10240 打开,控制core文件生成大小不超过10240kb。
ulimit -c 0 关闭,控制core文件生成大小不超过0kb,即无法生成core文件。
例:用 ulimit -a 进行查看资源限制。
在这里插入图片描述
上图 core file size 是0说明关闭了core dump
ulimit -c 10240进行开启。
在这里插入图片描述
关于core文件协助我们进行调试
在运行程序时出现异常,产生core文件后。
gdb 文件名(进入调试模式)
core-file core 直接定位到出错代码

云服务器默认将进程core退出,进行了特定的设定,默认core是被关闭的。
为什么默认关闭核心转储功能?防止未知的core dump一直进行,产生core文件,导致服务器磁盘被打满。

信号的保存

实际执行信号的处理动作称之为信号的递达。有三种方式1.默认 2.忽略 3.自定义
信号从产生到递达之间的状态,称为信号的未决(pending)。
进程可以选择阻塞(block)某个信号。
在这里插入图片描述
如果一个信号被阻塞(屏蔽),则该信号永远不会被递达处理,除非解除阻塞。
阻塞vs忽略 区别:忽略是一种信号的递达方式,阻塞是不让指定信号递达(忽略是已读不回,阻塞是根本收不到)。

操作系统发送信号,实际上就是操作系统在目标进程中写入信号,如何写入?就是在两个位图和一个函数指针数组中写入!如下图。
在这里插入图片描述
进程的PCB结构体中有两个uint32_t的变量——block和pending,它们有32个bit位,修改相应的bit位就能记录该信号是否传入,handler是一个函数指针数组,里面存储的是信号执行的方法

关于三张位图匹配的操作

信号集数据类型——sigset_t

sigset_t 是 POSIX 标准中定义的一个数据类型(类似于int double float),用于表示信号集(signal set)。它本质上是一个位图结构,其中每一位对应一个特定的信号。

//定义数据类型
//仅是定义到栈上
sigset_t s;//没有将数据设置进内核中!

信号集基本操作函数

int sigemptyset(sigset_t *set);    // 初始化空信号集(不包含任何信号)
int sigfillset(sigset_t *set);     // 初始化包含所有信号的信号集
int sigaddset(sigset_t *set, int signum);    // 添加信号到信号集
int sigdelset(sigset_t *set, int signum);    // 从信号集中删除信号
int sigismember(const sigset_t *set, int signum);  // 检查信号是否在信号集中

注意:sigset_t 是不透明类型,必须使用标准函数操作,不能直接访问其内部
例子:

//仅仅是定义到栈上,没有将数据结果设置进内核中
sigset_t s;
sigemptyset(&s);//初始化空信号集(不包含任何信号)
sidaddset(&s,2;// 添加2号信号到信号集

sigprocmask——操作block表

调用函数sigpromask可以读取或更改信号屏蔽字(阻塞信号集),简而言之,该函数可以检查和修改block位图

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

参数说明
how: 指定如何修改信号掩码,可取以下值:

关键字作用
SIG_BLOCKset 中的信号添加到当前阻塞信号集中(阻塞这些信号)相当于mask = mask| set
SIG_UNBLOCK从当前阻塞信号集中移除 set 中的信号(解除阻塞这些信号)相当于mask = mask &~ set
SIG_SETMASK直接将当前阻塞信号集设置为 set(完全替换)相当于mask = set

set: 指向信号集的指针,包含要修改的信号。如果为 NULL,则 how 参数被忽略,函数仅获取当前信号掩码到 oldset。

oldset: 输出型参数,它的作用是用来保存调用前的信号屏蔽字。(oldset保存的是set设置前的那一次信号屏蔽字。)

返回值
成功时返回 0,失败时返回 -1 并设置 errno。

例子:

#include <signal.h>
#include <stdio.h>
#include <unistd.h>int main() {sigset_t new_set, old_set;// 初始化一个空的信号集sigemptyset(&new_set);// 添加 SIGINT 到信号集sigaddset(&new_set, SIGINT);// 阻塞 SIGINT 信号if (sigprocmask(SIG_BLOCK, &new_set, &old_set) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is now blocked. Try Ctrl+C...\n");sleep(5);  // 在这5秒内Ctrl+C不会终止程序// 恢复原来的信号掩码if (sigprocmask(SIG_SETMASK, &old_set, NULL) == -1) {perror("sigprocmask");return 1;}printf("SIGINT is unblocked. Try Ctrl+C again...\n");sleep(5);return 0;
}

sigpending——操作pending表

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

参数说明
set输出型参数,指向 sigset_t 类型的指针,用于接收pending 信号集。
返回值
成功:返回 0。
失败:返回 -1,并设置 errno 。

signal——操作handler表

#include <signal.h>void (*signal(int signum, void (*handler)(int)))(int);

参数说明
signum: 要处理的信号编号(如 SIGINT、SIGTERM 等)

handler: 信号处理函数,可以是:

关键字作用
SIG_IGN忽略该信号
SIG_DFL恢复默认行为
自定义函数用户定义的信号处理函数

总结:
在这里插入图片描述

一个场景

  1. 屏蔽2号信号。
  2. 获取进程的pending位图,打印所有的pending位图,循环往复。
  3. 未来我们给目标发送2号信号——2号信号不会被递达,一直在pending位图中。
  4. 获取进程的pending位图,打印所有的pending位图,循环往复。
  5. 解除屏蔽2号信号——2号信号最终被递达。
  6. 获取进程的pending位图,打印所有的pending位图,循环往复。

例子:屏蔽二号信号

#include<iostream>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>void PrintSig(sigset_t &pending)
{std::cout << "Pending bitmap:";for(int signo = 31;signo>0;signo--){if(sigismember(&pending,signo)){std::cout << "1";}else{std::cout << "0";}}std::cout << std::endl;
}
int main()
{//1.屏蔽2号信号sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);    sigaddset(&block, 2);//目前所有的修改,根本没有设置进当前进程的PCB中的block位图中//1.1开始屏蔽2号信号,其实就是设置进内核中int n = sigprocmask(SIG_SETMASK, &block, &oblock);//oblock保存的是block设置前的那一次信号屏蔽字。while(true){//2.获取进程的pending位图sigset_t pending;sigemptyset(&pending);n = sigpending(&pending);//3.打印pending位图中收到的信号PrintSig(pending);sleep(1);}std::cout << "block 2 signal success"<< std::endl;return 0;
}

结果:按下ctrl+C后,信号写入pending中,但是block位图中2号信号设置屏蔽,导致程序不终止。
在这里插入图片描述

如果能将所有信号屏蔽呢?是否进程将不会被杀死?
结论9,19号信号不允许被屏蔽,18号信号会被特殊处理。

例子:解除屏蔽2号信号

#include<iostream>
#include<signal.h>
#include<unistd.h>
#include<sys/wait.h>
void handler(int signo)
{std::cout << signo << "信号被递达处理..." << std::endl;
}
void PrintSig(sigset_t &pending)
{std::cout << "Pending bitmap:";for(int signo = 31;signo>0;signo--){if(sigismember(&pending,signo)){std::cout << "1";}else{std::cout << "0";}}std::cout << std::endl;
}
int main()
{//对2号信号进行自定义捕捉——不让进程因为2号信号而终止signal(2,handler);//1.屏蔽2号信号sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);    sigaddset(&block, 2);//目前所有的修改,根本没有设置进当前进程的PCB中的block位图中//1.1开始屏蔽2号信号,其实就是设置进内核中int n = sigprocmask(SIG_SETMASK, &block, &oblock);//oblock保存的是block设置前的那一次信号屏蔽字。int cnt = 0;while(true){//2.获取进程的pending位图sigset_t pending;sigemptyset(&pending);n = sigpending(&pending);//3.打印pending位图中收到的信号PrintSig(pending);if(sigismember(&pending,2)){cnt++; }//4.解除对2号信号的屏蔽if(cnt == 5){std::cout<<"解除对2号信号的屏蔽"<<std::endl;n = sigprocmask(SIG_UNBLOCK,&block,&oblock);//2号信号会被递达,默认处理是终止进程cnt++;}sleep(1);}std::cout << "block 2 signal success"<< std::endl;return 0;
}

结果:在一段时间后Ctrl+c,发送二号信号,首先存储进pending位图中,5秒钟后解除对2号信号的屏蔽,这时2号信号被递达处理,之后pending位图中2号信号位被清空。
在这里插入图片描述
细节

  1. 递达信号的时候,就一定会把对应的pending位图清0.
  2. 先清0,再递达,还是先递达,再清0?
    答案是先清0,再递达。

验证:

void handler(int signo)
{sigset_t pending;sigemptyset(&pending);int n = sigpending(&pending);//正在处理2号信号std::cout << "递达中...";PrintSig(pending);// 是0:递达之前,pending 2号已经被清0. 是1:pending 2号被清0一定是递达后 std::cout << signo << "信号被递达处理..." << std::endl;
}

结果:
在这里插入图片描述

总结:
在这里插入图片描述


http://www.mrgr.cn/news/97582.html

相关文章:

  • Linux信号——信号的处理(3)
  • QT6(12)3.3.1 Qt元对象系统概述:QObject 类与 QMetaObject 类,类型转换 qobject_cast<T>()。3.3.3 属性系统:帮助文档,
  • 【题解-Acwing】798. 差分矩阵
  • 【第十三届“泰迪杯”数据挖掘挑战赛】【2025泰迪杯】【代码篇】A题解题全流程(持续更新)
  • vue3 处理文字 根据文字单独添加class
  • linux第三次作业
  • JVM核心机制:类加载×字节码引擎×垃圾回收机制
  • 使用Docker安装及使用最新版本的Jenkins
  • el-table,新增、复制数据后,之前的勾选状态丢失
  • STM32江科大----IIC
  • 高安全等级车规芯片在星载控制终端上的应用
  • Nodejs回调函数
  • python应用之使用pdfplumber 解析pdf文件内容
  • 使用stm32cubeide stm32f407 lan8720a freertos lwip 实现udp client网络数据转串口数据过程详解
  • JavaScript基础--22-call、apply 和 bind
  • #MongoDB 快速上手
  • springcloud进阶
  • Python星球日记 - 第10天:模块与包
  • php调用大模型应用接口实现流式输出以及数据过滤
  • 原子操作(cpp atomic)