1:进程的数据结构
struct process {
struct process *next;
#if PROCESS_CONF_NO_PROCESS_NAMES
#define PROCESS_NAME_STRING(process) ""
#else
const char *name;
#define PROCESS_NAME_STRING(process) (process)->name
#endif
PT_THREAD((* thread)(struct pt *, process_event_t, process_data_t));
struct pt pt;
unsigned char state, needspoll;
};
1.因为要维持一个进程的链表,struct process *next是指向它的下一个进程,声明一个process时初始化为0;
2.const char *name指的是这个进程的名字;
3.第三个展开来就是 char (* thread)(struct pt *, process_event_t, process_data_t),thread是一个函数指针,也就是说process包含了一个函数,每个process都包含了一个函数,process的执行其实就是这个函数体的执行。
4.再来看看pt的定义:
struct pt {
lc_t lc; //typedef unsigned short lc_t;
};
struct pt pt在这有何用,这正是protothread的精髓所在,看contiki内在ls-switch.h文件里的一段说明:
This implementation of local continuations uses the C switch()statement to resume execution of a function somewhere inside the function's body. The implementation is based on the fact that switch() statements are able to jump directly into the bodies of control structures such as if() or while() statements.
上面已经提到过lc表示local continuation,它是为switch()语句服务的,保留process的pt值,在下一次process执行的时候便可通过switch语句直接跳到要执行的语句,从而可以恢复process的执行,实现对process的控制。
5.还有unsigned char state, needspoll,state表示process的状态,具体定义在process.h文件中;一般process初始化的时候needspoll的值为0,假如needspoll的值为1的话,可以优先执行,具体先不深入讨论。
简而言之,needspoll为1的进程有更高的优先级。具体表现为,当系统调用process_run()函数时,把所有needspoll标志为1的进程投入运行,而后才从事件队列取出下一个事件传递给相应的监听进程。
与needspoll相关的另一个变量poll_requested,用于标识系统是否存在高优先级进程,即标记系统是否有进程的needspoll为1。
staticvolatileunsigned charpoll_requested;
2 事件
2.1 事件数据结构
structevent_data
{
process_event_t ev;
process_data_t data;
structprocess*p;
};
typedef unsigned charprocess_event_t;
typedef void*process_data_t;
ev-----标识所产生事件
data---保存事件产生时获得的相关信息,即事件产生后可以给进程传递的数据
p------指向监听该事件的进程
1.2 事件分类
事件可以被分为三类:时钟事件(timer events)、外部事件、内部事件。那么,Contiki核心数据结构就只有进程和事件了,把etimer理解成一种特殊的事件。
二、 事件队列
Contiki用环形队列组织所有事件(用数组存储),如下:
static struct event_data events[PROCESS_CONF_NUMEVENTS];
图示事件队列如下:
三、系统定义的事件
3.1 系统事件
系统定义了10个事件,源码和注释如下:
/*配置系统最大事件数*/
#ifndef PROCESS_CONF_NUMEVENTS
#define PROCESS_CONF_NUMEVENTS 32
#endif
#define PROCESS_EVENT_NONE 0x80 //函数dhcpc_request调用handle_dhcp(PROCESS_EVENT_NONE,NULL)
#define PROCESS_EVENT_INIT 0x81 //启动一个进程process_start,通过传递该事件
#define PROCESS_EVENT_POLL 0x82 //在PROCESS_THREAD(etimer_process, ev, data)使用到
#define PROCESS_EVENT_EXIT 0x83 //进程退出,传递该事件给进程主体函数thread
#define PROCESS_EVENT_SERVICE_REMOVED 0x84
#define PROCESS_EVENT_CONTINUE 0x85 //PROCESS_PAUSE宏用到这个事件
#define PROCESS_EVENT_MSG 0x86
#define PROCESS_EVENT_EXITED 0x87 //进程退出,传递该事件给其他进程
#define PROCESS_EVENT_TIMER 0x88 //etimer到期时,传递该事件
#define PROCESS_EVENT_COM 0x89
#define PROCESS_EVENT_MAX 0x8a /*进程初始化时,让lastevent=PROCESS_EVENT_MAX,即新产生的事件从0x8b开始,函数process_alloc_event用于分配一个新的事件*/
注:PROCESS_EVENT_EXIT与PROCESS_EVENT_EXITED区别
事件PROCESS_EVENT_EXIT用于传递给进程的主体函数thread,如在exit_process函数中的p->thread(&p->pt, PROCESS_EVENT_EXIT, NULL)。而PROCESS_EVENT_EXITED用于传递给进程,如call_process(q, PROCESS_EVENT_EXITED, (process_data_t)p)。(助记:EXITED是完成式,发给进程,让整个进程结束。而g一般式EXIT,发给进程主体thread,只是使其退出thread)
3.2 一个特殊事件
如果事件结构体event_data的成员变量p指向PROCESS_BROADCAST,则该事件是一个广播事件在do_event函数中,若事件的p指向的是PROCESS_BROADCAST,则让进程链表process_list所有进程投入运行。部分源码如下:
#define PROCESS_BROADCAST NULL//广播进程
/*保存待处理事件的成员变量*/
ev =events[fevent].ev;
data =events[fevent].data;
receiver=events[fevent].p;
if(receiver==PROCESS_BROADCAST)
{
for(p =process_list;p !=NULL;p =p->next)
{
if(poll_requested)
{
do_poll();
}
call_process(p,ev,data);
}
}
一、定时器概述
Contiki包含一个时钟模型和5个定时器模型(timer, stimer, ctimer, etimer, and rtimer)[1],5种timer简述如下:
timer--be used to check if a time period has passed[1]
stimer--be used to check if a time period has passed[1]
ctimer--Active timer, calls a functionwhen it expires, Used by Rime[2]
etimer--Active timer, sends an event whenit expires[2]
rtimer--Real-time timer, calls a functionat an exact time[2]
注:
(1) timer与stimer区别在于the resolution of time:timers use system clock ticks while stimers use seconds to allow much longer time periods[1]。
(2) Unlike the other timers, the timer and stimer libraries can be safely used from interrupts which makes them especially useful in low level drivers.
timer仅包含起始时刻和间隔时间,所以timer只记录到期时间。通过比较到到期时间和新的当前时钟,从而判断该定时器是不是到期。
2.2 timerlist
全局静态变量timerlist,指向系统第一个etimer,图示timerlist如下:
staticstructetimer *timerlist;