100-多线程与信号

不知道你是否还记得之前在进程中的信号处理时,提到过阻塞信号集未决信号集的概念,如果你已经忘记了,请参考《阻塞信号与未决信号》一文回忆一下。

1. 多线程程序中的信号

在多线程中,每一个线程都有属于自己的阻塞信号集与未决信号集。当一个线程派生另一个线程的时候,会继承父线程的阻塞信号集,但是不会继承未决信号集,并且新线程会清空未决信号集。

2. 相关函数

2.1 设置阻塞信号集的函数

在多线程程序中,如果要设置线程的阻塞信号集,不能再使用 sigprocmask 函数,而应该使用 pthread_sigmask,其定义如下:

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

这个函数的用法和 sigprocmask 是一样的。

  • how 参数

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

表示你指定的信号集合

  • oldset

返回旧的阻塞信号集

  • 返回值 int

0 表示成功,-1 失败。

2.2 获取未决信号的函数

该函数仍然是 sigpending,没有变化。它的原型如下:

int sigpending(sigset_t *set);

2.3 信号发送函数

kill 函数只能给指定的进程发送函数,而是使用 pthread_kill 可以给指定的线程发送函数。它的原型如下:

int pthread_kill(pthread_t thread, int sig);

3. 实验

程序 th_sig 做了下面几个工作:

  • 在主线程阻塞了 SIGQUIT 信号
  • 在 fun1 线程中阻塞了 SIGINT 信号
  • 在 fun2 线程中什么也没阻塞

3.1 代码

// th_sig.c
#include <unistd.h>
#include <stdio.h>
#include <signal.h>
#include <pthread.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("");
}

// fun1 线程
void* fun1(void* arg) {
  // 阻塞 SIGINT 信号
  sigset_t mask, st; 
  sigemptyset(&mask);
  sigaddset(&mask, SIGINT);
  pthread_sigmask(SIG_BLOCK, &mask, NULL);

  while(1) {
    printf("I'm fun1:\t");
    sigpending(&st);
    printsigset(&st);
    sleep(3);
  }
}

// fun2 线程
void* fun2(void* arg) {
  sigset_t st; 

  while(1) {
    printf("I'm fun2:\t");
    sigpending(&st);
    printsigset(&st);
    sleep(3);
  }
}

int main() {
  // 创建线程前阻塞 SIGQUIT
  sigset_t mask, st; 
  sigemptyset(&mask);
  sigaddset(&mask, SIGQUIT);
  pthread_sigmask(SIG_BLOCK, &mask, NULL);

  pthread_t tid1, tid2;
  pthread_create(&tid1, NULL, fun1, NULL);
  pthread_create(&tid2, NULL, fun2, NULL);

  sleep(2);
  pthread_kill(tid1, SIGINT);
  while(1) {
    printf("I'm main:\t");
    sigpending(&st);
    printsigset(&st);
    sleep(3);
  }
  return 0;
}

3.2 编译与运行

  • 编译
$ gcc th_sig.c -o th_sig -lpthread
  • 运行


这里写图片描述
图1 运行结果

关于图 1 的说明:左侧是线程的名字,右侧是打印的未决队列,也就是未被信号处理函数处理的信号。其中第 1 列是 SIGHUP 信号,第 2 列表示 SIGINT 信号,第 3 列表示 SIGQUIT 信号。

当主线程休眠 2 秒后,给线程 fun1 发送了一个 SIGINT 信号,此时 fun1 的未决信号集中第 2 列变成了 1 (图 1 上的第 5 行)。

在后面某个时候,按下了 CTRL + \,表示发送信号 SIGQUIT 给前台进程组中的进程。接下来,可以发现,所有的线程未决信号集中的第 3 列都变成了 1. 这说明了两件事:

  • 线程继承了父线程阻塞信号集
  • kill 信号会发送给进程中的所有线程

4. 总结

  • 每个线程有自己的阻塞信号集与未决信号集
  • 线程会继承父线程的阻塞信号集,新线程会清空未决信号集
  • pthread_kill 发送信号给指定线程
  • kill 发送信号给所有线程
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值