用直接消息传递机制实现高可用性(HA)的软件设计――新生代RTOS:OSE
 

2010-08-27 作者:张永军 来源:ed-china.com

 

用传统的方法设计一个复杂的嵌入式系统,在软件任务/进程间或子系统间通信时,使用消息传递机制会带来很多帮助。特别是对那些需要高可用性的系统(比方说一个一年只允许有不多于一秒的停机时间的系统),消息传递机制的使用显得尤为重要。本文将介绍在作高可用性,硬件冗余系统的设计时,基于消息机制的任务间或处理器间通信的原理。

用传统的方法设计一个复杂的嵌入式系统,在软件任务/进程间或子系统间通信时,使用消息传递机制会带来很多帮助。特别是对那些需要高可用性的系统(比方说一个一年只允许有不多于一秒的停机时间的系统),消息传递机制的使用显得尤为重要。传统的实时操作系统所使用的任务间通信机制如:邮箱,信号量,互斥,管道及事件标志等在普通的基于单CPU的系统中尚且可以使用,当对于基于分布式多CPU硬件的高可用系统而言,这些传统的消息传递机制已显得力不从心!

消息传递架构很自然地为各软件部件间提供了清晰的分界。软件部件间的通信具有很好的界面,并且是一种松耦合异步机制。硬件方面可以用Supervisor和User模式以及MMU边界来进行强制分界。传统的消息传递机制有过多的消息拷贝开销,同时对消息的传递也缺乏相应的监控。本文将提供另一种消息传递的机制,这种消息传递机制对单CPU的任务间通信和多CPU的处理器间通信(在多处理器上的任务间进行通信)同样有效,与传统方法相比,这种直接消息传递机制大大减少了消息拷贝开销,同时对消息的传递也提供了许多的监控措施。使用这种消息传递机制的软件结构显得更清晰,简单,从而使软件有更高的质量和更好的可维护性。

这种直接的消息传递机制也使软件的动态下载变得更容易,在下载过程中新的软件模块取代旧的软件模块,但其它软件模块仍然继续保持正常工作。

本文将介绍在作高可用性,硬件冗余系统的设计时,基于消息机制的任务间或处理器间通信的原理。

直接消息传递模式Vs.传统的RTOS消息传递

直接消息传递是指在并发的任务间将一些非连续的消息直接互相传递,而不借助任何其它的RTOS机构,比如“邮箱”等。这样的消息除了消息ID号以外,可以包含大量的数据。见图1。

这种任务间的通信机制本来就是开发来用于多CPU和并行处理应用的,在这种应用中使用这种消息传递机制可以在不同的CPU上的任务间传递数据,而不需要各CPU上的软件进行同步。各CPU上的软件的同步往往导致比较长的时间延迟。

由于基于直接消息传递机制的RTOS有很高的性能,且易于编程,调试和维护,不光是对多CPU复杂系统,而且对单CPU的简单应用也一样具有很高的价值。随着系统复杂程度的增加,系统可能需要由一个单CPU的系统发展为多CPU的系统,如果用户开始就使用基于直接消息传递机制的RTOS,这种系统的扩展和升级将变得非常平滑,非常容易。直接消息传递机制与传统的RTOS所采用的方法完全不同。在传统的RTOS中,一个任务的消息并不是直接发给另一个任务,而是要借助于消息队列或邮箱等RTOS独立的部件,一个任务要先向消息队列发送消息,然后另一个任务可以检查并从消息队列中接受消息。在传统的操作系统中,一个任务每次只能从消息队列的头上获取消息,因此,如果一个任务需要5种不同的消息,那就要5个不同的消息队列――每种消息一个消息队列。在这种传统操作系统的应用中消息队列的数目与任务数目是不一样的――往往消息队列的数目要大于任务数目,任务ID和消息队列ID必需由应用软件分别进行管理。见图2。

基于直接消息传递机制的RTOS允许一个任务直接发消息到另一个任务,无需消息队列或邮箱的介入,也就不需要有消息队列ID了。基于直接消息传递机制的RTOS只需指明接受任务的ID。

如果消息已到达接收任务,但任务并未准备好,则RTOS就会自动的为接收任务建立一个队列,并将消息排列于队列中。一个任务只需要一个队列,这个队列也不是一个独立的RTOS“对象”,只是接收任务的一部分,也不需要自己的消息队列ID,而是靠接收任务的ID来识别。这样,不管以先入先出(FIFO)方式进行接收或按消息类型进行选择性接收,消息就可以被“自己”的任务所接收。按类型选择接收消息的方式进一步加强了每个任务一个消息队列的直接消息传递机制的功能。见图3。

这种机制使数据通信有很高的性能。这是因为:在传统的RTOS中,所有消息从发送任务到接收任务都要经过两次拷贝:第一次拷贝是当发送任务发送消息时,消息从发送任务的消息准备区域拷贝到属于RTOS的“掩藏”的存储区域中;第二次拷贝在接收任务接受消息时从RTOS的存储区域拷贝到接收任务的消息接收区域中。如果消息长度较大的话,这种两次拷贝的开销也是非常可观的。

与传统的RTOS相反,直接消息传递机制通常不进行消息的拷贝,只是直接将消息指针从发送任务传送到接受任务。在整个消息传递过程中,发送消息一直位于开始的内存位置,没有做任何的移动或拷贝。使用直接消息传递的RTOS在发送任务发出消息后,先删除发送任务中此消息的指针(不让发送任务在对此消息进行任何操作),然后在消息发送到接受任务时再恢复接收任务中此消息的指针(使接受任务可以对此消息进行操作),这样就可以保证发送和接受任务在任何时候都不能同时对消息进行操作。

这种直接消息传递不光适用于单CPU的系统,对多CPU的分布式系统同样有效。只是当一个CPU上的任务要和另一个CPU上的任务通信时,由于此时传递消息指针已经不再适合,所以发送消息时要做消息拷贝。但这种消息拷贝只需要一次就足够了,与传统的两次消息拷贝相比,开销仍然要小的多。同样对于使用MMU的系统,跨越MMU内存保护段边界的消息传递也需要对消息进行一次拷贝。

这种直接消息传递可以保证RTOS对消息的传递有足够多的监控。由于避免了消息传递的两次拷贝,在消息头上加一些监控字段对效率的开销并不大。具体的做法是:在消息的头部增加一个管理区域,并在消息缓冲尾加上结束标记。一个消息的管理区域包含:发送任务ID,接收任务ID,消息长度,当前消息的属主(任何时候只有消息的属主才可以对消息进行操作,消息的属主可以是发送任务,接收任务或RTOS本身)。使用直接消息传递的RTOS负责消息传递过程中更新消息的管理区域。同时RTOS也会检查消息的结束标记,以确定消息缓冲是否超界。见图4。为了保证每个消息有足够大小的缓存区来写入RTOS的管理区域,以存放一些信息,直接消息传递模式的RTOS要求所有的消息缓存区都从RTOS获得,因此,RTOS也必须具备无碎片的动态内存分配功能,能提供预先定义格式的消息缓存区。

直接消息传递Vs.信号量(共享内存)

信号量(semaphore)本身不能传递数据,因为信号量本身不包含任何数据,它们只包括一个非0的整型计数器“count”, 或称之为计数器或令牌。一个二进制的信号量通常用来保护共享内存,使它同时只能有一个任务访问该存储区,当一个任务向信号量申请对一个共享内存进行操作时,如果另一任务正在对此区域进行操作,那么信号量会迫使此任务等待,直到另一任务释放使用权。

换句话说,用信号量就意味着两个通信的任务必须进行同步,在一个多CPU的分布式系统中,这种不同CPU上任务的同步往往是不可行的,当然在有些技术条件下,是可以建立这种跨CPU的任务间同步,只是实际的同步延时是不可接受的。在做每一个单CPU系统的设计时我们都应该考虑到这种系统将来都会有扩展到多CPU的分布式系统的可能。如果我们开始时就使用直接的消息传递机制,这种扩展是十分简单的。而如果使用信号量,我们需要做许多各CPU上任务同步的工作,往往需要痛苦地做许多底层及应用层程序的修改。

信号量还有其它的缺点,例如,信号量容易造成任务死锁,比如当一个任务等待的信号量被另一任务获得,而此任务已被删除或由于种种原因不能归还令牌,这种情况下,令牌会永远失去,等待的任务就要一直地等待下去。信号量造成的死锁很难调试,因为没有包含任何用于调试的管理区域。

信号量还经常受优先级反转的困扰,最著名的一次就是据1997年12月的RISKS-FORUM DIGEST登载的火星探路者事件,是由于一个低优先级的任务阻挡了高优先级的任务,且未发出任何警告和表示。在遇到优先级反转的问题时,信号量会引起系统的不可预测的系统时延。在单处理器上,优先级反转的问题还可以通过在二进制信号量基础做一些改进(叫互斥)来解决,但在多CPU上,这个互斥就不管用了。同样对于单CPU的系统,

由于将来都存在升级为多CPU的分布式系统的可能,因此从开始就应该避免使用互斥,而使用直接消息传递。传统的RTOS使用信号量/互斥是用来进行共享资源互斥的。在任何时候只允许一个任务对共享资源进行操作,尽管直接消息传递机制使用了另外一种方法,但却具有同样的互斥功能。传统的互斥问题见图5。直接消息传递机制不使用信号量/互斥同样可以完成互斥功能。在上图中,可以将共享资源(打印机)封装在自己的任务中,有时我们称之为“资源监控”任务,这样所有需要打印的任务都不能直接操作打印机,而是向“资源监控”任务发送打印请求消息,此打印请求消息可以包含打印的数据,“资源监控”任务可以接受这种消息,每次只能接收一个并按顺序将其打印出来,如图6。

请注意这种设计中的三个任务可以很好地在一个CPU中工作,三个任务也可以位于三个不同的CPU中,因为直接消息传递机制可以很有效也很透明地在不同CPU间或不同MMU段间发送消息。跨越存储地址空间的直接消息传递

由于现代的微处理器往往都集成了MMU模块,直接消息传递机制应能够将消息从一个地址空间传递到另一个地址空间。这时如果再用零拷贝的消息传递方式就会造成指针的错误。如图7,以红色的条为界,划分地址空间,左侧的任务要发消息到另一侧的任务。

使用直接消息传递的RTOS在发送消息前会在接收任务所处的地址空间存中申请一块消息缓冲,然后会将消息从原来的位置拷贝到这个新申请的缓冲中去,同时会释放原来处于左边地址空间的消息缓冲。

请注意,图7中左边两个基本点任务间消息的传递,不需要RTOS做消息拷贝,因为它们都处于同一地址空间。

分布式系统:Link Handlers

使用直接消息传递的RTOS最初就是设计用于分布式系统的,实践证明也是非常适用于分布式系统的。应用程序不必了解分布式系统的结构,也不需要知道应用程序是否就处于一个分布式的环境中,应用程序只需知道从哪个任务传递到哪个任务即可。

使用直接消息传递的RTOS使用一个叫做“Link Handler”的操作系统部件来完成系统跨CPU边界的透明消息传递。在一个有着多CPU的系统中,每个需要与别的CPU上的任务通信的CPU上都要运行Link Handler。它们提供了跨CPU任务间通信的逻辑通道,对应用层程序来讲是完全透明的,消息传递就如图1一样简单,尽管三个任务可能运行于三个CPU上。

运行于不同CPU上的Link Handler都是一种点对点的关系,因此任何一个点的通信错误不会使整个系统通信出错。这一点和使用传统RTOS的传统的主从式结构不一样(如果传统的RTOS支持多CPU的话)。

另外,Link Handler对应用层程序的一个很重要的功能是能够一直监测别的CPU上的任务。Link Handler可以与RTOS内核相配合,当一个重要的任务被删除时从RTOS的内核获得相应的通知,Linh Handler可以周期性地发出“Heart Beat”消息,用以检查远端硬件的完整性和通信链路工作是否正常。当一个重要的任务不存在或不可操作时,相应感兴趣的任务会立即收到从Link Handler获得的通知。这种RTOS支持的系统看护机构会对高可用性,容错性的冗余系统的设计带来很多的便利,这会将设计容错系统的许多负担直接加到直接消息传递机制上,有RTOS来完成,免除了应用层程序在容错性上所要做的工作。

Link Handler的这种跨CPU的消息传递的透明性使具有“动态负载平衡”的设计变得更加容易。这些“负载平衡”的系统上的任务可以在不同的CPU上移来移去;或者说软件负载可以在不同的CPU上相互转移,条件是同样的代码在互联的CPU上具有可重用性。

设计和调试

直接消息传递模式比传统RTOS应用设计简单,应用层程序的设计更简单,系统调试也更简单。传统的RTOS会受到一些机制的制约,例如邮箱,信号量,互斥,管道,集合和事件标志,以及数量不等的消息队列,使得调试工具很难注重一个具体的调试对象;而对直接消息传递模式的RTOS主要注重任务和消息就可以了,RTOS给消息加了一些相关的监管信息(图4),支持使用直接消息传递RTOS的调试工具能访问这些信息并提供给开发者,使之不仅能观察到这些信息,并且能对它们进行修改,大大加强了调试功能。而传统的消息传递方式由于缺乏这些信息,所以很难调试。

例如,支持直接消息传递机制RTOS的调试工具可以用来监控,跟踪及停止子系统上的软件……这些都是基于消息的发送和接受及任务的调度来实现的。调试工具的动作可以被条件触发,比方说触发源可以是一个特定的消息发送方/接受方……另外这种工具还可以监控内存及CPU的使用状况。

本文小结

直接消息传递机制简化了任务间的通信,增强了功能,提高了性能,简化了应用层的设计。用户的设计可以开发得更快更可靠,也可以进一步将设计变为多CPU的分布式系统,使用直接消息传递会使这种升级更简单,不需对应用层软件做太大的修改。

支持直接消息传递的RTOS代表了下一代RTOS技术,将把我们带入更高可用性,更复杂分布的,更可靠的,容错的嵌入式系统时代。如果您想对直接消息传递的RTOS有近一步的了解,具体请访问www.enea.com。



基于模型的整车电子电气架构设计
嵌入式设备上的 Linux 系统开发
Linux 的并发可管理工作队列
ARM嵌入式系统的问题总结分析
嵌入式系统设计与实例开发
WinCE6.0的EBOOT概要
更多...   


UML +RoseRealtime+嵌入式
C++嵌入式系统开发
嵌入式白盒测试
手机软件测试
嵌入式软件测试
嵌入式操作系统VxWorks


中国航空 嵌入式C高质量编程
使用EA和UML进行嵌入式系统分析设计
基于SysML和EA的嵌入式系统建模
上海汽车 嵌入式软件架构设计
北京 嵌入式C高质量编程
北京 高质高效嵌入式开发
Nagra linux内核与设备驱动原理
更多...   
 
 
 
 
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号