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

 

    [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 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
//#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信号不能被阻塞(设置信号集时, 若有信号集中这两个信号,会被内核从信号集中剔除)
*/

 

你可能感兴趣的:(第6章,信号,阻塞信号集,线程)