Linux--进程信号(pending, block, Handler)集

文章详细阐述了Linux系统中非实时信号的处理方式,包括信号在进程PCB结构体内的位图表示、三大集合表(block、pending、handler)的作用以及信号集的概念和操作函数。通过示例展示了如何自定义信号集,将信号阻塞及恢复,说明了在不同状态下信号的处理行为。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

信号在产生之后,到未被处理的这段时间内,是保存在进程的PCB结构体内的一张位图中的,位图的每个比特位的编号就代表着改信号是否产生,比特位为1表示该信号产生,0表示不存在。

本篇文章就来详细的解答信号在内核中具体的保存和处理方式。

注意,本文谈到的都是非实时信号。

一,三大集合表

1,block集合表代表着该信号是否被阻塞,如果该某个信号的block位图被修改为1,即表示该信号被阻塞,即使产生了该信号,进程也不会处理该信号。

2,pending集合代表的是该信号是否产生了,如果对应的比特位为1,代表着该信号已经产生,如果此时该信号未被阻塞,那么就会在合适的时候对该信号进行处理。(这个合适的时候就是当进程从内核态转化为用户态的时候)。  处理的方式就是对应handler表中的方式。

3,handler集合代表着该信号的处理方式,处理方式有三种

        一是SIG_DFL,即该信号的默认处理方式,比如2号信号就是结束进程,那么当2号信号被处理的时候,进程就会被结束。

        二是SIG_IGN,即忽略该信号。当该信号需要被处理的时候,什么也不做,就是对该信号进行忽略处理。

        三是用户自定义方法,当我们用signal或者sigaction函数对某个信号设置了自定义的捕捉方法,那么该信号处理的时候就会调用我们自定义的方法,而不是默认的信号处理方法。

二,信号集

Linux系统为我们提供了对block和pending信号集的操作方式。

1,概念:

每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。 因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号 的“有效”或“无效”状态。

阻塞信号集也叫做当 前进程的信号屏蔽字(Signal Mask)。

在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。

未决状态是指一个信号产生但是还未被处理的状态。

 2,信号集操作函数

sigset_t类型对于每种信号用一个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些bit则依赖于系统 实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_ t变量。

#include <signal.h>  头文件

int sigemptyset(sigset_t *set);  将该信号集的所有比特位设置为0

int sigfillset(sigset_t *set);  将该信号集的所有比特位设置为1

int sigaddset (sigset_t *set, int signo);  为该信号集添加某个信号signo

int sigdelset(sigset_t *set, int signo);   为该信号集删除某个信号signo

int sigismember(const sigset_t *set, int signo);  查看该signo信号是否存在于该信号集中                                                                                    存在返回1 ,反之返回0

 通过以上的介绍,我们就可以自己先设置好一个信号集,将某个信号添加到我们自定义的信号集中,然后再通过下面的  sigprocmask  函数将自定义的信号集设置到进程的内核block信号集中。

sigprocmask函数

调用函数sigprocmask可以读取或更改进程的信号屏蔽字(阻塞信号集)。

#include  <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

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

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后 根据set和how参数更改信号屏蔽字。如果不需要备份老的信号屏蔽字就将oset设置为nullptr。

 sigpending函数

读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。

下面我们就使用上面谈到的函数写一个小的实验。

实验内容:

1,自定义一个信号集,将2号信号设置阻塞,并将该信号添加到该进程的内核block表中并         读取该进程原来的block表,将该表存放在我们自定义的oldset信号集中。

2,对2号信号进行捕捉,并在10秒内发送2号信号,观察结果。

3,在执行10秒之后,再将原本的block信号集设置回内核中。再次发送2号信号,观察结果

代码:

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

using namespace std;

void handler(int signo)
{
    cout << "signal num : " << signo  << " pid : "<< getpid() <<endl;
}
int main()
{
    signal(2, handler);//对2号信号设置自定义捕捉方法
    
    sigset_t set, oldset;
    //对自定义信号集进行初始化
    sigemptyset(&set);
    sigemptyset(&oldset);

    sigaddset(&set, 2);//将2好信号添加到set中
    
    //将set设置到该进程的内核block表中,并将原本的block表的信息存放到oldset中。
    sigprocmask(SIG_SETMASK, &set, &oldset);

    int cnt = 10;
    while(1)
    {
        //循环等待我们发送2号信号
        sleep(1);
        cout << "main pid : " << getpid() << endl;

        if(--cnt == 0)
        {
            sigprocmask(SIG_SETMASK, &oldset, nullptr);
        }
    }

    return 0;
}

实验现象:

 我们可以看到当我们在10秒内发送2好信号的时候,对2号信号的自定义捕捉的行为并没有发生。

因为此时的block表中2号比特位的值为1,表示该信号是被阻塞的状态。即使pending中的位图为1,2号信号也不会被处理。

而在10秒过后,原本老的block信号集被设置回内核中,即恢复了对2号信号的阻塞,所以2号信号就执行了我们自定义的捕捉方法。

<think>好的,我现在要回答用户关于阻塞信号和忽略信号区别的问题。首先,我需要回顾一下之前提供的引用内容,确保理解正确。 根据引用1,阻塞信号是指信号处于pending状态,内核检查信号队列时,如果信号被阻塞就不会处理。而引用3提到,阻塞信号通常是在关键代码块前使用sigprocmask来阻塞,之后处理未决信号。引用2和4则说明忽略信号可能涉及默认处理或自定义函数,比如SIG_IGN。 首先,我需要明确两者的定义。阻塞信号是暂时阻止信号被递送,但信号仍然存在队列中。而忽略信号是当信号递送时,进程直接丢弃它,不采取任何行动。 接下来,我需要比较它们的实现方式。阻塞通常通过信号掩码,如sigprocmask设置阻塞合。而忽略是通过将信号的处理函数设置为SIG_IGN,或者使用sigaction结构体中的sa_handler。 然后,考虑生命周期。阻塞是临时的,通常在代码块结束后解除。忽略则是持久的,除非显式更改处理方式。 还要提到对子进程的影响,引用3指出子进程继承父进程信号设置。所以阻塞和忽略的设置都可能被继承,但execv会重置忽略的设置? 另外,信号处理时机:阻塞的信号会在解除阻塞后递送,而忽略的信号直接被丢弃,不会进入队列。 可能用户还关心实际应用场景,比如阻塞用于保护关键代码,忽略用于避免某些信号的干扰。 需要确保回答结构清晰,分点说明区别,并引用相关参考资料。同时生成相关问题,比如如何设置阻塞或忽略,它们的应用场景等。</think>### 阻塞信号与忽略信号的区别 在Linux信号处理中,**阻塞信号**和**忽略信号**是两种不同的信号处理机制,主要区别体现在信号的生命周期、处理方式和应用场景上。 --- #### 1. **定义与处理时机** - **阻塞信号**(Blocking Signals) 通过信号掩码(Signal Mask)将指定信号标记为**暂不处理**。此时信号仍会进入内核的未决信号队列(Pending Queue),但暂时不会递送给进程。当信号解除阻塞后,内核才会检查并处理该信号[^1][^3]。 **示例代码**: ```c sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); // 阻塞SIGINT信号 sigprocmask(SIG_BLOCK, &mask, NULL); // 设置信号掩码 ``` - **忽略信号**(Ignoring Signals) 进程主动告知内核**不处理**某个信号。当信号发生时,内核直接丢弃该信号,既不保留在队列中,也不触发任何动作(如终止进程或调用处理函数)[^2][^3]。 **示例代码**: ```c signal(SIGINT, SIG_IGN); // 忽略SIGINT信号 ``` --- #### 2. **信号的生命周期** - **阻塞信号**:信号仍存在于未决队列中,**等待解除阻塞后被处理**。例如,在关键代码段执行前阻塞信号,执行后恢复处理[^3]。 - **忽略信号**:信号直接被丢弃,**永远不会进入队列或触发处理**。 --- #### 3. **应用场景** - **阻塞信号**的典型场景: - 保护关键代码段(如修改共享数据)时,避免被信号中断[^3]。 - 在多线程编程中,控制信号递送的时机。 - **忽略信号**的典型场景: - 防止特定信号干扰进程(如忽略`SIGPIPE`防止因管道断开而终止进程)。 -进程继承父信号的忽略设置(但通过`execv`创建的子进程会重置信号处理方式)[^3]。 --- #### 4. **实现方式对比** | 特性 | 阻塞信号 | 忽略信号 | |--------------------|----------------------------|----------------------------| | **信号队列** | 信号保留在未决队列 | 信号直接被丢弃 | | **处理函数** | 可能触发自定义处理函数或默认行为 | 无任何处理行为 | | **设置方法** | `sigprocmask`或`sigsuspend` | `signal(SIGXXX, SIG_IGN)`或`sigaction`[^2][^4] | | **持久性** | 临时性(需手动解除) | 持久性(除非显式修改) | --- #### 5. **代码示例对比 **阻塞信号后解除**: ```c sigset_t mask, oldmask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmask(SIG_BLOCK, &mask, &oldmask); // 阻塞SIGINT // 关键代码段... sigprocmask(SIG_SETMASK, &oldmask, NULL); // 解除阻塞 ``` **忽略信号**: ```c signal(SIGINT, SIG_IGN); // 忽略Ctrl+C信号 // 即使按下Ctrl+C,进程也不会终止 ``` ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南山忆874

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值