本文将在讨论硬件抽象层基本结构的基础上,提出一种适用于大规模接入汇聚路由器的HAL的通用性软件结构设计及实现方式,提供高效、可靠的内部通信,并针对多用户接入数量不确定的情况,提出动态加载虚拟驱动模块的实现方法,增强路由器面向ACR接入方式的可用性。
1 硬件抽象层基本结构及功能实现
根据文献提出的方案,高性能路由器硬件抽象层可分为内部通信、虚拟驱动及设备管理三大模块,这三部分模块相互配合,共同完成面向实际的用户设备接口的功能模拟及硬件细节的屏蔽,并对其进行统一协调的管理。硬件抽象层对用户设备接口的功能模拟主要由虚拟驱动模块完成,包括数据包的收发及协议报文的预处理等工作,为上层协议软件提供标准的API函数;而对用户设备的接口管理则由上层网络管理软件通过设备管理模块对其进行管理配置及监控;内部通信模块运行于内部以太网络,协调各模块之间的功能接口,保证各从处理单元与主处理单元之间实时可靠的数据传输。其基本结构如图1所示。
图1 硬件抽象层基本结构示意图
根据各模块的功能可知,硬件抽象层内部通信模块是各分处理单元与主处理单元信息交互的重要传输通道。内部通信模块汇集各底层设备的数据并根据类型分流至各上层处理模块,同时,数据维护模块对虚拟设备及各处理单元的维护信息也需要通过内部通信模块进行。因此,内部通信模块采用何种基于内部以太网的数据传输实现方式,对路由器内部数据的实时、有效、可靠传输起着至关重要的作用。当前内部通信模块采用基于分隔符的TCP传输方式,在应用层数据包的起始部分附加有特定格式的分隔符和数据长度域,解决了由于Nagle算法产生的包粘滞问题。但该方式没能解决TCP传输方式的消耗过大、实时性不强的问题。同时,消除分割符恢复报文的完整性也增加了应用程序的处理复杂度,从而不可避免地增加系统的开销并降低系统的实时性。系统的实时性对于用户业务急剧增多的ACR路由器而言是一个迫切需要解决的问题。UDP是一个面向消息的传输协议,其最大数据缓冲区长度为8192~65536字节,满足一次传输一个完整报文的条件。在内部以太网中采用UDP传输方式具有明显的优势。但由于UDP协议的无连接性,导致它是一个不可靠传输,文中第二部分将讨论如何实现一种基于UDP的内部通信的可靠性传输机制。
硬件抽象层对用户设备接口的功能模拟主要通过虚拟驱动进行,路由器业务类型的扩展使得用户接口数量增多并呈现接入时间的不确定性,从而带来用户设备管理上的难度。针对此种情况,文中第三部分提出动态加载虚拟驱动模块的实现方法,增强路由器面向多用户接入方式的可用性。
2 基于UDP传输方式的内部通信的可靠性实现
内部通信模块处于硬件抽象层的底层,运行于内部交换网络,完成底层硬件与上层控制软件的数据传输,实现对底层硬件的初步屏蔽分离;针对分布式体系结构特点及多用户接入的业务需求,内部通信模块以Client\Server的方式分别运行于主处理单元模块及各线路接口单元模块上,采用UDP传输协议进行通信,主要基于以下几点考虑:
首先,UDP协议是一个无连接协议,传输数据之前源端与终端不需建立连接,因此不需维护连接状态。这样服务器端可以使用一个或几个端口同时向多个客户端发送消息,符合分布式结构体系的要求。
其次,UDP信息包很短,只有8个字节,相对于TCP的20个字节的信息包的额外开销很小,便于数据的快速传递。
再次,吞吐量不受拥塞控制算法的调节,只受应用软件生成数据的速率、传输带宽和计算机性能的影响,适用于内部以太网络的数据传输。
但由于UDP方式的无连接性,使得UDP传输的可靠性不强。而可靠性是内部通信模块所必须具有的性能,因此考虑在应用软件中实现UDP传输方式的可靠性保证,主要采用以下方式:
2.1 多线程无连接的C/S通信方式
服务器端运行在Linux操作系统下,采用多线程方式收发各类数据;客户端运行在Vxworks操作系统,采用多任务方式收发各类数据。这样由于多线程及多任务并行运行的特性,在内部以太网的传输条件下,使得收发数据的速率可以满足系统的要求。基本的基于UDP协议的无连接客户端/服务器端通信程序如图2所示。
图2 基于UDP协议的无连接客户端/服务器端通信程序
该通信过程采用多个客户端(各从处理单元)对一个服务器端(主处理单元)的方式,使多个用户接口模块可以在不同时间接入主控。内部通信根据所传递数据的不同类型,采用相对固定的不同的端口号,不同的客户端采用不同的IP地址,从相同的端口收发同类数据。在服务器端通过select()系统调用,既可以轮询各个socket端口以便及时接收不同端口的数据,又起到定时器的作用。当规定时间内收不到数据时,能够及时返回继续在阻塞模式下等待,从而既能及时收发数据,又降低资源消耗。
2.2 三次握手过程
每个客户端与服务器端进行真正的数据传输之前,首先要进行一个握手的建立过程,如图3所示。握手过程成功后则表示双方通信通道正常,只有在得知握手成功后双方才可以正常地收发报文,从而克服了UDP协议方式的面向无连接性。为了随时检测和维护双方链路的通连性,每个客户端与服务端在一定的间隔时间内要互发KEEPALIVE报文。如果在规定的时间内收不到对方的KEEPALIVE报文,说明断链,要进行相应的断链处理。
图3 握手建立过程
2.3 接收端丢失确认及滑动窗口
发送UDP报文时在自定义的内部数据头中加入所发送数据的序号,接收端收到后发送确认信息,如果发送方在规定时间内没有收到确认信息,则认为该包丢失,会连同原包的序号重新发送。
滑动窗口的目的主要是为了实现流量控制,防止拥塞。每个发送方维护一个重发队列,保存着一定数量的发送而没被确认的报文,该队列剩余空间的大小可以限制应用部分发包的速率。由于UDP协议是基于消息的传输协议而非基于流的,因此不必考虑发送端可以接收多少数据,只需知道能否接收数据即可。
总之,采用UDP传输控制方式主要考虑到其传输简单快速、额外开销较小的特点,但这是以牺牲一定的可靠性为前提的,因此必须在应用程序中增加可靠性保护机制。在实际应用中证明上述方法可靠高效,能够维护内部通信有序、快速的数据传输。
3 基于多用户的用户接入管理
在Linux操作系统下,系统把设备映射为一个特殊的设备文件,用户程序可以像对其他文件一样对该设备文件进行读写操作。虚拟驱动模块运行在Linux操作系统下,模拟从处理单元上的接口单元,形成收发协议报文功能和数量与此一致的硬件抽象层虚拟接口单元。因此,每个实际的接口单元都在内核中对应一个注册的虚拟设备,以便于上层控制软件对数据平面的管理与数据交互。
3.1 多用户虚拟设备驱动程序的动态加载
虚拟驱动在内核中的功能通过动态加载方式实现。通常的动态加载方式是将驱动程序作为一个整体模块,在需要时再加入内核;由于多用户接入方式使得在某一时刻内核中注册的接口单元数量不确定,如果实施一次性加载会冗余太多,不利于资源的有效利用。因此,在内核中加载一个基本模块的前提下,实现各虚拟设备的动态加载过程,达到以一个基本的虚拟设备控制多个设备驱动模块的功能。
如图4所示,对虚拟驱动设备的控制由内部通信模块与设备管理模块共同完成。设备管理模块通过内部通信模块下达加载、卸载虚拟驱动的命令,通过内部通信模块与虚拟驱动的控制通道进行。内部通信模块通过调用ioctl()采用不同的命令字完成对虚拟驱动模块的控制过程。
图4模块动态加载过程
基本驱动模块的加载采用通常的驱动模块加载方式,即调用module_init()函数进行基本模块的初始化及在内核中的注册过程。以该基本驱动模块为基础,当内部通信模块收到加载某个用户设备接口的命令后,通过调用该基本模块的Base_ioctl()在内核中注册一个新的驱动设备,该注册设备才是与实际接口单元相对应的虚拟驱动模块,应用程序对用户设备数据的读写都是通过这些注册的接口设备而非基本设备提供的标准函数进行。这样的动态加载过程使得当没有设备加载时在内核中只存在一个基本的虚拟驱动模块,只有需要注册的用户才将其对应的设备接口的虚拟驱动模块加载到内核中,从而减少系统冗余,便于管理。
各用户接口单元与虚拟驱动的数据交互通过内部通信模块与虚拟驱动的数据通道进行,所对应的系统调用为该注册设备的dev_ioctl()。在该功能函数中,实现用户空间与内核空间的数据交互。
3.2 对多用户接口设备虚拟驱动的管理
为实现内核虚拟驱动模块与实际接口单元的一一对应,必须解决各驱动模块的命名原则问题。将每个实际接口单元在接入段拓扑中的位置设置为不同的参数,在内部通信中这些参数作为传输数据的报头信息出现,根据它们可以生成一个唯一的字符串作为对应该接口单元的虚拟驱动设备名称,而且根据设备名称亦可还原出实际接口单元的拓扑信息,以供内部通信使用。在内核中维护一个由各注册设备名称所组成的动态链表,每个链表节点维护一个收发报文的数据队列,虚拟驱动与其他模块的数据交互都通过该链表进行。
3.3 对虚拟设备数据读写过程
对数据的读写过程主要是在虚拟驱动模块、内部通信模块及上层控制软件之间进行。虚拟驱动模块运行在内核空间,而内部通信模块运行在用户空间,因此,主要解决用户空间与内核空间的数据传递问题。通过memcpy_tofs()及memcpy_fromfs()系统调用用户空间与内核空间的数据交互。
在内核中维护一个由各注册设备名称所组成的动态链表,每个链表节点维护一个收发报文的数据队列,虚拟驱动与其他模块的数据交互都通过该链表进行。接收报文过程:内部通信模块将从接口单元接收的报文通过ioctl()调用传给虚拟驱动。该函数通过struct net_device *dev结构找到对应的虚拟设备的dev_ioctl()功能函数,调用memcpy_fromfs()将数据拷贝至内核空间,经过处理后通过netif_rx()函数通知上层协议有数据传入。发送报文过程:虚拟驱动将从上层软件取出的数据放至自身维护的通过虚拟接口设备名称维护的数据队列中,内部通信模块通过ioctl()论询各接口设备数据队列是否有数据可读,如果有数据,虚拟驱动通过memcpy_tofs()调用将数据拷贝至用户空间提供的缓冲区中。
文中针对大规模用户接入方式的特性,讨论了一种基于ACR/Tbit路由器的硬件抽象层的通用性软件结构设计及实现方式,并研究了其关键技术,包括基于UDP传输方式的内部通信的可靠性实现及基于多用户的动态模块加载技术,适用于路由器承载业务量的扩展和多用户接入特性,并且在上层软件实现中,基本上可以不考虑底层硬件细节,增强了路由器的开放性及可扩展性。
评论
查看更多