6 缓冲及内存管理
在一个通信系统中,缓冲及内存管理系统必须能准备好分配不同大小的缓冲区,从包含有几百字节有用数据的全长TCP报文段,到只含几个字节的ICMP 应答响应。同时,为了避免拷贝,应该尽可能地让数据类型缓冲区驻留在内存中,而不是由网络子系统,如应用内存或ROM,来管理。
6.1 包缓冲区――pbuf
Pbuf是lwIP包的内部表示,被设计为最小化栈的特殊需要。Pbufs类似于BSD实现中的mbufs。Pbuf结构支持为包内容动态分配内存和让包数据驻留在静态内存中。Pbufs能被一个称为pbuf链的链接到一个链表中,以至一个包能跨越多个pbufs。
Pbufs有三种类型:PBUF_RAM,PBUF_ROM和PBUF_POOL。图1表示PBUF_RAM类型,包含有存在内存中由pbuf子系统管理的包数据。图2显示了一个pbuf链表,第1个是PBUF_RAM类型,第2个是PBUF_ROM类型,意味着它包含有不被pubf子系统管理的内存数据。图3描述了PBUF_POOL,其包含有从固定大小pbuf池中分配来的pbuf。一个pbuf链可以包含多个不同类型的pbuf。
这三种类型有不同的用处。PBUF_POOL类型主要由网络设备驱动使用,因为分配单个pbuf快速且适合中断句柄使用。PBUF_ROM类型由应用程序发送那些在应用程序内存空间中的数据时使用。这些数据不会在pbuf递交给TCP/IP栈后被修改,因此这个类型主要用于当数据在ROM中时。PBUF_ROM中指向数据的头部被存在链表中其前一个PUBF_RAM类型的pbuf中,如图2所示。
PBUF_RAM类型也用于应用程序发送动态产生的数据。这情况下,pbuf系统不仅为应用程序数据分配内存,也为将指向(prepend)数据的头部分配内存。如图1所示。Pbuf系统不能预知哪种头部将指向(prepend)那些数据,只假定最坏的情况。头部的大小在编译时确定。
本质上,进来的pbuf是PBUF_POOL类型,而出去的pbuf是PBUF_ROM或PBUF_RAM类型。
从图1,图2可以看出pbuf的内部结构。Pbuf结构包含有两个指针,两个长度字段,一个标志字段,和一个参考计数。Next字段指向统一链表中的下一个pbuf。有效载荷指针指向该pbuf中数据的起始点。Len字段包含有该pbuf数据内同的长度。Tot_len字段是当前pbuf和所有链表接下来中的len字段值的总和。简单说,tot_len字段是len字段及下一个pbuf中tot_len字段值的总和。Flags字段表示pbuf类型而ref字段包含一个参考计数。Next和payload字段是本地指针,其大小由处理器体系结构决定。两个长度字段是16位无符号整数,而flags和ref字段都是4比特大小。Pbuf的总大小决定于使用的处理器体系结构。在32位指针和4字节校正的体系结构上,总大小是16字节,而在16位指针和1自己校正的体系结构上,总大小是9字节。
Pbuf模块提供了操作pbuf的函数。Pbuf_alloc()可以分配前面提到的三种类型的pbuf。Pbuf_ref()增加引用计数,pbuf_free()释放分配的空间,它先减少引用计数,当引用计数为0时就释放pbuf。Pbuf_realloc()收缩空间以使pbuf只占用刚好的空间保存数据。Pbuf_header()调整payload指针和长度字段,以使一个头部指向pbuf中的数据。Pbuf_chain()和pbuf_dechain()用于链表化pbuf。
6.2 内存管理
内存管理只是简单地支持pbuf机制。它处理连续内存的分配和释放,并能收缩先前分配的内存块。内存管理使用系统总内存贡献出的小部分,这确保网络系统不会使用所有可用的内存,并且其他程序的操作不会因为网络系统使用完所有内存而受打断。
在内部,内存管理在每个分配的内存块头部记录一个小的结构以跟踪所有已分配的内存。图4所示结构保存两个指针分别指向前一个和后一个分配的内存块。它同时有一个已使用标志指示该内存块是否已经分配。
内存分配是通过搜索一个未使用的足够大小的内存块给所请求的空间分配。首次适应法使搜索时第一块足够大小的内存被分配出去。当一内存块被释放时,已用标志被设为0。为了防止碎片,通过检查前一个和后一个内存块的已用标志,可以把未用的块结合起来形成更大的未用的块。
7 网络层接口
lwIP中物理网络硬件的设备驱动被表示为一个像BSD中那样的一个网络接口结构。图5显示了网路接口结构。网络接口被保存在一个全局的链表中,通过结构中的next指针连接起来。每个网络接口都有一个名字,存在name字段中。两字符名字指示网路接口使用的设备驱动种类,只在运行时通过人工操作完成接口配置。Name字段由设备驱动设置,并且能反映出由网络接口表示的硬件类型。例如,蓝牙驱动的网路接口其name值应为bt,IEEE 802.11b WLAN其name值应为wl。因为name值没必要是唯一的,因此num字段用来区别统一类型驱动的不同网络接口。
三个IP地址,ip_addr, netmask和gw由IP层使用来发送和接收包,将在下面描述它们。不可能使用超过一个IP地址去配置一个网络接口,一个网络接口只能对应一个IP地址。
Input指针指向一个函数,该函数是在收到一个包时由设备驱动调用的。
一个网络接口是通过output指针指向一个设备驱动的。这个指针指向设备驱动中的一个函数用来在物理网络上传输一个包,IP层要发送包时会调用该函数。这个字段由设备驱动初始化函数设置。Output函数的第三个参数,ipaddr,是将接收实际链路层帧的主机IP地址,它没必要和IP包的目的地址一样。特别地,当发送一个IP到不在本地网络的主机时,链路层帧将被送到网络中的一台路由器上。这种情况下,传递给output函数的IP地址将会是该路由器的IP地址。
最后,state指针指向设备驱动指示网络接口的特定状态,由设备驱动设置。
(待续)