【Linux】详解信号的保存&&信号屏蔽字的设置

简介: 【Linux】详解信号的保存&&信号屏蔽字的设置

一、信号处理的一些常见概念

  • 实际执行信号的处理动作称为信号递达(Delivery)。
  • 信号从产生到递达之间的状态,称为信号未决(Pending)。
  • 进程可以选择阻塞 (block )某个信号。被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。
  • 注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。
  • 阻塞一个信号和是否收到这个信号是没有关系的。也就是说,在还没收到一个信号之前就可以在内核中设置对这个信号进行阻塞。

二、信号保存以及阻塞的内核级理解

在进程的PCB中,其实是有三张表。

       一张为block位图(阻塞位图),也就是一个32位的整形变量,其中取高31位来表示是否阻塞对应的信号,比如说block位图中第0个比特位不用,第1个比特位表示是否阻塞1号信号,第一个比特位为1就表示阻塞1号信号,为0就表示不阻塞1号信号,依次类推,第2到第31个比特位也是同样的道理。

       一张为pending位图(未决位图),也是一个32位的整形变量,其中取高31位来表示是否收到对应的信号,比如说pending位图中第0个比特位不用,第1个比特位表示是否收到1号信号,第一个比特位为1就表示收到1号信号,为0就表示没有收到1号信号。

       另一张是一个函数指针数组,该数组中每一个下标中都存放了收到对应信号后的处理方法。如果我们不对方法做自定义写入,那么进程在收到对应信号后执行的就是默认的方法,如果自定义写入了那执行的就是我们写入的方法。

      在上图中,三个数组(前两张位图也可以看成数组)应该横着看,依次表示该信号是否被阻塞,是否收到该信号,以及执行该信号的处理方法。 常规信号在递达之前产生多次只计一次,也就是说,当在一段时间内有多个相同的信号到来但却来不及被处理时,在pending位图里只会记录一次而实时信号在递达之前产生多次可以依次放在一个队列里。

三、查看pending位图

      其中sigset_t类型的解释就在下面,set为一个输出型参数,我们传入一个sigset_t类型的参数set,pending位图中的值就被set参数获得了。如果获取成功sigpending函数返回0,是被返回-1。

四、设置信号屏蔽字操作(修改block位图)

       从上面的介绍中我们也可以看到,其实block位图和pending位图的结构是十分相似的,所以未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,可以用来修改进程block位图中的信号屏蔽字sigset_t就是一种数据类型,跟int、double这些数据类型没有区别。

3.1、信号集操作函数

      sigset_t虽然是一种数据类型,但是我们并不能直接手动的修改sigset_t类型的值,必须要调用对应的系统调用函数。

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset (sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo);
  • sigemptyset:初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
  • sigfillset:初始化set所指向的信号集,使其中所有信号的对应bit置1位,表示该信号集的有效信号包括系统支持的所有信号。
  • sigaddset:在set信号集中添加signo信号。
  • sigdelset:在set信号集中删除signo信号。
  • sigismember:用于测试一个指定的信号是否已加入至一个特定的信号集中。

       我们设置完信号集set的值后,set并没有被设置进进程的PCB中,还需要我们调用系统调用函数设置。

3.2、设置信号屏蔽字

利用sigprocmask系统调用函数可以设置进程的信号屏蔽字

第一个参数how有三个选项:

  • SIG_BLOCK:set包含了我们希望添加到当前信号屏蔽字中的信号,相当于mask=mask|set。
  • SIG_UNBLOCK:set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set。
  • SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask=set。

      第二个参数set是我们设置的信号屏蔽字,第三个参数为输出型信号屏蔽字,是原来的信号屏蔽字。

3.3、设置信号屏蔽字的例子

      下面是一个设置屏蔽2号信号,有解除屏蔽2号信号的例子。在程序运行起来到程序运行到20秒期间,我给程序发送2号信号,应该看到pending位图中2号信号的位置为1但程序不退出,到了20秒时程序退出。

      下面是打印pending表的函数,如果收到信号,对应的比特位就置1,如果没有收到就置0。

void print(const sigset_t& pending)
{
    for(int i = 31; i>=1; i--)
    {
        if(sigismember(&pending, i))
            std::cout << "1";
        else
            std::cout << "0";
    }
    std::cout << std::endl;
}

2号信号加入到信号屏蔽集中:

    sigset_t set, oldset;
    sigemptyset(&set);
    sigemptyset(&oldset);
 
    sigaddset(&set, 2);//将2号信号加入到要屏蔽的信号集中

set设置进进程的PCB中:

    //设置set进block位图中
    int n = sigprocmask(SIG_SETMASK, &set, &oldset);
    if(n == -1)
    {
        std::cout << "设置屏蔽字错误!" << std::endl;
        return 1;
    }

查看pending表,观察退出状态:

    int cnt = 0;
 
    //查看pending位图,给进程发送2号信号,pending位图中应该出现2号信号,但是进程不会退出
    //等到20秒时程序退出
    while(true)
    {
        cnt++;
        if(cnt == 20)
            sigprocmask(SIG_UNBLOCK, &set, &oldset);
        sigset_t pending;
        sigemptyset(&pending);
        int m = sigpending(&pending);
        print(pending);
        sleep(1);
    }

发送2号信号,程序到20秒时退出:

四、总结

       31个信号中并不是所有信号都可以被屏蔽掉,9号信号(SIGKILL)和19号信号(SIGSTOP)是无法被屏蔽掉的。若想获取上述的完整的代码,请移步本人码云:240427 · 沈旭彬/C++代码 - 码云 - 开源中国 (gitee.com)

相关文章
|
6天前
|
监控 Oracle 关系型数据库
Linux平台Oracle开机自启动设置
【11月更文挑战第8天】在 Linux 平台设置 Oracle 开机自启动有多种方法,本文以 CentOS 为例,介绍了两种常见方法:使用 `rc.local` 文件(较简单但不推荐用于生产环境)和使用 `systemd` 服务(推荐)。具体步骤包括编写启动脚本、赋予执行权限、配置 `rc.local` 或创建 `systemd` 服务单元文件,并设置开机自启动。通过 `systemd` 方式可以更好地与系统启动过程集成,更规范和可靠。
|
7天前
|
Oracle Ubuntu 关系型数据库
Linux平台Oracle开机自启动设置
【11月更文挑战第7天】本文介绍了 Linux 系统中服务管理机制,并详细说明了如何在使用 systemd 和 System V 的系统上设置 Oracle 数据库的开机自启动。包括创建服务单元文件、编辑启动脚本、设置开机自启动和启动服务的具体步骤。最后建议重启系统验证设置是否成功。
|
17天前
|
关系型数据库 MySQL Linux
Linux系统如何设置自启动服务在MySQL数据库启动后执行?
【10月更文挑战第25天】Linux系统如何设置自启动服务在MySQL数据库启动后执行?
63 3
|
3天前
|
网络协议 安全 Linux
Linux 上设置自己的公共时间服务器
Linux 上设置自己的公共时间服务器
11 0
|
1月前
|
Ubuntu Linux
Linux实践|设置静态 IP 地址
Linux实践|设置静态 IP 地址
61 0
Linux实践|设置静态 IP 地址
|
1月前
|
Linux 应用服务中间件 nginx
Linux下权限设置之suid、sgid、sticky
Linux下权限设置之suid、sgid、sticky
|
2月前
|
NoSQL Linux Redis
Linux Redis 服务设置开机自启动
【9月更文挑战第2天】在 Linux 系统中,可使用两种方法设置 Redis 开机自启动:一是通过创建 `redis.service` 文件并利用 systemd 进行管理,包括定义服务参数和启动脚本;二是编辑 `/etc/rc.local` 文件,在其中添加启动命令。推荐使用 systemd 方法,因为它更符合现代 Linux 系统的设计理念。设置完成后,可通过 `sudo systemctl status redis.service` 检查服务状态。
296 3
|
2月前
|
Linux Shell
10-8|linux date设置时间
10-8|linux date设置时间
|
2月前
|
Unix Linux Python
Cron定时设置在linux和mac中的使用
文章详细说明了如何在Linux和Mac操作系统中使用Cron进行定时任务的设置,并提供了多个Cron表达式的实例。
38 0
|
3月前
|
Linux 调度
Linux0.11 信号(十二)(下)
Linux0.11 信号(十二)
25 1