Lwip 协议栈的设计与实现
(中文版)
Swedish Institute of Computer Science
February 20, 2001
作者:Adam Dunkels adam@sics.se
翻译:果农(QQ: 10205001)
核桃(QQ: 329147)
佳旭(QQ:3232253)
整理:佳旭(QQ:3232253)
本文为QQ群ARM TCPIP LCD(群号:10988210)版权所有
未经作者许可不得用于商业用途
摘要
LWIP是TCP/IP协议栈的一种实现。LWIP的主要目的是减少存储器利用量和代码尺寸,使
LWIP适合应用于小的、资源有限的处理器如嵌入式系统。为了减少处理器和存储器要求,lwIP
可以通过不需任何数据拷贝的API进行裁减。
本文叙述了lwIP的设计与实现。叙述了协议实现及子系统中所使用的算法和数据结构如存储
和缓冲管理系统。还包括LWIP API的参考手册和使用LWIP 的一些代码例子。
目录
1 Introduction …………………………………………………………………………………………..1
2 Protocol layering ……………………………………………………………………………………..1
3 Overview ……………………………………………………………………………………………...2
4 Process model ………………………………………………………………………………………...2
5 The operating system emulation layer ……………………………………………………………...3
6 Buffer and memory management……………………………..…………………………….. ……...3
6.1 Packet buffers -pbufs………………………………………………………………………………3
6.2 Memory management………………………………………………………………………………..5
7 Network interfaces…………………………………………….……………………………… ……..5
8 IP processing………………………………………………….……………………………………… 7
8.1 Receiving packets…………………………………………………………………………………….7
8.2 Sending packets………………………………………………………………………………………7
8.3 Forwarding packets…………………………………………………………………………………..8
8.4 ICMP processing …………………………………………………………………………………….8
9 UDP processing……………………………………………………………………………….. ……...8
10 TCP processing……………………………………………………………………………… ……...9
10.1 Overview……………………………………………………………………………………………9
10.2 Data structures……………………………………………………………………………………..10
10.3 Sequence number calculations…………………………………………………………………….12
10.4 Queuing and transmitting data…………………………………………………………………….12
10.4.1 Silly window avoidance…………………………………………………………………………13
10.5 Receiving segments………………………………………………………………………………..13
10.5.1 Demultiplexing…………………………………………………………………………………..13
10.5.2 Receiving data…………………………………………………………………………………...14
10.6 Accepting new connections ……………………………………………………………………….14
10.7 Fast retransmit……………………………………………………………………………………..14
10.8 Timers……………………………………………………………………………………………..14
10.9 Round-trip time estimation………………………………………………………………………..15
10.10Congestion control………………………………………………………………………………..15
11 Interfacing the stack ………………………………………………………………………………15
12 Application Program Interface……………………………………………………………. ……..16
12.1 Basic concepts……………………………………………………………………………………..16
12.2 Implementation of the API……………………………………………………………………….. 17
13 Statistical code analysis ……………………………………………………………………………17
13.1 Lines of code………………………………………………………………………………………18
13.2 Object code size…………………………………………………………………………………...19
14 Performance analysis……………………………………………………………………… ……...20
15 API reference………………………………………………………………………………. ……...21
15.1 Data types………………………………………………………………………………………….21
15.1.1 Netbufs…………………………………………………………………………………………..21
15.2 Bu®er functions …………………………………………………………………………………..21
15.2.1 netbuf new()……………………………………………………………………………………..21
15.2.2 netbuf delete()…………………………………………………………………………………...21
15.2.3 netbuf alloc()…………………………………………………………………………………….22
15.2.4 netbuf free()……………………………………………………………………………………..22
15.2.5 netbuf ref()………………………………………………………………………………………22
15.2.6 netbuf len()……………………………………………………………………………………... 23
15.2.7 netbuf data()……………………………………………………………………………………. 23
15.2.8 netbuf next()……………………………………………………………………………………. 23
15.2.9 netbuf ¯rst()……………………………………………………………………………………..24
15.2.10 netbuf copy()…………………………………………………………………………………...24
15.2.11 netbuf chain()…………………………………………………………………………………..24
15.2.12 netbuf fromaddr()………………………………………………………………………………24
15.2.13 netbuf fromport()……………………………………………………………………………… 25
16 Network connection functions …………………………………………………………………….25
16.0.14 netconn new()…………………………………………………………………………………..25
16.0.15 netconn delete()………………………………………………………………………………...25
16.0.16 netconn type()…………………………………………………………………………………..25
16.0.17 netconn peer()…………………………………………………………………………………. 25
16.0.18 netconn addr()………………………………………………………………………………… 26
16.0.19 netconn bind()………………………………………………………………………………… 26
16.0.20 netconn connect(……………………………………………………………………………….26
16.0.21 netconn listen()…………………………………………………………………………………26
16.0.22 netconn accept()………………………………………………………………………………..26
16.0.23 netconn recv()………………………………………………………………………………….27
16.0.24 netconn write()…………………………………………………………………………………28
16.0.25 netconn send()………………………………………………………………………………….29
16.0.26 netconn close()…………………………………………………………………………………30
17 BSD socket library ………………………………………………………………………………...30
17.1 The representation of a socket…………………………………………………………………….30
17.2 Allocating a socket ………………………………………………………………………………..30
17.2.1 The socket() call…………………………………………………………………………………30
17.3 Connection setup…………………………………………………………………………………..31
17.3.1 The bind() call…………………………………………………………………………………...31
17.3.2 The connect() call………………………………………………………………………………. 31
17.3.3 The listen() call………………………………………………………………………………….32
17.3.4 The accept() call ……………………………………………………………………………….. 32
17.4 Sending and receiving data………………………………………………………………………..33
17.4.1 The send() call ………………………………………………………………………………….33
17.4.2 The sendto() and sendmsg() calls ………………………………………………………………34
17.4.3 The write() call…………………………………………………………………………………. 34
17.4.4 The recv() and read() calls………………………………………………………………………35
17.4.5 The recvfrom() and recvmsg() calls…………………………………………………………….36
18 Code examples …………………………………………………………………………………….36
18.1 Using the API …………………………………………………………………………………….36
18.2 Directly interfacing the stack…………………………………………………………………….. 39
Bibliography …………………………………………………………………………………………..41
1 序论
在过去的几年里,人们对计算机互连和计算机无线互连支持设备的兴趣不断的增长,计算
机逐渐与日常使用的设备无缝结合,并且价格不断下降。同时,无线网络技术如Bluetooth
[HNI+98] 和 IEEE 802.11b WLAN [BIG+97]不断显现。这也在一些领域譬如医疗保健、安全保
卫、交通运输、加工业等引起了许多新引人入胜的情节。小的设备如传感器能被联入现有的网
络如全球因特网,并可以在任何地方对其进行监控。
在过去的几年里,互联网技术证明自己具有足够的灵活性来合并不断改变的网络的环境。
与当初为低速网络譬如ARPANET网而产生的互联网相比,今天的大范围连接的互联网技术在带宽
和误码率方面都与原来有着巨大的差异。由于互联网的大量应用,把将来的无线互连网络应用
于现有的互连网络将会给我们带来巨大的收益。并且,大面积互连的互联网也是一强劲趋势。
自从人们经常对像传感器这样的小设备有小的物理外形和便宜的价格的要求,实现一较少
的处理和存储要求的互连协议就成为必须解决的问题。本文描述了一种称为LWIP的小到足以满
足最小系统要求的TCP/IP协议栈的设计与实现。
本文结构如下编排:第2,3和4部分对lwIP栈作一个概述,第5部分叙述操作系统模拟层,
第6部分叙述缓存和存储管理。第7部分介绍lwIP抽象的网络接口,第8,9,和10部分叙述IP,
UDP,和TCP协议的实现。第11和12部分叙述怎样与lwIP进行接口并介绍lwIP API。第13和14部
分分析了实现过程。最后,15部分提供了lwIP API用户参考手册,17和18部分展示了多种代码
例子。
2 协议分层(Protocol layering)
TCP/IP协议被设计为分层结构,各协议层分别解决通信问题的一部份。这一分层对于协议
的设计、实现可起一个指导作用,各个协议可分开实现。然而协议严格的按分层结构来实现,
各层之间的通讯可能会导致总体性能的降低 [[Cla82a] 。 为克服这些问题, 协议的某些内部
方面可传达给其它协议共享,但必须注意,保证只有那些重要信息才在各层共享。
尽管底层协议或多或少可以进行交叉存取,大部分TCP/IP协议,还是在应用层协议与底层
协议之间进行严格的区分。在大部分操作系统中,底层协议被作为与应用层程序具有通讯接口
的操作系统内核的一部分。应用程序被看作是TCP/IP协议的抽象,网络通讯与进程间通讯或者
文件I/O只有很小的差别。这意味着,因为应用程序不知道被底层协议所使用的缓冲机制,它不
能利用缓冲机制对经常使用的数据进行缓冲。同样,当应用程序发送数据时,在数据被网络代
码处理前,必须把这些数据从应用程序存储区被拷贝到内部缓冲区。
最小系统中使用的操作系统像lwIP的目标系统在内核和应用进程之间常常并不存在严格的保护
屏障。这就允许应用程序和底层协议之间使用一种更宽松的方案,通过共享内存。特别地,应
用层可以意识到底层协议所使用的缓存处理机制。因此,应用可以更有效地重用缓冲区。而且,
既然应用进程和网络代码可以使用相同的内存,应用可以直接读写内部缓存,因此节省了执行
拷贝的开销。
3 总述(Overview)
正如其他TCP/IP协议的实现,分层协议的设计为LWIP的设计与实现提供一向导。每一个协
议都作为一个模块来实现,提供一些与其他协议的接口函数。尽管各层分开实现,但正如上面
所讨论的,为了同时提高处理速度和内存利用两方面的性能,一些层在设计时违背这一原则。
例如:当检验一接收到的TCP段(segment)的校验和(checksum)和分解TCP段时,源和目的IP
地址必须被告知TCP模块。LWIP实现时不是通过函数调用把IP地址传递给TCP,而是TCP模块通过
获取IP报头的结构进而自己提取这一信息。
LWIP有几个模块组成,除了实现TCP/IP协议的各个模块(IP、ICMP、UDP、和 TCP),同时设
计了许多支持模块。这些支持模块组成了操作系统模拟层(第5章)、缓冲和存储管理子系统(第
6章)、网络接口函数(第7章)和一些处理因特网校验和的函数。LWIP还包括关于API的摘要(第
12章)。
4 进程模型(Process model)
协议实现的过程模型以把系统划分成为不同的过程的方法进行描述。用于实现通讯协议的
过程模型使每个协议作为孤立的过程运行。这种模型使用严格的协议分层,协议之间的通讯结
点必须被严格定义。虽然这种方法有其诸多优势如协议能在运行时被增加,代码一般容易理解
和调试,但也有不利因素。 严格的分层,正如先前所述,并不总是实现协议的最好方法。 同
时,更重要的,每跨越一层,必须做一次上下文切换。这将意味着,接受一个TCP段要进行三次
上下文切换:从网络接口的驱动,到IP处理,再到TCP处理,最终到应用处理。根据网络接口的
设备驱动程序,对于IP过程,对于TCP过程和最后。 在大多数操作系统中一个上下文切换所花
的代价都是相当昂贵的。
另一个较普通的方法是把通信协议封装在操作系统的内核。 在这种内核实现通讯协议的情况
下,应用程序通过系统调用完成通讯。 通讯协议之间不严格区分,但可以使用交叉协议分层技
术。
lwIP所使用的过程模型是:把所以协议封装到一个单一的过程中,从而与操作系统内核分
开。应用程序可能也驻留在lwIP处理过程中,或者在单独的过程中。 TCP/IP栈和应用程序之间
的通信可以通过函数调用实现,也可以通过更为抽象的API。
以上两种LWIP的实现方法各有其优缺点。把LWIP作为一个过程的主要优点是便于在不同的
操作系统上移植。由于LWIP的设计目标是面向小的操作系统,这些操作系统一般不支持进程外
交换(swapping out processes)或者虚拟存储,这样由于LWIP处理过程交换或者翻页到磁盘
而引起的不得不等待磁盘响应造成的延迟将不再是一个问题。尽管在获得服务响应前必须等待
调度仍然是一个问题,但是,在LWIP设计时,这并没有妨碍它在一操作系统内核中实现。
5 操作系统模拟层
为了使lwIP便于移植,与操作系统有关的功能函数调用和数据结构没有在代码中直接使用。而
是当需要这样的函数时,操作系统模拟层将加以使用。操作系统模拟层向诸如定时器、处理同
步、消息传送机制等的操作系统服务提供一套统一的接口。原则上,移植lwIP到其他
操作系统时,仅仅需要实现适合该操作系统的操作系统模拟层。
操作系统模拟层提供了由TCP使用的定时器功能。操作系统模拟层提供的定时器是一次性的
定时器,当超时发生时,调用一个已注册函数至少要200ms的间隔。
进程同步机制仅提供了信号量。即使在操作系统底层中信号量不可用,也可以通过其他信
号原语像条件变量或互锁来模拟。
信息传递的实现使用一种简单机制,用一种称为“邮箱”的抽象方法。邮箱做两种操作:邮
寄和提取。邮寄操作不会阻塞进程;邮寄到邮箱的消息由操作系统模拟层排入队列直到另一个
进程来提取它们。即使操作系统底层对邮箱机制不支持,也容易用信号量实现。
6 缓冲和存储管理
通讯系统中的存储和缓冲管理必须能够适应大小变化的缓冲区,从几百字节的包含完全大
小TCP段的缓冲区到仅仅包含几个字节的短的ICMP回报。而且,为了避免拷贝它应当尽可能让缓
冲区的数据内容驻留在内存中,网络子系统不管理像应用存储或ROM这样的内存。
6.1包缓冲器-pbufs
Pbuf在lwIP的内部表示一包,也是为了最小限度的使用栈这一特殊需要而设计。 Pbufs类似
于用于BSD实现的mbufs。 pbuf结构既支持分配动态内存来保存包内容,也支持把包数据存储
在静态存储区。Pbufs能在一张列表中一起被连在一起,称为一个pbuf链,这样一个包可以跨越
若干个pbufs。
Pbufs具有三种类型,PBUF RAM,PBUF ROM,和PBUF POOL。 图1中pbuf描绘了PBUF
RAM类型,和储存的被pbuf子系统管理的数据包。 图2中的pbuf是被链在一起的pbuf的一个例
子,在其中链的第一个pbuf具有PBUF RAM类型(where the first pbufin the chain is of the PBUF
RAM type),而第二个具有PBUF ROM类型,这意味着它具有不被pbuf系统管理的存储数据。
第三种类型的pbuf ,PBUF POOL如图3所示,包括从共有的固定大小的pbufs分配的固定大小的
pbufs (consists of fixed size pbufs allocated from a pool of fixedsize pbufs.)。 一个pbuf链可能包
括多重类型的pbufs。
三种类型有不同的使用。 PBUF_POOL主要被网络设备驱动程序使用,因为对操作系统来说分
配单一的pbuf速度较快并且适合用于中断管理(suitable foruse in an interrupt handler)。 当应用
程序发送位于被应用程序管理的存储区的数据时,PBUF_ROM被使用。 在pbuf被移交到TCP/IP
栈后,数据不能修改,因此这一pbuf类型,这类型主要用于数据位于ROM时(因此名称为
PBUF_ROM )。 PBUF_ROM pbuf中的数据可能会用到的头存储在PBUF_RAM pbuf中,它链接在
PBUF_ROM pbuf的前面,如图2所示。
当应用程序发送动态地产生的数据时,PBUF_RAM类型的Pbufs也被使用。 在这种情况下,
pbuf系统不光为应用数据分配存储空间,也为将要发送的数据的报头准备空间。如图1所示。
pbuf系统不能预先知道为将要发送的数据准备什么样的报头,并且假定最坏的情况。报头大小
在编译时可动态配置。
实质上,进入pbufs的是PBUF_POOL类型,离开pbufs的是PBUF_ROM或PBUF_RAM类型。
pbuf的内部的结构如图1~3。 pbuf结构包括两个指针,两长度域,一个flags域,和一参考
计数。pbuf链中next域是一个指向下一个pbuf的指针。Payload指针指向pbuf中的数据的起始位
置。len域包含pbuf的数据内容的长度。Tot_len域包含当前的pbuf的长度和在pbuf链中接下来
的pbufs的所有len领域的总数。换句话说,tot_len域是len域和pbuf链中的随后的pbuf中的
tot_len域的值的总和。flags域表明pbuf的类型,而ref领域包含一参考计数。 Next和payload
域是内部指针和依赖于处理器体系结构的数据大小。两个长度域为16位无符号整形,flags和ref
域均为4bit宽。pbuf结构整个的大小取决于所使用的处理器体系结构中一个指针的大小及可能
的最小alignment的大小。 在带有32位指针和4个字节alignment的体系结构,整个的大小为16
字节,16位指针和1个字节alignment的体系结构上,大小是9个字节。
pbuf模块为操纵pbufs提供了函数。函数pbuf_alloc() 完成分配一个pbuf的任务,它能够
分配上面所说的三种pbuf中的任何一种。pbuf_ref()增加参考计数。 pbuf_free()完成释放分
配的工作,它首先减少pbuf的参考计数。 如果参考计数到达零表示pbuf已经被释放。 函数
pbuf_realloc()收缩pbuf使它刚好能够包含数据大小。 pbuf_header()调整payload 指针和长
度域,以便对pbuf中的数据的报头进行预先估计。 buf_chain() 和pbuf_dechain()用于用链接
pbufs。
6.2内存管理
支持pbuf调度的存储管理非常简单。它处理内存中连续区域的分配和释放,可以紧缩一
个预先分配的内存块。内存管理器使用系统中总内存的专用部分,这确保网络系统不会使用所
有可利用内存,而且如果网络系统用了所有它自己的内存其他程序的操作也不会影响它。
在内部,内存管理通过将一种小的结构放置在每一被分配的内存块的顶端上来追踪分配的
内存。这个结构(图4)中设置两个指针指向内存中下一个和前一个分配块,还有一个used标志
用来指示这个分配块是否已经被分配。
通过搜索一个未使用的内存块来分配内存,这个内存块对于请求分配来说足够大。使用最
先适用原则,因此第一块被使用的内存足够大。当一个分配块释放时,used标志被设为0。为了
防止碎片,检测下一个和上一个分配块的used标志,如果它们还没被使用,几个块合并成一个
大未使用块。
7网络接口
硬件设备驱动程序中,lwIP用一个类似于BSD的网络接口结构来描述物理硬件。 网络接口
结构如图5所示。通过next指针,网络接口被连成一个全局链表(global linked list)。
每个网络接口有一个名字,存储在图5中的name字段。这个两个字符的名字识别用于网络接
口中的设备驱动类型,而且当接口在运行时由人为操作来配置。这个名字由设备驱动设置,应
当映射由网络接口表示的硬件类型。例如,蓝牙驱动网络接口可能使用名字btd,而IEEE802.11b
WLAN硬件可能使用名字wl。由于这些名字不必是唯一的,num字段用来区分同类设备中的不同的
网络接口。