Chinaunix首页 | 论坛 | 博客
  • 博客访问: 170273
  • 博文数量: 27
  • 博客积分: 566
  • 博客等级: 中士
  • 技术积分: 487
  • 用 户 组: 普通用户
  • 注册时间: 2007-10-07 19:07
文章分类

全部博文(27)

文章存档

2013年(23)

2012年(1)

2007年(3)

我的朋友

分类: 嵌入式

2013-04-18 13:53:01

protothread是contiki的进程控制模型,是contiki关于进程的精华知识,详情可参考 其它关于contiki的资料也可参考,里面介绍了关于Contiki的process、protothread、timer,以及communication stack的一些内容,归纳和总结了里面的一些库函数,这些其实在contiki的源码中都可以找到。

以contiki 2.6中最简单的example/hello_world.c为例。程序的代码如下:

  1. PROCESS(hello_world_process, "Hello world process");
  2. AUTOSTART_PROCESSES(&hello_world_process);
  3. /*---------------------------------------------------------------------------*/
  4. PROCESS_THREAD(hello_world_process, ev, data)
  5. {
  6.   PROCESS_BEGIN();

  7.   printf("Hello, world\n");
  8.   
  9.   PROCESS_END();
  10. }
contiki程序有着非常规范的程序步骤,PROCESS宏是用来声明一个进程;AUTOSTART_PROCESS宏是使这个进程自启动,PROCESS_THREAD里面定义的是程序的主体,并且主体内部要以PROCESS_BEGIN()开头,以PROCESS_END()来结束。详细可参考 Contiki 宏定义 或者在线文档。接下来宏展开这些宏来看清它们的真面目:

PROCESS

PROCESS(hello_world_process, "Hello world process"); 展开代码如下:

  1. static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data);
  2. struct process hello_world_process = { ((void *)0), "Hello world process", process_thread_hello_world_process};
声明了一个process_thread_hello_world_process的函数,以及一个 process 结构体 hello_world_process。完整的 C 代码展开:

  1. struct process hello_world_process = { ((void *)0), "Hello world process", process_thread_hello_world_process};
  2. struct process * const autostart_processes[] = {&hello_world_process, ((void *)0)};

  3. static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
  4. {
  5.     {
  6.         char PT_YIELD_FLAG = 1;
  7.         switch((process_pt)->lc)
  8.         {
  9.             case 0:
  10.             ;
  11.             printf(“Hello, world!\n”); /* Initialization code goes here */
  12.         }

  13.         }
  14.         PT_YIELD_FLAG = 0;
  15.         (process_pt)->lc = 0;;
  16.         return 3;
  17.     }
  18. }

process_start

要启动一个进程,必须要用到process_start函数,process_start所干的工作就是

  1. 先把将要执行的进程加入到进程队列process_list的首部,如果这个进程已经在process_list中,就return;
  2. 接下来就把state设置为PROCESS_STATE_RUNNING并且初始化pt的lc为0(这一步初始化很重要,关系到protothread进程模型的理解);
  3. 最后通过函数process_post_synch()给进程传递一个PROCESS_EVENT_INIT事件,让其开始执行

为什么process_post_synch()中要把process_current保存起来呢,process_current指向的是一个正在运行的process,当调用call_process执行这个hello_world这个进程时,process_current就会指向当前的process也就是hello_world这个进程,而hello_world这个进程它可能会退出或者正在被挂起等待一个事件,这时process_current = caller 语句正是要恢复先前的那个正在运行的process。

call_process

接下来展开call_process(),这个函数中才是真正的执行了hello_world_process的内容。假如进程process的状态是PROCESS_STATE_RUNNING以及进程中的thread函数不为空的话,就执行这个进程:
  • 首先把process_current指向p;
  • 接着把process的state改为PROCESS_STATE_CALLED;
  • 执行hello_world这个进程的body也就是函数p->thread,并将返回值保存在ret中,如果返回值表示退出或者遇到了PROCESS_EVENT_EXIT的时事件后,便执行exit_process()函数,process退出。不然程序就应该在挂起等待事件的状态,那么就继续把p的状态维持为PROCESS_STATE_RUNNING。

exit_process

exit_process函数有两个参数,前一个是要退出的process,后一个是当前的process。在这个进程要退出之前,必须要给所有的进程都要发送一个PROCESS_EVENT_EXITED事件,告诉所有与之相关的进程它要走了,如果收到这个事件的进程与要死的进程有关的话,自然会做出相应的处理(一个典型的例子是,如果一个程序要退出的话,就会给etimer_process进程发送一个PROCESS_EVENT_EXITED事件,那么收到这个事件之后,etimer_process就会查找timerlist看看哪个timer是与这个进程相关的,就把它从timerlist中清除)。

如果要退出的process就是当前的process,那么就只需要把它从进程列表中清除即可;如果要退出的process不是当前的process,那么当前的process就要给要退出的process传递一个PROCESS_EVENT_EXIT事件让其进程的body退出,然后再将其从进程列表中清除。

总结来说,当Protothread程序运行到PROCESS_WAIT_EVENT()时,判断其运行条件是否满足,若不满足,则阻塞。通过PROCESS_WAIT_EVENT宏展开的程序代码可以得知,Protothread的阻塞其实质就是函数返回,只不过在返回前保存了当前的阻塞位置(也就是lc值),待下一次Protothread被调用时,通过switch语句直接跳到阻塞位置执行,再次判断运行条件是否满足,并执行后续程序或继续阻塞。

Protothread具有以下的特色和优点:
  • A design point between events and threads
  • Programming primitive: conditional blocking wait
            PT_WAIT_UNTIL(condition)
  • 3.Single stack
            Low memory usage, just like events
  • Sequential flow of control
            No explicit state machine(状态机), just like threads
            Programming language helps us: if and while

NOTE:
  • 在PROCESS_THREAD内部,auto变量在process挂起时也就是函数返回时是不会被保存的,所以如果需要的话,要把变量设置为static的,这是为了使进程挂起的时候变量的值还在。
  • 在PROCESS_THREAD内部不要用switch语句。


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