摘要:
本文给出两个交互进程的实例,先给出程序源代码,运行截图,接着解读该程序,最后深入源码详细分析。
创建两个进程,一个打印Hello,另一个打印World,使其交互打印。
一、源代码
声明进程print_hello_process & print_world_process,所涉及宏参见博文《Contiki学习笔记:实例hello_world剖析》:
- //filename:interact_two_process.c
-
#include "contiki.h"
-
#include "debug-uart.h"
-
static process_event_t event_data_ready;
-
-
/*声明两个进程*/
-
PROCESS(print_hello_process, "Hello");
-
PROCESS(print_world_process, "world");
-
AUTOSTART_PROCESSES(&print_hello_process, &print_world_process); //让该两进程自启动
定义进程print_hello_process:
- /*定义进程print_hello_process*/
-
PROCESS_THREAD(print_hello_process, ev, data)
-
{
-
PROCESS_BEGIN();
-
static struct etimer timer;
-
-
etimer_set(&timer, CLOCK_CONF_SECOND / 10); //#define CLOCK_CONF_SECOND 10将timer的interval设为1 详情见4.1
-
-
usart_puts("***print hello process start***\n");
-
-
event_data_ready = process_alloc_event(); //return lastevent++; 新建一个事件,事实上是用一组unsigned char值来标识不同事件
-
-
while (1)
-
{
-
PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER); //详情见4.2,即etimer到期继续执行下面内容
-
-
usart_puts("Hello\n");
-
-
process_post(&print_world_process, event_data_ready, NULL); //传递异步事件给print_world_process,直到内核调度该进程才处理该事件。详情见4.3
-
-
etimer_reset(&timer); //重置定时器,详情见4.4
-
}
-
-
PROCESS_END();
-
}
定义进程print_world_process:
- /*定义进程print_world_process*/
-
PROCESS_THREAD(print_world_process, ev, data)
-
{
-
PROCESS_BEGIN();
-
-
usart_puts("***print world process start***\n");
-
-
while (1)
-
{
-
PROCESS_WAIT_EVENT_UNTIL(ev == event_data_ready);
-
usart_puts("world\n");
-
}
-
-
PROCESS_END();
-
}
二、运行结果截图
三、程序解读
系统启动,执行一系列初始化(串口、时钟、进程等),接着启动系统进程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函数
- //etimer_set(&timer, CLOCK_CONF_SECOND/10);
-
void etimer_set(struct etimer *et, clock_time_t interval)
-
{
-
timer_set(&et->timer, interval); //设置定时器timer,源码见2.1.1
-
add_timer(et); //将这个etimer加入timerlist
-
}
4.1.1 timer_set函数
- //用当前时间初始化start,并设置间隔interval
-
void timer_set(struct timer *t, clock_time_t interval)
-
{
-
t->interval = interval;
-
t->start = clock_time(); //return current_clock;
-
}
-
-
clock_time_t clock_time(void)
-
{
-
return current_clock;
-
}
-
-
static volatile clock_time_t current_clock = 0;
-
typedef unsigned int clock_time_t;
4.1.2 add_timer函数
- /*将etimer加入timerlist,并update_time(),即求出下一个到期时间next_expiration*/
-
static void add_timer(struct etimer *timer)
-
{
-
struct etimer *t;
-
-
etimer_request_poll(); //事实上执行process_poll(&etimer_process),即将进程的needspoll设为1,使其获得更高优先级,详情见下方
-
-
/*参数验证,确保该etimer不是已处理过的*/
-
if (timer->p != PROCESS_NONE) //如果是PROCESS_NONE,则表示之前处理过,该etimer已到期(注1)
-
{
-
for (t = timerlist; t != NULL; t = t->next)
-
{
-
if (t == timer) /* Timer already on list, bail out. */
-
{
-
update_time(); //即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration时间,就有timer到期
-
return ;
-
}
-
}
-
}
-
-
/*将timer加入timerlist*/
-
timer->p = PROCESS_CURRENT();
-
timer->next = timerlist;
-
timerlist = timer;
-
-
update_time(); //即求出下一个到期时间next_expiration(全局静态变量),即还有next_expiration时间,就有timer到期
-
}
注1:关于PROCESS_NONE可以参考博文《Contiki学习笔记:系统进程etimer_process》。
函数etimer_request_poll直接调用process_poll,一步步展开如下:
- void etimer_request_poll(void)
-
{
-
process_poll(&etimer_process);
-
}
-
void process_poll(struct process *p)
-
{
-
if (p != NULL)
-
{
-
if (p->state == PROCESS_STATE_RUNNING || p->state == PROCESS_STATE_CALLED)
-
{
-
p->needspoll = 1;
-
poll_requested = 1; //全局静态变量,标识是否有进程的needspoll为1
-
}
-
}
-
}
4.2 PROCESS_WAIT_EVENT_UNTIL宏
- //PROCESS_WAIT_EVENT_UNTIL(ev == PROCESS_EVENT_TIMER);
- /*宏PROCESS_WAIT_EVENT_UNTIL,直到条件c为真时,进程才得以继续向前推进*/
- #define PROCESS_WAIT_EVENT_UNTIL(c) PROCESS_YIELD_UNTIL(c)
- #define PROCESS_YIELD_UNTIL(c) PT_YIELD_UNTIL(process_pt, c) //Yield the currently running process until a condition occurs
- #define PT_YIELD_UNTIL(pt, cond) \
- do \
- { \
- PT_YIELD_FLAG = 0; \
- LC_SET((pt)->lc); \ //注:保存断点,下次从这里开始执行!!!
- if((PT_YIELD_FLAG == 0) || !(cond)) { \
- return PT_YIELDED; \
- } \
- } while(0)
-
-
- #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:
- //process_post(&print_world_process, event_data_ready, NULL); 即把事件event_data_ready加入事件队列
-
int process_post(struct process *p, process_event_t ev, process_data_t data)
-
{
-
static process_num_events_t snum;
-
if (nevents == PROCESS_CONF_NUMEVENTS)
-
{
-
return PROCESS_ERR_FULL;
-
}
-
-
snum = (process_num_events_t)(fevent + nevents) % PROCESS_CONF_NUMEVENTS;
-
events[snum].ev = ev;
-
events[snum].data = data;
-
events[snum].p = p;
-
++nevents;
-
-
#if PROCESS_CONF_STATS
-
if (nevents > process_maxevents)
-
{
-
process_maxevents = nevents;
-
}
-
#endif
-
return PROCESS_ERR_OK;
-
}
4.4 etimer_reset函数
- /*Reset the timer with the same interval.*/
-
void etimer_reset(struct etimer *et)
-
{
-
timer_reset(&et->timer);
-
add_timer(et); //详情见4.1.2
-
}
-
-
void timer_reset(struct timer *t)
-
{
-
t->start += t->interval; //为什么不t->start = clock_time()?为了程序可预测性?
-
}
阅读(9438) | 评论(0) | 转发(3) |