Linux系统编程49 信号 - sigprocmask() 设置信号集当中信号的mask信号屏蔽字

sigprocmask() :虽然我不知道信号什么时候来,但是我可以决定 什么时候响应信号


信号集:

NAME
sigemptyset, sigfillset, sigaddset, sigdelset, sigismember - POSIX signal set operations

SYNOPSIS
#include <signal.h>

   int sigemptyset(sigset_t *set);  清空信号集

   int sigfillset(sigset_t *set); 填充满 信号集

   int sigaddset(sigset_t *set, int signum); 向信号集中添加信号

   int sigdelset(sigset_t *set, int signum); 从型号集中删除信号

   int sigismember(const sigset_t *set, int signum); 判断 信号是否存在于指定集合中

信号机类型:sigset_t


信号屏蔽字处理

sigprocmask(); // 控制 信号相应过程中的 mask位图,mask 位图 用来标记是否响应各个信号,1为响应 0为忽略

NAME
       sigprocmask, rt_sigprocmask - examine and change blocked signals

SYNOPSIS
       #include <signal.h>

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

对 set信号集中的所有信号 作 how 动作操作,oldset保留的是 对该信号集操作之前的 信号集中信号的状态

 The behavior of the call is dependent on the value of how, as follows.

   SIG_BLOCK 对目标信号集中的所有信号进行阻塞,即将mask信号屏蔽位置0
          The set of blocked signals is the union of the current set and the set argument.

   SIG_UNBLOCK  对目标信号集中的所有信号解除阻塞  即将mask信号屏蔽位置1
          The signals in set are removed from the current set of blocked signals.  It is permissible to attempt to unblock a signal which is not blocked.

   SIG_SETMASK  回复信号集之前的状态,并保存信号集恢复之前的当前状态
          The set of blocked signals is set to the argument set.

RETURN VALUE  0为成功,-1为失败
   sigprocmask() returns 0 on success and -1 on error.  In the event of an error, errno is set to indicate the cause.

实验:
每秒向标准输出打印一个* 打印五秒换行。程序不受 CTRL+C 即 SIGINT信号影响,收到 SIGINT信号 响应信号 打印!。

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

void sig_handler(int s)
{
	write(1,"!",1);
}

int main()
{
	int i,j;

	signal(SIGINT,sig_handler);
	for(j=0;j < 1000;j++)
	{

		for(i=0 ; i<5 ; i++)
		{
			write(1,"*",1);
			sleep(1);
		}	
		write(1,"\n",1);
	}


	exit(0);
}

修改: sigprocmask(SIG_BLOCK…)+ sigprocmask(SIG_UNBLOCK…)
只在两行*之间输出 !, 即 选择响应 SIGINT 信号的时间。

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


void sig_handler(int s)
{
	write(1,"!",1);
}

int main()
{
	int i,j;
	sigset_t set;//创建信号集

	signal(SIGINT,sig_handler);
	
	sigemptyset(&set); //清空信号集
	sigaddset(&set,SIGINT); // 添加 SIGINT信号 到信号集


	for(j=0;j < 1000;j++)
	{

		sigprocmask(SIG_BLOCK,&set,NULL); // 对 set信号集中的信号 进行阻塞,即将mask信号屏蔽位置0
		for(i=0 ; i<5 ; i++) 
		{
			write(1,"*",1);
			sleep(1);
		}	
		write(1,"\n",1);
		sigprocmask(SIG_UNBLOCK,&set,NULL); // 对 set信号集中的信号 解除阻塞,即将mask信号屏蔽位置1
	}


	exit(0);
}


mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ gcc sigprocmask.c 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ ./a.out 
***^C*^C*^C^C^C^C^C
!*^C^C^C^C^C^C*^C^C^C^C^C^C*^C**
!*****
*^C*^C^C^C^C^C*^C^C*^C*
!*****
*****
*****
*****
*****
****^C*^C
!***^C^C**
!*****^\Quit (core dumped)
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ 

注意:两次sigprocmask之间

sigprocmask(SIG_BLOCK,&set,NULL);

注意1
这之间 已经屏蔽了 SIGINT 信号,即将SIGINT 的mask信号屏蔽位置0,所以就算有信号来,mask信号屏蔽位 按位与 pending信号状态位,结果也是0,所以不响应 SIGINT信号的动作,即不打印!

注意2
只有解除 SIGINT 信号阻塞,即将mask信号屏蔽位置1,并赶上 程序从内核态切换回用户态的时候 才会计算mask信号屏蔽位 按位与 pending信号状态位 的结果,并响应SIGINT 信号动作,即 打印!

注意3
而且 不论 发送多少个 SIGINT 信号,只会打印一个 !,即只会响应一次信号。这就是之前 在 信号的响应过程 中说的 标准信号为什么要丢失 这个问题了,信号的响应过程那篇文章很关键,信号的响应过程那篇文章很关键,信号的响应过程那篇文章很关键,信号的响应过程那篇文章很关键,信号的响应过程那篇文章很关键


sigprocmask(SIG_UNBLOCK,&set,NULL);

并且 还有一点很重要,发现实验中 SIGINT 信号 也不会打断阻塞的系统调用了。只有 SIG_BLOCK 之后才会,原因同上,因为已经屏蔽了该信号。

修改: sigprocmask(SIG_BLOCK…)+ sigprocmask(SIG_SETMASK …)

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


void sig_handler(int s)
{
	write(1,"!",1);
}

int main()
{
	int i,j;
	sigset_t set,oset;//创建信号集

	signal(SIGINT,sig_handler);
	
	sigemptyset(&set); //清空信号集
	sigaddset(&set,SIGINT); // 添加 SIGINT信号 到信号集


	for(j=0;j < 1000;j++)
	{

// 对 set信号集中的信号 进行阻塞,即将mask信号屏蔽位置0. 并且将 信号集阻塞之前的状态保存到 oset
		sigprocmask(SIG_BLOCK,&set,&oset); 
		for(i=0 ; i<5 ; i++) 
		{
			write(1,"*",1);
			sleep(1);
		}	
		write(1,"\n",1);

//即 恢复 oset信号集,即解除阻塞
		sigprocmask(SIG_SETMASK,&oset,NULL); 
	}


	exit(0);
}


mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ gcc sigprocmask.c 
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ ./a.out 
***^C*^C*^C^C^C^C^C
!*^C^C^C^C^C^C*^C^C^C^C^C^C*^C**
!*****
*^C*^C^C^C^C^C*^C^C*^C*
!*****
*****
*****
*****
*****
****^C*^C
!***^C^C**
!*****^\Quit (core dumped)
mhr@ubuntu:~/Desktop/xitongbiancheng/parallel/signal/test$ 

结果一样

通常的做法是:一定要保存和回复 修改之前的信号集状态

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


void sig_handler(int s)
{
	write(1,"!",1);
}

int main()
{
	int i,j;
	sigset_t set,saveset;//创建信号集

	signal(SIGINT,sig_handler);
	
	sigemptyset(&set); //清空信号集
	sigaddset(&set,SIGINT); // 添加 SIGINT信号 到信号集

//在当前模块修改之前 保存信号集状态,以便 当前模块执行完成后恢复 信号集状态
sigprocmask(SIG_UNBLOCK,&set,&saveset);

	for(j=0;j < 1000;j++)
	{

		sigprocmask(SIG_BLOCK,&set,NULL); // 对 set信号集中的信号 进行阻塞,即将mask信号屏蔽位置0
		for(i=0 ; i<5 ; i++) 
		{
			write(1,"*",1);
			sleep(1);
		}	
		write(1,"\n",1);
		sigprocmask(SIG_UNBLOCK,&set,NULL); // 对 set信号集中的信号 解除阻塞,即将mask信号屏蔽位置1
	}

// 恢复 之前信号集状态
sigprocmask(SIG_SETMASK,&saveset,NULL);

	exit(0);
}

猜你喜欢

转载自blog.csdn.net/LinuxArmbiggod/article/details/114114857