contiki-定时器etimer

  Contiki内核是基于事件驱动和Protothreads机制,事件既可以是外部事件(比如按键,数据到达),也可以是内部事件(如时钟中断)。定时器的重要性不言而喻,Contiki提供了5种定时器模型,即timer(描述一段时间,以系统时钟滴答数为单位),stimer(描述一段时间,以秒为单位),ctimer(定时器到期,调用某函数,用于Rime协议栈),etimer(定时器到期,触发一个事件),rtimer(实时定时器,在一个精确的时间调用函数)。

  鉴于etimer在Contiki使用的广泛性,管理这些etimer由系统进程etimer_process管理,本小节详细简单介绍etimer相关技术细节。

etimer组织结构

  etimer作为一类特殊事件存在,也是跟进程绑定。除此之外,还需要变量描述定时器属性,etimer结构体如下:

/**
 * A timer.
 *
 * This structure is used for declaring a timer. The timer must be set
 * with etimer_set() before it can be used.
 *
 * \hideinitializer
 */
struct etimer {
  struct timer timer; //包含起始时刻和间隔两成员变量
  struct etimer *next; //指向下一个etimer
  struct process *p;
};
/**
 * A timer.
 *
 * This structure is used for declaring a timer. The timer must be set
 * with timer_set() before it can be used.
 *
 * \hideinitializer
 */
struct timer {
  clock_time_t start;
  clock_time_t interval;
};

  成员变量timer用于描述定时器属性,包含起始时刻及间隔,将起始时刻与间隔相加与当前时钟对比,便可知道是否到期。变量p指向所绑定的进程(p为NULL则表示该定时器与所有进程绑定)。成员变量next,指向下一个etimer,系统所有etimer被链接成一个链表,链表头是timerlist,如下图所示:

static struct etimer *timerlist;

 

添加etimer

  定义一个etimer结构体,调用etimer_set函数将etimer添加到timerlist,函数etimer_set流程图如下:

void
etimer_set(struct etimer *et, clock_time_t interval)
{
  timer_set(&et->timer, interval);//设置起始时刻和时间间隔
  add_timer(et);//将etimer添加到timerlist
}
/**
 * Set a timer.
 *
 * This function is used to set a timer for a time sometime in the
 * future. The function timer_expired() will evaluate to true after
 * the timer has expired.
 *
 * \param t A pointer to the timer
 * \param interval The interval before the timer expires.
 *
 */
void timer_set(struct timer *t, clock_time_t interval)
{
  t->interval = interval; //设置时间间隔
  t->start = clock_time(); //设置起始时刻
}
static void add_timer(struct etimer *timer)
{
  struct etimer *t;

  etimer_request_poll();//提升etimer系统进程的优先级

  if(timer->p != PROCESS_NONE) {//该etimer处理过
    for(t = timerlist; t != NULL; t = t->next) {
      if(t == timer) {
        /* Timer already on list, bail out. */
        /*此etimer已经在etimer链表中*/
        timer->p = PROCESS_CURRENT();//将现有进程赋给此etimer的进程
        update_time();
        return;
      }
    }
  }

  /* Timer not on list. */
  /*此etimer不在etimer链表中,放到etimer链表表头*/
  timer->p = PROCESS_CURRENT();
  timer->next = timerlist;
  timerlist = timer;

  update_time();
}

  etimer_set首先设置etimer成员变量timer的值(由timer_set函数完成),即用当前时间初始化start,并设置间隔interval,接着调用add_timer函数,该函数首先将管理etimer系统进程etimer_process优先级提升,以便定时器时间到了可以得到更快的响应。接着确保欲加入的etimer不在timerlist中(通过遍历timerlist实现),若该etimer已经在etimer链表中,则无须将etimer加入链表,仅更新时间。否则将该etimer插入到timerlist链表头位置,并更新事件(update_timer)。这里更新时间的意思是求出etimer链表中,还需要多长next_expiration(全局静态变量)时间,就会有etimer到期。

etimer管理

  Contiki用一个系统进程etimer_process管理所有的etimer定时器。进程退出时,会向所有进程发送事件PROCESS_EVENT_EXITED,当然也包括etimer系统进程etimer_process。当etimer_process拥有执行权的时候,便查看是否有相应的etimer绑定到该进程,若有就删除这些etimer。除此之外,etimer_process还会处理到期的etimer,etimer_process的thread函数流程图如下:

 

 

  

PROCESS_THREAD(etimer_process, ev, data)
{
  struct etimer *t, *u;
    
  PROCESS_BEGIN();

  timerlist = NULL;
  
  while(1) {
    PROCESS_YIELD();
    /*事件是否为退出事件*/
    if(ev == PROCESS_EVENT_EXITED) {
        struct process *p = data;
        
        while(timerlist != NULL && timerlist->p == p) {
            timerlist = timerlist->next;
        }
        /*如果有退出进程绑定etimer,删除所有相关的etimer*/
        if(timerlist != NULL) {
            t = timerlist;
            while(t->next != NULL) {
                if(t->next->p == p) {
                    t->next = t->next->next;
                } 
                else
                    t = t->next;
            }
        }
        continue;
    } else if(ev != PROCESS_EVENT_POLL) {
      continue;
    }

  again:
    
    u = NULL;
    /*处理所有到期的etimer*/
    for(t = timerlist; t != NULL; t = t->next) {
        if(timer_expired(&t->timer)) {
        if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) {
      
            /* Reset the process ID of the event timer, to signal that the
             etimer has expired. This is later checked in the
             etimer_expired() function. */
            t->p = PROCESS_NONE;
            if(u != NULL) {
                u->next = t->next;
            } else {
                timerlist = t->next;
            }
            t->next = NULL;
            update_time();
            goto again;
            } else {
                etimer_request_poll();
            }
        }
      u = t;
    }
    
  }
  
  PROCESS_END();
}

  etimer_process获得执行权时,若传递的是退出事件,遍历整个timerlist,将与该进程(通过参数data传递)相关的etimer从timerlist删除,而后转去所有到期的etimer。通过遍历整个etimer查看到期的etimer,若有到期,发绑定的进程触发事件PROCESS_EVENT_TIMER,并将etimer的进程指针设为空(事件已加入事件队列,处理完毕),接着删除该etimer,求出下一次etimer到期时间,继续检查是否还有etimer到期。提升etimer_process优先级,若接下来都没有etimer到期了,就退出。总之,遍历timerlist,只要etimer到期,处理之后重头遍历整个链表,直到timerlist没有到期的etimer就退出。

参考博客:http://jelline.blog.chinaunix.net

posted @ 2016-12-04 23:37  一只奋斗的考拉  阅读(1761)  评论(0编辑  收藏  举报