注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

Code@Pig Home

喜欢背着一袋Code傻笑的Pig .. 忧美.欢笑.记忆.忘却 .之. 角落

 
 
 

日志

 
 

[unix] timer 的实现  

2009-05-04 19:41:52|  分类: unix |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |

                    UNIX 下 timer 的实现
------------------------------------------------------------

unix 下,timer 一般是通过 SIGALRM 这个信号(signal)来实现的。

信号(signal)是 unix 下软中断(software interrupt)机制,其模仿了硬件的中断
机制,给每个进程(process)发送信号,以处理特定的事件。

 --- alarm() 实现 timer ---

unsigned int alarm(unsigned int sec);

例子:

#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

static int counter = 0;

static void
my_alarm(int signo)
{
    printf("got it!\n");
    counter++;

    alarm(1);

}

int
main(void)
{
    if (signal(SIGALRM, my_alarm) == SIG_ERR)
    {
        perror("signal error");
        exit(-1);
    }

    alarm(1);
    while (counter < 10)
        pause();

    return 0;

}

 --- ualarm() 实现 timer ---

useconds_t ualarm(useconds_t start, useconds_t interval);

alarm() 仅是 sec 秒后让系统发送一次 SIGALRM,因此,my_alarm() 中必须再次
调用 alarm()。且 alarm() 精度不高,可以换用 ualarm(),设置 microsecond
级别的精度(1,000,000 ms == 1 sec)。并且 interval 不为 0 的时候,SIGALRM
是定期发送的。

对于 ualarm(),start 是距 ualarm() 调用后第一次 SIGALRM 发生的时间,而
interval 则是以后每次 SIGALRM 的时间间隔。如下:

   | ------- start ------ | -- interval -- |
   @ -------------------> # -------------> * -------------> * ...
ualarm()              SIGALRM           SIGALRM

例子:

#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

static int counter = 0;

static void
my_alarm(int signo)
{
    printf("got it!\n");
    counter++;

}

#define USPS 1000000; /* microsecond per second */

int
main(void)
{
    if (signal(SIGALRM, my_alarm) == SIG_ERR)
    {
        perror("signal error");
        exit(-1);
    }

    ualarm(1 * USPS, 1 * USPS);
    while (counter < 10)
        pause();

    return 0;

}

 --- setitimer() 实现 timer ---

除了 alarm/ualarm,标准还定义了 setitimer(),也可控制 SIGALRM。
(setitimer() 还可以控制 SIGVTALRM、SIGPROF,这里就不讨论了)。

struct timeval {
    long  tv_sec;       /* seconds */
    long  tv_usec;      /* and microseconds */

};

struct itimerval {
    struct timeval  it_interval;  /* timer interval */
    struct timeval  it_value;     /* current value */
};

/*
 * Names of the interval timers, and structure defining a timer setting.
 */
#define ITIMER_REAL     0   /* SIGALRM   */
#define ITIMER_VIRTUAL  1   /* SIGVTALRM */
#define ITIMER_PROF     2   /* SIGPROF   */

int setitimer(int which, const struct itimerval *value, struct itimerval
*oldvalue);

标准规定,只要 it_interval 不为 0,则 SIGALRM 会定期发送;否则,只发送一
次 SIGALRM。

#include <sys/time.h>
#include <signal.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>

static int counter = 0;

static void
my_alarm(int signo)
{
    printf("got it!\n");
    counter++;

}

int
main(void)
{
    struct itimerval new, old;

    /* 第一次 SIGALRM 的触发时间 */
    new.it_value.tv_sec  = 1;
    new.it_value.tv_usec = 0;

    /* 以后每次 SIGALRM 的触发间隔 */
    new.it_interval.tv_sec  = 1;
    new.it_interval.tv_usec = 0;

    if (signal(SIGALRM, my_alarm) == SIG_ERR)
    {
        perror("signal() error\n");
        exit(1);
    }

    if (setitimer(ITIMER_REAL, &new, &old) < 0)
    {
        printf("setitimer error : %s\n", strerror(errno));
        exit(1);
    }

    while ( counter < 10 )
        pause();

    return 0;

}

 --- alarm/ualarm 的实现 ---

上面可以看出,alarm/ualarm 的功能都包含在了 setitimer() 中,完全可以用
setitimer() 来实现 alarm/ualarm,OpenBSD 便是这么做的。其中,
alarm/ualarm 都是作为 library call 来实现的,而 setitimer 是 sys call。

/* /lib/libc/gen/ualarm.c */

#define USPS    1000000     /* # of microseconds in a second */

/*
 * Generate a SIGALRM signal in ``usecs'' microseconds.
 * If ``reload'' is non-zero, keep generating SIGALRM
 * every ``reload'' microseconds after the first signal.
 */
useconds_t
ualarm(useconds_t usecs, useconds_t reload)
{
    struct itimerval new, old;

    new.it_interval.tv_usec = reload % USPS;
    new.it_interval.tv_sec = reload / USPS;

    new.it_value.tv_usec = usecs % USPS;
    new.it_value.tv_sec = usecs / USPS;

    if (setitimer(ITIMER_REAL, &new, &old) == 0)
        return ((useconds_t)(old.it_value.tv_sec * USPS +
old.it_value.tv_usec));
    /* else */
        return ((useconds_t)-1);

}

/* /lib/libc/gen/alarm.c */

unsigned int
alarm(unsigned int secs)
{
    struct itimerval it, oitv;
    struct itimerval *itp = &it;

    timerclear(&itp->it_interval);
    itp->it_value.tv_sec = secs;
    itp->it_value.tv_usec = 0;
    if (setitimer(ITIMER_REAL, itp, &oitv) < 0)
        return ((unsigned int) -1);
    if (oitv.it_value.tv_usec)
        oitv.it_value.tv_sec++;
    return (oitv.it_value.tv_sec);

}

 --- 不可靠信号 ---

因为历史原因,某些系统上,使用 signal() 设定的信号处理函数,在信号处置时
会被重置为默认值。虽然可以通过如下办法来解决这个问题:

static void
my_alarm(int signo)
{
    signal(SIGALRM, my_alarm);
    printf("got it!\n");
    counter++;

}

但在特殊情况下依旧存在 bug,这在《UNIX 环境高级编程》中有详细讨论。因此
signal() 称为不可靠信号。为了保证安全,在设定信号的时候可以使用可靠信号
sigaction(),保证信号处理函数不会被重置。

linux 存在上面说的情况,FreeBSD 不存在,不过还是用 sigaction() 比较保险。
  评论这张
 
阅读(1447)| 评论(0)
推荐 转载

历史上的今天

在LOFTER的更多文章

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017