袁瑞成,刘 波,乔 磊,刘健培
目前中国在轨运行的大部分航天器内部各个计算机之间是通过总线进行互联的,网络应用在我国航天器上还处于空白.随着载人三期、探月三期等重大专项的实施,特别是空间站的建设,航天器整个系统的规模越来越庞大,各个计算机系统之间联系越来越密切,执行的任务越来越复杂.现有使用总线来连接各个计算机的通信方式已不能满足应用需求.因此利用网络来实现各个计算机系统之间的通讯势在必行,研究航天领域的专用网络协议具有重要的工程应用价值.
目前航天器内部各个系统之间的通讯主要分两种数据包,一种是控制数据包,用于传递各个模块间的控制信息,另一种是普通数据包,主要是传递各个模块之间正常通讯数据.在航天器上,数据包小,数量众多的控制数据包占有很大比例.控制数据包主要是用于向某一模块发送控制信号,控制数据包对数据传输的实时性要求很高.航天器对数据传输的可靠性要求很高,数据在传输过程中必须保证数据的正确性和完整性.设计专用的TCP/IP协议栈并且移植到大量使用的空间操作系统SpaceOS2上是非常有必要的.
选择业界常用的TCP/IP协议栈有BSD,Linux,LwIP,UIP,Uc/IP,NicheStrack等,其对比如表1-1所示.
除去商业协议栈,开源协议栈中比较轻量级的是LwIP、Uc/IP、uIP,复杂一些的是BSD和Linux协议栈.
按照协议特性丰富程度排序,大致是:
Linux协议栈> BSD协议栈>LwIP>Uc/IP>uIP
代码大小的顺序也大致是:
Linux协议栈> BSD协议栈>LwIP>Uc/IP>uIP
首先Linux协议栈与Linux其余部分关系复杂,协议栈模型自成一家,基本不具备移植到其余操作系统的可能.其次uIP功能过于简单,比较适合没有操作系统的场景.最后,Uc/IP绑定到了UCOS,以后可能会有License授权问题,而且协议栈模型与BSD相似,见表1所示.与LwIP相比,BSD协议栈的特性是(同时也是BSD协议栈代码更大的原因)下列四条.
1) 支持动态IP路由:LwIP支持IP转发,但是不支持动态IP路由;
2) BSD协议栈具有高实时高性能的同时可以压缩到较小的体积.
3) 支持大量已有的网络应用程序:大部分的网络应用程序的接口都是兼容于BSD Socket API的,同时可能会有大量POSIX文件系统的接口,而BSD协议栈基于Unix,日后增加文件系统接口相对容易;
4) 使用者众多,不论是从Unix派生的老牌桌面或服务器系统(如OpenBSD、netBSD、freeBSD等),还是路由器操作系统,(如思科IOS,最新版思科IOS XR使用QNX的协议栈,而QNX的协议栈来源于netBSD;JunOS操作系统,基于freeBSD),还是典型的RTOS,(如VxWorks、RTEMS、QNX、LynxOS等),使用的都是BSD的协议栈.经过多年的使用和测试,BSD协议栈的健壮性、稳定性、性能都有一定的保证,并且以后为扩展其他功能例如防火墙接口提供了重要的支持.
表1 协议栈对比表Tab.1 Protocol stackcontrast table
如果只是需要通过IP接入网络,而且内存等资源也很有限,那么LwIP将是个不错的选择,而且LwIP的移植相对简单得多.如果需要支持动态IP路由、复杂的网络应用程序,BSD协议栈是个更好的选择.
从以上角度考虑,本文使用BSD协议栈作为移植原型.
图1显示了各种BSD版本的年表,包括重要的TCP/IP特征.显示在左边的版本是公开可用源代码版,它包括所有联网代码:协议本身、联网接口的内核例程及很多应用和实用程序(如Telnet和FTP等).
在BSD因资助问题停止开发前,最终停留的版本是4.4BSD-Lite Release 2.这是特性最丰富(如新的拥塞控制算法、多播、“长肥管道”修改以及其他类似的研究特性),也最稳定的版本,基本上后续“伯克利派生系统”(指SunOS4.x、SVR4、 AIX、FreeBSD、NetBSD等基于伯克利源代码开发的系统)都使用该版本作为基线版本.
所以,最终确定本文的TCP/IP协议栈移植源是4.4BSD-Lite Release 2.
复杂的数据通信系统不是使用单一的协议来处理所有的传输任务,而是一整套相互合作的协议,这些协议合称协议族.协议族通常使用分层的概念设计和实现,所以又称协议栈.TCP/IP协议软件在概念上分成4层,而在一个实际的系统中实现协议栈时,要顾及到具体硬件接口、OS特性与接口、缓冲区管理、协议之间的接口、错误处理等细节,协议软件的模块结构比概念结构要复杂.
1.3.1 TCP/IP协议栈的分层概念结构
网络协议通常分不同层次进行开发,每一层分别负责不同的通信功能.一个协议族,比如TCP/IP,是一组不同层次上的多个协议的组合.TCP/IP通常被认为是一个四层协议系统,如图2所示.每一层负责不同的功能:
1) 链路层,有时也称作数据链路层或网络接口层,通常包括操作系统中的设备驱动程序和计算机中对应的网络接口卡.它们一起处理与电缆(或其他任何传输媒介)的物理接口细节.
2) 网络层,有时也称作互联网层,处理分组在网络中的活动,例如分组的选路.在TCP/IP协议族中,网络层协议包括IP协议(网际协议),ICMP协议(网际控制报文协议),以及IGMP协议(网际组管理协议).
3) 传输层,主要为两台主机上的应用程序提供端到端的通信.在TCP/IP协议族中,有两个互不相同的传输协议:TCP(传输控制协议)和UDP(用户数据报协议).TCP为两台主机提供高可靠性的数据通信.它所做的工作包括把应用程序交给它的数据分成合适的小块交给下面的网络层,确认接收到的分组,设置发送最后确认分组的超时时钟等.由于运输层提供了高可靠性的端到端的通信,因此应用层可以忽略所有这些细节.而另一方面,UDP则为应用层提供一种非常简单的服务.它只是把称作数据报的分组从一台主机发送到另一台主机,但并不保证该数据报能到达另一端.任何必需的可靠性必须由应用层来提供.这两种运输层协议分别在不同的应用程序中有不同的用途.
4) 应用层,负责处理特定的应用程序细节.如Telnet 远程登录、FTP 文件传输协议等通用的应用程序.
从概念上讲,从一台设备的应用程序向另一台设备的应用程序发送一个报文,意味着这样一个过程:该报文在发送设备的协议软件中逐层下行,通过网络转发,再在接收设备的协议软件中逐层上行.
以以太网中2台运行FTP协议的设备之间的通信为例,图3说明了通信过程需要涉及到的所以协议,以及协议报文的处理过程.
1.3.2 TCP/IP协议软件的模块实现结构
具体到本文,是要在SpaceOS上实现TCP/IP协议栈,那么除了协议栈本身各个协议的逻辑外,还需要考虑到以下多个方面:
(1) 接口方面
1) 与硬件接口卡的接口:使用统一的END驱动模型,协议栈通过MUX层与END驱动通信.
2) 与SpaceOS的接口:网络任务的管理、信号量提供互斥与同步、开关中断保护临界数据、定时中断以实现定时器、协议栈内存池的分配与释放.
3) 应用接口:应用程序使用socket接口与协议栈通信.
(2) 协议栈的实现方面
1) 缓冲区管理:使用mBlk-clBlk-Cluster的三级缓冲区管理结构.
2) 定时器:使用time sheel(时间轮)算法实现软件定时器.
3) 协议之间的接口:使用protosw等数据结构将接口抽象化(协议之间通过函数指针,而不是直接调用函数名).
4) 协议栈代码的组织:协议本身的实现代码、协议与OS驱动等的接口代码、协议用到的例程分、初始化与剪裁代码,分成不同的文件夹组织.
(3) 测试、演示与验证方面
1) 在协议栈之外实现FTP、TFTP、Telnet、Http(以及Web Server)、Yaffs文件系统、flash驱动等模块.
(4) 代码质量方面
1) 模块间的耦合性:OS、应用程序、硬件驱动、协议之间等接口都尽量解耦.
2) 代码大小与裁剪性:通过用函数指针以及宏控制初始化函数的办法剪裁代码.
3) 性能:使用独立的网络处理任务、缓冲队列、高效的路由查找算法等提高性能.
4) 可靠性:使用心跳监视网络任务、减少任务状态等方式提高可靠性.
最终,在SpaceOS上实现TCP/IP协议栈的实现结构如图4所示:
其中,整个实现的核心是TCP/IP协议栈本身的逻辑代码,而TCP/IP的核心又是IP.
应用层通过Socket层使用协议栈,而协议栈通过MUX层与驱动层通信.
整个协议栈使用单独的一个网络任务netTask处理所有数据包的接收和处理,具体过程如下:
1) netTask与驱动通过环形工作队列和信号量进行通信;
2) netTask根据数据包中的MAC头(链路层)中类型字段决定将包放入ARP队列还是IP输入队列;
3) IP层根据IP头中的类型字段决定调用ICMP处理ICMP子类型、IGMP、还是送往UDP或者TCP队列,根据目的IP地址决定数据包的去向(送往上层处理、转发、丢弃、组播);
4) UDP(TCP)通过UDP(TCP)头中的端口号决定将包送往对应的Socket队列.
5) 如果相应Socket队列有应用层任务在等待接收,则释放信号量,然后应用任务通过Socket队列接收数据.
应用层发送数据时,通过Socket调用UDP/TCP的输出函数,然后UDP/TCP添加头后,调用IP层的输出函数,IP层通过目的IP查找路由,决定数据包的输出接口,并通过ARP过程获得IP地址对应的MAC地址,然后将包送入对应输出接口的队列中,最后调用驱动的输出函数将队列中的数据包发送出去.整个过程都使用函数调用,即发生在应用层任务的环境中.
除协议栈的实现外,应用层有TFTP、FTP、Telnet、HTTP 4个任务实现4个协议和应用逻辑.
协议相关部分与SpaceOS的接口通过一个统一的接口文件解耦,协议部分的代码只使用该文件中的函数,而与SpaceOS隔离开来.
应用层程序都需要使用到文件系统,本文使用YAFFS这个与基本不需要操作系统文件操作支持的Flash文件系统.
在YAFFS与应用程序之间有薄薄的一层文件系统接口,该接口同时也支持对协议栈的控制操作.
本节介绍一下传输层的具体设计实现,传输层数据包格式的如图5所示.
其中:
1) 本地地址和本地端口域表示发送数据包一方;
2) 目的地址和目的端口域表示接受数据包一方;
3) 序列号域表示发送方发送数据包的序号;
4) 确认号域用于对最新收到的一个数据包的确认回答;
5) 数据包ACK位被设置为1,表明数据包是确认数据包;
6) 建立连接的时,数据包SYN置为1,表示数据包是请求建立连接的数据包;
7) 释放一个连接时,数据包FIN置为1,表示数据包是释放连接的数据包;
8) RST位被置为1,用于重置一个混乱的连接,也可用于拒绝一个连接请求;
9) 优先级域是用4位表示该数据包在网络中传输的优先级,网络中节点在传输数据包时,会根据数据包的优先级,优先传输高优先级的数据包;
10) 校验和域用于校验数据包;
11) 数据域就是存放要传输的数据.
网络节点在传输层主要是用套接字来控制,通过套接字可以查看网络连接相关的信息.套接字的数据结构如下:
typedef struct sock{
struct sock *pre;
struct sock *next;
unsigned char dest_addr;
unsigned char local_addr;
unsigned char dest_port;
unsigned char local_port;
unsigned char sock_state;
unsigned char priority;struct tp_cb *trans_control_block;
unsigned int sock_sequence;
unsigned int sock_count;
SEM_ID sock_semid;
unsigned int sock_have_data;
}SOCKET;
使用3次握手过程创建一个新连接,如图6所示,发送方向接收方发送一个SYN数据包,然后一直处于睡眠状态,直到收到接收方返回的ACK数据包;发送方再次向接收方发送一个ACK数据包这样才建立了一个新的连接.如果在指定的时间内发送方无法收到ACK数据包,就会重新发送一个SYN数据包.如此尝试若干次之后仍然无法建立连接就停止连接,并向上层应用提示无法建立连接.在建立连接的过程中,SYN数据包的优先级由套接字中优先级确定的,网络中节点在转发数据包的时候会根据数据包优先级由高到低的次序转发数据包.
数据传输模块主要是由接收数据和发送数据两个处理过程构成.
发送数据过程:将指定要发送的数据分割成若干个数据包,将数据包按照数据组织的先后顺序连续分配序号.在发送一个数据包之后,直到接收方返回确认包才会发送下一个数据包,否则等待超时重新发送此数据包.
从指定的连接上接受数据:接收到一个数据包之后,校验数据的完整性和正确性后,向发送方发送一个包含下一个想要接收数据包的序号的确认数据包,将接收到数据包按照序号的先后顺序将数据合并成完整的数据,并存放到指定的位置.
在数据传输的过程中,数据包按照分配的序号依次向网络中发送数据包,其中数据包中包含优先级,用来满足实时数据传输的要求.当有实时数据传送的时候,将数据包分配高优先级.当网络中节点接收到数据包时,就根据数据包的优先级对数据包重新排序,高优先级的数据包网络节点将优先转发.这样就保证了实时数据能够及时传输.
当一个数据包n发送后,发送方就会设置一个定时器,如果在指定的时间中无法接受到接收方返回的数据包n的确认数据包,发送方就会重新发送数据包n,在经过若干次重复之后仍无法接收到确认数据包,发送方就认定接收方已失效,就会断开连接,见图7所示.
释放指定的连接:使用四次握手过程来释放一个连接.为了释放一个连接,任何一方都可以发送一个设置了FIN位的数据包,它表示没有数据需要发送,当另一方接受到FIN数据包时就会向对方放回一个ACK数据包并也发送一个FIN数据包.这两个数据包可以合并为同一个数据包.当对方接受FIN数据包时就会再返回一个ACK数据包.最后经过如此四次握手之后,才能释放一个连接,见图8所示.
本模块实现移植BSD协议栈需要的OS功能函数,包括:网络任务的管理、信号量提供互斥与同步、开关中断保护临界数据、定时中断以实现定时器、协议栈内存池的分配与释放.
另外,添加网络应用程序时,还需要实现消息队列、时间管理、一部分C库函数等.
主要设计思路是:实现一个单独的接口文件,这个文件调用SpaceOS的函数实现接口,所有新增的代码只依赖于这个接口文件,不允许直接调用SpaceOS的函数,同时做到调用SpaceOS函数的最小化,这样,就保证了新增代码与SpaceOS二者的相对独立性,当修改新增代码、修改SpaceOS代码、或者裁剪代码时,可以很容易的完成.
3.2.1 模块接口
接口包括:任务管理接口函数,中断管理接口函数,消息队列管理接口函数,内存管理接口函数,时间管理接口函数,定时器中断回调函数(图9).
具体接口代码示例:
∥VxWorks任务创建接口:
int taskSpawn
(
char *name,
int priority,
int options,
int stackSize,
FUNCPTR entryPt,
int arg1, int arg2,int arg3,int arg4,int arg5,int arg6,
int arg7,int arg8,int arg9,int arg10
);
STATUStaskDelete
(int tid);
STATUStaskSuspend
(int tid);
STATUStaskResume
(inttid);
STATUStaskDelay
(int ticks);
int taskIdSelf (void);
STATUStaskLock (void);
STATUStaskUnlock (void);
int vx_get_taskIdCurrent(void);
SEM_ID vx_get_taskIdCurrent_selectSem(void);
∥中断管理接口函数:
STATUSintConnect (VOIDFUNCPTR *vector, VOIDFUNCPTR routine,int parameter);
int intLock (void);
int intUnlock (int oldSR);
BOOL intContext (void);
∥内存管理接口函数:
void *malloc (size_t nBytes)
void free (void *ptr);
∥消息队列管理接口函数:
MSG_Q_ID msgQCreate (int maxMsgs, int maxMsgLength, int options);
STATUSmsgQDelete(MSG_Q_ID sgQId);
STATUSmsgQSend (MSG_Q_ID msgQId, char *buffer, UINT nBytes, int timeout, int priority);
int msgQReceive (MSG_Q_ID msgQId, char *buffer, UINT maxNBytes,int timeout);
∥定时器中断回调函数:
void OS_TickHook(void)
{
hardclock_tick_callback();
}
∥SpaceOS2任务创建接口:
int ret;
ret = OSTaskSpawn((unsigned int)name, (unsigned int)priority, (unsigned int)0, (unsigned int)stackSize,
taskWrapper, arg1, arg2, arg3, (int)entryPt);
if(NULL == ret)
{}
return ret;
3.2.2 模块实现
整个接口函数集合在bsd_port_if.c中实现.
总共实现了如图10的函数.
其中调用函数入口部分代码为:
static int taskWrapper(
register int arg1,
register int arg2,
register int arg3,
register int arg4)
{
FUNCPTR realTaskEntryPt = (FUNCPTR)arg4;
(*realTaskEntryPt)(arg1, arg2, arg3);
#ifndef MODIFY_FOR_DEBUG
taskSuspend(0);
#else
taskDelete(0);
#endif
}
taskSpawn的是VxWorks系统中的任务创建接口,因为直接将其移植时会导致各种不可预料的关联问题,所以将其进行接口替换的操作,如移植到SpaceOS2的系统接口上原理图11所示.
另外,C库的接口的在libc目录下,这一部分代码与OS关系不大,更多的是与编译器有关.
基本的测试场景如图12所示.目标板使用BM3803处理器,主频设置为66 M,使用SpaceOS2操作系统.性能测试的主要目标是通过测试得出协议栈的性能指标,分析性能瓶颈.所以将采用对比的方式进行测试.
协议栈在目标板上运行时,如果从应用层发送数据,数据将通过路径“UDP/TCP层->IP层->网卡驱动->网卡”发出;如果将目标板用作路由,数据将通过路径“网卡->网卡驱动->IP层->网卡驱动->网卡”转发.在此过程中,除去应用本身,性能度量有网卡IO读写性能、网卡驱动性能、IP层性能、UDP /TCP层性能.这些性能度量受多方面的参数影响.
1) 硬件配置方面:如CPU的频率、CPU与网卡的总线宽度和接口方式、是否支持DMA等.
2) 软件配置方面:如驱动设计方式、协议栈缓冲区大小、代码是否在RAM中运行、是否启用CACHE等.
3) 系统级别方面:如网络环境、对端设备速率等.
主要测试协议栈不同层次(涉及单向传输与转发2种)在不同帧长、CACHE是否使能、CPU主频、指令运行在RAM/FLASH等参数影响下的性能度量.
测试结论如下:
以主频66 M,程序运行在RAM中为例,不同CACHE状态下转发速率(IP路由)与单向发送速率的关系如表2(平均帧长约1 000字节)所示.
表2 转发速率与单向发送速率对比(主频66 M,程序运行于RAM)Tab.2 The comparison of retransmission and one-way transmission
① 转发速率约是单向传输速率的一半;
② CACHE对协议栈性能影响较大.指令CACHE对性能影响最大,约是数据CACHE影响的一倍.开关数据/指令CACHE对速率的影响比例约是“关数据CACHE+关指令CACHE:开数据CACHE+关指令CACHE:关数据CACHE+开指令CACHE:开数据CACHE+开指令CACHE=1∶1.5∶3∶5”;
③ 主频大小与性能高低成稳定的正比,基本上主频增加一倍,吞吐率也增加一倍;
④ 对当前目标板而言,影响协议性能最大的瓶颈在于网卡IO处.首先,因为DM9000是通过IO方式读写数据的,每个字(16bits)都需要用读写指令移动数据,其次,因为sparc的字节序与DM9000字节序相反,每个字在移动前需要交换顺序(表中DRV-MAC的差异即时交换顺序导致的速率差异);
⑤ 程序运行在RAM中吞吐率是运行在FLASH中的一倍多.但是如果打开指令CACHE,则吞吐率相差不大.说明只要指令CACHE命中率高,基本上读FLASH的IO速率就不会成为瓶颈(表3);
⑥ 长帧吞吐率明显高于短帧.越小的帧由于前导码、帧间隔和处理总帧数的原因,其吞吐量就越低;
⑦ 协议栈的IP层与UDP层速率相差不大,而TCP层则相差较多.主要是因为TCP是可靠连接,协议复杂,处理量大,数据的确认机制等导致的.各层吞吐率比例约是“IP层∶UDP层∶TCP层的=10∶9∶7”;
⑧ 一些指标数据
吞吐率:在目标板上,如果主频设置为66 M,数据CACHE与指令CACHE全开,将程序运行在RAM中,能得到最高的吞吐率:IP层24.7 Mb/s,UDP层22.1 Mb/s,TCP层16.8 Mb/s.如果主频设置为66 M,数据CACHE与指令CACHE全关,将程序运行在FLASH中,能得到的吞吐率:IP层4.5 Mb/s,UDP层4.1 Mb/s,TCP层3.5 Mb/s.
表3 程序运行在RAM与运行在FLASH中对比(主频66 M,包长1 464)Tab.3 Program running in RAM and FLASH
丢帧率:在协议栈吞吐量临界区域,丢帧率小于0.5%(实测平均约0.2%-0.4%).
时延:对点传输,IP层时延小于5 ms(实测平均约1~3 ms),UDP层时延小于5 ms(实测平均约3~5 ms),TCP层连接建立时延小于5 ms(实测平均约3~5 ms),TCP层数据时延小于10 ms(实测平均约3~8 ms).
突发数据量:大于50帧.具体与帧长和缓冲区配置有关,实测约100 K数据量.
随着中国航天工程的不断深入,网络应用必然会在以后的航天型号中得到广泛应用,针对航天型号任务的特殊应用需求,设计适合航天领用应用的自主可控操作系统及网络协议具有重要的意义.本文结合当前控制数据包传输量大、数据包小以及实时性要求高的特点,改进优化了BSD协议,并将其移植到使用率很高的SpaceOS2上,可以满足航天型号对控制数据包实时性高的要求,实现了在自主研发操作系统上之上的网络数据传输目的.本文工作已在某航天型号计算机上进行了测试验证,取得了较好的效果.