【Linux信号】二:未决信号集、阻塞信号集、信号集操作函数

2024-08-08 17:17:09 浏览数 (2)

1. 阻塞信号集与未决信号集

Linux内核的进程控制块PCB是一个结构体task_struct,除了包含进程id、状态、工作目录、用户id、组id、文件描述符表、还包含了信号相关的信息,主要指阻塞信号集和未决信号集。

  • 阻塞信号集:也叫信号屏蔽字,将某些信号加入集合,对他们设置屏蔽,当屏蔽某个信号后,再收到该信号,该信号的处理将推后(解除屏蔽后)。
  • 未决信号集:
    • 信号产生,未决信号集中描述该信号的位立刻翻转为1,表信号处于未决状态;当信号被处理对应位翻转回为0,这一时刻往往非常短暂。
    • 信号产生后由于某些原因主要是阻塞不能抵达,这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态。

未决信号集就是没有被处理的信号,未决信号集实际上是一个32位数,每一位代表一个信号,当信号产生的时候,就把对应的位反转为1,如果该信号未被处理就反转回0,处理了就保持为1。

而阻塞信号集会影响到未决信号集,比如说我在阻塞信号集中将2号信号为置为1,也就是将2号信号屏蔽,那么未决信号集中2号信号对应的位就会变为1(未决状态),一直阻塞在这种状态。

内核通过读取未决信号集来判断信号是否应被处理,信号屏蔽字mask可以影响未决信号集,而我们可以在应用程序中自定义set来改变mask来达到屏蔽指定信号的目的。

总结来说,未决信号集就是当前进程未处理的信号的集合,它是一个32位字,改字的每一个位对应一个信号,如果该位为1表示信号还未被处理,如果改为置为0,表示信号已经被处理或者没有传递该信号。阻塞信号集,就是对信号进行阻塞或屏蔽设置的一个32位信号屏蔽字,同样每一位对应一个信号,如果某一位设置为1,那么该位对应的信号将被屏蔽,该信号会被延后处理,此时如果信号产生,那么未决信号集中对应的位置1,一直到该信号被解除屏蔽的时候(也就是阻塞信号集中对应位置0),才会去处理该信号,处理完信号,未决信号集中对应位反转回0。

2. 信号集设定函数

代码语言:javascript复制
#include <signal.h>
typedef unsigned long sigset_t; /*信号集类型,其实就是一个32位的字*/

/*清空信号集,将某个信号集清0*/
int sigemptyset(sigset_t *set);

/*填充信号集,将某个信号集置1*/
int sigfillset(sigset_t *set);

/*将某个信号signum加入信号集set*/
int sigaddset(sigset_t *set, int signum);

/*将某个信号清出信号集,从信号集ste中删除信号signum,(其实就是本来某个屏蔽信号字中置1的位清0)*/
int sigdelset(sigset_t *set, int signum);

/*判断某个信号是否在信号集中,
返回值 在集合:1;不在:0;出错:-1 (其余四个函数成功返回0,失败返回-1)*/
int sigismember(const sigset_t *set, int signum);

3. sigprocmask函数与sigpending函数

3.1 sigprocmask函数

  • 包含头文件及函数原型
代码语言:javascript复制
#include <signal.h>

int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 函数功能 sigprocmask() is used to fetch and/or change the signal mask of the calling thread. 设置阻塞或解除阻塞信号集,用来屏蔽信号或解除屏蔽,其本质是读取或修改进程的PCB中的信号屏蔽字。需要注意的是,屏蔽信号只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢弃处理。
  • 函数参数
    • how:假设当前的信号屏蔽字为mask
      • SIG_BLOCK:设置阻塞,set表示需要屏蔽的信号,相当于 mask = mask | set 。
      • SIG_UNBLOCK:解除阻塞,set表示需要解除屏蔽的信号,相当于 mask = mask & ~set 。
      • SIG_SETMASK:替换信号集,set表示用于替代原始屏蔽集的新屏蔽集,相当于 mask = set,直接把传入的set设置为当前阻塞信号集。调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
    • set:传入参数,是一个位图(32位),set中哪位置1,就表示当前进程屏蔽哪个信号。
    • oldset:传出参数,保存旧的信号屏蔽集,可用于恢复上次设置。
  • 函数返回值 sigprocmask() returns 0 on success and -1 on error.

3.2 sigpending函数

  • 包含头文件及函数原型
代码语言:javascript复制
#include <signal.h>

int sigpending(sigset_t *set);
  • 函数功能 sigpending() returns the set of signals that are pending for delivery to the calling thread (i.e., the signals which have been raised while blocked). The mask of pending signals is returned in set. 获取当前进程的未决信号集。
  • 函数参数
    • set:传出参数,传出当前未决信号集。
  • 函数返回值 sigpending() returns 0 on success and -1 on error.

3.3 示例:打印未决信号集

代码语言:javascript复制
/************************************************************
  >File Name  : print_sigset.c
  >Author     : Mindtechnist
  >Company    : Mindtechnist
  >Create Time: 2022年05月23日 星期一 14时20分42秒
************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <signal.h>

int main(int argc, char* argv[])
{
    sigset_t mvector;
    /*设置阻塞信号*/
    sigemptyset(&mvector); /*先清一下*/
    sigaddset(&mvector, SIGINT); /*添加一个屏蔽信号ctrl c*/
    sigaddset(&mvector, SIGQUIT);
    /*在阻塞信号集中添加了两个信号ctrl c和ctrl ,等待按键产生信号,最开始没有信号产生,
    未决信号集中对应位为0,当按键产生信号的时候,未决信号集中对应的2、3位将置1,并阻塞信号*/
     sigaddset(&mvector, SIGKILL); /*9号信号不能被阻塞,设了也没用*/
    sigprocmask(SIG_BLOCK, &mvector, NULL); /*设置阻塞信号集,上面只是在阻塞信号集
                                            中增加了一个信号,并没有设置,所以不会生效,
                                            通过sigprocmask设置后才能生效————这一步
                                            就相当于把当前进程阻塞信号集的SIG_BLOCK
                                            信号给屏蔽了*/
    while(1)
    {
        /*循环打印未决信号集*/
        sigpending(&mvector); /*获取当前未决信号集*/
        int signo = 1;
        for(signo = 1; signo < 32; signo  ) /*1-31号信号,依次判断是否在未决信号集中*/
        {
           if(sigismember(&mvector, signo) == 1)
           {
              printf("1");
           }
           else
           {
              printf("0");
           }
        }
        printf("n");
        sleep(1);
        }
    return 0;
}

编译并运行,可以看到最开始没有信号产生,未决信号集中对应位为0,32位全为0。当按键产生信号的时候,未决信号集中对应的2、3位将置1,未决信号集变为0110000000000000000000000000000。此时按键ctrl c或者ctrl 产生信号后,不会再终止进程,只会将未决信号集对应位置1,因为信号已经被屏蔽了。但是,9号信号不能被阻塞,我们可以通过 kill -9 杀死该进程。

0 人点赞