APUE笔记:alarm和pause竞争

笔记批注:APUE 10.10节 函数alarm和pause。

在书P269 有这样一段话:(3) 在第一次调用alarm和pause之间有一个竞争条件。在一个繁忙的系统,有可能在我们调用pause之前超时,并调用了信号处理程序,如果发生了这种情况,则在调用pause后,如果没有捕捉到其他信号,调用者将被永远挂起。 而解决的办法是用longjmp和setjmp。我看了代码后,就想着可不可以使用变量来控制,具体如下:

#include 
#include 

static int gflag = 0 ;		//当出现在pause之前执行alarm,避免一直pause情况
void sigact( int signo );
unsigned int sleep_self( unsigned int sec );
void sig_int( int signo );	//tune these loops to run for more than 5 seconds

int main(int argc , char *argv[]){
	unsigned int ret;

	ret = sleep_self( 0 );
	printf( "return value = %u \n " , ret );
	return 0;
}

void sigact( int signo ){
	//do nothing
	printf( "sigact\n" );
	gflag = 1;
}

unsigned int sleep_self( unsigned int sec ){
	int ret ,ret1;
	if( signal( SIGALRM , sigact ) == SIG_ERR )
		oops( "signal" );
	
	if( gflag == 0 ){		//还没有执行alarm
		ret = alarm( sec );
		printf( "test:测试从信号处理函数返回位置,若打印两次,则是从"
			"上一栈的接下一句返回,\n" );
		ret1 = pause();		//pause  until alarm
		printf( "pause return value = %d , alarm return "
				"value = %d \n" , ret1 ,ret);
	}
	return alarm(0);
}

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

	printf( "\nsig_int start \n " );

	k = 0;
	for( i = 0 ; i < 300000; i++ )
		for( j = 0 ; j < 8000 ; j++ )
			k += i*j;
	printf( "sig_int finished\n" );
}


首先做的测试是睡眠0s  ,这样做是为了确保在pause之前 调用alarm,其实我错了, 看看结果APUE笔记:alarm和pause竞争_第1张图片

从结果来看, 压根没有进入sigact函数,也就是执行了alarm后接着执行了pause函数,然后进入等待状态,被挂起。然后我查询了一下alarm(0),得到的解释是:当在调用alarm()前已经设置了一个闹钟,那么我们可以调用alarm(0)来取消此闹钟,并返回剩余时间。所以用alarm(0)并不能行。换招:

2.在sleep_self函数中添加代码sig_int(0);,同时修改代码ret = sleep_self( 2 );

sig_int(0);
printf( "test:测试从信号处理函数返回位置,若打印两次,则是从"
	"上一栈的接下一句返回,\n" );
printf( "after gflag = %d \n " , gflag );

也就是让alarm后 程序在进入pause前至少运行5s,我们看结果:

APUE笔记:alarm和pause竞争_第2张图片


程序先进入sig_int , 打印sig_int start  然后过2s 打印sigact , 然后过5s打印sig_int finished ,然后就处于等待状态了,此时,gfla = 1,但还是执行了pause,说明并没有从上次运行处继续运行, 而是接着运行。


3.实验longjmp 和setjmp

#include 
#include 
#include 

jmp_buf env ;		//当出现在pause之前执行alarm,避免一直pause情况

void sigact( int signo );
unsigned int sleep_self( unsigned int sec );
void sig_int( int signo );	//tune these loops to run for more than 5 seconds

int main(int argc , char *argv[]){
	unsigned int ret;

//	if( signal( SIGINT , sig_int ) == SIG_ERR )
//		oops( "signal" );
	ret = sleep_self( 2 );

	printf( "return value = %u \n " , ret );
	return 0;
}

void sigact( int signo ){
	//do nothing
	printf( "sigact\n" );
	longjmp( env , 1 );
}

unsigned int sleep_self( unsigned int sec ){
	int ret ,ret1;
	if( signal( SIGALRM , sigact ) == SIG_ERR )
		oops( "signal" );

	if( setjmp( env ) == 0 ){		//还没有执行alarm
		ret = alarm( sec );
		sig_int(0);
		printf( "test:测试从信号处理函数返回位置,若打印两次,则是从"
			"上一栈的接下一句返回,\n" );
		ret1 = pause();		//pause  until alarm
		printf( "pause return value = %d , alarm return "
				"value = %d \n" , ret1 ,ret);
	}
	return alarm(0);
}

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

	printf( "sig_int start \n " );

	k = 0;
	for( i = 0 ; i < 300000; i++ )
		for( j = 0 ; j < 6000 ; j++ )
			k += i*j;
	printf( "sig_int finished\n" );
}

运行结果: APUE笔记:alarm和pause竞争_第3张图片

可以看到执行顺序就对了,进入sig_int , 然后过两秒进入sig_act , 然后跳到setjmp比较, 不满足, 执行return alarm(0); 结束。

4.关于pause函数,APUE同页有这样一句话:只有执行了一个信号处理程序并从其返回,pause才返回。在这种情况下,pause返回-1,errno设置为EINTR。

#include 
#include 

static int gflag = 0 ;		//当出现在pause之前执行alarm,避免一直pause情况
void sigact( int signo );
unsigned int sleep_self( unsigned int sec );
void sig_int( int signo );	//tune these loops to run for more than 5 seconds

int main(int argc , char *argv[]){
	unsigned int ret;

	if( signal( SIGINT , sig_int ) == SIG_ERR )
		oops( "signal" );
	ret = sleep_self( 0 );
	printf( "return value = %u \n " , ret );
	return 0;
}

void sigact( int signo ){
	//do nothing
	printf( "sigact\n" );
	gflag = 1;
}

unsigned int sleep_self( unsigned int sec ){
	int ret ,ret1;
	if( signal( SIGALRM , sigact ) == SIG_ERR )
		oops( "signal" );
	
	if( gflag == 0 ){		//还没有执行alarm
		ret = alarm( sec );
		//sig_int(0);
		printf( "test:测试从信号处理函数返回位置,若打印两次,则是从"
			"上一栈的接下一句返回,\n" );
		//printf( "after gflag = %d \n " , gflag );
		ret1 = pause();		//pause  until alarm
		printf( "pause return value = %d , alarm return "
				"value = %d \n" , ret1 ,ret);
	}
	return alarm(0);
}

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

	printf( "\nsig_int start \n " );

	k = 0;
	for( i = 0 ; i < 300000; i++ )
		for( j = 0 ; j < 8000 ; j++ )
			k += i*j;
	printf( "sig_int finished\n" );
}

运行上述代码,键入中断制符(ctrl+c)

APUE笔记:alarm和pause竞争_第4张图片

可见, 当执行pause后,程序挂起, 键入ctrl+c后, sig_int结束后, pause也返回了-1, 程序也能够正常退出了,可见,pause函数捕捉了SIGINT信号 


你可能感兴趣的:(linux系统编程)