信号集和信号屏蔽函数

信号集是一个或多个信号的集合,主要用在信号屏蔽函数中。数据类型为sigset_t。

信号屏蔽字是指一个进程中当前阻塞而不能送给该进程的信号集。

与信号集设置相关的函数有下面几个:

#include <signal.h>

int sigemptyset(sigset_t *set);

功能:set为信号集,将信号集清空,对应将所有信号屏蔽字置0;

int sigfillset(sigset_t *set);

功能:将所有信号加入到信号集中,对应将所有信号屏蔽字置1;

int sigaddset(sigset_t *set, int signo);

功能:将某个信号加入到信号集中,对应将信号屏蔽字某位置1;

int delset(sigset *set, int signo);

功能:将某个信号从信号集中删除,对应将信号屏蔽字某位置0;

上述函数返回:成功返回0,出错返回-1;

int sigismember(const sigset_t *set, int signo);

功能:测试信号集中是否包含某个信号,对应判断信号屏蔽字某位是否为1;

返回值:真返回1,假返回0,出错返回-1;


注意:所有进程在使用信号集前,要对信号集调用sigemptysetsigfillset一次。因为C编译器把未赋初值的外部和静态变量都初始化为0,而这与是否给定的信号集实现对应却并不清楚。


设置信号屏蔽字要调用下面的信号屏蔽函数。

#include <signal.h>

int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);

功能:利用set去覆盖内核中信号屏蔽字,oset存放原有的信号屏蔽字;

返回:成功返回0,出错返回-1;

int sigpending(sigset_t *set);

功能:获取信号未决字的内容(通过set返回当前正在阻塞的信号的信号集);

返回值:成功返回0,出错返回-1;

参数how

  • SIG_BLOCK:利用set中信号设置信号屏蔽字;
  • SIG_UNBLOCK:利用set中信号不设置信号屏蔽字;
  • SIG_SETMASK:利用set中信号去替换内核信号屏蔽字;

说明:1 进程可以暂时屏蔽信号,使得进程在执行过程中发生的相应信号暂时被阻塞,等待进程解除信号屏蔽后再由内核或驱动将该                信号递送给进程;

            2 信号屏蔽可屏蔽程序执行过程中的中断;

            3 若oset是非空指针,则返回进程的当前信号屏蔽字;

            4 若set为空,则进程信号屏蔽字不变,how值无意义;

            5 注意,不可以阻塞SIGKILLSIGSTOP信号;


下面给出一个mask(信号屏蔽字)的实例。

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

void out_set(sigset_t set)
{
	int i = 1;
	for(; i < 31; ++i)
	{
		// 判断信号屏蔽字的某些位是否置1
		if(sigismember(&set, i))
		{
			printf("%d\n", i);
		}
	}
}

void sig_handler(int signo)
{
	printf("begin process the %d\n", signo);
	// 获得正在处理信号时内核中的信号屏蔽字内容
	sigset_t oset;
	sigemptyset(&oset);
	if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0)
		perror("sigprocemask error");
	out_set(oset);// 输出处理信号时的信号屏蔽字
	printf("finish process the %d\n", signo);
}

int main(void)
{
	if(signal(SIGUSR1, sig_handler) == SIG_ERR)
		perror("signal sigusr1 error");
	if(signal(SIGUSR2, sig_handler) == SIG_ERR)
		perror("signal sigusr2 error");
	if(signal(SIGINT, sig_handler) == SIG_ERR)
		perror("signal sigint error");

	// 定义一个信号集
	sigset_t oset;//放置内核信号屏蔽字的内容
	printf("before signal occured mask:\n");
	// 清空信号集oset
	sigemptyset(&oset);
	//在信号发生前,获得信号屏蔽字的内容
	if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0)
		perror("sigprocmask error");
	out_set(oset);//输出产生信号前的信号屏蔽字
	printf("process %d wait signal...\n", getpid());
	pause();// 进程暂停等待信号

	printf("after signal occured mask:\n");
	sigemptyset(&oset);
	// 信号发生后(信号处理完毕后),获得信号屏蔽字的内容
	if(sigprocmask(SIG_BLOCK, NULL, &oset) < 0)
		perror("sigprocmask error");
	out_set(oset);// 输出信号产生并处理完毕后的信号屏蔽字的内容 

	return 0;
}
程序分别输出信号产生前,处理信号时和信号处理完毕三个阶段的信号屏蔽字。

由结果可以看出,在信号产生前后,信号屏蔽字全置为了0。


下面给出一个pending(信号未决字)的实例。

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

void out_set(sigset_t set)
{
	int i = 1;
	for(; i <= 31; ++i)
	{
		if(sigismember(&set, i))
		{
			printf("%d,", i);
		}
	}
	printf("\n");
}

void sig_handler(int signo)
{
	printf("begin the signal handler\n");
	int i = 0;
	sigset_t set;
	//循环不断去获得同类信号
	for(; i < 20; ++i)
	{
		sigemptyset(&set);
		if(sigpending(&set) < 0)
			perror("sigpending error");
		else
		{
			printf("pending signal:");
			out_set(set);
			sigemptyset(&set);
		}
		printf("i is %d\n", i);
		sleep(1);
	}
	printf("end the signal handler\n");
}

int main(void)
{
	if(signal(SIGTSTP, sig_handler) == SIG_ERR)
		perror("signal sigtstp error");

	printf("process %d wait signal...\n", getpid());
	pause(); // 进程暂停等待信号
	printf("process finished\n");

	return 0;
}
在for循环中,我们不断的去获得SIGTSTP信号,当同时有两个及以上的SIGTSTP信号产生时,相应的信号未决字才被置为1。同时,如果产生多个SIGTSTP信号(2个以上),则只被处理两次,其他的被屏蔽掉。


利用信号屏蔽技术可以防止函数执行过程中被信号中断,下面给出一个实例来说明。

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

int g_v[10];//全局变量
int *h_v;//堆中的数据

void set(int val)
{
	int a_v[10];// 局部变量
	int i = 0;//注意到这里的i是局部变量
	for(; i < 10; ++i)
	{
		a_v[i] = val;
		g_v[i] = val;
		h_v[i] = val;
		sleep(1);//给一个发送信号的时间
	}

	printf("g_v:");
	for(i = 0; i < 10; ++i)
	{
		if(i != 0)
			printf(",%d", g_v[i]);
		else
			printf("%d", g_v[i]);
	}
	printf("\n");

	printf("h_v:");
	for(i = 0; i < 10; ++i)
	{
		if(i != 0)
			printf(",%d", h_v[i]);
		else
			printf("%d", h_v[i]);
	}
	printf("\n");

	printf("a_v:");
	for(i = 0; i < 10; ++i)
	{
		if(i != 0)
			printf(",%d", a_v[i]);
		else
			printf("%d", a_v[i]);
	}
	printf("\n");
}

void sig_handler(int signo)
{
	if(signo == SIGTSTP)
	{
		printf("SIGTSTP occured\n");
		set(20);// 在信号处理函数中又调用set
		printf("end SIGTSTP\n"); 
	}
}

int main(void)
{
	if(signal(SIGTSTP, sig_handler) == SIG_ERR)
		perror("signal sigtstp error");
	
	h_v = (int*)calloc(10, sizeof(int));
	printf("begin running main\n");

	//屏蔽信号(1~31)
	sigset_t sigset;
	sigemptyset(&sigset);
	sigfillset(&sigset);// 要屏蔽所有的信号
	//这里我们不需要获得原内核中的信号屏蔽字,第三个参数设为NULL
        if(sigprocmask(SIG_SETMASK, &sigset, NULL) < 0)
		perror("sigprocmask error");

 	set(10);
	//解除信号屏蔽
	if(sigprocmask(SIG_UNBLOCK, &sigset, NULL) < 0)
		perror("sigprocmask error");
	printf("end running main\n");

	return 0;
}
这里我们对“函数的可重入性”中的函数进行了改进。在执行set函数之前,先通过sigfillset函数和sigprocmask函数屏蔽了1~31号信号,这样的话,在执行set过程中如果发生了信号,则不会影响第一次的结果输出。后面我们对信号解除了屏蔽,这样的话,在第一次执行set中产生的信号仍然被捕获,再次执行set函数。运行结果如下:


在第一次赋值过程中产生了一个SIGTSTP信号。


  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值