Contiki学习笔记:系统进程etimer_process

7528阅读 0评论2011-10-23 Jelline
分类:嵌入式

摘要:

    系统进程etimer_process管理timelist,本文先给出etimer_process的启动,而后深入源码分析etimer_process的执行主体,追踪了其所涉及到的宏和函数。


一、启动etimer_process

    在main函数中,先是进行一系列初始化,接着启动系统进程etimer_process和指针数组autostart_processes[]里的所有进程。源代码如下:

  1. int main()
  2. {
  3.   dbg_setup_uart();
  4.   clock_init();
  5.   process_init();

  6.   process_start(&etimer_process, NULL); //启动etimer_process
  7.   autostart_start(autostart_processes); //启动指针数组autostart_processes[]里的所有进程

  8.   while (1)
  9.   {
  10.     do
  11.     {}while (process_run() > 0);
  12.   }
  13.   return 0;
  14. }

    关于process_start启动一个进程,可以参考博文《Contiki学习笔记:启动一个进程process_start》。


二、etimer_process执行主体剖析

    在博文《Contiki学习笔记:启动一个进程process_start》中,我们得知,进程退出时,需向所有进程(当然也包括etimer_process)发送事件PROCESS_EVENT_EXITED。etimer_process收到该事件,就会遍历timerlist,并把与该退出进程相关的etimer从timerlist删除。紧接着,遍历timerlist,检查etimer是否有到期的,凡有timer到期就把事件PROCESS_EVENT_TIMER加入到事件队列中,并将该etimer成员变量p指向PROCESS_NONE。在这里,PROCESS_NONE用于标识该etimer是否到期,即由etimer_expired函数根据etimer的p是否指向PROCESS_NONE来判断该etimer是否到期。源代码如下:

  1. //PROCESS(etimer_process, "Event timer");
  2. PROCESS_THREAD(etimer_process, ev, data)
  3. {
  4.   struct etimer *t, *u;

  5.   PROCESS_BEGIN();

  6.   timerlist = NULL;

  7.   while (1)
  8.   {
  9.     PROCESS_YIELD(); /*见2.1*/

  10.     /*进程退出时,需向所有进程(当然也包括etimer_process)发送事件PROCESS_EVENT_EXITED。etimer_process收到该事件,就会遍历timerlist,并把与该退出进程相关的etimer从timerlist删除*/
  11.     if (ev == PROCESS_EVENT_EXITED)
  12.     {
  13.       struct process *p = data;  //此时通过data传递将要退出的进程,data是void *类型

  14.       /*遍历timerlist,查找是否有etimer绑定该退出进程*/
  15.       while (timerlist != NULL && timerlist->p == p)
  16.       {
  17.         timerlist = timerlist->next;
  18.       }

  19.       /*有etimer绑定该退出进程,将etimer从timerlist删除*/
  20.       if (timerlist != NULL)
  21.       {
  22.         t = timerlist;
  23.         while (t->next != NULL)
  24.         {
  25.           if (t->next->p == p)
  26.           {
  27.             t->next = t->next->next;
  28.           }
  29.           else
  30.             t = t->next;
  31.         }
  32.       }

  33.       continue; //删除所有与退出进程绑定的etimer
  34.     }
  35.     else if (ev != PROCESS_EVENT_POLL)  //不解?
  36.     {
  37.       continue;
  38.     }

  39.     again:

  40.     u = NULL;
  41.     for (t = timerlist; t != NULL; t = t->next)
  42.     {
  43.       if (timer_expired(&t->timer)) //检查该etimer的timer是不是到期,返回1表示过期,详情见2.2
  44.       {
  45.         if (process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK//详情见2.3,即把事件PROCESS_EVENT_TIMER加入到事件队列
  46.         {
  47.           /*成功加入事件队列*/

  48.           t->p = PROCESS_NONE; //如果etimer的p指向的是PROCESS_NONE,则表示该etimer已到期。用于后续etimer_expired()函数判断该etimer是否到期,etimer_expired(),也见2.2

  49.           if (u != NULL)
  50.           {
  51.             u->next = t->next;
  52.           }
  53.           else
  54.           {
  55.             timerlist = t->next;
  56.           }
  57.           t->next = NULL;
  58.           update_time(); /*见2.4,即求出下一个到期时间next_expiration (全局静态变量),即还有next_expiration时间,就有timer到期*/
  59.           goto again;
  60.         }
  61.         else
  62.         {
  63.           etimer_request_poll(); //详情见2.5 若加入事件PROCESS_EVENT_TIMER出错(可能是事件队列已满),执行etimer_request_poll(),即process_poll(&etimer_process),使其有更高的优先级
  64.         }
  65.       }
  66.       u = t;
  67.     }

  68.   }

  69.   PROCESS_END();
  70. }

2.1 PROCESS_YIELD宏

  1. /*即进程主动让出执行权*/
  2. #define PROCESS_YIELD() PT_YIELD(process_pt)

  3. /*继续展开如下,Yield from the current protothread*/
  4. #define PT_YIELD(pt) \
  5. do { \
  6. PT_YIELD_FLAG = 0; \
  7. LC_SET((pt)->lc); \
  8. if(PT_YIELD_FLAG == 0) { \
  9. return PT_YIELDED; \
  10. } \
  11. } while(0)

  12. #define LC_SET(s) s = __LINE__; case __LINE__:  /*保存被中断的行数*/

2.2 timer_expired和etimer_expired

2.2.1 timer_expired

  1. /*即检查timer是不是到期了,若到期返回1,否则返回0*/
  2. int timer_expired(struct timer *t)
  3. {
  4.   clock_time_t diff = (clock_time() - t->start) + 1;
  5.   return t->interval < diff;
  6. }

注:为什么不直接return diff >= t->interval,源代码注释给出原因,如下:

  1. Note: Can not return diff >= t->interval so we add 1 to diff and return t->interval < diff - required to avoid an internal error in mspgcc.

2.2.2 etimer_expired

    etimer_expired跟timer_expired不一样,后者到期是指定时器超时(通过比较当前时间与start+interval来判断timer是否到期),而前者到期是指已发送事件PROCESS_EVENT_TIMER给etimer绑定的进程(通过etimer的p指向是否为PROCESS_NONE来判断)。源码如下:

  1. int etimer_expired(struct etimer *et)
  2. {
  3.   return et->p == PROCESS_NONE;
  4. }

2.3 process_post函数

  1. //即把把事件加入到事件队列
  2. int process_post(struct process *p, process_event_t ev, process_data_t data)
  3. {
  4.   static process_num_events_t snum;

  5.   /*调试信息,直接无视*/
  6.   if (PROCESS_CURRENT() == NULL)
  7.   {
  8.     PRINTF(
  9.       "process_post: NULL process posts event %d to process '%s', nevents %d\n",
  10.       ev, PROCESS_NAME_STRING(p), nevents);
  11.   }
  12.   else
  13.   {
  14.     PRINTF(
  15.       "process_post: Process '%s' posts event %d to process '%s', nevents %d\n",
  16.       PROCESS_NAME_STRING(PROCESS_CURRENT()), ev, p == PROCESS_BROADCAST ?
  17.       "": PROCESS_NAME_STRING(p), nevents);
  18.   }

  19.   /*事件队列已满,返回PROCESS_ERR_FULL,即1*/
  20.   if (nevents == PROCESS_CONF_NUMEVENTS)
  21.   {
  22.     #if DEBUG
  23.       if (p == PROCESS_BROADCAST)
  24.       {
  25.         printf(
  26.           "soft panic: event queue is full when broadcast event %d was posted from %s\n", ev, PROCESS_NAME_STRING(process_current));
  27.       }
  28.       else
  29.       {
  30.         printf(
  31.           "soft panic: event queue is full when event %d was posted to %s frpm %s\n", ev, PROCESS_NAME_STRING(p), PROCESS_NAME_STRING(process_current));
  32.       }
  33.     #endif /* DEBUG */
  34.     return PROCESS_ERR_FULL;
  35.   }

  36.   /*将事件加入事件队列,并绑定进程p*/
  37.   snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
  38.   //事件队列用环形组织
  39.   events[snum].ev = ev;
  40.   events[snum].data = data;
  41.   events[snum].p = p;
  42.   ++nevents;

  43.   #if PROCESS_CONF_STATS
  44.     if (nevents > process_maxevents)
  45.     {
  46.       process_maxevents = nevents;
  47.     }
  48.   #endif /* PROCESS_CONF_STATS */

  49.   return PROCESS_ERR_OK;
  50. }

2.4 update_time函数

  1. /*即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration时间,就有timer到期*/
  2. static void update_time(void)
  3. {
  4.   clock_time_t tdist;
  5.   clock_time_t now;
  6.   struct etimer *t;

  7.   if (timerlist == NULL)
  8.   {
  9.     next_expiration = 0;
  10.   }
  11.   else
  12.   {
  13.     now = clock_time();
  14.     t = timerlist;

  15.     /*遍历timerlist,找出最近到期的timer,并求得下一个到期时间next_expiration*/
  16.     tdist = t->timer.start + t->timer.interval - now; //还有tdist就到期了
  17.     for (t = t->next; t != NULL; t = t->next)
  18.     {
  19.       if (t->timer.start + t->timer.interval - now < tdist)
  20.       {
  21.         tdist = t->timer.start + t->timer.interval - now;
  22.       }
  23.     }
  24.     next_expiration = now + tdist;
  25.   }
  26. }

2.5 etimer_request_poll函数

  1. void etimer_request_poll(void)
  2. {
  3.   process_poll(&etimer_process);
  4. }

  5. //将进程p的needspoll设为1,使其获得更高优先级,即让etimer_process更快地再次获得执行权
  6. void process_poll(struct process *p)
  7. {
  8.   if (p != NULL)
  9.   {
  10.     if (p->state == PROCESS_STATE_RUNNING || p->state == PROCESS_STATE_CALLED)
  11.     {
  12.       p->needspoll = 1;
  13.       poll_requested = 1; //全局变量,标识是否有needspoll为1的进程
  14.     }
  15.   }
  16. }

上一篇:Contiki学习笔记:进程、事件、etimer关系
下一篇:Contiki学习笔记:目录