,
(南京航空航天大学 江苏省航空动力系统重点实验室,南京210016)
嵌入式设备接入互联网是物联网时代不可阻挡的大趋势。轻量级TCP/IP 协议栈(Lightweight Internet Protocol,LwIP)能够适应嵌入式环境中非常有限的存储容量和计算资源,并实现了TCP/IP协议栈的基本功能,为设备的网络连接提供优质的服务。但是面对一些嵌入式应用对通信实时性的苛刻要求,LwIP依然有些力不从心。本文将对LwIP通信进行分析和实时性改进,以提高嵌入式设备的网络性能,促进电子设备连接互联网的趋势向前发展。
在LwIP协议栈实际使用中,发现了单向数据通信的实时性问题。嵌入式系统以TI公司的TM4C1294NCPDT微处理器为核心,该处理器内部集成了以太网物理层,在芯片外部配置A/D和D/A转换模块。通过移植LwIP协议栈建立嵌入式系统和电脑上位机的网络连接,上位机每20 ms向嵌入式系统发送一次数据,数据为正弦波信号。嵌入式系统接收到数据后通过D/A转换将数字量转为模拟电压信号,模拟信号在示波器上显示。使用中发现嵌入式系统生成的模拟量信号刷新频率很低,每250 ms刷新一次数据,刷新效果如图1所示。
图1 LwIP以太网通信效果图
设计实验测试此时以太网通信的延迟时间,由于上位机的Windows操作系统定时器的定时精度较低,实验采用另一套一样的嵌入式系统取代上位机进行以太网通信,实验系统结构图如图2所示。
图2 以太网通信实时性测试实验系统结构图
A负责采集信号发生器的模拟信号并立即将采集值通过以太网传给B,B接收到数据后立即通过D/A转换生成模拟量在示波器中显示,同时信号发生器发生的模拟信号也在示波器中显示。示波器中两个信号的相位差即为A/D转换时间、D/A转换时间以及以太网通信延迟时间之和, A/D和D/A转换的时间很短,实验测得不超过960 μs,往往可以忽略不计。通过实验测得整个系统通信的延迟高达224 ms左右,测试结果图如图3所示,无法满足控制的实时性要求。
图3 LwIP以太网通信延迟测试图
LwIP协议栈数据通信的具体流程图如图4所示,LwIP发送数据所用函数是tcp_write(),该函数无法实现数据的立即发送,实际上tcp_write()被调用后立即将控制权交给tcp_enqueue()函数,该函数实现的功能是将需要发送的应用数据分割成适当大小的TCP段,然后再将这些TCP段放到所属连接的传输队列中。当需要发送数据时,LwIP协议栈调用tcp_output()函数,该函数首先判断发送和接收窗口是否大于发送的数据量,如果窗口足够大,则调用IP层的ip_route()和ip_output_if()函数发送数据。当LwIP协议栈收到数据后,网络接口设备驱动会调用ip_input(),该函数解析校验IP数据包将TCP数据包传递到协议层,在协议层会调用tcp_input()接收来自IP层的数据包并对该数据包进行校验,随后TCP状态机的实现函数tcp_process()会在不同地方调用函数tcp_receive(),处理输入的TCP数据段。如果接收到ACK包,则表示接收端可以接收数据,此时如果传输队列中存在数据,LwIP会直接调用tcp_output()函数将数据发送出去。
图4 LwIP协议栈数据收发流程图
无论应用数据的大小,TCP/IP协议每个数据包都要在数据前加上40字节的数据包首部。如果直接发送通信中的短帧数据包,在滑动窗口机制下,接收和发送窗口都会变得很小,大大降低了通信效率,在发送长帧数据包时容易造成网络拥塞,这种现象被称为“糊涂窗口综合症(Silly Window Syndrome)”。
为了避免这种通信拥塞,John Nagle于1984年提出一种解决方案,称为Nagle算法,该算法阻止了发送端逐个数据段发送,强迫发送端等待随后需要发送的数据段,将这些数据组成一个更大的数据包一起发送,这样可以大大减少通信中短帧数据包的数量,同时也增大了TCP发送和接收窗口。Nagle算法虽然提高了以太网通信的发送效率,减少了网络拥塞,但是降低了通信的实时性,特别是对于以短帧数据包通信为主的嵌入式通信。在一些对通信实时性要求比较高的场合,希望能够立即将数据发送出去。LwIP协议栈中Nagle算法在以下4种情况下不会阻止立即发送数据:
① 发送端没有未被确认的数据包;
② 发送端有超过一个需要发送的数据包;
③ 数据包长度超过最长数据包大小(MSS);
④ TF_NODELAY或者TF_INFR标志位被置位。
TCP协议发送完数据后会等待接收端返回的ACK包,因为Nagle算法,发送端在这段等待的时间内会阻止发送数据。同样为了提高通信效率,接收端收到数据后不会将ACK包立即发送,TCP协议栈设置了“延迟ACK(delayed ACK)”定时器用于延迟ACK包的发送,如果延迟的时间内有多个数据段到达,则允许协议栈发送一个ACK包确认多个数据包段,如果有数据段需要发送,ACK包也可以被该数据段捎带过去。Windows的TCP/IP协议栈“延迟ACK”定时器的定时周期一般为200 ms,LwIP一般默认的定时周期为250 ms。LwIP的“延迟ACK”定时器处理函数在快速定时器函数tcp_fasttmr()中实现,其部分代码如下所示:
if (pcb->flags &TF_ACK_DELAY) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK "));
tcp_ack_now(pcb);
tcp_output(pcb);
pcb->flags &=~(TF_ACK_DELAY | TF_ACK_NOW);
}
该段代码的功能是判断通信块是否处于延迟ACK状态,如果是,则立即发送ACK包,并清除延迟ACK标志位。综上所述,以太网通信的实时性不仅受到发送端TCP协议Nagle算法的影响,同时也受到接收方延迟ACK定时器影响。
嵌入式系统和PC机网络通信抓包测试结果如图5所示,192.168.0.17为嵌入式系统的IP地址,49160为端口号,192.168.0.201为PC机的IP地址,1000为端口号。从图中可以看出,LwIP在接收到PC机送达的网络数据包后并没有立即发送ACK包,而是在等待约250 ms后再发送。PC机在没有接收到ACK包的期间是不发送任何数据的。所以通过分析LwIP协议栈和通信抓包测试,可以得到上述通信实时性问题产生的原因如下:嵌入式系统接收到网络数据后由于延迟ACK没有立即返还ACK包,因为Nagle算法在接收到ACK包前上位机阻止了数据段的发送,直到大约250 ms后进入延迟ACK定时处理函数tcp_fasttmr()才将ACK包发送。
图5 以太网通信抓包测试图
通过上述分析已知通信延迟的根本原因在于TCP协议的延迟ACK策略和Nagle算法。解决这种问题有两种思路:
① 缩短发送端延迟ACK定时器的定时周期;
② 关闭接收方TCP协议栈的Nagle算法。
针对思路①,延迟ACK定时器的定时周期可以在文件tcp_impl.h中设置,通过修改参数TCP_TMR_INTERVAL的数值改变定时器的定时周期,本文将延迟时间修改成20 ms,文件tcp_impl.h中参数TCP_TMR_INTERVAL定义如下:
#define TCP_TMR_INTERVAL 250/* The TCP timer interval in milliseconds. */
针对思路②,本文所使用上位机作为以太网通信的服务器,用C#语言编写,在程序初始化服务器Socket的代码后面加入如下语句:serverSocket.SetSocketOption(SocketOptionLevel.Tcp,SocketOptionName.NoDelay,true);即可关闭上位机TCP协议栈的Nagle算法。对于LwIP协议栈,文件tcp_impl.h中的函数tcp_do_output_nagle(tpcb)用来判断是否使用Nagle算法,直接将该函数的返回值设为1,即可关闭Nagle算法。
上述两种思路下的信息交互如图6所示,延迟ACK定时器并不是在收到数据后开始定时,而是在LwIP协议栈进程中一直定时,所以接收到上位机发来的数据包后,LwIP协议栈会在少于20 ms的时间内发送ACK包,数据传输的时间远远小于1 ms。上位机每20 ms发送一次数据包,每次发送前LwIP的ACK包已经到达,所以上位机TCP协议的Nagle算法不会阻碍数据的发送。在通信实时性上,缩短延迟ACK定时器周期和关闭上位机Nagle算法的效果基本相同。通过测试也可以证实该结论,通信效果如图7所示,其中上面一条曲线是关闭上位机Nagle算法得到的曲线,下面一条曲线是缩短延迟ACK定时周期得到的曲线,这两条曲线相位相同,可见这两种方法有相同的实时效果。
图6 两种方法下的信息交互图
图7 两种方法修改后的通信效果图
简单地关闭TCP协议的Nagle算法往往容易导致通信阻塞,且上位机程序往往多种多样且不开源,程序难以修改,所以不推荐关闭Nagle算法。简单地修改延迟ACK定时器周期会由于通信周期改变需要重新下载程序,使用起来很不方便,且如果延迟ACK定时周期过小容易造成ARM资源的浪费。本文对LwIP协议栈的延迟ACK功能做了改进,首先独立延迟ACK定时器,重新设置延迟ACK定时器周期,避免缩短延迟ACK定时周期造成ARM使用效率的大大降低。其次,设计算法使延迟ACK定时器的周期自适应上位机发送的周期。判断定时器周期是否合适的算法如图8所示。
图8 基于数据统计的周期判断算法流程图
图8中,进入ACK延迟定时处理函数,查询通信块pcb的状态,如果通信块处于延迟ACK状态,说明在这段定时周期内有数据接收,发送端Nagle算法延迟了数据包发送,将Flag置1并滚动放入长度为m的标志保存序列。如果标志保存序列的和等于m,即最近m次周期内每次发送端Nagle算法都起了作用,表明延迟ACK定时周期偏长。如果通信块没有处于延迟ACK状态,说明定时周期内没有接收到数据,将Flag置0并滚动放入标志保存序列,如果标志保存序列和小于l,即最近m次周期内至少有(m-l)次发送端还没来得及发送数据,表明延迟ACK定时周期偏短。如果标志保存序列的和处于l和m之间,则表明此时定时周期合适,无需修改。该算法的原理是基于近m次的通信块的状态进行统计判断延迟ACK定时器周期是否合适的,一般m取值范围为10~15,l取值约为4/5 m。得到延迟ACK定时器周期判定结果后,需要对周期进行处理,具体处理步骤如图9所示。
图9 定时周期处理流程图
图9中的Told为此时延迟ACK定时器周期,Tnew为待设定的延迟ACK定时器周期。如果判断定时周期偏长,则将此时定时周期Told除以2赋值给Tnew,这样可以让延迟ACK定时器周期迅速降低到发送端发送周期以下,保证通信的实时性。如果判断定时周期偏短,若此时定时周期大于20 ms,则将此时定时周期Told加上10%赋值给Tnew,若此时定时周期不大于20 ms,则将此时定时周期Told加上1 ms赋值给Tnew。如果判断定时周期合适,则不对此时的定时周期做任何处理。
将最近k次Tnew的值存入长度为k的序列,k的一般取值范围为4~8,对序列的平均值四舍五入并设置成新的延迟ACK定时器周期。求平均值是为了避免偶然影响。将该算法应用到LwIP协议栈中,利用图2的实验系统进行通信测试,将发送端发送周期时间设为1 ms,测试效果如图10(a)所示,测得此时系统通信延迟为1.4 ms,考虑到A/D和和D/A转换时间为960 μs,此时以太网通信延迟时间仅仅为440 μs,通信延迟测试结果如图10(b)所示。图10(a)和图10(b)证明该算法可以将原来的LwIP单向通信周期从250 ms降低到1 ms,通信延迟也从原来的224 ms降低到不到1 ms。
延迟ACK定时器周期算法对发送端的发送周期需要一个自适应过程,自适应时间不能太长,分别设置发送端的数据发送周期为1 ms、20 ms、50 ms和100 ms,测试所设计的算法自适应时间,测试结果图分别如图11(a)、图11(b)、图11(c)、图11(d)所示。从图中可以看出该算法的自适应时间最大为1 s左右,具有较快的响应速度。
分别设置不同的通信周期,通过JTAG口得到此时LwIP协议栈中延迟ACK定时器定时周期的值,测试4次,得到的数据如表1所列,可以看出,本发明可以保证自适应的延迟ACK定时器周期在80%~100%通信周期之间,这样可以在保证通信实时性的基础上提高ARM的使用效率。
本文先将LwIP协议栈中的延迟ACK定时器独立出来,然后设计了一种基于数据统计的自适应延迟ACK
图10 刷新时间为1 ms时改进LwIP协议栈通信测试图
图11 发送端不同发送周期下的自适应时间测试图
图11 发送端不同发送周期下的自适应时间测试图(续)
表1 延迟ACK周期测试表
[1] Adam Dunkels.Design and implementation of the LWIP TCP/IP stack [EB/OL].[2018-01].http://www.sics.se/~adam/lwip/documentation.html.
[2] 张齐,劳炽元.轻量级协议栈LWIP的分析与改进[J].计算机工程与设计,2010,31(10):2169-2171,2256.
[3] 梅小华.提高51单片机TCP通信效率的软件方法[J].华侨大学学报:自然科学版,2011,32(2):235-237.
[4] 王超.基于混合TCP-UDP的HTTP协议实现方法[J].单片机与嵌入式系统应用,2003(2):26-28.
[5] 王宝宝,余世明,王振宇.嵌入式Internet中Nagle算法及其应用研究[J].中国工程科学,2014,16(2):101-105.
[6] 孙乐鸣,江来,代鑫.嵌入式TCP/IP协议栈LWIP的内部结构探索与研究[J].电子元器件应用,2008(3):79-82.
林志祥(硕士研究生),主要从事航空发动机控制系统设计与仿真;张天宏(教授),主要研究方向为嵌入式控制系统、系统控制与仿真。