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