免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1213 | 回复: 0
打印 上一主题 下一主题

Linux 下定时器的实现方式分析 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2009-11-20 10:16 |只看该作者 |倒序浏览
定时器属于基本的基础组件,不管是用户空间的程序开发,还是内核空间的程序开发,很多时候都需要有定时器作为基础组件的支持,但
使用场景的不同,对定时器的实现考虑也不尽相同,本文讨论了在 Linux
环境下,应用层和内核层的定时器的各种实现方法,并分析了各种实现方法的利弊以及适宜的使用环境。
概论
定时器属于基本的基础组件,不管是用户空间的程序开发,还是内核空间的程序开发,很多时候都需要有定时器作为基础组件的支持,但使用场景的不同,对
定时器的实现考虑也不尽相同,本文讨论了在 Linux
环境下,应用层和内核层的定时器的各种实现方法,并分析了各种实现方法的利弊以及适宜的使用环境。
首先,给出一个基本模型,定时器的实现,需要具备以下几个行为,这也是在后面评判各种定时器实现的一个基本模型 [1]:
StartTimer(Interval, TimerId, ExpiryAction)
注册一个时间间隔为 Interval 后执行 ExpiryAction 的定时器实例,其中,返回 TimerId 以区分在定时器系统中的其他定时器实例。
StopTimer(TimerId)
根据 TimerId 找到注册的定时器实例并执行 Stop 。
PerTickBookkeeping()
在一个 Tick 内,定时器系统需要执行的动作,它最主要的行为,就是检查定时器系统中,是否有定时器实例已经到期。注意,这里的 Tick 实际上已经隐含了一个时间粒度 (granularity) 的概念。
ExpiryProcessing()
在定时器实例到期之后,执行预先注册好的 ExpiryAction 行为。
上面说了基本的定时器模型,但是针对实际的使用情况,又有以下 2 种基本行为的定时器:
Single-Shot Timer
这种定时器,从注册到终止,仅仅只执行一次。
Repeating Timer
这种定时器,在每次终止之后,会自动重新开始。本质上,可以认为 Repeating Timer 是在 Single-Shot Timer
终止之后,再次注册到定时器系统里的 Single-Shot Timer,因此,在支持 Single-Shot Timer 的基础上支持
Repeating Timer 并不算特别的复杂。
基于链表和信号实现定时器 (2.4 版内核情况下 )
在 2.4 的内核中,并没有提供 POSIX timer [ 2 ]的支持,要在进程环境中支持多个定时器,只能自己来实现,好在
Linux 提供了 setitimer(2) 的接口。它是一个具有间隔功能的定时器 (interval
timer),但如果想在进程环境中支持多个计时器,不得不自己来管理所有的计时器。 setitimer(2) 的定义如下:
清单 1. setitimer 的原型
   
        
            
            #include  
int setitimer(int which, const struct itimerval *new_value,struct itimerval *old_value);
            
        
   
setitimer 能够在 Timer 到期之后,自动再次启动自己,因此,用它来解决 Single-Shot Timer 和 Repeating Timer 的问题显得很简单。该函数可以工作于 3 种模式:
ITIMER_REAL 以实时时间 (real time) 递减,在到期之后发送 SIGALRM 信号
ITIMER_VIRTUAL 仅进程在用户空间执行时递减,在到期之后发送 SIGVTALRM 信号
ITIMER_PROF 进程在用户空间执行以及内核为该进程服务时 ( 典型如完成一个系统调用 ) 都会递减,与 ITIMER_VIRTUAL 共用时可度量该应用在内核空间和用户空间的时间消耗情况,在到期之后发送 SIGPROF 信号
定时器的值由下面的结构定义:
清单 2. setitimer 定时器的值定义
   
        
            
            struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value;     /* current value */
};
struct timeval {
        long tv_sec;                /* seconds */
        long tv_usec;               /* microseconds */
};
            
        
   
setitimer() 以 new_value 设置特定的定时器,如果 old_value 非空,则它返回 which
类型时间间隔定时器的前一个值。定时器从 it_value 递减到零,然后产生一个信号,并重新设置为 it_interval,如果此时
it_interval 为零,则该定时器停止。任何时候,只要 it_value 设置为零,该定时器就会停止。
由于 setitimer()
不支持在同一进程中同时使用多次以支持多个定时器,因此,如果需要同时支持多个定时实例的话,需要由实现者来管理所有的实例。用
setitimer() 和链表,可以构造一个在进程环境下支持多个定时器实例的 Timer,在一般的实现中的
PerTickBookkeeping 时,会递增每个定时器的 elapse 值,直到该值递增到最初设定的 interval 则表示定时器到期。
基于链表实现的定时器可以定义为:
清单 3. 基于链表的定时器定义
   
        
            
            typedef int timer_id;
/**
* The type of callback function to be called by timer scheduler when a timer
* has expired.
*
* @param id                The timer id.
* @param user_data        The user data.
* $param len               The length of user data.
*/
typedef int timer_expiry(timer_id id, void *user_data, int len);
/**
* The type of the timer
*/
struct timer {
        LIST_ENTRY(timer) entries;/** interval             */
        timer_expiry *cb;          /**
            
        
   
定时器的时间间隔以 interval 表示,而 elapse 则在 PerTickBookkeeping() 时递增,直到
interval 表示定时器中止,此时调用回调函数 cb 来执行相关的行为,而 user_data 和 len 为用户可以传递给回调函数的参数。
所有的定时器实例以链表来管理:
清单 4. 定时器链表
   
        
            
            /**
* The timer list
*/
struct timer_list {
        LIST_HEAD(listheader, timer) header;  /**
            
        
   
这里关于链表的实现使用了 BSD 风格关于链表的一组宏,避免了再造轮子;该结构中,old_sigfunc 在 init_timer
初始定时器链表时候用来保存系统对 SIGALRM 的处理函数,在定时器系统 destory 时用来恢复到之前的处理函数; ovalue
的用途与此类似。
清单 5. 定时器链表的创建和 Destroy
   
        
            
            /**
* Create a timer list.
*
* @param count  The maximum number of timer entries to be supported initially.
*
* @return        0 means ok, the other means fail.
*/
int init_timer(int count)
{
        int ret = 0;
        if(count  MAX_TIMER_NUM) {
               printf("the timer max number MUST less than %d.\n", MAX_TIMER_NUM);
               return -1;
        }
        memset(&timer_list, 0, sizeof(struct timer_list));
        LIST_INIT(&timer_list.header);
        timer_list.max_num = count;
        /* Register our internal signal handler and store old signal handler */
        if ((timer_list.old_sigfunc = signal(SIGALRM, sig_func)) == SIG_ERR) {
                return -1;
        }
        timer_list.new_sigfunc = sig_func;
     /*Setting our interval timer for driver our mutil-timer and store old timer value*/
        timer_list.value.it_value.tv_sec = TIMER_START;
        timer_list.value.it_value.tv_usec = 0;
        timer_list.value.it_interval.tv_sec = TIMER_TICK;
        timer_list.value.it_interval.tv_usec = 0;
        ret = setitimer(ITIMER_REAL, &timer_list.value, &timer_list.ovalue);
        return ret;
}
/**
* Destroy the timer list.
*
* @return          0 means ok, the other means fail.
*/
int destroy_timer(void)
{
        struct timer *node = NULL;
        if ((signal(SIGALRM, timer_list.old_sigfunc)) == SIG_ERR) {
                return -1;
        }
        if((setitimer(ITIMER_REAL, &timer_list.ovalue, &timer_list.value)) id);
                free(node->user_data);
                free(node);
        }
        memset(&timer_list, 0, sizeof(struct timer_list));
        return 0;
}
            
        
   
添加定时器的动作非常的简单,本质只是一个链表的插入而已:
清单 6. 向定时器链表中添加定时器
   
        
            
            /**
* Add a timer to timer list.
*
* @param interval  The timer interval(second).
* @param cb              When cb!= NULL and timer expiry, call it.
* @param user_data Callback's param.
* @param len              The length of the user_data.
*
* @return          The timer ID, if == INVALID_TIMER_ID, add timer fail.
*/
timer_id  add_timer(int interval, timer_expiry *cb, void *user_data, int len)
{
        struct timer *node = NULL;
        if (cb == NULL || interval user_data = malloc(len);
                 memcpy(node->user_data, user_data, len);
                 node->len = len;
         }
         node->cb = cb;
         node->interval = interval;
         node->elapse = 0;
         node->id = timer_list.num;
         LIST_INSERT_HEAD(&timer_list.header, node, entries);
    return node->id;
}
http://www.linuxeden.com/html/develop/20091105/69004.html


本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u2/88379/showart_2099009.html
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP