作者:筱庆儿宝贝 | 来源:互联网 | 2023-10-15 07:20
不知道你是否还记得之前在进程中的信号处理时,提到过阻塞信号集与未决信号集的概念,如果你已经忘记了,请参考《阻塞信号与未决信号》一文回忆一下。
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 参数
表示你指定的信号集合
返回旧的阻塞信号集
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 代码
#include
#include
#include
#include
void printsigset(const sigset_t *set) {int i;for (i &#61; 1; i <&#61; 64; i&#43;&#43;) {if (i&#61;&#61;33) putchar(&#39; &#39;);if (sigismember(set, i) &#61;&#61; 1)putchar(&#39;1&#39;);elseputchar(&#39;0&#39;);}puts("");
}
void* fun1(void* arg) {sigset_t mask, st; sigemptyset(&mask);sigaddset(&mask, SIGINT);pthread_sigmask(SIG_BLOCK, &mask, NULL);while(1) {printf("I&#39;m fun1:\t");sigpending(&st);printsigset(&st);sleep(3);}
}
void* fun2(void* arg) {sigset_t st; while(1) {printf("I&#39;m fun2:\t");sigpending(&st);printsigset(&st);sleep(3);}
}int main() {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&#39;m main:\t");sigpending(&st);printsigset(&st);sleep(3);}return 0;
}
3.2 编译与运行
$ gcc th_sig.c -o th_sig -lpthread
图1 运行结果
关于图 1 的说明&#xff1a;左侧是线程的名字&#xff0c;右侧是打印的未决队列&#xff0c;也就是未被信号处理函数处理的信号。其中第 1 列是 SIGHUP 信号&#xff0c;第 2 列表示 SIGINT 信号&#xff0c;第 3 列表示 SIGQUIT 信号。
当主线程休眠 2 秒后&#xff0c;给线程 fun1 发送了一个 SIGINT 信号&#xff0c;此时 fun1 的未决信号集中第 2 列变成了 1 &#xff08;图 1 上的第 5 行&#xff09;。
在后面某个时候&#xff0c;按下了 CTRL &#43; \&#xff0c;表示发送信号 SIGQUIT 给前台进程组中的进程。接下来&#xff0c;可以发现&#xff0c;所有的线程未决信号集中的第 3 列都变成了 1. 这说明了两件事&#xff1a;
- 线程继承了父线程阻塞信号集
- kill 信号会发送给进程中的所有线程
4. 总结
- 每个线程有自己的阻塞信号集与未决信号集
- 线程会继承父线程的阻塞信号集&#xff0c;新线程会清空未决信号集
- pthread_kill 发送信号给指定线程
- kill 发送信号给所有线程