线程的阻塞信号集 --详解

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 20:57   2291   0

  [1]概述
1.每个线程都拥有独立的阻塞信号掩码。

2.开会时关闭手机是一种比较极端的例子。更合理的做法是暂时屏蔽部分人的电话。对于某些重要的电话,比如儿子老师的电话、父母的电话或老板的电话,是不希望被屏蔽的。信号也是如此。进程在执行某些操作的时候,可能只需要屏蔽一部分信号,而不是所有信号。

3.信号集: 数据类型为 sigset_t,sigset_t 的类型是位掩码,每一个比特代表一个信号。

4.SIGKILL 信号和 SIGSTOP 信号不能被阻塞。(设置信号集时会被内核剔除)(避免出现神仙进程)

5.对于多线程的进程而言,每一个线程都有自己的阻塞信号集。

[2]常用API
int sigemptyset(sigset_t *set); // 初始化信号集set中的信号为空
int sigfillset(sigset_t *set); // 将所有信号添加进信号集set
int sigaddset(sigset_t *set, int signum); // 在信号集中添加signum信号
int sigdelset(sigset_t *set, int signum); // 在信号集中删除signum信号
int sigismember(const sigset_t *set, int signum); // 判断signum是否存在于set信号集中
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); // 设置信号集
oldset: 如果oldset为非NULL,则信号掩码的先前值存储在oldset中,故一般设置为NULL。
how的选项:
SIG_BLOCK: 在当前阻塞信号集中增加set信号集中的信号
SIG_UNBLOCK:在当前阻塞信号集中删除set信号集中的信号
SIG_SETMASK:阻塞信号集被设置为set信号集。


[3]pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
1.为了更显式地设置线程的阻塞信号掩码,线程库提供了 pthread_sigmask 函数来设置线程的阻塞信号掩码。

2.事实上 pthread_sigmask 函数和 sigprocmask 函数的行为是一样的。
pthread_sigmask函数将调用sigprocmask函数(内核源码)


[4]注意
1.如果阻塞了某个信号A, 然后调用pause。在程序的执行过程中如果一直给进程发送
信号A,pause函数将不会返回, 因为发送的信号都被阻塞。

2.对于信号集中阻塞的不可靠信号a, 在阻塞过程中, 发送多个信号a时,之前挂起的信号a会被抛弃;
解除阻塞后, 最终发送到目标进程的信号a只有一个。

3.对于信号集中阻塞的可靠信号b,在阻塞过程中, 发送多个信号a时,会创建一个队列来管理阻塞的信号;
解除阻塞后, 最终发送到目标进程的信号b = 信号b的发送次数。

4.SIGKILL 信号和 SIGSTOP 信号不能被阻塞。(设置信号集时会被内核剔除)

[5]示例代码

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include <strings.h>
#include <string.h>
#include <sys/types.h>
#include <errno.h>
#include <signal.h>
//#define CUR printf("cur:%d\n", __LINE__)

int i=0, j=0;
void sig_ctlc(int sig)     // sighandler_t
{
 printf("is %d enter: sig_ctlc\n", ++i);
}
void sig_min1(int sig)   // sighandler_t
{
 printf("is %d enter: sig_min1\n", ++j);
}

int main(int argc, char **argv) // 为了简化代码, 将不判断返回值
{
 int ret;
 sigset_t set; // 创建线程阻塞信号集
 
 // 安装信号 
 if(SIG_ERR == signal(SIGINT, sig_ctlc))   // 安装不可靠型号
  perror("SIGINT install err\n");
 if(SIG_ERR == signal(SIGRTMIN+1, sig_min1))  // 安装可靠型号
  perror("SIGRTMIN+1 install err\n");

 // 设置阻塞信号集
 sigemptyset(&set);     // 初始化信号集为空
 sigaddset(&set, SIGINT);    // 信号集中添加SIGINT信号
 sigaddset(&set, SIGRTMIN+1);  // 信号集中添加SIGRTMIN+1信号
 sigaddset(&set, SIGKILL);   // 设置不会成功, 会被内核剔除
 sigaddset(&set, SIGSTOP);   // 设置不会成功, 会被内核剔除
 sigprocmask(SIG_BLOCK, &set, NULL); // 设置阻塞信号集为原信号集加上SIGINT.SIGRTMIN+1

 // 分别发送2个SIGINT和SIGRTMIN+1信号
 kill(getpid(), SIGINT);
 kill(getpid(), SIGINT);
 kill(getpid(), SIGRTMIN+1);
 kill(getpid(), SIGRTMIN+1);
 printf("%d :send sig: SIGINT SIGRTMIN+1\n", __LINE__); // 50行

 // 解除阻塞信号SIGINT.SIGRTMIN+1
 sigemptyset(&set);     // 初始化信号集为空
 sigaddset(&set, SIGINT);    // 信号集中删除SIGINT信号
 sigaddset(&set, SIGRTMIN+1);    // 信号集中删除SIGRTMIN+1信号
 sigprocmask(SIG_UNBLOCK, &set, NULL); // 设置阻塞信号集为原信号集中删除SIGINT.SIGRTMIN+1
 // 这里会先去处理信号(同类信号只处理一个), 再往下执行
 printf("remove end\n");

 // 如果在这儿发送SIGKILL能够使程序退出, 就说明SIGKILL被阻塞成功
 // 如果发送SIGSTOP信号后,能够发送SIGCONT信号显示"[1]+  Stopped ", 说明SIGSTOP信号发送成功
 while(1);
 return 0;
}

/*  执行结果:
 book@gui_hua_shu:$ ./a.out &
 book@gui_hua_shu:$ 50 :send sig: SIGINT SIGRTMIN+1
 is 1 enter: sig_min1
 is 2 enter: sig_min1
 is 1 enter: sig_ctlc
 remove end
 killall -9 a.out // 程序退出了
 // 再次运行程序
 book@gui_hua_shu:$ ./a.out & 
 book@gui_hua_shu:$ killall -17 a.out  // 发送暂停信号
 book@gui_hua_shu:$ killall -19 a.out  // 发送继续执行信号
 [1]+  Stopped    ./a.out         // 程序成功继续执行  

 结论:
  1.对于信号集中阻塞的不可靠信号a, 在阻塞过程中, 发送多个信号a时,之前挂起的信号a会被抛弃;
     解除阻塞后, 最终发送到目标进程的信号a只有一个
  2.对于信号集中阻塞的可靠信号b,在阻塞过程中, 发送多个信号a时,会创建一个队列来管理阻塞的信号;
     解除阻塞后, 最终发送到目标进程的信号b = 信号b的发送次数
  3.SIGKILL和SIGSTOP信号不能被阻塞(设置信号集时, 若有信号集中这两个信号,会被内核从信号集中剔除)
*/

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP