39-装信号的容器 sigset



看到这个标题有同学会产生一些疑问(手动黑人问号),信号能被装进容器吗?答曰:能。

为什么需要容器来装信号?其实还是方便 linux 内核处理信号,当然也方便用户程序处理。试想一下,如果同时发送了好多不同的信号给进程,然而进程一次只能处理一个信号,所以进程得把接收到的信号先保存起来才行。

1. sigset_t

POSIX 定义一个数据结构 —— sigset_t ,从名称也可以看出这是通过 typedef 定义的数据类型。本质上它是一个 64 位宽度的整数(有符号无符号都无所谓了)。

为何叫 sigset ? 既然出现了 set,这就表明了这是一个集合,所以每种信号只会在这个集合里出现一次。前面你已经知道,信号的编号是从1 到 64 这个范围。而 sigset_t 正好是一个 64 位整数,这正好可以让每一个比特位表示一个信号。所以有时候 sigset_t 也被称为信号位图。

2. 操作 sigset_t 的函数

既然 sigset_t 是一种数据结构,那就有操作这种数据结构的一系列函数了。操作 sigset_t 的函数主要有 5 个。

  • sigemptyset (清空集合,所有比特位置 0)
  • sigfillset (填充集合,所有比特位置 1)
  • sigaddset (添加信号到集合,将某一比特位置 1)
  • sigdelset (删除某个信号,将某一比特位置 0)
  • sigismember (是否存在某个信号,判断某一比特位是否为 1)

这些函数的功能都非常简单易懂,后面我用例子来说明。

3. 实例

这段代码稍微有点长,这里分成几个小的实验来拆解它。(完整代码见 3.7 节)

3.1 打印 sigset_t 的函数

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("");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

这个函数主要用来打印 sigset_t 中所有的比特位。

3.2 验证 sigset_t 是64位整数

unsigned int test[2] = {0xf0f0f0f0, 0xf0f0f0f0};
printsigset((sigset_t*)test);
  • 1
  • 2

打印后的结果为:

00001111000011110000111100001111 00001111000011110000111100001111
  • 1

3.3 创建一个 sigset_t

sigset_t st; 
printsigset(&st);
  • 1
  • 2

由于 sigset_t 未初始化,所以这里打印的结果是随机的。

3.3 填充 sigset_t

sigfillset(&st);
printsigset(&st);
  • 1
  • 2

结果:

11111111111111111111111111111110 01111111111111111111111111111111
  • 1

上面的结果发现有 2 个比特位为 0,他们分别是信号 32 和 33 对应的位置,你可以通过 kill -l 查看下,为什么出现这样的结果。

3.3 清空 sigset_t

sigemptyset(&st);
printsigset(&st);
  • 1
  • 2

结果:

00000000000000000000000000000000 00000000000000000000000000000000
  • 1

清空集合后,所有的比特位都被置 0 了。

3.4 添加信号

sigaddset(&st, SIGHUP);
sigaddset(&st, SIGINT);
sigaddset(&st, SIGKILL);
sigaddset(&st, SIGSYS);
sigaddset(&st, SIGRTMIN);
sigaddset(&st, SIGRTMAX);
printsigset(&st);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

上面这段代码一共添加了 6 个不同的信号。

结果:

11000000100000000000000000000010 01000000000000000000000000000001
  • 1

可以发现信号对应的比特位都被置 1 了

3.5 删除信号

sigdelset(&st, SIGKILL);
printsigset(&st);
  • 1
  • 2

这里只删除了信号 9(SIGKILL).

结果:

11000000000000000000000000000010 01000000000000000000000000000001
  • 1

发现第 9 个比特位被置 0了。

3.6 判断集合里是否存在某个信号

if (sigismember(&st, SIGKILL)) {
  printf("SIGKILL is member\n");
}
if (sigismember(&st, SIGINT)) {
  printf("SIGINT is member\n");
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

结果:

SIGINT is member
  • 1

因为 SIGKILL 刚刚被删除了,所以这里只打印了 SIGINT.

3.7 完整代码

#include <unistd.h>
#include <signal.h>
#include <stdio.h>

void printsigset(const sigset_t *set)
{
  int i;
  for (i = 1; i <= 64; i++) {
    if (i==33) putchar(' ');
    if (sigismember(set, i) == 1)
      putchar('1');
    else
      putchar('0');
  }
  puts("");
}


int main() {
  sigset_t st; 
  printf("1. create set\n");
  printsigset(&st);

  printf("\n2. vertify sigset_t is a 64-bit integer\n");
  unsigned int test[2] = {0xf0f0f0f0, 0xf0f0f0f0};
  printsigset((sigset_t*)test); // 这种方法不被推荐,仅供测试用。


  // fill set
  printf("\n3. fill set\n");
  sigfillset(&st);
  printsigset(&st);

  // empty set
  printf("\n4. empty set\n");
  sigemptyset(&st);
  printsigset(&st);

  // add sig
  printf("\n5. add SIGHUP(1), SIGINT(2), SIGKILL(9), SIGSYS(31), SIGRTMIN(34) and SIGRTMAX(64) to set\n");
  sigaddset(&st, SIGHUP);
  sigaddset(&st, SIGINT);
  sigaddset(&st, SIGKILL);
  sigaddset(&st, SIGSYS);
  sigaddset(&st, SIGRTMIN);
  sigaddset(&st, SIGRTMAX);
  printsigset(&st);

  // delete sig
  printf("\n6. delete SIGKILL from set\n");
  sigdelset(&st, SIGKILL);
  printsigset(&st);

  // is member
  printf("\n");
  if (sigismember(&st, SIGKILL)) {
    printf("SIGKILL is member\n");
  }
  if (sigismember(&st, SIGINT)) {
    printf("SIGINT is member\n");
  }

  return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64

4. 总结

  • 掌握信号位图数据结构 sigset_t
  • 掌握操作 sigset_t 的 5 个函数

虽然可以使用整数直接赋值的方法来修改 sigset_t 的内容,但是这种方法是不被推荐的。也许在这个系统上是用 64 位整数来存储数据,但是你不能保证别的系统上也这样。所以我们尽量还是使用前面讲到的 5 个操作函数来处理 sigset_t 。

猜你喜欢

转载自blog.csdn.net/weixin_38054045/article/details/80898076