事件驱动与消息 什么是消息?消息是数据信息的一种存储形式。从程序的角度看,消息就是一段存储着特定数据的内存块, 数据的存储格式是设计者预先约定好的, 只要按照约定的格式读取这段内存, 就能获得消息所承载的有用信息。 消息是有时效性的。任何一个消息实体都是有生命周期的,它从诞生到消亡先后经历了生成、 存储、 派发、 消费共 4 个阶段:消息实体由生产者生成, 由管理者负责存储和派发, 最后由消费者消费。 被消费者消费之后, 这个消息就算消亡了, 虽然存储消息实体的内存中可能还残留着原来的数据, 但是这些数据对于系统来讲已经没有任何意义了, 这也就是消息的时效性。说到这里,大家有没有发现,这里的“消息” 和前面一直在说的“事件” 是不是很相似?把“消息” 的这些特点套用在“事件” 身上是非常合适的, 在我看来, 消息无非是事件的一个马甲而已。 我们在设计单片机程序的时候,都怀着一个梦想,即让程序对事件的响应尽可能的快,理想的情况下,程序对事件要立即响应,不能有任何延迟。这当然是不可能的,当事件发生时,程序总会因为这样那样的原因不能立即响应事件。 为了不至于丢失事件,我们可以先在事件相关的 ISR 中把事件加工成消息并把它存储在消息缓冲区里, ISR 做完这些后立即退出。主程序忙完了别的事情之后,去查看消息缓冲区,把刚才 ISR 存储的消息读出来, 分析出事件的有关信息, 再转去执行相应的响应代码, 最终完成对本次事件的响应。 只要整个过程的时间延迟在系统功能容许的范围之内, 这样处理就没有问题。将事件转化为消息,体现了以空间换时间的思想。再插一句,虽然事件发生后对应的 ISR 立即被触发,但是这不是严格意义上的“响应”, 顶多算是对事件的“记录”, “记录” 和“响应” 是不一样的。事件是一种客观的存在,而消息则是对这种客观存在的记录。 对于系统输入而言,事件是其在时间维度上的描述;消息是其在空间维度上的描述,所以,在描述系统输入这一功能上,事件和消息是等价的。对比一下程序清单 List9 中的实现方式, 那里是用全局标志位的方式记录事件, 对于某些特殊的事件还配备了专门的缓冲区, 用来存储事件的额外信息, 而这些额外信息单靠全局标志位是无法记录的。 现在我们用消息+消息缓冲区的方式来记录事件,消息缓冲区就成了所有事件共用的缓冲区,无论发生的事件有没有额外的信息,一律以消息的形式存入缓冲区 。 为了记录事件发生的先后顺序,消息缓冲区应该做成以“先入先出” 的方式管理的环形缓冲队列。事件生成的消息总是从队尾入队,管理程序读取消息的时候总是从队头读取,这样,消息在缓冲区中存储的顺序就是事件在时间上发生的顺序,先发生的事件总是能先得到响应。 一条消息被读取之后, 管理程序回收存储这个消息的内存, 将其作为空闲节点再插入缓冲队列的队尾,用以存储将来新生成的消息。图 7 所示为使用了消息功能的事件驱动机制示意图。不知道有没有人对图中的“消费者”有疑问, 这个“消费者” 在程序中指的是什么呢? 既然这个事件/消息驱动机制是为系统应用服务的, 消费者当然就是指应用层的代码了, 更明确一点儿的话, 消费者就是应用代码中的状态机 。
用消息的方法来实现事件驱动机制完全解决了前面提到的那两个问题,即不同事件集中爆发时,无法记录事件发生的前后顺序。同一事件集中爆发时,容易遗漏后面发生的那次事件。对于第一种情况,消息(事件)在缓冲队列中是以“先入先出” 的方式存储的,存储顺序就代表了事件发生的先后顺序。 对于第二种情况, 任何被 ISR 捕捉到的事件都会以一个独立的消息实体存入缓冲队列, 即使前后两个是同一个事件, 只要 ISR 反应够快就不会遗漏事件。实际上, ISR 的主要工作就是填写消息实体, 然后将其存入缓冲队列, 做这些工作只占用 CPU 很短的时间。
|