笔记批注: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 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,其实我错了, 看看结果
从结果来看, 压根没有进入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 );
程序先进入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 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" ); }
可以看到执行顺序就对了,进入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 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" ); }
可见, 当执行pause后,程序挂起, 键入ctrl+c后, sig_int结束后, pause也返回了-1, 程序也能够正常退出了,可见,pause函数捕捉了SIGINT信号