40-阻塞信号与未决信号

来源:互联网 发布:淘宝工作包括那些 编辑:程序博客网 时间:2024/05/21 21:36

有时候,你并不希望你的进程处理信号。比如接收到 SIGINT 后对它置之不理。linux 提供了一个函数 sigprocmask 来帮助我们实现此功能。

在一个进程中,保存了两个信号集(在PCB中),分别是阻塞信号集,还有一个未决信号集。当你使用 sigprocmask 的时候,就会修改阻塞信号集。有关未决信号集请阅读本文第 2 节。

如果一个信号加入阻塞信号集,该信号的信号处理函数就不会被调用。

1. sigprocmask 函数

此函数用于修改阻塞信号集。

1.1 函数原型

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

1.2 函数介绍

实际上,该函数不仅可以阻塞你指定的信号,也可以将之前阻塞的信号撤销。具体是通过 how 参数来控制的。

1.3 参数及返回值

  • how 参数

    • SIG_BLOCK 该选项表示将 set 参数指示的信号集中的信号添加到进程阻塞集中
    • SIG_UNBLOCK 该选项与功能 SIG_BLOCK 相反,表示将进程阻塞信号集中指定的信号删除
    • SIG_SETMASK 该选项表示将进程阻塞信号集直接设定为你指定的 set
  • set 参数

表示你指定的信号集合

  • oldset

返回旧的阻塞信号集

  • 返回 int

    0 表示成功,-1 失败。

2. 未决信号

初学者估计看到这个词就懵圈了,我第一次也挺懵圈,搞不懂为啥有些书把简单的东西搞复杂。

说的通俗点,未决信号,就是你的进程已经接收到了信号了,只是还没被信号处理函数处理的那些信号。

当你的进程一收到信号,它首先进入到未决信号集中(就是一个 sigset_t),当未决信号集中的信号被信号处理函数(你自己定义的或者系统默认的)处理,就会从未决信号集中删除。

你可以使用 sigpending 函数获取未决信号集。它的函数原型如下:

int sigpending(sigset_t *set);

使用起来也是相当简单。

3. 实例

该程序的功能是先把 SIGINT、SIGTSTP 加入到了进程阻塞信号集中去。接下来,每隔一秒打印一次未决信号集,第 10 次的时候,又把 SIGINT 信号从阻塞信号集中删除。

  • 代码
// sigblock.c#include <unistd.h>#include <signal.h>#include <stdio.h>#include <stdlib.h>void printsigset(const sigset_t *set){  for (int i = 1; i <= 64; i++) {    if (i==33) putchar(' ');    if (sigismember(set, i) == 1)      putchar('1');    else      putchar('0');  }  puts("");}void handler(int sig) {  if (sig == SIGINT) printf("hello SIGINT\n");  if (sig == SIGQUIT) printf("hello SIGQUIT\n");}int main() {  printf("I'm %d\n", getpid());  sigset_t st, oldst;  sigemptyset(&st);  sigaddset(&st, SIGINT);  sigaddset(&st, SIGTSTP);  sigprocmask(SIG_BLOCK, &st, &oldst);  printf("new set:");  printsigset(&st);  printf("old set:");  printsigset(&oldst);  if (SIG_ERR == signal(SIGINT, handler)) {    perror("signal SIGINT");    return 1;  }  if (SIG_ERR == signal(SIGQUIT, handler)) {    perror("signal SIGQUIT");    return 1;  }  puts("");  int n = 0;  while(1) {    sigpending(&st);    printsigset(&st);    puts("");    sleep(1);    if (n == 10) {      sigset_t tmp;      sigemptyset(&tmp);      sigaddset(&tmp, SIGINT);      sigprocmask(SIG_UNBLOCK, &tmp, NULL);     }       ++n;  }  return 0;}
  • 编译和运行
$ gcc sigblock.c -o sigblock$ ./sigblock

运行过程中,按下 Ctrl + CCtrl + Z 程序都没反应,但是可以看到未决信号集中这两个信号比特位被置位。10 秒后,SIGINT 从阻塞信号集中被删除,即使没有按下 Ctrl + C 发送信号,信号处理函数也被执行,这之后发现未决非信号集中的 SIGINT 比特位被置 0.

  • 结果
I'm 5939new set:01000000000000000001000000000000 00000000000000000000000000000000old set:00000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 0000000000000000000000000000000000000000000000000000000000000000 00000000000000000000000000000000^C01000000000000000000000000000000 00000000000000000000000000000000 // 此处按下了 ctrl c01000000000000000000000000000000 00000000000000000000000000000000^Z01000000000000000001000000000000 00000000000000000000000000000000 // 此处按下了 ctrl z^\hello SIGQUIT // 此处按下了 ctrl \01000000000000000001000000000000 0000000000000000000000000000000001000000000000000001000000000000 0000000000000000000000000000000001000000000000000001000000000000 0000000000000000000000000000000001000000000000000001000000000000 0000000000000000000000000000000001000000000000000001000000000000 0000000000000000000000000000000001000000000000000001000000000000 00000000000000000000000000000000hello SIGINT // 执行到这里 SIGINT 从阻塞信号集中被删除00000000000000000001000000000000 00000000000000000000000000000000 

4. 总结

  • 理解进程PCB的两个信号集(阻塞信号集和未决信号集)
  • sigprocmask 用来修改阻塞信号集
  • sigpending 用来获取未决信号集(不能修改)

最后,有两个信号是无法被阻塞的,你知道是哪两个信号吗?

0 0