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

信号在产生之后,到未被处理的这段时间内,是保存在进程的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号信号就执行了我们自定义的捕捉方法。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

南山忆874

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

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

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

打赏作者

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

抵扣说明:

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

余额充值