天天看点

101-多线程与信号(sigwait)

在上一节中,我们使用了 sigpending 从未决队列中取出信号,并打印挨个打印,这种做法相当的麻烦,而且不太科学。如果没有未决信号集中一直没有信号,岂不是在浪费 cpu?

幸好,sigwait 函数可以帮我们解决这个问题。

1. sigwait 函数

如果在线程中调用 sigwait,它会一直等待它指定的信号,直到未决信号集中出现指定的信号为止,同时 sigwait 还会从未决信号集中取出该信号返回,并将该信号从未决非信号集中删除。

如果多线程中调用 sigwait 等待同一个信号,只会有一个线程可以从 sigwait 中返回。

如果 sigwait 要等待的信号被捕获,要么 sigwait 函数返回,要么调用相应的信号处理函数,到底是哪种情况这取决于操作系统实现。一般情况下,我们需要将 sigwait 要等待的信号添加到阻塞集中,以避免歧义。

sigwait 函数原型如下:

int sigwait(const sigset_t *set, int      

参数 set 表示要等待哪些信号,一旦 sigwait 函数返回,会从未决信号集中取出信号,放到参数 sig 指向的内存中。

注意:在 apue 中文版本中,参数 sig 被翻译成了发送信号的数量……所以大家在看书的时候,对有疑惑的地方一定要看英文原版,然后做实验验证。
101-多线程与信号(sigwait)

图1 翻译错误

图 1 中被圈出来的部分,翻译的都是有问题的,第一个刚刚已经指出了,另外一点,pending 这个单词不应该被翻译为挂起,而是未决。

虽然译者翻译错误,但是我们还是要感谢译者为此书的翻译倾注了大量心血,毕竟我们自己直接读原版是很费时费力的。最好的方式,就是两者结合起来,对照着看。

2. 实验

程序 sigwait.c 采用了另一种处理信号的方式。它将异步信号处理方式改成了同步处理(一个信号不会因为另一个信号被打断,信号的处理都是按照顺序一个一个来的),大大简化了程序的设计。

2.1 代码

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <pthread.h>
#include <errno.h>

#define PPERR(err, msg) do { errno = err; perror(msg); exit(-1); } while(0)

void* sig_thread(void *arg) {
  sigset_t *st = arg;
  int err, sig;

  for(;;) {
    // sigwait 函数一直等待指定的信号出现(在未决信号集中)
    err = sigwait(st, &sig);
    if (err != 0) PPERR(err, "sigwait");
    // 在这里可以执行相应的处理程序对信号进行处理。
    printf("handle sig: %d\n", sig);
  }
  return NULL;
}

int main() {
  int err;
  pthread_t tid;
  sigset_t st; 
  sigemptyset(&st);
  sigaddset(&st, SIGINT);
  sigaddset(&st, SIGTSTP);
  // 将我们想要处理的信号加入阻塞集,防止因为默认处理导致进程退出
  err = pthread_sigmask(SIG_BLOCK, &st, NULL);
  if (err != 0) PPERR(err, "pthread_sigmask");
  pthread_create(&tid, NULL, sig_thread, (void*)&st);
  pthread_join(tid, NULL);
  return 0;
}      

2.2 编译和运行

  • 编译
$ gcc sigwait.c -o sigwait -lpthread      
  • 运行
101-多线程与信号(sigwait)

图2 程序启动后,分别按下 CTRL+ C、CTRL + Z、CTRL + \

因为我们的程序没有处理 SIGQUIT 信号,所以按下 CTRL + \ 后,整个进程终止了。

3. 总结

  • 掌握 sigwait 函数语义
  • 理解文中的实验
思考:本文的信号处理方式和传统的注册信号处理函数有什么区别?不同的方式各有什么优缺点?