protothread是contiki的进程控制模型,是contiki关于进程的精华知识,详情可参考 。其它关于contiki的资料也可参考,里面介绍了关于Contiki的process、protothread、timer,以及communication stack的一些内容,归纳和总结了里面的一些库函数,这些其实在contiki的源码中都可以找到。
以contiki 2.6中最简单的example/hello_world.c为例。程序的代码如下:
-
PROCESS(hello_world_process, "Hello world process");
-
AUTOSTART_PROCESSES(&hello_world_process);
-
/*---------------------------------------------------------------------------*/
-
PROCESS_THREAD(hello_world_process, ev, data)
-
{
-
PROCESS_BEGIN();
-
-
printf("Hello, world\n");
-
-
PROCESS_END();
-
}
contiki程序有着非常规范的程序步骤,PROCESS宏是用来声明一个进程;AUTOSTART_PROCESS宏是使这个进程自启动,PROCESS_THREAD里面定义的是程序的主体,并且主体内部要以PROCESS_BEGIN()开头,以PROCESS_END()来结束。详细可参考
Contiki 宏定义 或者在线文档。接下来宏展开这些宏来看清它们的真面目:
PROCESS
PROCESS(hello_world_process, "Hello world process"); 展开代码如下:
-
static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data);
-
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 代码展开:
-
struct process hello_world_process = { ((void *)0), "Hello world process", process_thread_hello_world_process};
-
struct process * const autostart_processes[] = {&hello_world_process, ((void *)0)};
-
-
static char process_thread_hello_world_process(struct pt *process_pt, process_event_t ev, process_data_t data)
-
{
-
{
-
char PT_YIELD_FLAG = 1;
-
switch((process_pt)->lc)
-
{
-
case 0:
-
;
-
printf(“Hello, world!\n”); /* Initialization code goes here */
-
}
-
-
}
-
PT_YIELD_FLAG = 0;
-
(process_pt)->lc = 0;;
-
return 3;
-
}
-
}
process_start
要启动一个进程,必须要用到process_start函数,process_start所干的工作就是
-
先把将要执行的进程加入到进程队列process_list的首部,如果这个进程已经在process_list中,就return;
-
接下来就把state设置为PROCESS_STATE_RUNNING并且初始化pt的lc为0(这一步初始化很重要,关系到protothread进程模型的理解);
-
最后通过函数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)
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) |