LoveUnix » 编程开发 & Rational » 用alarm()实现sleep()时的问题
让LU留住您的每

一天 让LU博客留住您的每一天
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]

页: [1]


Powered by Discuz! Archiver 5.5.0  © 2001-2006 Comsenz Inc.