标准化模块接口--统一消息

原本今晚想写如何搞动态加载和动态补丁的,但很不幸,翻遍了硬盘,也没找到之前的代码,连网盘里都没备份。这时候,才焕然大悟--半年前我换上如今的笔记本,淘汰了那台老掉牙的台式机。所幸硬盘没丢,不过一时时也无法读里面的数据了。等过些日子,读出里面的数据再谈动态加载和动态补丁技术。今天说些简单的,能在软件设计中当即用得上的,模块间通讯技术--统一消息。编程

统一消息模型,最先的启发是UT的Wacos SSI。那是一个很不错的通讯模型,容许模块间的通讯统一成队列通讯;而在物理上,模块可能位于各类网络中的不一样的实体,又或者是不一样的进程,线程。记得那会调试核心网的程序,在板卡上是没有什么调试环境的,除了WindShell(同CSHELL)外,就没什么支撑了。因而咱们就把软件用GDB加载到目标机(无盘工做站),而后开始测试。有人不理解了,这没啥啊!现实是这价值很大,大型系统的嵌入式开发,能争取到的机房空间、设备和板卡老是奇缺,就当时的状况来讲,咱们三四我的才能分到一套设备。Wacos_SSI的队列通讯技术,让咱们能够把目标机作成功能板块,且只须要极少许的修改,就能和实际系统的主控板进行通信联测,工做效率的提高自不待言。安全

再后来,哥在Nortel的时候知道了TIPC协议,好象是E///和IBM捣腾出来的东西。思路上,和Wacos SSI很接近。所不一样的是,Wacos SSI在消息头里使用了IP地址,而TIPC则是自定义的节点地址,也所以包含了一个额外的节点地址和特定网络间的地址翻译过程。另一个区别是,Wacos SSI考虑了远程节点间通讯和本地通讯的差异,只有远程通讯时才传递消息实体,而本地则是传递标识(Handle)来快速完成。TIPC则没讲述这个层次的程序设计问题,也所以在工程实践中应用寥寥。网络

现现在,UT没了,Nortel也没了。特别是UT,十多年过去了,哥特别怀念那段日子,和个人那个团队。无奈,哥就是灾星,跟喜欢的公司相克。不少局外人都说UT不咋的,就一个作小灵通的;可哥的眼里,那的许多软件开发团队,战斗力一点不比Huawei差。就说哥作的网关城域交换机,才十来我的,而huawei是几十人,好几倍啊,最后市场表现仍是势均力敌。固然,我仍是蛮佩服huawei的,他们的东西真心作的漂亮,维护界面人性化,不像咱们的,不少事情要命令行来实现。不过咱们也有特色,就是架构作的很是好,以致于客户的需求,老是能很快实现,并且基本上对现有功能是0风险。呵呵,听说气死很多人!数据结构

这当中,有三大功臣:多线程

  • Wacos SSI;
  • 状态机;
  • 数据驱动模型。

状态机的代码,已经在昨晚的内存泄漏里的连接里提供了,有兴趣能够下载或是用在喜欢的地方,哥只但愿它有更多机会发挥价值。架构

嗯,Wacos SSI排在第一!是的,Wacos SSI的消息通讯让咱们的系统变得很是柔性,模块与模块间几乎没有什么复杂的耦合。想一想如今那些公司招聘需求里,要求什么多任务多线程编程能力,精通什么信号量和同步技术,哥就想哭,这就是咱们的软件水平,时刻准备着处在玩死本身。哥作程序,只考虑CPU有几个线程核,至于系统有几个进程线程,都是这个决定的,并且合并拆解任务,都是分分钟能改代码实现的事。跟哥一块儿作软件,就只要记住几点:不管你和谁通讯,你只要知道他的地址,而后发消息给他就行了;而你也只要看着本身的队列,有消息就干活,没消息就歇着。至于发消息,就一个标准的函数,而消息封装格式,也是统一的。至于系统函数库里提供的什么信号量,管道啥的,千万别尝试在应用里面使用,不然,编译器会用编译错误来告诉你行不通。函数

有点扯远了,回到正题。性能

统一消息的定义,包含两个部分,消息标签和消息头,具体以下:测试

typedef struct _MSG_TAG_TYPE_
{
zAddr_t srcAddr;
zAddr_t dstAddr;
zHandle_t msgHandle;
} PACKED zMsg_t;

typedef struct _MSG_HEAD_TYPE_
{
byte_t sysrsvd[8]; //reserved for adding src & dst addresses on network. 
word_t msgLen;
word_t msgId; 
dword_t srcInst;
dword_t dstInst;
} PACKED zMsgHdr_t;

typedef struct _MSG_HEAD_EX_TYPE_
{
zAddr_t srcAddr;
zAddr_t dstAddr; 
word_t msgLen;
word_t msgId; 
dword_t srcInst;
dword_t dstInst;
byte_t msgBuf[1];
} PACKED zMsgHdrEx_t;

 

zMsg_t结构是消息标签,应用程序收、发消息时,都是收发的这个数据结构,以下:
int zMsgSend(zMsg_t *msg);
一般来讲,咱们应该把这个消息标签作的比较小,由于作的太大,来回复制它的内容是须要耗费CPU时间的。好比,你能够将zAddr_t定义成word,zHandle_t定义成dword,这样只须要8字节就够了。不过记得字节对齐,通常来讲,要保证长度是4的倍数。spa

消息头就是消息内容的头部格式段,除了这个头部,剩下的就是应用自定义的payload部分。zMsgHdr_t和zMsgHdrEx_t实质上是同样的。这里面的地址部分,不是必须的,只有当消息透过网络或是总线传递时,才是必须的,不然无法由边界模块还原。而对于应用,如无特别约定,那几个字节是无心义且内容不肯定的。

消息标签和消息间是经过msgHandle关联。这样,当消息在本地传递时,msgHandle指向的是一块普通内存;而当消息在本地进程间通讯时,则指向共享内存;至于网络或是某个总线传递,边界模块负责本地内存数据和网络数据间的转换。如此一来,最大程度的减小实际消息体的拷贝开销,让消息传递变得高效,且细节处理对应用透明。

Wacos SSI的地址部分,填的是IP地址;固然,它还定义了一个模块号来配合这个地址使用。整个通讯过程很简单,应用只须要申请一个队列,并告知SSI,这个队列和哪一个目的模块号使用。正常状况下,这个作法都能知足需求,但碰上程序模块从新规划或是特俗测试目的,就有点力不从心了。所以,哥在zMsg_t标签里完全放弃了IP+module的地址组成,改成TIPC的地址方式。不过这也就让系统必须维护一个路由表,用来完成特定目的地址到队列的映射。

统一消息路由表定义以下:

typedef struct Z_UDP_ADDR_TYPE
{
dword_t ip;
word_t port;
} zUDPAddr_t;
typedef struct Z_MSGQ_ADDR_TYPE
{
void *qid;
} zQueAddr_t;

typedef struct Z_MSGQ_OUT_TYPE
{
zAddr_t addr; 
zUDPAddr_t udpAddr;
zQueAddr_t queAddr; 
} zMsgRoute_t;

路由表项里首先是地址,对应的是消息的目的地址。接下来是网络地址和队列地址,能够有一个或是都有。

  • 仅队列地址:说明是本地(或者是须要经隐形边界代理转发)的消息,目的地址为队列全部者;
  • 仅网络地址:说明是远程消息,且应该直接网络发送,无需通过边界代理,目的地址为远端模块地址;
  • 含两类地址:远程消息,应用发送时经过队列地址送入边界模块,再经过网络地址发送,,目的地址为远端模块地址。

总上面的关系能够看出,队列和地址间的关系是一对多的关系,即多个地址的消息可能被投送到同一个队列。这就让模块合并变得异常容易,固然,不安规则出牌的模块何时什么方法都白搭。一般来讲,若是有IP网络的通讯要求,系统就须要建立一个基础的网络边界模块。这个模块自己可能并不须要地址,而只须要提供一个消息聚合的队列。固然,在一个开放的网络环境下,这个边界模块可能还须要作些安全性的工做,好比过滤非法消息等,这能够经过在模块内额外配置源IP地址,端口或是源目的地址等实现。若是远端并不支持zMsg_t工做,则这时候的边界模块就须要作好消息的翻译过程,为远端模块分配映射模块地址。固然,这些都是本地的,不属于路由表内容。

从地址映射到真实的目的队列或是网络地址,是个频繁的操做,设计上必需要很是高效。对于地址很是少的系统,好比总共才七八个模块,能够用一个紧凑的数据来作,简单且不妨碍效率。但对于有数十或是上百个地址的系统来讲,遍历方法就不可取了。这时应该用二分搜索,或是平衡二叉树。好比城域交换机,有十来块子功能卡,每张卡上有十来个模块,整个系统的地址空间有一百多,采用二分搜索,最多8次就够了!相比消息处理函数的指令数,这部分开销彻底能够接受。而从另外一个角度来讲,统一消息让程序变得简单可控,系统内减小了消息的拷贝操做,所带来的系统效率和性能提高,远远大于查询路由表的开销。

当嵌入式世界有了统一消息后,哪些多线程的开发技巧还有很大价值么?通常应用开发者真的须要理解这些知识么?

相关文章
相关标签/搜索