Linux学习笔记21——sigsetjmp和siglongjmp 信号里的非局部跳转

1,原型

前面提到过,setjmp和longjmp函数可以实现非局部跳转,实现goto语句都不易实现的功能。

上一节又提到过,在进入信号处理程序的时候,我们总是先屏蔽当前的信号,以避免在执行这个信号处理程序的时候又收到了一模一样的信号。当信号处理程序返回时,要先回复信号屏蔽字,解除屏蔽。

现在问题来了,如果在信号处理程序中调用setjmp和longjmp,实现非局部跳转,那信号屏蔽字会恢复吗??

不同的操作系统对这个问题的回答是不同的。

所以在信号处理程序中我们应当使用一套特殊的setjmp和longjmp,即sigsetjmp好siglongjmp.它有输入参数,让你可以控制是否恢复屏蔽字。原型如下:

#include <setjmp.h>
int sigsetjmp(sigjmp_buf env, int savemask);
vodi siglongjmp(sigjmp_buf env, int val);

其原型和用法都与setjmp和longjmp非常接近。只是多了一个savemask参数。其为0代表不保存、不恢复屏蔽字;为非0代表需要保存、恢复屏蔽字。屏蔽字也同样保存在env中。所以我们有了个新的类型sigjmp_buf.

2,例子

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <signal.h>
#include <setjmp.h>

static sigjmp_buf jmpbuf;//信号处理程序中的跳转环境
static volatile sig_atomic_t canjump;//是否可以跳转的标志

void sig_usr_1(int signo);//信号处理程序
void pr_mask(const char *str); //打印当前屏蔽字

int main(){
	//10.15 sigsetjmp和siglongjmp
	// 1,注册信号
	if(signal(SIGUSR1, sig_usr_1) == SIG_ERR)
	{
		printf("signal error!\n");
		exit(1);
	}

	// 2,打印当前屏蔽字
	pr_mask("starting main: ");

	// 3,设置跳转的位置
	if (sigsetjmp(jmpbuf, 1)) //1代表需要恢复屏蔽字
	{
		pr_mask("ending main: ");
		exit(0);
	}

	// 4,跳转标注
	canjump = 1;

	// 5,死循环
	while(1)
	{
		pause;
	}	
}

//定义信号处理程序
void sig_usr_1(int signo)
{
	if (canjump)
	{
		printf("recived SIGUSR, signo = %d\n",signo);
		pr_mask("sig_usr1: ");
		canjump = 0;
		siglongjmp(jmpbuf, 1); //跳转到sigsetjmp处,其使其返回1
	}
}

//打印当前屏蔽字
void pr_mask(const char *str)
{
	sigset_t sigset;

	//获取当前屏蔽字
	if (sigprocmask(0, NULL, &sigset) < 0)
	{
		printf("sigprocmask error !\n");
		exit(1);
	}

	//判断一些在不在当前屏蔽字中
	printf("%s", str);
	if (sigismember(&sigset, SIGUSR1))
		printf("	SIGUSR1");

	if (sigismember(&sigset, SIGUSR2))
		printf("	SIGUSR2");

	if (sigismember(&sigset, SIGALRM))
		printf("	SIGALRM");

	printf("\n");
}

注意,这里用到了canjump这个变量。因为我们的跳转发生在信号处理程序中。而信号实际上是随时随地都可能触发的。如果在注册了信号之后,尚未设置跳转就触发了信号,此时jmpbuf还没有设置正确的值,跳转会发生错误。所以我们通过canjump这个变量告诉程序是不是可以跳转了。由于在主线程和异步的信号处理程序里都需要访问、修改这个变量,所以我们将其设为sig_atomic_t类型。这个类型保证其读写在一个机器指令内完成,因而不会被打断。sig_atomic_t不能是结构体,只能是数字类型,通常就是int。sig_atomic_t总是与volatile合用。这样,只有在我们设置完跳转之后,才进行跳转。

 

程序执行的结果如下:

➜  code ./study_Linux &
[1] 303
starting main:               #主线程里没有屏蔽任何信号
➜  code kill -USR1 303      #发送USR1信号
recived SIGUSR, signo = 10   #进入了信号处理程序
sig_usr1:       SIGUSR1      #信号处理程序中已经屏蔽了USR1
ending main:                 #回到了主函数,已经没有任何屏蔽

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值