2007-9-4 17:04
sanbodhi
用alarm()实现sleep()时的问题
[align=center][size=3][color=#000000][font=宋体]今天读到[/font][font=Times New Roman]APUE[/font][font=宋体]的第十章《信号》的时候,参照书上所讲,自己用[/font][font=Times New Roman]alarm()[/font][font=宋体]实现了一个[/font][font=Times New Roman]mysleep()[/font][font=宋体]函数。程序如下:[/font][/color][/size][/align]
[font=Times New Roman][size=3][color=#000000][/color][/size][/font]
[size=3][color=#000000][font=宋体][code]
#include <stdio.h>
#include <setjmp.h>
#include <unistd.h>
#include <signal.h>
typedef void Sigfunc(int);
static jmp_buf env_alarm;
volatile int rtn_val;
static void old_sig_alarm(int signo)
{
printf("in old_sig_alarm()\n");
// longjmp(env_alarm,1);
}
static void new_sig_alarm(int signo)
{
printf("in new_sig_alarm()\n");
// longjmp(env_alarm,1);
}
int mysleep(unsigned int sec)
{
int old_alrm = -1;
Sigfunc *p;
if( (p = signal(SIGALRM,new_sig_alarm)) == SIG_ERR )
return(sec);
if(setjmp(env_alarm)==0)
{
old_alrm = alarm(sec); //old_alarm保存此函数之前的alarm的秒数
if(old_alrm>0) //说明调用mysleep()之前有alarm()语句
{
if(old_alrm<sec) // alarm(3); mysleep(5); 的情况
{
signal(SIGALRM,p);
alarm(old_alrm);
rtn_val = 0;
pause();
}
else //alarm(5); mysleep(3); 的情况,也是我所遇到问题的情况
{
rtn_val = old_alrm - sec;
pause();
}
}
else //调用mysleep()之前没有alarm()语句
{
rtn_val = 0;
pause();
}
}
signal(SIGALRM,p); //返回之前把SIGALRM的处理函数恢复为默认的
return(alarm(rtn_val));
}
int main(void)
{
signal(SIGALRM,old_sig_alarm); //设置SIGALRM的默认处理函数
alarm(5);
mysleep(3);
pause();
return 0;
}
[/code][/font][/color][/size]
[size=3][color=#000000][font=宋体]程序的一些说明[/font][font=Times New Roman]:[/font][/color][/size]
[size=3][color=#000000][font=Times New Roman][/font][/color][/size]
[size=3][color=#000000][font=Times New Roman]1[/font][font=宋体]、[/font][font=Times New Roman]old_sig_alarm()[/font][font=宋体]函数用来模拟[/font][font=Times New Roman]SIGALRM[/font][font=宋体]信号默认的信号处理函数,而[/font][font=Times New Roman]new_sig_alarm()[/font][font=宋体]函数用作捕捉[/font][font=Times New Roman]SIGALRM[/font][font=宋体]信号的处理函数。[/font][font=Times New Roman]mysleep()[/font][font=宋体]中用函数指针[/font][font=Times New Roman]p[/font][font=宋体]保存[/font][font=Times New Roman]old_sig_alarm[/font][font=宋体],以便退出函数的时候恢复为默认的信号处理函数(我的程序中用[/font][font=Times New Roman]old_sig_alarm[/font][font=宋体]模拟默认函数)。[/font][/color][/size]
[size=3][color=#000000][font=Times New Roman][/font][/color][/size]
[size=3][color=#000000][font=Times New Roman]2[/font][font=宋体]、[/font][font=Times New Roman]main()[/font][font=宋体]函数在调用[/font][font=Times New Roman]mysleep(m)[/font][font=宋体]之前已经设置了一个[/font][font=Times New Roman]alarm(n)[/font][font=宋体],[/font][font=Times New Roman]n[/font][font=宋体]在[/font][font=Times New Roman]mysleep(m)[/font][font=宋体]中用[/font][font=Times New Roman]old_alrm[/font][font=宋体]存放。[/font][/color][/size]
[size=3][color=#000000]
[/color][/size]
[size=3][color=#000000][font=宋体]如果[/font][font=Times New Roman]alarm[/font][font=宋体]的时间早于[/font][font=Times New Roman]mysleep[/font][font=宋体]的结束时间,则直接处理[/font][font=Times New Roman]alarm()[/font][font=宋体],然后终止[/font][font=Times New Roman]mysleep()[/font][font=宋体]函数。如:[/font][/color][/size]
[size=3][color=#000000][font=宋体][code]
int main(void)
{
…
alarm(3);
mysleep(5);
…
}
[/code][/font][/color][/size]
[size=3][color=#000000][font=宋体][/font][/color][/size]
[size=3][color=#000000][font=宋体] 如果[/font][font=Times New Roman]alarm[/font][font=宋体]的时间超过[/font][font=Times New Roman]mysleep[/font][font=宋体]的时间,则先[/font][font=Times New Roman]mysleep[/font][font=宋体]结束以后,再等到[/font][font=Times New Roman]alarm[/font][font=宋体]到时间并调用默认的处理函数。(根据[/font][font=Times New Roman]APUE[/font][font=宋体]上说,对于[/font][font=Times New Roman] [/font][font=宋体]先[/font][font=Times New Roman]alarm(5) [/font][font=宋体]然后[/font][font=Times New Roman] sleep(3)[/font][font=宋体],是否会产生[/font][font=Times New Roman]2[/font][font=宋体]个[/font][font=Times New Roman]SIGALRM[/font][font=宋体]信号根据不同系统的具体实现而不同。我的程序是会产生[/font][font=Times New Roman]2[/font][font=宋体]个[/font][font=Times New Roman]SIGALRM[/font][font=宋体]信号的,即[/font][font=Times New Roman]mysleep[/font][font=宋体]结束以后依然处理之前的[/font][font=Times New Roman]alarm[/font][font=宋体])。如:[/font][/color][/size]
[size=3][color=#000000][font=Times New Roman][code]
int main(void)
{
…
alarm(5);
mysleep(3);
pause();
…
}
[/code][/font][/color][/size]
[size=3][color=#000000][font=Times New Roman][/font][/color][/size]
[size=3][color=#000000][font=Times New Roman]3[/font][font=宋体]、根据[/font][font=Times New Roman]APUE[/font][font=宋体],如果[/font][font=Times New Roman]mysleep[/font][font=宋体]的实现写成:[/font][/color][/size]
[size=3][color=#000000][font=宋体][/font][/color][/size]
[size=3][color=#000000][font=宋体][code]
int mysleep(int sec)
{
…
signal(SIGALRM,new_sig_alarm);
alarm(sec);
pause();
…
}
[/code][/font][/color][/size]
[size=3][color=#000000][font=宋体][/font][/color][/size]
[size=3][color=#000000][font=宋体] 当系统繁忙的时候,可能产生竞争条件。即,执行[/font][font=Times New Roman]alarm(sec)[/font][font=宋体]以后,因为竞争,所以没有执行到[/font][font=Times New Roman]pause()[/font][font=宋体]一句[/font][font=Times New Roman]alarm()[/font][font=宋体]就到时间了。这时会调用[/font][font=Times New Roman]new_sig_alarm()[/font][font=宋体]处理信号,然后再执行[/font][font=Times New Roman]pause()[/font][font=宋体]。这样进程就会永远挂起。为了避免这种情况,程序引入了[/font][font=Times New Roman]setjmp[/font][font=宋体]和[/font][font=Times New Roman]longjmp[/font][font=宋体],在[/font][font=Times New Roman]new_sig_alarm() [/font][font=宋体]与[/font][font=Times New Roman]old_sig_alarm()[/font][font=宋体]中加入一句[/font][font=Times New Roman] longjmp[/font][font=宋体],这样无论是否产生竞争,函数都不会永远挂起。具体实现见上面贴的程序。具体实现中,我用[/font][font=Times New Roman]rtn_val[/font][font=宋体]保存[/font][font=Times New Roman]mysleep[/font][font=宋体]函数返回之前需要[/font][font=Times New Roman]alarm[/font][font=宋体]的时间,有可能是[/font][font=Times New Roman]0[/font][font=宋体],[/font][font=Times New Roman] [/font][font=宋体]也有可能是[/font][font=Times New Roman] old_alarm-sec[/font][font=宋体]。为了[/font][font=Times New Roman]longjmp[/font][font=宋体]的时候[/font][font=Times New Roman]rtn_val[/font][font=宋体]不会被恢复成为[/font][font=Times New Roman]setjmp[/font][font=宋体]时的值,我把[/font][font=Times New Roman]rtn_val[/font][font=宋体]申明为[/font][font=Times New Roman]volatile[/font][font=宋体]。[/font][/color][/size]
[font=宋体][size=3][color=#000000][color=red][/color][/color][/size][/font]
[font=宋体][size=3][color=#000000][color=red]程序的问题[/color]:[/color][/size][/font]
[size=3][color=#000000][font=Times New Roman] [/font][font=宋体]当把[/font][font=Times New Roman]old_sig_alarm() [/font][font=宋体]与[/font][font=Times New Roman]new_sig_alarm()[/font][font=宋体]中的[/font][font=Times New Roman]longjmp[/font][font=宋体]语句注释起来,程序可以正常执行。即,运行[/font][font=Times New Roman]3[/font][font=宋体]秒之后输出[/font][font=Times New Roman] in new_sig_alarm()[/font][font=宋体],再过[/font][font=Times New Roman]2[/font][font=宋体]秒,输出[/font][font=Times New Roman]in old_sig_alarm()[/font][font=宋体]。可以看到,退出[/font][font=Times New Roman]mysleep()[/font][font=宋体]之前,[/font][font=Times New Roman]SIGALARM[/font][font=宋体]的信号处理函数被还原成了默认的[/font][font=Times New Roman]old_sig_alarm()[/font][font=宋体]。[/font][/color][/size]
[size=3][color=#000000][font=Times New Roman] [/font][font=宋体]但是,当把[/font][font=Times New Roman]2[/font][font=宋体]句[/font][font=Times New Roman]longjmp[/font][font=宋体]的注释去掉以后,运行[/font][font=Times New Roman]3[/font][font=宋体]秒输出了[/font][font=Times New Roman]in new_sig_alarm()[/font][font=宋体]后,程序就永远挂起了。[/font][/color][/size]
[size=3][color=#000000][font=Times New Roman] [/font][font=宋体]请问这是怎么回事啊?(由于涉及信号,用[/font][font=Times New Roman]gdb[/font][font=宋体]仿佛也不好调试)[/font][/color][/size]