Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1641485
  • 博文数量: 124
  • 博客积分: 4078
  • 博客等级: 中校
  • 技术积分: 3943
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 11:28
个人简介

新博客:http://sparkandshine.net/

文章分类

全部博文(124)

分类: 嵌入式

2011-10-18 17:02:41

摘要:

    本文给出两个交互进程的实例,先给出程序源代码,运行截图,接着解读该程序,最后深入源码详细分析。


创建两个进程,一个打印Hello,另一个打印World,使其交互打印。

一、源代码

    声明进程print_hello_process & print_world_process,所涉及宏参见博文《Contiki学习笔记:实例hello_world剖析》:

  1. //filename:interact_two_process.c
  2. #include "contiki.h"
  3. #include "debug-uart.h"

  4. static process_event_t event_data_ready;

  5. /*声明两个进程*/
  6. PROCESS(print_hello_process, "Hello");
  7. PROCESS(print_world_process, "world");

  8. AUTOSTART_PROCESSES(&print_hello_process, &print_world_process);  //让该两进程自启动

定义进程print_hello_process:

  1. /*定义进程print_hello_process*/
  2. PROCESS_THREAD(print_hello_process, ev, data)
  3. {
  4.   PROCESS_BEGIN();
  5.   static struct etimer timer;

  6.   etimer_set(&timer, CLOCK_CONF_SECOND / 10); //#define CLOCK_CONF_SECOND 10将timer的interval设为1 详情见4.1

  7.   usart_puts("***print hello process start***\n");

  8.   event_data_ready = process_alloc_event(); //return lastevent++; 新建一个事件,事实上是用一组unsigned char值来标识不同事件

  9.   while (1)
  10.   {
  11.     PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); //详情见4.2,即etimer到期继续执行下面内容

  12.     usart_puts("Hello\n");

  13.     process_post(&print_world_process, event_data_ready, NULL); //传递异步事件给print_world_process,直到内核调度该进程才处理该事件。详情见4.3

  14.     etimer_reset(&timer); //重置定时器,详情见4.4
  15.   }

  16.   PROCESS_END();
  17. }

定义进程print_world_process:

  1. /*定义进程print_world_process*/
  2. PROCESS_THREAD(print_world_process, ev, data)
  3. {
  4.   PROCESS_BEGIN();

  5.   usart_puts("***print world process start***\n");

  6.   while (1)
  7.   {
  8.     PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready);
  9.     usart_puts("world\n");
  10.   }

  11.   PROCESS_END();
  12. }

二、运行结果截图

三、程序解读

    系统启动,执行一系列初始化(串口、时钟、进程等),接着启动系统进程etimer_process,而后启动进程print_hello_process和print_world_process。那么print_hello_process与print_world_process是怎么交互的呢?

    进程print_hello_process一直执行到PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER),此时etimer还没到期,进程被挂起。转去执行print_world_process,待执行到PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready)被挂起(因为print_hello_process还没post事件)。而后再转去执行系统进程etimer_process,若检测到etimer到期,则继续执行print_hello_process,打印Hello,并传递事件event_data_ready给print_world_process,初始化timer,待执行到PROCESS_WAIT_EVENT_UNTIL(while死循环),再次被挂起。转去执行print_world_process,打印world,待执行到PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready)又被挂起。再次执行系统进程etimer_process,如此反复执行。

四、源码详解

4.1 etimer_set函数

  1. //etimer_set(&timer, CLOCK_CONF_SECOND/10);
  2. void etimer_set(struct etimer *et, clock_time_t interval)
  3. {
  4.   timer_set(&et->timer, interval)//设置定时器timer,源码见2.1.1
  5.   add_timer(et);    //将这个etimer加入timerlist
  6. }

4.1.1 timer_set函数

  1. //用当前时间初始化start,并设置间隔interval
  2. void timer_set(struct timer *t, clock_time_t interval)
  3. {
  4.   t->interval = interval;
  5.   t->start = clock_time(); //return current_clock;
  6. }

  7. clock_time_t clock_time(void)
  8. {
  9.   return current_clock;
  10. }

  11. static volatile clock_time_t current_clock = 0;
  12. typedef unsigned int clock_time_t;

4.1.2 add_timer函数

  1. /*将etimer加入timerlist,并update_time(),即求出下一个到期时间next_expiration*/
  2. static void add_timer(struct etimer *timer)
  3. {
  4.   struct etimer *t;

  5.   etimer_request_poll(); //事实上执行process_poll(&etimer_process),即将进程的needspoll设为1,使其获得更高优先级,详情见下方
  6.       
  7.   /*参数验证,确保该etimer不是已处理过的*/
  8.   if (timer->p != PROCESS_NONE) //如果是PROCESS_NONE,则表示之前处理过,该etimer已到期(注1)
  9.   {
  10.     for (t = timerlist; t != NULL; t = t->next)
  11.     {
  12.       if (t == timer) /* Timer already on list, bail out. */
  13.       {
  14.         update_time(); //即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration时间,就有timer到期
  15.         return ;
  16.       }
  17.     }
  18.   }

  19.   /*将timer加入timerlist*/
  20.   timer->p = PROCESS_CURRENT();
  21.   timer->next = timerlist;
  22.   timerlist = timer;

  23.   update_time(); //即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration时间,就有timer到期
  24. }

注1:关于PROCESS_NONE可以参考博文《Contiki学习笔记:系统进程etimer_process》。

函数etimer_request_poll直接调用process_poll,一步步展开如下:

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

  5. void process_poll(struct process *p)
  6. {
  7.   if (p != NULL)
  8.   {
  9.     if (p->state == PROCESS_STATE_RUNNING || p->state == PROCESS_STATE_CALLED)
  10.     {
  11.       p->needspoll = 1;
  12.       poll_requested = 1//全局静态变量,标识是否有进程的needspoll为1
  13.     }
  14.   }
  15. }

4.2 PROCESS_WAIT_EVENT_UNTIL宏

  1. //PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);

  2. /*宏PROCESS_WAIT_EVENT_UNTIL,直到条件c为真时,进程才得以继续向前推进*/
  3. #define PROCESS_WAIT_EVENT_UNTIL(c) PROCESS_YIELD_UNTIL(c)

  4. #define PROCESS_YIELD_UNTIL(c) PT_YIELD_UNTIL(process_pt, c) //Yield the currently running process until a condition occurs

  5. #define PT_YIELD_UNTIL(pt, cond)        \
  6.   do \
  7.   {                        \
  8.     PT_YIELD_FLAG = 0;                \
  9.     LC_SET((pt)->lc);                \         //注:保存断点,下次从这里开始执行!!!
  10.     if((PT_YIELD_FLAG == 0) || !(cond)) {    \
  11.       return PT_YIELDED;            \
  12.     }                        \
  13.   } while(0)
  14.   
  15.   
  16. #define LC_SET(s) s = __LINE__; case __LINE__//保存行数,即lc被设置成该LC_SET所在的行

    从源代码可以看出,第一次执行PROCESS_WAIT_EVENT_UNTIL总是被挂起(因为PT_YIELD_FLAG==0为真,会直接执行return PT_YIELDED)。

    该进程下一次被调试的时候,总是从PROCESS_BEGIN()开始,而PROCESS_BEGIN宏包含两条语句:其一,将PT_YIELD_FLAG置1;再者,switch(pt->lc),从而跳转到case __LINE__(见上述源码的注)。接着判断if条件,此时PT_YIELD_FLAG=1,若条件为真,则不执行return,继续后续的内容,从而进程接着执行。

4.3 process_post函数

    精简后(去除一些用于调试的代码)源码如下,详情参考博文《Contiki学习笔记:系统进程etimer_process》2.3:

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

  5.   if (nevents == PROCESS_CONF_NUMEVENTS)
  6.   {
  7.     return PROCESS_ERR_FULL;
  8.   }

  9.   snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
  10.   events[snum].ev = ev;
  11.   events[snum].data = data;
  12.   events[snum].p = p;
  13.   ++nevents;

  14.   #if PROCESS_CONF_STATS
  15.     if (nevents > process_maxevents)
  16.     {
  17.       process_maxevents = nevents;
  18.     }
  19.   #endif

  20.   return PROCESS_ERR_OK;
  21. }

4.4 etimer_reset函数

  1. /*Reset the timer with the same interval.*/
  2. void etimer_reset(struct etimer *et)
  3. {
  4.   timer_reset(&et->timer);
  5.   add_timer(et); //详情见4.1.2
  6. }

  7. void timer_reset(struct timer *t)
  8. {
  9.   t->start += t->interval;  //为什么不t->start = clock_time()?为了程序可预测性?
  10. }

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