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

[linux仓库]信号保存[进程信号·肆]

🌟 各位看官好,我是

🌍 Linux == Linux is not Unix !

🚀 今天来学习Linux的信号保存,明白进程是如何识别信号及相关函数。

👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享更多人哦!

信号保存

对信号产生有了一定的理解后,就可以从时间维度上讲解信号保存的话题

信号相关概念

  • 实际执⾏信号的处理动作称为信号递达(Delivery)
  • 信号从产⽣到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (Block )某个信号。(屏蔽某个信号 --> 既然能屏蔽就能解除)
  • 被阻塞的信号产⽣时将保持在未决状态,直到进程解除对此信号的阻塞,才执⾏递达的动作.
  • 注意 : 阻塞和忽略是不同的,只要信号被阻塞就不会递达,⽽忽略是在递达之后可选的⼀种处理动作。

  

信号是否被阻塞在内核中是用位图进行表示:pending位图表示是否收到信号,block位图表示信号是否被屏蔽.

  

进程如何识别信号

信号在内核中的表示示意图:

  • 每个信号都有两个标志位分别表⽰阻塞(block)和未决(pending),还有⼀个函数指针表⽰处理动作。信号产⽣时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例⼦中,SIGHUP信号未阻塞也未产⽣过,当它递达时执⾏默认处理动作。
  • SIGINT信号产⽣过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产⽣过,⼀旦产⽣SIGQUIT信号将被阻塞,它的处理动作是用户⾃定义函数sighandler。

  

这表明什么呢?信号是否收到信号,是否阻塞,用什么方法进行处理.都是围绕这张表进行的啊!

结论:围绕这信号,进程能识别信号,本质是三张表:block表pending表handler表 --> 由理论转实操: 都是对这三张表的操作.

struct task_struct {
.../* signal handlers */sigset_t blockedstruct sigpending pending;
...
}struct sigpending {struct list_head list;sigset_t signal;
}typedef struct {unsigned long sig[_NSIG_WORDS];
} sigset_t;struct sighand_struct {atomic_t count;struct k_sigaction action[_NSIG]; // #define _NSIG 64spinlock_t siglock;
};

sigset_t

从上图来看,每个信号只有⼀个bit的未决标志, ⾮0即1, 不记录该信号产生了多少次,阻塞标志也是这样表示的。因此, 未决和阻塞标志可以⽤相同的数据类型sigset_t来存储, , 这个类型可以表⽰每个信号的“有效”或“无效”状态, 在阻塞信号集中“有效”和“⽆效”的含义是该信号是否被阻塞, ⽽在未决信号集中“有 效”和“⽆效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的“屏蔽”。

    // 用户层面设置位图sigset_t block, oblock;

sigprocmask

int sigprocmask(int how, const sigset_t *_Nullable restrict set, sigset_t *_Nullable restrict oldset);

该函数主要用来检查和修改block位图

  • how:指示如何修改
  • set : 输入型参数 --> 若为非空指针,更该进程的信号屏蔽字
  • oldset : 输出型参数 --> 若为非空指针,返回老图,方便后悔操作

如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset⾥,然后 根据set和how参数更改信号屏蔽字。

  • 返回值:若成功则为0,若出错则为-1

假设当前信号屏蔽字为mask,下面为how参数可选值

  

如果调用sigprocmask解除了对当前若⼲个未决信号的阻塞,则在sigprocmask返回前,⾄少将其中⼀个信号递达。

因此,对这三张表的操作,可以通过sigprocmask对block位图进行修改,对pending可以通过进程对pending位图进行修改,sigpending函数可以修改pending位图(如下介绍),signal函数可以设置三种处理行为.

sigpending

  

信号集操作函数

sigset_t类型对于每种信号⽤⼀个bit表⽰“有效”或“⽆效”状态, ⾄于这个类型内部如何存储这些bit则依赖于系统实现, 从使⽤者的⻆度是不必关⼼的, 使⽤者只能调⽤以下函数来操作sigset_ t变量.

int sigemptyset(sigset_t *set);

int sigfillset(sigset_t *set);

int sigaddset(sigset_t *set, int signo);

int sigdelset(sigset_t *set, int signo);

int sigismember(const sigset_t *set, int signo);  

  • 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表⽰该信号集不包含任何有效信号。
  •  函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表⽰ 该信号集的有效信号包括系统⽀持的所有信号。
  • 注意,在使⽤sigset_ t类型的变量之前,⼀定要调 ⽤sigemptyset或sigfillset做初始化,使信号集处 于确定的 状态。初始化sigset_t变量之后就可以在调⽤sigaddset和sigdelset在该信号集中添加或删除某种有效信号。

这四个函数都是成功返回0,出错返回-1。sigismember是⼀个布尔函数,⽤于判断⼀个信号集的有效信号中是否包含 某种 信号,若包含则返回1,不包含则返回0,出错返回-1.

这里提出几层理解:

  • 问题1:屏蔽所有信号 ? 9号信号不可被捕捉,不可被屏蔽 -- done
  • 问题2:如果解除对2号的屏蔽,我也要看到pending位图 1 -> 0 -- done
  • 问题3:2号信号被递达,pending 位图由1置为0,是在递达前还是递达后置为0

为了验证问题2,这里写了一段程序:先对2号信号进行屏蔽,接着我通过kill指令向该进程发送2号信号,应该观察到pending位图由0置1,等待一段时间让该进程对2号信号解除屏蔽,此时信号会被递达,应该观察到pending位图的2号信号由1重新置为0,必要的时候可以加上自定义捕捉查看是否捕捉到2号信号.

为了验证问题3,可以这样处理,如果是递达后处理2号信号时,在自定义捕捉方法中应一直为1,如果为0则证明是递达前.

void PrintPending(sigset_t &pending)
{std::cout << "[pid: " << getpid() << "] " << "sigpending list: ";// 右->左, 低->高 , 0000 0000for (int signo = 31; signo > 0; signo--){if (sigismember(&pending, signo)){std::cout << "1";}else{std::cout << "0";}}std::cout << "\r\n";
}void handler(int signo)
{std::cout << "我获取到了: " << signo << " 信号" << std::endl;// 不要让进程终止// 在对2号信号捕捉的代码中,获取pending && 打印??sigset_t pending;sigemptyset(&pending);// 2.1 获取当前进程的pending信号集sigpending(&pending);// 2.2 不断打印所有的pending信号集中的信号std::cout << "###########################" << std::endl;PrintPending(pending);std::cout << "###########################" << std::endl;
}int main()
{// 0.设置2号信号的处理动作,不要让他终止signal(2, handler);// 1.屏蔽2号信号// 1.1 用户层面设置位图sigset_t block, oblock;sigemptyset(&block);  // 进行初始化sigemptyset(&oblock); // 是输出型,也可以不初始化sigaddset(&block, SIGINT); // 这里的时候,我们有没有设置当前进程的信号屏蔽字?没有!// 1.2将栈上位图设置到内核的信号屏蔽字-->block图sigprocmask(SIG_SETMASK, &block, &oblock);int cnt = 10;while (true){sigset_t pending;sigemptyset(&pending);// 2.1获取当前进程的pending信号集sigpending(&pending);// 2.2不断打印所有pending信号集中的信号PrintPending(pending);// 验证取消block,看信号是否递达cnt--;if (cnt == 0){// 解除对2号的屏蔽std::cout << "解除对2号的屏蔽啦!" << std::endl;// 取消blocksigprocmask(SIG_SETMASK, &oblock, nullptr);}sleep(1);}return 0;
}

总结

本文介绍了Linux系统中信号保存的相关概念和操作。信号从产生到递达之间存在未决状态,进程可通过阻塞位图屏蔽信号。内核使用三张表(block、pending、handler)管理信号状态。文章详细讲解了如何通过sigprocmask函数修改block位图,以及使用sigpending、signal等函数操作信号集。通过实验程序验证了信号屏蔽、解除和递达过程中pending位图的变化,说明信号处理的核心是对这三张表的操作。关键点包括:信号递达前后pending位图的变化、9号信号的特殊性,以及如何通过编程验证信号处理流程。

http://www.dtcms.com/a/491496.html

相关文章:

  • AI产品经理学习笔记3 - Agent认知与原理分析
  • 建站代理赚钱吗做企业网站需要提供什么
  • 网站开发先写什么后写什么wordpress屏蔽功能org
  • RFSOC47DR+CPU 6U VPX射频信号处理板
  • 厦门网站建设缑阳建wordpress公告模板
  • debug - MDK - arm-none-eabi - 同样的工具链,源码,编译参数,elf是一样的
  • 前端-配置Prettier与ESLint9
  • 中企动力网站建设公司百度指数下载手机版
  • 蚂蚁 S21 XP+ HYD 500T矿机评测:SHA-256算法与高效液冷系统
  • 蚂蚁 S19 XP Hyd 3U 512T矿机评测:高效水冷设计,适合BTC/BCH挖矿
  • 【Win32 多线程程序设计基础第四章笔记】
  • 2024.6卷一阅读短语
  • 企业营销推广型网站建设怎么创造软件app
  • Rust 的错误处理:别拿类型系统当护身符
  • 用栈实现记忆存储——C++语言自制时间计算器
  • 实验二 呼吸灯功能实验
  • 动力 网站建设珠海专业网站建设费用
  • 博客系统测试
  • 高德地图电子围栏/地图选区/地图打点
  • 自己动手建设网站过程dede珠宝商城网站源码
  • Git的分支
  • 基础拓展
  • 手机微网站建设河南网站建设的详细策划
  • 剧本杀小程序系统开发:内容生态与商业模式的双轮驱动
  • 网站备案表不会写引流网站怎么做
  • 【系统分析师】写作框架:数据灾务技术与应用
  • 香港云服务器域名无法访问的原因
  • 荆门网站建设服务上海网站制作网络推广方法
  • 系统那个网站好什么是网站平台开发
  • 网站管理后台地址深圳做网站排名