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

常见信号术语

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

信号在内核中的表示

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

Pending表

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

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

Block表

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

有了Block表和pending表,我们就能知道有哪些信号要被处理,哪些信号不要被处理。那么怎么处理一个信号呢?这个时候就要观察handler表里的内容了。

handler表

在进程PCB中有一个handler表,该表实际上就是一个函数指针数组(sighandler_t handler[32]) 数组的下标其实就是信号的编号。数组的内容一个函数指针,指向对应信号的处理函数。有了这个表,我们就能知道一个信号在该进程的 响应方式,显然,对handler数组某一元素内容的修改,即修改某一信号的响应方式。

在这里插入图片描述

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后程序没有被终止,说明这个信号确实被阻塞了,然后未决表显示该信号一直出于未决状态,没有被递达。

  • 18
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux进程是指在Linux操作系统中运行的程序实例。Linux进程讲座主要涉及了进程的基本概念、生命周期、进程控制、进程间通信等方面的知识。 首先,进程是计算机运行程序的基本单位,每个进程都有自己的内存空间、执行状态以及相关资源。进程的创建是通过系统调用fork()或者exec()来进行的。进程的创建会产生一个该进程的唯一标识符PID(Process ID),通过PID可以对进程进行管理和控制。 进程的生命周期包括创建、运行和结束三个阶段。进程的创建由父进程通过fork()系统调用来完成,创建的进程称为子进程进程的运行是指进程的代码在操作系统的调度下执行。进程的结束是指进程完成了任务或者被强制终止。 进程控制是指对进程的管理和调度。操作系统通过调度算法决定了每个进程的执行顺序和运行时间片,以提供公平和高效的资源分配。进程的状态包括就绪、运行和阻塞三个状态,操作系统根据进程的状态来进行调度。 进程间通信是指不同进程之间进行信息交换和数据共享的机制。常见的进程间通信方式有管道、消息队列、信号量、共享内存等。这些机制可以实现不同进程之间的数据传输和同步,提高系统的并发性和灵活性。 总之,Linux进程是操作系统中的基本概念,通过进程的创建、运行、结束以及进程控制和进程间通信等机制,实现了多任务的同时执行和资源的共享。通过学习Linux进程,我们能更好地理解和应用操作系统的原理和技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值