apue程序清单10_6中信号处理程序提早终止的问题

13 篇文章 1 订阅
11 篇文章 0 订阅

在apue10.10节中,sleep2函数为避免alarm和pause之间的竞争条件,使用了setjmp和longjmp,原始实现如下:

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

static void
sig_alrm(int signo)
{
	/* nothing to do, just return to wake up the pause */
}

unsigned int
sleep1(unsigned int nsecs)
{
	if (signal(SIGALRM, sig_alrm) == SIG_ERR)
		return(nsecs);
	alarm(nsecs);		/* start the timer */
	pause();			/* next caught signal wakes us up */
	return(alarm(0));	/* turn off timer, return unslept time */
}

上述sleep1函数有一个问题,设置alarm和调用pause之间有时间间隔,即alarm可能在调用pause函数之前超时,进程随后调用pause而被挂起,由于没有alarm唤醒进程,它将被永远挂起。

解决上述问题可以使用setjmp和longjmp,修改后的sleep2函数版本如下:

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

static jmp_buf	env_alrm;

static void
sig_alrm(int signo)
{
	longjmp(env_alrm, 1);
}

unsigned int
sleep2(unsigned int nsecs)
{
	if (signal(SIGALRM, sig_alrm) == SIG_ERR)
		return(nsecs);
	if (setjmp(env_alrm) == 0) {
		alarm(nsecs);		/* start the timer */
		pause();			/* next caught signal wakes us up */
	}
	return(alarm(0));		/* turn off timer, return unslept time */
}
上述代码中信号处理程序返回时保证不在执行pause函数,这样就避免了alarm和pause的竞争条件。但是这样的实现也会带来一个问题,即调用longjmp会提早终止其他的信号处理程序。在linux系统中,当一个信号在执行他的信号处理程序时,它只会阻塞同类型的信号,但是其他类型的信号就可以打断当前正在执行的信号处理程序。设想以下情景:当进程正在执行一个SIGINT的信号处理程序时,一个alarm信号超时,新的信号会中断SIGINT的信号处理程序转而去执行alarm的handler,而在alarm的handler中我们调用longjmp返回,这样就会跳过SIGINT信号处理程序的栈帧而直接返回到了main黄色函数,相当于longjmp提早终止了SIGINT的信号处理程序。一个例子如下:

#include "apue.h"

unsigned int	sleep2(unsigned int);
static void		sig_int(int);

int
main(void)
{
	unsigned int	unslept;

	if (signal(SIGINT, sig_int) == SIG_ERR)
		err_sys("signal(SIGINT) error");
	unslept = sleep2(5);
	printf("sleep2 returned: %u\n", unslept);
	exit(0);
}

static void
sig_int(int signo)
{
	int				i, j;
	volatile int	k;

	/*
	 * Tune these loops to run for more than 5 seconds
	 * on whatever system this test program is run.
	 */
	printf("\nsig_int starting\n");
	for (i = 0; i < 300000; i++)
		for (j = 0; j < 4000; j++)
			k += i * j;
	printf("sig_int finished\n");
}

sig_int 中的程序执行时间会超过5秒钟,即大于我们设置的alarm时间,这样alarm就会在sig_int返回前超时,从而打断sig_int的信号处理程序。执行程序得到:
Rev-1-0:~/文档$ ./test10_6
^C
sig_int starting
sleep2 returned: 0
从中可见longjmp使sig_int的信号处理程序提前终止。没有执行longjmp返回时栈中空间是这样的:

(栈的底部,高地址)
   main的栈帧
   sleep2的栈帧
   sig_int的栈帧
   sig_alrm的栈帧
执行longjmp返回之后栈空间是这样的:

(栈的底部,高地址)
   main的栈帧
   sleep2的栈帧
即longjmp直接跳过了sig_int的栈帧,从而导致了sig_int的异常终止。



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值