【进程信号】信号阻塞的原理

简介: 【进程信号】信号阻塞的原理

常见信号术语

  • 信号递达(Delivery):实际执行信号的处理动作
  • 信号未决(Pending):信号从产生到递达的中间状态
  • 信号阻塞(Block):进程可以选择阻塞某一个信号,被阻塞的的信号保持在未决状态,直到接触阻塞才会被递达。
  • 信号忽略:信号在递达之后的一种不作为。与阻塞不同,被阻塞的信号没有被递达。
  • 捕获信号:进程通过自定义信号处理程序来处理信号,而不是使用系统的默认动作

信号在内核中的表示

对于信号的学习,我们将其分为三个部分:产生信号、接收信号、处理信号。现在我们已经知道了信号是如何产生的,那我们的进程又是如何接收信号的呢?换句话说,一个进程是如何知道操作系统给它发信号了呢?

Pending表

其实在进程的PCB里面有一个信号位图(Signal Bitmap),用于表示有哪些发送给该进程但是还没有被处理的信号。每一个位都表示一种信号,如果有信号发送给该进程,那么这个位图对应的信号位就会置为1。假如给进程发送一个3号信号,那么这个位图的第3位就会置为1。因为信号的发送与进程实际处理之间是异步的,所以发送信号之后进程可能不会立即处理这个信号。

这个信号位图也被称为Pending(未决)表

有了Pending表,进程就能知道接收了哪些信号并准备处理信号,而一个信号是否要被处理,则需要观察该信号是否被阻塞。此时需要查看进程的Block表的内容。(处理信号相当于和响应信号)

Block表

同样的,PCB里面还有一个位图表示被进程阻塞的信号集。其每一个比特位都表示一个信号编号,如果是1则表示该对应信号被阻塞,即使信号到达,进程也不会处理。直到信号从该Block表中移除。Block表起到的作用实际上就是一个信号掩码(sig mask)。

sigset_t

观察上面图片,信号的未决表和阻塞表都是位图结构,且每一个比特位的位置都是表示信号的编号。于是我们可以统一用一个数据类型sigset_t(位图)来储存pending表和Block表。这个类型可以表示每个信号的有效或者无效状态。在阻塞信号集中,有效表示阻塞,在pending信号集中,有效表示未决。

我们可以通过signal函数来自定义一个信号的处理动作,实际上就是修改handler数组中元素内容·

信号集操作函数

操作系统提供了一些函数来管理信号集(sigset_t,包括Block和pending表),以下是常见的信号集操作函数(都包含在signal.h头文件中):

sigemptyset

功能:初始化一个空的信号集,即将信号集中所有信号清楚,也就是让信号集中的每一个比特位清0。

原型:

int sigemptyset(sigset_t *set);
  • set指向的信号集清空
  • 成功返回0,否则返回-1.

sigfillset

功能:初始化一个满的信号集,跟sigemptyset函数相反,让信号集中每一个比特位都置为1。

原型:

int sigfillset(sigset_t *set);
  • set指向的信号集填满
  • 成功返回0,否则-1.

sigaddset

功能:从信号集中添加一个指定的信号。

原型:

int sigaddset(sigset_t *set, int signum);
  • set指向的信号集中添加一个signum信号
  • 成功放回0,否则-1.

sigdelset

从信号集中删除一个指定的信号

原型:

int sigdelset(sigset_t* set,int signum);
  • set指向的信号集中删除一个signum信号
  • 成功返回0,失败则返回-1.

sigismember

功能:检查一个指定的信号是否在信号集中

原型:

int sigismember(const sigset_t *set,int signum);
  • 如果set指向的信号集中有signum信号就返回1,不在返回0,查看失败返回-1。

sigprocmask

功能:检查或者修改信号屏蔽字(阻塞信号集)。用来阻塞或者解阻信号。

跟上面的sigaddset和sigdelset函数不一样的是,sigprocmask可以修改进程的Block表

原型:

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • how:指定操作类型。以下是常见的操作类型:
  • SIG_BLOCK:将set指向的信号集中的信号添加到阻塞信号集中
  • SIG_UNBLOCK:从阻塞信号集中移除set指向的信号集中的信号
  • SIG_SETMASK:将set指向的信号集设置为新的阻塞信号集
  • set指向一个sigset_t类型的对象,用于修改阻塞信号集。如果该参数为NULL,则表示不修改阻塞信号集。
  • oldset:指向一个sigset_t类型的对象,用于存储被set修改之前的阻塞信号集
  • 成功返回0,失败返回-1.

sigpending

功能:获取当前进程的未决信号集(pending表)

原型:

int sigpending(sigset_t *set);
  • set指向的信号集重置为当前进程的未决信号集
  • 成功返回0,否则-1.

程序测试

为了更好的理解信号未决和信号阻塞。现在给出一个代码测试,观察其运行结果:

#include <iostream>
#include <signal.h>

using namespace std;

void PrintSet(sigset_t *set)
{
    for (int i = 31; i >= 1; i--)
    {
        if (sigismember(set, i))
        {
            putchar('1');
        }
        else
        {
            putchar('0');
        }
    }
    puts("");
}

int main()
{
    sigset_t s, p;
    sigemptyset(&s);//初始化信号集
    sigaddset(&s, SIGINT);//添加2号
    sigprocmask(SIG_BLOCK, &s, NULL);
    while (1)
    {
        sigpending(&s);
        PrintSet(&s);
        sleep(1);
    }

    return 0;
}

代码中阻塞了信号2(SIGINT),也就是我们ctrl+c发出的信号。在接收到ctrl+c信号时,观察其Pending表的内容。

我们看到,按下ctrl+c后程序没有被终止,说明这个信号确实被阻塞了,然后未决表显示该信号一直出于未决状态,没有被递达。

相关文章
|
3月前
|
算法 调度 UED
操作系统中的进程管理:原理与实践
在数字世界的心脏跳动着无数进程,它们如同细胞一般构成了操作系统的生命体。本文将深入探讨进程管理的奥秘,从进程的诞生到成长,再到最终的消亡,揭示操作系统如何协调这些看似杂乱无章却又井然有序的活动。通过浅显易懂的语言和直观的比喻,我们将一起探索进程调度的策略、同步机制的重要性以及死锁问题的解决之道。准备好跟随我们的脚步,一起走进操作系统的微观世界,解锁进程管理的秘密吧!
73 6
|
3月前
|
Linux C语言
C语言 多进程编程(四)定时器信号和子进程退出信号
本文详细介绍了Linux系统中的定时器信号及其相关函数。首先,文章解释了`SIGALRM`信号的作用及应用场景,包括计时器、超时重试和定时任务等。接着介绍了`alarm()`函数,展示了如何设置定时器以及其局限性。随后探讨了`setitimer()`函数,比较了它与`alarm()`的不同之处,包括定时器类型、精度和支持的定时器数量等方面。最后,文章讲解了子进程退出时如何利用`SIGCHLD`信号,提供了示例代码展示如何处理子进程退出信号,避免僵尸进程问题。
|
3月前
|
NoSQL
gdb中获取进程收到的最近一个信号的信息
gdb中获取进程收到的最近一个信号的信息
|
4月前
|
Linux API C语言
Linux源码阅读笔记02-进程原理及系统调用
Linux源码阅读笔记02-进程原理及系统调用
|
5月前
|
弹性计算 DataWorks 关系型数据库
DataWorks操作报错合集之DataX在执行过程中接收到了意外的信号15,导致进程被终止,该怎么处理
DataWorks是阿里云提供的一站式大数据开发与治理平台,支持数据集成、数据开发、数据服务、数据质量管理、数据安全管理等全流程数据处理。在使用DataWorks过程中,可能会遇到各种操作报错。以下是一些常见的报错情况及其可能的原因和解决方法。
|
4月前
|
消息中间件 存储 网络协议
从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
在操作系统中,进程间通信(IPC)是至关重要的,它提供了多种机制来实现不同进程间的数据交换和同步。本篇文章将详细介绍几种常见的IPC方式,包括管道、信号、消息队列、共享内存、信号量和套接字,帮助你深入理解并合理应用这些通信方式,提高系统性能与可靠性。
436 0
|
5月前
|
Python
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
Python的`signal`模块提供了访问底层操作系统提供的信号机制的方式。信号是操作系统用来通知进程发生了某种情况(如用户按下Ctrl+C)的一种机制。
|
5月前
|
存储 NoSQL Unix
【Linux】进程信号(下)
【Linux】进程信号(下)
44 0
|
5月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
5月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
186 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)