本文介绍了Contiki主要数据结构之事件,深入源码分析事件结构体,图示事件队列,并介绍事件处理和新事件加入是如何影响事件队列的。
1. 事件
1.1 事件结构体
事件也是Contiki重要的数据结构,其定义如下:
struct event_data
{
process_event_t ev;
process_data_t data;
struct process *p;
};
typedef unsigned char process_event_t;
typedef void * process_data_t;
各成员变量含义如下:
ev
,标识所产生事件data
,保存事件产生时获得的相关信息,即事件产生后可以给进程传递的数据p
,指向监听该事件的进程
1.2 事件分类
事件可以被分为三类:时钟事件(timer events)、外部事件、内部事件。那么,Contiki核心数据结构就只有进程和事件了,把etimer理解成一种特殊的事件。
2. 事件队列
Contiki用环形队列组织所有事件(用数组存储),如下:
static struct event_data events[PROCESS_CONF_NUMEVENTS];
图示事件队列如下:
上图源文件,在这里:Contiki事件队列图示.vsd。
3. 系统定义的事件
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是完成式,发给进程,让整个进程结束。而一般式EXIT,发给进程主体thread,只是使其退出thread
3.2 一个特殊事件
如果事件结构体event_data
的成员变量p
指向PROCESS_BROADCAST
,则该事件是一个广播事件(为么不用一个特殊事件来标识广播事件,而采用这种费解方式?)。在do_event
函数中,若事件的p
指向的是PROCESS_BROADCAST
,则让进程链表process_list所有进程投入运行。详情见详情见博文《深入理解process_run函数》中2.2小节,部分源码如下:
#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);
}
}