Chinaunix首页 | 论坛 | 博客
  • 博客访问: 988552
  • 博文数量: 200
  • 博客积分: 5011
  • 博客等级: 大校
  • 技术积分: 2479
  • 用 户 组: 普通用户
  • 注册时间: 2008-06-27 15:07
文章分类

全部博文(200)

文章存档

2009年(12)

2008年(190)

我的朋友

分类:

2008-12-08 12:13:50

10.10 alarm and pause functions

本篇将讲述了使用pausealarm来模仿sleep(n)的机制,以前有的事先就这样的,但是是有bug的。然后讲述了改良的使用pause, alarm, setlongjmp, longjmp来实现的sleep,但是,还是有bug。得出结论:这些东西要小心使用,并列举了几种合理的使用方法,特别是在作类似teminal device i/o等会block的操作的时候,需要一种超时检查机制。最后说其实,要实现i/oblock的超时检查机制,还可以使用select/poll

#include

 

unsigned int alarm(unsigned int seconds);

 

Returns: 0 or number of seconds until previously set alarm

 

#include

 

int pause(void);

 

Returns: 1 with errno set to EINTR

 

(一)alarm函数

一个进程最多只有一个alarm,因此新的回覆盖旧的。

Alarm(0)用来取消以前定义的alarm

SIGALRM信号的结果默认是进程被terminate

(二)pause函数

当受到一个信号后,并且执行了该信号的handler之后,pause才返回。

(三)用pausealarm模仿sleep(n),sleep1

Figure 10.7. Simple, incomplete implementation of sleep

#include    

#include    

 

static void

sig_alrm(int signo)

{

    /* nothing to do, just return to wake up the pause */

}

 

unsigned int

sleep1(unsigned int nsecs)

{

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)

        return(nsecs);

    alarm(nsecs);       /* start the timer */

    pause();            /* next caught signal wakes us up */

    return(alarm(0));   /* turn off timer, return unslept time */

}

这样做的缺点:

1.没有考虑以前的alarm的超时时间还有多少,(我认为这一点并不至关重要)

2.你的sleep如果被别人调用,会有副作用,即将别人的SIGALRM信号的handler该改了,所以在你的sleep结束之前要给人家改回去。

3.存在race condition,即调用了alarm(nsecs)之后,如果schedule执行别的进程去了,当超时后,kernel给本进程发送信号,本进程会执行handler。之后,等执行pause()的时候,信号可能不会再发出。就死死的block了。当然谁叫你没有将该信号block呢。

(四)用pause, alarm, setlongjmp, longjmp来改良sleep1=sleep2

Figure 10.8. Another (imperfect) implementation of sleep

#include  

#include  

#include  

 

static jmp_buf  env_alrm;

 

static void

sig_alrm(int signo)

{

    longjmp(env_alrm, 1);

}

 

unsigned int

sleep2(unsigned int nsecs)

{

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)

        return(nsecs);

    if (setjmp(env_alrm) == 0) {

        alarm(nsecs);       /* start the timer */

        pause();            /* next caught signal wakes us up */

    }

    return(alarm(0));       /* turn off timer, return unslept time */

}

这个实现第一次在执行setlongjmp的时候,返回0表明记录位置成功,然后alarm()被调用,handler执行,handler使用longjmp修改了执行路径,它并不是简单的返回,而是跳到setjmp处使其返回1 这样就会跳过pause调用,从而不会block

缺点:当此信号将别的信号的handler中断的时候,别的信号handler就不会成功完成。为什么我们只在此处关心他对信号handler的影响,因为在我们pause之后,是不可能在执行别的函数的,只可能去执行别的信号的handler,那么如果Ahandler在执行,你的SIGALRM一来,就打断了当前的Ahandler,而且最终跳回了sleep函数的stack frame,将A handlerstack frame就给取消了。看例子:

Figure 10.9. Calling sleep2 from a program that catches other signals

#include "apue.h"

 

unsigned int        sleep2(unsigned int);

static void         sig_int(int);

 

int

main(void)

{

    unsigned int        unslept;

 

    if (signal(SIGINT, sig_int) == SIG_ERR)

        err_sys("signal(SIGINT) error");

    unslept = sleep2(5);

    printf("sleep2 returned: %u\n", unslept);

    exit(0);

}

 

static void

sig_int(int signo)

{

    int            i, j;

    volatile int   k;

 

    /*

     * Tune these loops to run for more than 5 seconds

     * on whatever system this test program is run.

     */

    printf("\nsig_int starting\n");

    for (i = 0; i < 300000; i++)

        for (j = 0; j < 4000; j++)

            k += i * j;

    printf("sig_int finished\n");

}

上述三,四是在表述几种naïve的使用signal的方法。接下来来展现合理的使用:

(五)使用 alarm来实现time limit time out的操作

Figure 10.10. Calling read with a timeout

#include "apue.h"

static void sig_alrm(int);

int

main(void)

{

    int     n;

    char    line[MAXLINE];

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)

        err_sys("signal(SIGALRM) error");

    alarm(10);

    if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)

        err_sys("read error");

    alarm(0);

    write(STDOUT_FILENO, line, n);

    exit(0);

}

static void

sig_alrm(int signo)

{

    /* nothing to do, just return to interrupt the read */

}

Timeout发生,signal handler启动, readinterrupted,返回即可。

缺点:

1.如果read自动restart的话,那么这个time out 检查机制就无效了。

2Alarm(10)之后,调用read之前,可能发生类似上面sleep1race condition。使丢失alarm信号。一直block,所以,alarm()的时间要足够长。

(六)改良,使我们的time out机制不因read自动restart而失效的方法,即要求超时了就必须得停下来

Figure 10.11. Calling read with a timeout, using longjmp

#include "apue.h"

#include

static void       sig_alrm(int);

static jmp_buf    env_alrm;

int

main(void)

{

    int     n;

    char    line[MAXLINE];

    if (signal(SIGALRM, sig_alrm) == SIG_ERR)

        err_sys("signal(SIGALRM) error");

    if (setjmp(env_alrm) != 0)

        err_quit("read timeout");

    alarm(10);

    if ((n = read(STDIN_FILENO, line, MAXLINE)) < 0)

        err_sys("read error");

    alarm(0);

    write(STDOUT_FILENO, line, n);

    exit(0);

}

static void

sig_alrm(int signo)

{

    longjmp(env_alrm, 1);

}

一旦发生handler调用,就直接跳到setjmp调用处,判断情形,然后就执行别的代码去了,就不会再让被interrupted read返回了。默认情况下,如果handler返回,那么就会从被打断的system call处执行。可能system call就因此返回了,也可能他自己就restart了。

 

阅读(640) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~