阻塞信号

所谓阻塞,就是屏蔽掉某信号,让程序在收到某信号以后不做任何事情,包括默认动作也不执行。要实现这个需求,我们首先得了解一下系统对于每一个进程的信号是如何投递的,这个信号的走向流程是怎么样的,才能针对性的下手实现这个需求。


在每一个进程的 PCB 中,分别维护着两个信号集列表,这两个信号集列表分别叫“未决信号集”和“阻塞信号集”,未决信号集维护者所有进程已经投递过来的信号,但是这个信号集列表还并没有投递给进程触发,决定是否让进程触发这些信号要取决于阻塞信号集,当阻塞信号集中某个信号位为1的时候,这个信号状态就是被阻塞了,该信号就无法传递给进程去执行,反之如果信号位为0的时候,则证明没有对该信号进行阻塞,那么信号就可以成功投递到进程。如下图所示:

2015-07-01_230057

在程序中,我们是可以读取未决信号集和设定修改阻塞信号集的,他们都需要一个结构体类型 sigset_t,这个结构体大小是所有信号的两倍(128字节,可使用sizeof查看),这个结构体中维护了未决信号集和阻塞信号集所有位的状态,我们可以通过获取未决信号集列表填充到这个结构体中来查看当前未决信号集中信号状态,也可以设定好一个 sigset_t 结构传递给阻塞信号集让阻塞信号集阻塞某些信号。做这些操作需要下面一系列函数:

sigprocmask 设定阻塞信号集列表的屏蔽字

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);

返回值:若成功则为0,若出错则为-1

oset参数含义

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号屏蔽字备份到oset里,然后根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

how参数的含义:

SIG_BLOCK set包含了我们希望添加到当前信号屏蔽字的信号,相当于mask=mask|set
SIG_UNBLOCK set包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask=mask&~set
SIG_SETMASK 设置当前信号屏蔽字为set所指向的值,相当于mask=set

在使用sigprocmask函数时,要对 sigset_t 结构体做一些添加、删除、清空等操作,这需要使用到一些列对 sigset_t 结构体的操作函数:

int sigemptyset(sigset_t *set); // 全部置为0
int sigfillset(sigset_t *set); // 全部置为1
int sigaddset(sigset_t *set, int signum); // 给某一位置1
int sigdelset(sigset_t *set, int signum); // 给某一位置0
int sigismember(const sigset_t *set, int signum); // 查看某一位的值

sigpending 获取未决信号集列表

#include <signal.h>
int sigpending(sigset_t *set);

sigpending读取当前进程的未决信号集,通过set参数传出。调用成功则返回0,出错则返回-1。


【实现代码】

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

void printsigpend(void)
{
	int i;
	sigset_t sigpend;
	// 取到当前未决信号集中的内容
	sigpending(&sigpend);
	for (i = 1; i < 32; i++)
	{
		// 判断每一位是否被设定为1
		if (sigismember(&sigpend, i) == 1)
			printf("1");
		else
			printf("0");
	}
	printf("\n");
}

int main(int argc, char* argv[])
{
	sigset_t set, oldset;
	// 将阻塞信号集都置为0
	sigemptyset(&set);
	// 将SIGINT位置为1
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGQUIT);
	sigaddset(&set, SIGTSTP);
	// 将自己设定好的信号集应用到系统的信号集
	sigprocmask(SIG_SETMASK, &set, &oldset);

	while(1)
	{
		printsigpend();
		sleep(1);
	}
	return 0;
}

以上代码运行后的效果图如下:

2015-07-01_224237

由于 SIGINT、SIGQUIT、SIGTSTP 三个信号都已经被我们设置阻塞状态了,所以三个信号无论如何给进程发送,进程都不会响应,这就是信号阻塞的具体作用。

评论