APUE笔记:alarm和pause竞争

来源:互联网 发布:叶子楣 知乎 编辑:程序博客网 时间:2024/06/15 13:46

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

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

#include <stdio.h>#include <signal.h>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 secondsint 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 nothingprintf( "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 ){//还没有执行alarmret = alarm( sec );printf( "test:测试从信号处理函数返回位置,若打印两次,则是从""上一栈的接下一句返回,\n" );ret1 = pause();//pause  until alarmprintf( "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,其实我错了, 看看结果

从结果来看, 压根没有进入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,我们看结果:



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


3.实验longjmp 和setjmp

#include <stdio.h>#include <signal.h>#include <setjmp.h>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 secondsint 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 nothingprintf( "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 ){//还没有执行alarmret = alarm( sec );sig_int(0);printf( "test:测试从信号处理函数返回位置,若打印两次,则是从""上一栈的接下一句返回,\n" );ret1 = pause();//pause  until alarmprintf( "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" );}

运行结果:

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

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

#include <stdio.h>#include <signal.h>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 secondsint 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 nothingprintf( "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 ){//还没有执行alarmret = alarm( sec );//sig_int(0);printf( "test:测试从信号处理函数返回位置,若打印两次,则是从""上一栈的接下一句返回,\n" );//printf( "after gflag = %d \n " , gflag );ret1 = pause();//pause  until alarmprintf( "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)

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


0 0