本文介绍了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 events

上图源文件,在这里: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);
  }
}
本文系Spark & Shine原创,转载需注明出处本文最近一次修改时间 2022-03-20 16:36

results matching ""

    No results matching ""