Linux操作系统5-进程信号3(信号的捕捉流程,信号集,sigaction)

上篇文章:Linux操作系统5-进程信号3(信号的保存, 用户态与内核态,内核空间)-CSDN博客

本篇Gitee仓库:​​​​​​​myLerningCode/l26 · 橘子真甜/Linux操作系统与网络编程学习 – 码云 – 开源中国 (gitee.com)

本篇重点:信号的捕捉流程与信号集

目录

一. 信号的捕捉流程

1.1 信号捕捉后,什么时候处理?

1.2 信号集sigset_t 及其操作函数

 1.3 信号保存的测试

二. 信号捕捉的方法 

2.1 signal

2.2 sigaction 

2.3 多次接收相同信号


一. 信号的捕捉流程
1.1 信号捕捉后,什么时候处理?

        信号捕捉后,当由内核态转为用户态的时候处理(进入内核需要系统调用,中断,进程切换等操作)。

        切换到用户态时候,就会查找进程的Block表,Pending表,方法表这三个表。

        首先查找Block表,表中为1的直接忽略,若为0,则要找对应的Pending表。若Pending表为0,直接忽略,为1的话说明这个信号未阻塞并且收到这个信号,就会直接执行对应方法表中的方法

        当进程执行方法表中的方法时候,我们需要进入到内核态才能执行这部分代码。如果行为是用户自定义的,内核态也不能直接执行这部分代码,需要进入用户态之后再去执行相应的代码。执行完方法之后,会经过特定的调用重新回到内核态。

1.2 信号集sigset_t 及其操作函数

        Pending表就是 Pending信号集        Block表就是Block信号集

相关操作函数如下:

#include 

//将信号集set置空
int sigemptyset(sigset_t *set)

int sigfillset(sigset_t *set);    //将set置1

int sigaddset(sigset_t *set, int signo);    //添加signo到set中

int sigdelset(sigset_t *set, int signo);    //删除set中的signo

int sigismember(const sigset_t *set, int signo);    //判断一个signo是否在set中

sigpromask:用于修改Block表 

#include 

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

 //hao参数
 //SIG_BLOCK 将set添加到信号集oldset中 相当于 mask = mask | set
 //SIG_UNBLOCK 将oldset中的信号包含set的信号解除 相当于 mask = mask & (~set)
 //SIG_SETMASK    将当前信号集更改为mask 即 mask = set
 
 //oldset 输出型参数,更改信号屏蔽字之后,将之前的信号屏蔽字保存在oldset中。
 //set 使用上面的fill全置1或者其他调用之后,传入该调用即可进行屏蔽                                                                                                            

sigpending:用于修改Pending表 

#include 

int sigpending(sigset_t *set);
//set是输出型参数,根据输入set,将pending表保存在set中并且返回
//哪个进程使用sigpending就返回哪个表的pending表

通过 signal 我们可以修改方法表

通过 sigpromask 我们可以修改Block表

通过 sigpending 我们可以修改pending表

 1.3 信号保存的测试

        这里屏蔽2号信号,然后打印Block表

#include 
#include 
using namespace std;

#include 
#include 
#define BLOCK_SIGNAL 2
#define MAX_SIGNAL 31

static void show(const sigset_t &pending)
{
    for (int signo = MAX_SIGNAL; signo >= 1; signo--)
    {
        if (sigismember(&pending, signo))
            cout << "1";
        else
            cout << "0";
    }
    cout << endl;
}

int main()
{
    // 1.先尝试屏蔽指定的信号
    sigset_t block, oldblock, pending;
    // 1.1初始化
    sigemptyset(&block);
    sigemptyset(&oldblock);
    sigemptyset(&pending);
    // 1.2添加要屏蔽的信号
    sigaddset(&block, BLOCK_SIGNAL); //(到这里没有屏蔽信号,只是预定信息)
    // 1.3 屏蔽(设置进入内核)
    sigprocmask(SIG_SETMASK, &block, &oldblock);

    // 2.遍历打印Pending信号集
    while (true)
    {
        // 2.1初始化
        sigemptyset(&pending);
        // 2.2获取pending表
        sigpending(&pending);
        // 2.3
        show(pending);
        sleep(1);
    }
    return 0;
}

测试结果如下:

 可以看到,当我们输入2号信号之后,Block表中的第2位由0变1

二. 信号捕捉的方法 
2.1 signal

        我们通过signal可以自定义我们的信号捕捉方法。

具体内容可以看这一篇文章:Linux操作系统5-进程信号2(信号的4种产生方式,signal系统调用)-CSDN博客

2.2 sigaction 

#include 

//函数原型
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

//act是输入型参数,old是输出型参数
//通过这个调用我们可以将act的方法输入。并将原本的方法保存到oldact中

//sigaction是一个结构体,内部有方法表,block表

sigaction结构体的定义如下:

 

其中  sa_handler就是方法表        sa_mask就是Block表

我们可以通过sigaction来屏蔽某些信号同时设置某些信号的捕捉方法。

举例代码:

        这里屏蔽3号信号,同时自定义2号信号的捕捉方法

#include 
#include 
using namespace std;

#include 
#include 

// 自定义信号处理
void handler(int signo)
{
    while (true)
    {
        cout << getpid() << " 进程接收到信号:" << signo << " 并处理" << endl;
        sleep(1);
    }
}


int main()
{
    struct sigaction act, oldact;
    act.sa_flags = 0;          // 默认设置为0即可
    act.sa_handler = handler;  // 设置默认方法
    sigemptyset(&act.sa_mask); // 将屏蔽信号清空

    // 可以通过mask来屏蔽相应的信号, 这里屏3信号
    sigaddset(&act.sa_mask, SIGQUIT);

    sigprocmask(SIG_SETMASK, &act.sa_mask, &oldact.sa_mask);

    // 捕捉信号,设置2号信号
    sigaction(SIGINT, &act, &oldact);

    int count = 0;
    while (true)
    {
        cout << "我正在运行 pid:" << getpid() << " 次数:" << ++count << endl;
        sleep(1);
    }
    return 0;
}

运行结果如下:

可以看到,成功自定义了2号信号的捕捉方法。同时屏蔽了3号信号 

2.3 多次接收相同信号

如果一个信号在递达期间再次收到这个信号会咋样?

测试代码:

#include 
#include 
using namespace std;

#include 
#include 

// 自定义信号处理
void handler(int signo)
{
    int cnt = 10;
    while (cnt > 0)
    {
        cout << getpid() << " 进程接收到信号:" << signo << " [" << --cnt << "]" << endl;
        sleep(1);
    }
}


int main()
{
    struct sigaction act, oldact;
    act.sa_flags = 0;          // 默认设置为0即可
    act.sa_handler = handler;  // 设置默认方法
    sigemptyset(&act.sa_mask); // 将屏蔽信号清空

    // 可以通过mask来屏蔽相应的信号, 这里屏3信号
    // sigaddset(&act.sa_mask, SIGQUIT);
    // sigprocmask(SIG_SETMASK, &act.sa_mask, &oldact.sa_mask);

    // 捕捉信号,设置2号信号
    sigaction(SIGINT, &act, &oldact);

    int count = 0;
    while (true)
    {
        cout << "我正在运行 pid:" << getpid() << " 次数:" << ++count << endl;
        sleep(1);
    }
    return 0;
}

运行结果如下: 

         如果进程在递达某信号的时候,再次收到信号。由于当前正在处理这个信号,不会管这个信号直到信号递达结束。

1 我们在递达某一个信号期间,相同类型的信号无法被抵达 — 因为递达某一个信号的时候,会将这个信号加入到屏蔽字(Block表)

2 当信号递达完毕,会解除某一个信号的屏蔽

3 如果信号递达完毕,这个信号仍在Pending表中,会再次递达这个信号

4 如果信号在递达期间,收到本身的信号,会将Pending表由0 -> 1 。如果多次收到这个信号,由1 -> 1 相当于什么也没做

版权声明

   站内部分内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供网络资源分享服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请 联系我们 一经核实,立即删除。并对发布账号进行永久封禁处理。在为用户提供最好的产品同时,保证优秀的服务质量。


本站仅提供信息存储空间,不拥有所有权,不承担相关法律责任。

给TA打赏
共{{data.count}}人
人已打赏
大数据

hive之LEAD 函数详解

2025-3-3 10:15:49

大数据

【实战 ES】实战 Elasticsearch:快速上手与深度实践-1.2.1索引、文档、分片、副本

2025-3-3 10:15:52

0 条回复 A文章作者 M管理员
    暂无讨论,说说你的看法吧
个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索