本文结合Contiki OS实例分析protothread四种状态:PT_WAITING
、PT_YIELDED
、PT_EXITED
、PT_ENDED
,并给出Contiki事件相关函数与protothread状态,最后给出系统编程的参考API。
1. PT四种状态
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED 2
#define PT_ENDED 3
Contiki事件相关函数与protothread状态关系如下图(注-没有包含所有函数):
protothread状态示意图visio源文件:protothread状态.vsd。
1.1 PT_WAITING
宏PROCESS_WAIT_UNTIL(c)
用到了PT_WAITING
,该宏用于挂起进程直到条件c
成立,源码如下:
#define PROCESS_WAIT_UNTIL(c) PT_WAIT_UNTIL(process_pt, c)
宏PROCESS_WAIT_UNTIL(c)
不保证进程会让出执行权,源码注释如下:
This macro does not guarantee that the process yields, and should therefore be used with care. In most cases, PROCESS_WAIT_EVENT(),PROCESS_WAIT_EVENT_UNTIL(), PROCESS_YIELD() or PROCESS_YIELD_UNTIL() should be used instead.
宏PT_WAIT_UNTIL
并不保证进程会被挂起,当条件为真时,继续执行后续内容。宏展开如下:
#define PT_WAIT_UNTIL(pt, condition) \
do
{ \
LC_SET((pt)->lc); \
if(!(condition))
{ \
return PT_WAITING; \
} \
} while(0)
在PT_RESTART
宏也用到了PT_WAITING
,源码如下:
//Restart the protothread.
#define PT_RESTART(pt) \
do
{ \
PT_INIT(pt); \
return PT_WAITING; \
} while(0)
1.2 PT_YIELDED
(1)PT_YIELD_FLAG = 0
PT_YIELD_FLAG = 0
表示条件不满足,主动让出执行权。PT_END(pt)
、PT_YIELD(pt)
、PT_YIELD_UNTIL(pt, cond)
将PT_YIELD_FLAG
设置成0。
(2)PT_YIELD_FLAG =1
PT_YIELD_FLAG = 1
表示条件满足,继续执行后续代码。在PT_BEGIN(pt)
将PT_YIELD_FLAG
设置成1。
为了便好理解PT_YIELD_FLAG
,以PROCESS_WAIT_EVENT
宏作为分析对象,源代码如下:
#define PROCESS_WAIT_EVENT() PROCESS_YIELD()
#define PROCESS_YIELD() PT_YIELD(process_pt)
#define PT_YIELD(pt) \
do{ \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if(PT_YIELD_FLAG == 0) \
{
return PT_YIELDED; \
} \
}while(0)
#define LC_SET(s) s = __LINE__; case __LINE__: //保存程序断点,下次再运行该进程直接跳到case __LINE__
PROCESS_WAIT_EVENT
宏用于等待一个事件发生,如果检测到PT_YIELD_FLAG
为1,就继续执行PROCESS_WAIT_EVENT
宏后面的代码。若PT_YIELD_FLAG
为0了,就直接返回PT_YIELDED
,从博文《启动一个进程process_start》第三部分call_process
函数,可知并没有退出进程,只是把进程状态设为PROCESS_STATE_RUNNING
。call_process(struct process *p, process_event_t ev, process_data_t data)
函数部分代码如下:
ret = p->thread(&p->pt, ev, data); //才真正执行PROCESS_THREAD(name, ev, data)定义的内容
if (ret == PT_EXITED || ret == PT_ENDED || ev == PROCESS_EVENT_EXIT)
{
exit_process(p, p); //如果返回值表示退出、结尾或者遇到PROCESS_EVENT_EXIT,进程退出
}
else
{
p->state = PROCESS_STATE_RUNNING; //进程挂起等待事件
}
这有个疑问,什么时候把PT_YIELD_FLAG
设为1,才能以继续执行PROCESS_WAIT_EVENT
宏后面的代码。我也纳闷,在整个源码,只找到PT_BEGIN
宏将其设1。从源码分析,该进程没有机会执行PROCESS_WAIT_EVENT
宏后面的内容,只有其他进程传递一个事件PROCESS_EVENT_EXIT
让其退出,即process_post(&hello_world_process, PROCESS_EVENT_EXIT, NULL)
。但实际编辑,只要向该进程传递一个普通事件,即可执行PROCESS_WAIT_EVENT
宏后面的代码。很是不解,求助中……。(实例见附录)
上述问题已解决(2012-03-21)。当进程再次被调度的时候,会执行PROCESS_BEGIN
,而该宏包含两条语句:其一,PT_YIELD_FLAG=1
;其二,switch(pt->lc)
,从而转到被挂起的地方case __LINE__
继续执行。
1.3 PT_EXITED
宏PROCESS_EXIT
(让当前进程退出,自己结束自己的生命)用到了PT_EXITED
,源码如下:
#define PT_EXIT(pt) \
do
{ \
PT_INIT(pt); \
return PT_EXITED; \
} while(0)
执行进程主体thread时(call_process
函数),会返回结果,如果结果是PT_EXITED
或者PT_ENDED
(见1.4节),则进程退出(exit_process
函数),部分源码如下:
ret = p->thread(&p->pt, ev, data); //才真正执行PROCESS_THREAD(name, ev, data)定义的内容
if (ret == PT_EXITED || ret == PT_ENDED || ev == PROCESS_EVENT_EXIT)
{
exit_process(p, p); //如果返回值表示退出、结尾或者遇到PROCESS_EVENT_EXIT,进程退出
}
1.4 PT_ENDED
这个就不陌生了,在宏PROCESS_END
展开就有这么一句return PT_ENDED
,一层层展开如下:
#define PROCESS_END() PT_END(process_pt)
#define PT_END(pt) LC_END((pt)->lc); PT_YIELD_FLAG = 0; \
PT_INIT(pt); return PT_ENDED; }
2. 编程指南
理解上述内容后,相信对多个进程间事件如何交互比较清楚了。PROCESS_EVENT_EXIT
测试用例:
//filename:process_wait_event_test.c
#include "contiki.h"
#include "debug-uart.h"
PROCESS(hello_world_process, "Hello world");
PROCESS(post_event_process, "Post event");
AUTOSTART_PROCESSES(&hello_world_process, &post_event_process);
PROCESS_THREAD(hello_world_process, ev, data)
{
PROCESS_BEGIN();
usart_puts("Hello, world!\n");
while(1)
{
//usart_puts("while.Before wait event!\n");
PROCESS_WAIT_EVENT();
usart_puts("Have been posted an event!\n");
}
PROCESS_END();
}
PROCESS_THREAD(post_event_process, ev, data)
{
PROCESS_BEGIN();
usart_puts("Post event!\n");
static process_event_t event_post;
event_post = process_alloc_event();
process_post(&hello_world_process, event_post, NULL);
//process_post(&hello_world_process, PROCESS_EVENT_EXIT, NULL);
PROCESS_END();
}