event和process的关系是怎样体现出来的?
在contiki中,有四种timer,如下:
PROCESS(hello_world_process, "Hello world process");
AUTOSTART_PROCESSES(&hello_world_process);
/*---------------------------------------------------------------------------*/
PROCESS_THREAD(hello_world_process, ev, data)
{
/* Variables are declared static to ensure their values are kept between kernel calls. */
static struct etimer timer;
static int count = 0;
/* Any process must start with this. */
PROCESS_BEGIN();
/* Set the etimer to generate an event in one second. */
etimer_set(&timer, CLOCK_CONF_SECOND);
while(1) {
/* Wait for an event. */
PROCESS_WAIT_EVENT();
/* Got the timer's event~ */
if (ev == PROCESS_EVENT_TIMER) {
printf("Hello, world #%i\n", count);
count++;
/* Reset the etimer so it will generate another event after the exact same time. */
etimer_reset(&timer);
}
} // while (1)
/* Any process must end with this, even if it is never reached. */
PROCESS_END();
}
1.etimer_set()
首先process的body定义了一个etimer类型的数据timer,etimer的定义如下:
struct etimer {
struct timer timer;
struct etimer *next;
struct process *p;
};//A timer. This structure is used for declaring a timer. The timer must be set with etimer_set() before it can be used.
timer的定义如下:
struct timer {
clock_time_t start;
clock_time_t interval;
};//A timer. This structure is used for declaring a timer. The timer must be set with timer_set() before it can be used.
timer的start为起始时刻,interval为间隔时间。所以说struct timer is a passive timer, only keeps trackof its expiration time.
而struct etimer is an active timer, sends an event whenit expires.
一个etimer类型的数据,包含了一个timer,以及下一个etimer,同时也包含了一个进程, 根据介绍,初步理解,当etimer的timer到期时,就会发送一个事件从而invoke这个process,其实也就是给这个进程的PROCESS_THREAD宏参数ev传递一个值,可能是PROCESS_EVENT_TIMER。
接着对etimer类型的timer用etimer_set()进行初始化,函数的定义如下:
etimer_set(struct etimer *et, clock_time_t interval)
{
timer_set(&et->timer, interval);
add_timer(et);
}
timer_set的函数定义如下:
void
timer_set(struct timer *t, clock_time_t interval)
{
t->interval = interval;
t->start = clock_time();
}
clock_time()是返回一个当前的时刻,开机初始化为0,会随着系统的运行而增加。在hello_world这个进程中,是吧interval初始化为
再来看看add_timer()是怎么实现的。
static void
add_timer(struct etimer *timer)
{
struct etimer *t;
etimer_request_poll();
if(timer->p != PROCESS_NONE) {
/* Timer not on list. */
for(t = timerlist; t != NULL; t = t->next) {
if(t == timer) {
/* Timer already on list, bail out. */
update_time();
return;
}
}
}
timer->p = PROCESS_CURRENT();
timer->next = timerlist;
timerlist = timer;
update_time();
}
关于etimer_request_poll()函数,contiki上是这样描述的:
The function is called from timer interrupts, by the system. It the event timer aware that the clock has changed. This function is used to inform the event timer module that the system clock has been updated. Typically, this function would be called from the timer interrupt handler when the clock has ticked.
就是用来提醒etimer系统的始终已经改变。
它的定义如下:
void
etimer_request_poll(void)
{
process_poll(&etimer_process);
}
这里又涉及到了一个函数,process_poll(&etimer_process), etimer也是一个进程,名字叫etimer_process,它维护的是一个etimer链表叫timerlist,在同文件etimer.c中有定义:
PROCESS(etimer_process, "Event timer");
并且也定义了etimer_process进程的函数,只是没有让它自启动罢了:
PROCESS_THREAD(etimer_process, ev, data)
process_poll()的定义如下:
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;
}
}
}
关于process_poll()函数的说明,如下:
It is called from device drivers. Request a process to be polled. This function typically is called from an interrupt handler to cause a process to be polled. p is a pointer to the process' process structure.
在一个题目为“ProgrammingContiki Crash Course”作者是“Kista,Sweden”的ppt上介绍说,有两种方法可以让一个进程运行,一个是Post an event, 即传递一个事件,包含了process_post(process_ptr, eventno, ptr)和process_post_synch(process_ptr, eventno, ptr)这两个函数;一个是Poll the process,这个至今没怎么想通,它有一个函数就是process_poll(process_ptr)。猜测是前一个是app所为,后一个是system所为。
暂不知何意,先过之。add_timer()就是把这个etimer加入到etimer链表timerlist里面去,,并且把当前的process注册到这个etimer中。update_time()是遍历所timerlist,更新下一个最近的到期时刻next_expiration,etimer_set就先到此。etimer_set(&timer, CLOCK_CONF_SECOND);一秒钟产生一个事件。
2.PROCESS_WAIT_EVENT
#define PROCESS_WAIT_EVENT() PROCESS_YIELD()
#define PROCESS_YIELD() PT_YIELD(process_pt) //Yield the currently running process.
#define PT_YIELD(pt) \
do { \
PT_YIELD_FLAG = 0; \
LC_SET((pt)->lc); \
if(PT_YIELD_FLAG == 0) { \
return PT_YIELDED; \
} \
} while(0)
所以最后PROCESS_WAIT_EVENT宏展开来如下:
do { \
PT_YIELD_FLAG = 0; \
(process_pt)->lc = 38; case 38:; \
if(PT_YIELD_FLAG == 0) { \
return 1; \
} \
} while(0)
这其实就是protothread的精华,当Protothread程序运行到PT_WAIT_UNTIL时,判断其运行条件是否满足,若不满足,则阻塞。通过PROCESS_WAIT_EVENT宏展开的程序代码可以得知,Protothread的阻塞其实质就是函数返回,只不过在返回前保存了当前的阻塞位置,待下一次Protothread被调用时,直接跳到阻塞位置执行,再次判断运行条件是否满足,并执行后续程序或继续阻塞。
3.Got a timer's event
/* Got the timer's event~ */
if (ev == PROCESS_EVENT_TIMER) {
printf("Hello, world #%i\n", count);
count++;
#define PROCESS_EVENT_TIMER 0x88
如果protothread收到了一个PROCESS_EVENT_TIMER事件,就打印hello,world。
4.整段代码展开如下:
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};
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)
{
static struct etimer timer;
static uint8_t leds_state = 0;
{
char PT_YIELD_FLAG = 1;
switch((process_pt)->lc)
{
case 0:
;
etimer_set(&timer, CLOCK_CONF_SECOND);
while(1)
{
/* Wait for an event. */
//PROCESS_WAIT_EVENT();
do
{
PT_YIELD_FLAG = 0;
(process_pt)->lc = 38; case 38:;
if(PT_YIELD_FLAG == 0)
{
return 1;
}
} while(0);
/* Got the timer's event~ */
if (ev == PROCESS_EVENT_TIMER)
{
printf("Hello, world #%i\n", count);
count++;
/* Reset the etimer so it will generate another event after the exact same time. */
etimer_reset(&timer);
}
}
};
PT_YIELD_FLAG = 0;
(process_pt)->lc = 0;;
return 3;
}
}
程序的大致执行过程已经比较明了了,当程序执行到PROCESS_WAIT_EVENT的时候,基于protothread的机制程序会阻塞,即return 1,return的值有3种情况,这里要注意:
#define PT_WAITING 0
#define PT_YIELDED 1
#define PT_EXITED 2
#define PT_ENDED 3
5.看看main函数
while(1){
int r;
do {
/* Reset watchdog. */
watchdog_periodic();
r = process_run();
} while(r > 0);
watchdog_periodic()函数是和watchdog有关的,是为了防止程序跑飞(程序跑飞是指系统受到某种干扰后,程序计数器PC的值偏离了给定的唯一变化历程,导致程序运行偏离正常的运行路径.程序跑飞因素及后果往往是不可预计的.在很多情况下,程序跑飞后系统会进入死循环而导致死机,watchdog相当于系统警察,当系统发生严重错误(如程序进入死循环等)不能恢复的时候,WATCHDOG能够让系统重启。WATCHDOG的应用主要是在嵌入式操作系统中,避免了系统在无人干预时长时间挂起的情况)。
/**
* Run the system once - call poll handlers and process one event.
*
* This function should be called repeatedly from the main() program
* to actually run the Contiki system. It calls the necessary poll
* handlers, and processes one event. The function returns the number
* of events that are waiting in the event queue so that the caller
* may choose to put the CPU to sleep when there are no pending
* events.
*
* \return The number of events that are currently waiting in the
* event queue.
*/
/*---------------------------------------------------------------------------*/
int
process_run(void)
{
/* Process poll events. */
if(poll_requested) {
do_poll();
}
/* Process one event from the queue */
do_event();
return nevents + poll_requested;
}
/*---------------------------------------------------------------------------*/
这其中又包含了两个function,do_poll()和do_event(),还是不是很明白的这两者的区别,看看contiki operating system 关于这两者的描述吧:
do_poll()//Call each process' poll handler//调用轮询处理程序?
do_event()// Process the next event in the event queue and deliver it to listening processes.
只要poll_requested==1或者nevents>0循环就会一直执行。第二个好理解一点,从事件队列里取出一个事件然后传送给一个监听的进程,第一个不清楚,暂搁置之。看看do_poll()函数的代码。
/*---------------------------------------------------------------------------*/
/*
* Call each process' poll handler.
*/
/*---------------------------------------------------------------------------*/
static void
do_poll(void)
{
struct process *p;
poll_requested = 0;
/* Call the processes that needs to be polled. */
for(p = process_list; p != NULL; p = p->next) {
if(p->needspoll) {
p->state = PROCESS_STATE_RUNNING;
p->needspoll = 0;
call_process(p, PROCESS_EVENT_POLL, NULL);
}
}
}
struct process hello_world_process = { 0, "Hello world process", process_thread_hello_world_process};
如此声明的process的needspoll变量都是默认为0。
/*Process the next event in the event queue and deliver it to listening processes.*/
static void
do_event(void)
{
static process_event_t ev;
static process_data_t data;
static struct process *receiver;
static struct process *p;
/*
* If there are any events in the queue, take the first one and walk
* through the list of processes to see if the event should be
* delivered to any of them. If so, we call the event handler
* function for the process. We only process one event at a time and
* call the poll handlers inbetween.
*/
if(nevents > 0) {
/* There are events that we should deliver. */
ev = events[fevent].ev;
data = events[fevent].data;
receiver = events[fevent].p;
/* Since we have seen the new event, we move pointer upwards
and decrese the number of events. */
fevent = (fevent + 1) % PROCESS_CONF_NUMEVENTS;
--nevents;
/* If this is a broadcast event, we deliver it to all events, in
order of their priority. */
if(receiver == PROCESS_BROADCAST) {//#define PROCESS_BROADCAST NULL
for(p = process_list; p != NULL; p = p->next) {
/* If we have been requested to poll a process, we do this in
between processing the broadcast event. */
if(poll_requested) {
do_poll();
}
call_process(p, ev, data);
}
} else {
/* This is not a broadcast event, so we deliver it to the
specified process. */
/* If the event was an INIT event, we should also update the
state of the process. */
if(ev == PROCESS_EVENT_INIT) {
receiver->state = PROCESS_STATE_RUNNING;
}
/* Make sure that the process actually is running. */
call_process(receiver, ev, data);
}
}
}
struct event_data {
process_event_t ev;
process_data_t data;
struct process *p;
};
static process_num_events_t nevents, fevent;
static struct event_data events[PROCESS_CONF_NUMEVENTS];
PROCINIT(&etimer_process, &tcpip_process, &sensors_process);
procinit_init();
这是初始化三个系统进程的语句,ttimer_process,tcp_ip_process,sensor_process。
const struct process *procinit[] = {&etimer_process, &tcpip_process, &sensors_process, 0}
接着来看看procinit_init()函数:
void
procinit_init(void)
{
int i;
for(i = 0; procinit[i] != NULL; ++i) {
process_start((struct process *)procinit[i], NULL);
}
}
然后对各个进程执行call_process,就像执行一般的user process一样,所以接下来我们得看看etimer的PROCESS_THREAD宏,看看etimer_process的执行过程:
PROCESS_THREAD(etimer_process, ev, data)
{
struct etimer *t, *u;
PROCESS_BEGIN();
timerlist = NULL;
while(1) {
PROCESS_YIELD();
if(ev == PROCESS_EVENT_EXITED) {
struct process *p = data;
while(timerlist != NULL && timerlist->p == p) {
timerlist = timerlist->next;
}
if(timerlist != NULL) {
t = timerlist;
while(t->next != NULL) {
if(t->next->p == p) {
t->next = t->next->next;
} else
t = t->next;
}
}
continue;
} else if(ev != PROCESS_EVENT_POLL) {
continue;
}
again:
u = NULL;
for(t = timerlist; t != NULL; t = t->next) {
if(timer_expired(&t->timer)) {
if(process_post(t->p, PROCESS_EVENT_TIMER, t) == PROCESS_ERR_OK) {
/* Reset the process ID of the event timer, to signal that the
etimer has expired. This is later checked in the
etimer_expired() function. */
t->p = PROCESS_NONE;
if(u != NULL) {
u->next = t->next;
} else {
timerlist = t->next;
}
t->next = NULL;
update_time();
goto again;
} else {
etimer_request_poll();
}
}
u = t;
}
}
PROCESS_END();
}
etimer_process主要干了三件事:
第二件事,接受PROCESS_EVENT_EXIT事件,接着把这个退出的进程所对应的etimer从timerlist中清除;