事件驱动机制广泛应用于嵌入式系统,类似于中断机制,当有事件到来时(比如按键、数据到达),系统响应并处理该事件。相对于轮询机制,事件机制优势很明星,低功耗(系统处于休眠状态,当有事件到达时才被唤醒)和MCU利用率高。

  Contiki将事件机制融入Protothreads机制,每个事件绑定一个进程(广播事件例外),进程间的消息也是通过事件来传递的。用无符号字符型来标示事件。事件结构体event_data定义如下:

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;

  用无符号字符型标识一个事件,Contiki定义了10个事件(0x80~0x8A),其它的供用户使用。

#define PROCESS_EVENT_NONE            0x80
#define PROCESS_EVENT_INIT 0x81
#define PROCESS_EVENT_POLL 0x82
#define PROCESS_EVENT_EXIT 0x83
#define PROCESS_EVENT_SERVICE_REMOVED 0x84
#define PROCESS_EVENT_CONTINUE 0x85
#define PROCESS_EVENT_MSG 0x86
#define PROCESS_EVENT_EXITED 0x87
#define PROCESS_EVENT_TIMER 0x88
#define PROCESS_EVENT_COM 0x89
#define PROCESS_EVENT_MAX 0x8a

  每个事件绑定一个进程,如果p为NULL,表示该事件绑定所有进程(即广播事件PROCESS_BROADCAST)。除此之外,事件可以携带数据data,可以用着电进行进程间的通信(向另一进程传递带数据的事件)。

static process_num_events_t nevents, fevent;
static struct event_data events[PROCESS_CONF_NUMEVENTS]; #define PROCESS_CONF_NUMEVENTS 32

  Contiki用一个全局的静态数组存放事件,这意味着事件数目在系统运行之前就要指定(用户可以通过PROCESS_CONF_NUMEVENTS自选配置大小),通过数组下标可以快速访问事件。系统还定义另两个全局静态变量nevents和fevent,分别用于记录未处理事件总数及下一个待处理的位置。事件逻辑组成环形队列,存储在数组里。如下图:

  可见,对于Contiki系统而言,事件并没有优先级之分,而是先到先服务的策略,全局变量fevent记录了下一次待处理事件的下标。

事件产生

  Contiki有两种方式产生事件,即同步和异步。同步事件通过process_post_synch函数产生,事件触发后直接处理(调用call_process函数)。而异步事件产生是由process_post产生,并没有及时处理,而是放入事件队列等待处理,process_post流程图如下:

 int process_post(struct process *p, process_event_t ev, process_data_t data)
{
static process_num_events_t snum; if(PROCESS_CURRENT() == NULL) {
PRINTF("process_post: NULL process posts event %d to process '%s', nevents %d\n",
ev,PROCESS_NAME_STRING(p), nevents);
}
else {
PRINTF("process_post: Process '%s' posts event %d to process '%s', nevents %d\n",
PROCESS_NAME_STRING(PROCESS_CURRENT()), ev,
p == PROCESS_BROADCAST? "<broadcast>": PROCESS_NAME_STRING(p), nevents);
}
/*nevents未处理事件总数,事件队列满,返回PROCESS_ERR_FULL*/
if(nevents == PROCESS_CONF_NUMEVENTS) {
#if DEBUG
if(p == PROCESS_BROADCAST) {
printf("soft panic: event queue is full when broadcast event %d was posted from %s\n", ev, PROCESS_NAME_STRING(process_current));
} else {
printf("soft panic: event queue is full when event %d was posted to %s frpm %s\n", ev, PROCESS_NAME_STRING(p), PROCESS_NAME_STRING(process_current));
}
#endif /* DEBUG */
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 /* PROCESS_CONF_STATS */ return PROCESS_ERR_OK;
}

  process_post首先判断事件队列是否已满,若满返回错误,否则取得下一个空闲位置(因为环形队列,需要做余操作),而后,设置该事件并将未处理事件总数加1。

事件调度

  事件没有优先级,采用先到先服务策略,每一次系统轮询(process_run函数)只处理一个事件,do_event函数用于处理事件,其流程图如下:

 /*
* 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 > ) { /* 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和nevents*/
fevent = (fevent + ) % 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) {
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. 如果是初始化事件,设置进程状态为RUNNING*/
if(ev == PROCESS_EVENT_INIT) {
receiver->state = PROCESS_STATE_RUNNING;
} /* Make sure that the process actually is running. */
call_process(receiver, ev, data);//处理事件
}
}
}

  do_event首先取出该事件(即,将事件的值复制到一个新变量),更新总的未处理事件总数及下一个待处理事件的数组下标(环形队列,需要取余操作)。接着判断事件是否为广播事件PROCESS_BROADCAST,若是,考虑到处理广播事件可能需要更多的时间,为保证系统实时性,先运行高优先级的进程,而后再去处理事件(调用call_process函数)。如果事件是初始化事件PROCESS_EVENT_INIT(创建进程的时候会触发此事件),需要将进程状态设为PROCESS_STATE_RUNNING。

事件处理

  实际的事件处理是在进程的函数体thread,正如上文所述,call_process会调用thread函数,执行该进程。关键代码如下:

ret = p->thread(&p->pt, ev, data);

参考Jelline大神博客: http://jelline.blog.chinaunix.net

contiki-事件调度的更多相关文章

  1. 在指定时间干,必须干(kbmmw 中的事件调度)

    从去年开始,kbmmw 慢慢增加内涵,除了完善各种服务外,陆续增加和扩展了作为一个中间件必须有的功能, 例如,权限管理.日志系统.调度系统.内存调试等功能. 今天给大家介绍一下kbmmw 的调度事件, ...

  2. mysql 事件调度

    适用于mysql 5.1 及以后版本 1.查看是否开启 show variables like '%scheduler%' 2.查看进程 show processlist 3.事件调度器默认是关闭的, ...

  3. mysql数据库事件调度(Event)

    mysql中的事件调度器可以定时对数据库增加,删除和执行操作,相当于数据库中的临时触发器,与Linux系统中的执行计划任务一样,这样就可以大大降低工作量. 1.开启事件调度器 [root@node1 ...

  4. mysql 事件调度器

    1.mysql事件调度器,也就是计划任务,计划做某事,有两种方式: 2.在某个时间点做某事,AT TIMESTAMP [+ INTERVAL INTERVAL] 某个时间点加上偏移. 3.定时地做某事 ...

  5. mysql事件调度器定时删除binlog

    MySQL5.1.6起Mysql增加了事件调度器(Event Scheduler),可以用做定时执行某些特定任务,来取代原先只能由Linux操作系统的计划任务来执行的工作MySQL的事件调度器可以精确 ...

  6. MySQL的事件调度器

    自MySQL5.1.0起,增加了一个非常有特色的功能–事件调度器(Event Scheduler),可以用做定时执行某些特定任务,可以看作基于时间的触发器. 一.开启 事件调度默认是关闭的,开启可执行 ...

  7. MySQL计划任务(事件调度器)(Event Scheduler)

    http://www.cnblogs.com/c840136/articles/2388512.html https://dev.mysql.com/doc/refman/5.7/en/events- ...

  8. mysql事件调度器

    #查看mysql事件调度器是否开启 SHOW VARIABLES WHERE Variable_name = 'event_scheduler'; #开启mysql事件调度器功能 SET GLOBAL ...

  9. mysql事件调度器功能

    一.前言 自MySQL5.1.6起,增加了一个非常有特色的功能–事件调度器(Event Scheduler),可以用做定时执行某些特定任务(例如:删除记录.对数据进行汇总等等),来取代原先只能由操作系 ...

  10. MySQL事件调度器event的使用

    Q:假设,有一个需求,希望在某一个时刻系统调用一个begin end执行一下:十分钟以后执行一下begin end.亦或有一个需求,每个多长时间周期性执行begin end.那么这个时候该怎么办呢? ...

随机推荐

  1. Python多重装饰器

    多重装饰器,即多个装饰器修饰同一个对象[实际上并非完全如此,且看下文详解] 1.装饰器无参数: >>> def first(func): print '%s() was post t ...

  2. Recover lost Confluence password

    confluence重置admin密码 复方法: 1. 运行此sql 找到你的管理员帐户: select u.id, u.user_name, u.active from cwd_user u joi ...

  3. Linux_know

    Linux_know 在创建Linux分区时,一定要创建SWAP/根分区两个分区 Red Hat Linux 9中,系统默认的root用户对整个系统拥有完全的控制权. 当登录Linux时,一个具有唯一 ...

  4. 使用javascript获取服务器时间

    思路:采用异步请求的方式,发送请求,获取HTTP请求的response头,头部中包含时间,使用getResponseHeader('Date')即可. 注意:以下任何一种方法都不精确,因为请求包的传输 ...

  5. Qt qml listview 列表视图控件(下拉刷新、上拉分页、滚动轴)

    Qt qml listview下拉刷新和上拉分页主要根据contentY来判断.但要加上顶部下拉指示器.滚动条,并封装成可简单调用的组件,着实花了我不少精力:) [先看效果]    [功能] 下拉刷新 ...

  6. (更新)Java + 腾讯企业邮箱 + javamail + SSL 发送邮件

    咳咳~最头疼的就是莫名其妙的错误. 本来今年6月份运行通过的代码,过俩月就报错了. javax.mail.MessagingException: Could not connect to SMTP h ...

  7. C# 调用Excel 出现服务器出现意外情况. (异常来自 HRESULT:0x80010105 (RPC_E_SERVERFAULT)

    C# 调用Excel 出现服务器出现意外情况. (异常来自 HRESULT:0x80010105 (RPC_E_SERVERFAULT) private Microsoft.Office.Intero ...

  8. Jquery 下实现 图片大图预览效果

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http ...

  9. 据说年薪30万的Android程序员必须知道的帖子

    Android中国开发精英 目前包括: Android开源项目第一篇--个性化控件(View)篇       包括ListView.ActionBar.Menu.ViewPager.Gallery.G ...

  10. 30-React JSX IN DEPTH

    JSX IN DEPTH JSX 从根本上说,JSX只是提供了语法糖React.createElement(component, props, ...children)的功能.以下JSX代码: < ...