[ZT]嵌入式LwIP协议栈的内存管理

发布于 2008-06-20 17:11:00
嵌入式LwIP协议栈的内存管理 付晓军,夏应清,何 轩 华中师范大学 电子与信息工程系,湖北 武汉430079 2008-06-13

  摘 要: 在内存需求分析的基础上,阐述了LwIP TCP/IP协议栈中pbuf结构的基本原理和内存管理机制的实现。这对在嵌入式系统中实现TCP/IP协议栈,进行网络连接有重要意义。
  关键词: TCP/IP协议 LwIP协议栈 内存管理 pbuf结构

  目前,在嵌入式系统中引入TCP/IP协议栈及将嵌入式设备接入网络,已经成为嵌入式领域重要的发展方向。
  TCP/IP是一种基于OSI参考模型的分层网络体系结构,它由应用层、运输层、网络层、数据链路层、物理层组成。各层之间消息的传递通过数据报的形式进行。由于各层之间报头长度不一样,当数据在不同协议层之间传递时,对数据进行封装和去封装、增加和删除操作将十分频繁。
  在嵌入式系统开发中也经常遇到类似问题。用户数据从本地嵌入式设备传输到远程主机的过程中,要经过各层协议,对消息的封装、去封装和拷贝操作几乎是不可避免的。而通常所采用的用一段连续的内存区来存储、传递数据的做法会有以下的缺陷:
  (1)当从上层向下层传递数据时,下层协议需要对数据进行封装,而上层在申请内存时没有(也不应该)考虑下层的需要。这样会导致下层协议处理时需要重新申请内存并进行内存拷贝,从而影响程序的效率。
  (2)当从下层向上层传递数据时,下层协议专有的数据结构应当对上层协议不可见。因此也需要重新申请内存并进行内存拷贝。
  (3)随着数据的逐层处理,其内容可能有所增减,而连续内存很难处理这种动态的数据增删。
  因此,必须有一种能适应数据动态增删、但在逻辑上又呈现连续性的数据结构,以满足在各协议层之间传递数据而不需要进行内存拷贝。嵌入式TCP/IP协议栈要求简单高效,并减少对内存的需求。这些都需要相应的内存管理机制实现。
1 LwIP协议栈中pbuf介绍
  LwIP(Light weight IP)是瑞士计算机科学院Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。LwIP可以移植到操作系统上,也可以在无操作系统的情况下独立运行。 LwIP TCP/IP实现的重点是:在保持TCP协议主要功能的基础上,减少对RAM的占用。这使LwIP协议栈适合在低端嵌入式系统中使用。
  LwIP利用pbuf结构实现数据传递,它与BSD中的Mbuf很相似。pbuf的主要用途是保存在应用程序和网络接口间互相传递的用户数据。
  pbuf的内部结构为:

  struct pbuf{
  struct pbuf *next;  /*指向下一个pbuf*/
  void *payload;    /*指向pbuf数据中的起始位置*/
  u16_t tot_len;    /*该pbuf和后续pbuf中数据长度的总和*/
  u16_t len;      /*该pbuf中数据的长度*/
  u16_t flags;     /*pbuf的类型*/
  u16_t ref;      /*该pbuf被引用的次数*/
  }

2 LwIP内存管理的实现
  在运行TCP/IP协议栈的嵌入式系统中,可以把整个系统的存储区域分为协议栈管理的存储器和应用程序管理的存储器两部分。
2.1 协议栈管理的存储器
  协议栈管理的存储器是指TCP/IP内核能够操作的内存区域,主要用于装载待接收和发送的网络数据分组。当接收到分组或者有分组要发送时,TCP/IP 协议栈为这些分组分配缓存;接收到的分组交付给应用程序或者分组已经发送完毕后,对分配的缓存回收重用。协议栈分配的缓存必须能容纳各种大小的报文,例如从仅仅几个字节的ICMP回答报文到几百个字节的TCP分段报文。
  LwIP中的pbuf有四种类型:PBUF_POOL、PBUF_RAM、PBUF_ROM、PBUF_REF。这四种类型的pbuf都是从TCP/IP协议栈管理的存储器中分配的,其中PBUF_ROM和PBUF_REF与应用程序管理的存储区域密切相关。
  PBUF_POOL是具有固定容量的pbuf,主要供网络设备驱动使用,为收到的数据分组分配缓存。在协议栈管理的内存中初始化了一个pbuf池 (PBUF_POOL),具有相同尺寸的pbuf都是从这个pbuf池中分配得到。一般使用多个PBUF_POOL链接成一个链表,用于存储数据分组。如图1所示。
  LwIP用一个宏定义一个PBUF_POOL的大小。一个分组需要分配几个PBUF_POOL,而在数据较少时分配一个 PBUF_POOL即可。由于分配一个PBUF_POOL类型的pbuf很快,适合在中断处理中使用,所以PBUF_POOL主要供网络设备驱动使用,为收到的数据分组分配缓存。


  应用程序发送动态产生的数据时,可以用PBUF_RAM类型的pbuf。PBUF_RAM在事先划分好的内存堆中分配。对该内存堆的操作类似于C语言中的malloc/free。内存堆分配的结构如图2所示。图2中每个被分配的存储块附带了一个小结构,该结构的两个指针指向相邻的内存块。used标识位用来指示该内存块的分配情况,阴影部分表示已经被分配了,此时used为1。当需要一块N字节的存储块时,就对整个存储堆进行搜索。如果找到一块未用的(used=0)并且容量不小于N字节的区域就表示分配成功,并且置used为1。而分配的内存块使用完后需要释放,为了不产生碎片,相邻且未用的内存块需要进行合并。
  PBUF_POOL和PBUF_RAM都可以根据需要从存储器中动态分配,这种分配机制又称为动态存储器分配机制。该分配机制不仅能为应用程序的数据分配存储空间,而且能为协议首部分配存储空间。在层与层之间传递数据时,真正需要修改的只是数据的格式,使之符合各层的规范,而数据本身不需要变动。实际上数据格式反应的是各层的首部,当数据在各层之间传送时,需要动态地添加和移去相应的首部,用动态分配机制可以很好地实现。
2.2 应用程序管理的存储器
  应用程序管理的存储器是指应用程序管理、操作的存储区域,一般从该区域为应用程序发送数据分配缓存。虽然该存储区域不由TCP/IP协议栈管理,但在不严格分层的协议栈中,该存储区域必须与TCP/IP管理的存储器协同工作。为节省内存,LwIP不采取分级访问模式,而是通过指针访问数据。这样就不需要为数据的传递分配存储空间。应用程序发送的数据在交付LwIP后,LwIP就认为这些数据是不能被改动的,因此应用程序的数据被认为是永远存在并且不能被改变的。这一点与ROM 很相似,类型名PBUF_ROM也由此而来。
  如图3所示,PBUF_ROM的数据指针payload指向External memory(外部存储区)。External memory指不由TCP/IP协议栈管理的存储区,它可以是应用程序管理的存储器为用户数据分配的缓存,也可以是ROM区域,如静态网页中的字符串常量等。由于由应用程序交付的数据不能被改动,因此就需要动态地分配一个PBUF_RAM来装载协议的首部,然后将PBUF_RAM(首部)添加到 PBUF_ROM(数据)的前面。这样就构成了一个完整的数据分组。


  图3中的PBUF_ROM还可以是PBUF_REF。PBUF_
  REF和 PBUF_ROM的特性非常相似,都可以实现数据的零拷贝。但是当发送的数据需要排队时就表现出PBUF_REF的特性了。例如在发送分组时,待发送的分组需要在ARP队列中排队,假如这些分组中有PBUF_ROM类型的pbuf,则说明该类型pbuf中的数据位于应用程序的存储区域,是通过指针被 PBUF_ROM引用的。这样直到分组被处理之前,被引用的应用程序的这块存储区域都不能另作它用。在此情况下要用到PBUF_REF类型的pbuf。在排队时,LwIP会为PBUF_REF(接上页)
  类型的pbuf分配缓存(PBUF_POOL或PBUF_RAM),并将引用的应用程序的数据拷贝到分配的缓存中。这样应用程序中被引用数据的存储区域就能被释放。
  pbuf结构实现了层与层之间的数据传递,但其非常消耗内存,并且需要TCP/IP协议栈为之分配存储空间,例如协议控制udp_pcb、 tcp_pcb等。通常,嵌入式TCP/IP协议栈都不是严格分层的,尽量减少对内存的需求是实现嵌入式TCP/IP的重点,内核的内存管理机制直接关系到嵌入式TCP/IP协议栈的性能。
参考文献
1 张 峰,王 健.嵌入式实时操作系统中实现MBUF.微型电脑应用,2001;17(11)
2 Dunkels A.Design and Implementation LwIP TCP/IP Stack.2001,1
下载附件[lei.TIF]

查看更多

关注者
0
被浏览
4.3k
4 个回答
layer2
layer2 2008-06-27
lwip应用心得
lwIP是瑞士计算机科学院(Swedish Institute of Computer Science)的Adam Dunkels等开发的一套用于嵌入式系统的开放源代码TCP/IP协议栈。Lwip既可以移植到操作系统上,又可以
在无操作系统的情况下独立运行.

LwIP的特性如下:
(1) 支持多网络接口下的IP转发
(2) 支持ICMP协议
(3) 包括实验性扩展的的UDP(用户数据报协议)
(4) 包括阻塞控制,RTT估算和快速恢复和快速转发的TCP(传输控制协议)
(5) 提供专门的内部回调接口(Raw API)用于提高应用程序性能
(6) 可选择的Berkeley接口API(多线程情况下)
(7) 在最新的版本中支持ppp
(8) 新版本中增加了的IP fragment的支持.
(9) 支持DHCP协议,动态分配ip地址.

现在网上最新的版本是V0.6.4
1.lwip的进程模型(process model)

tcp/ip协议栈的process model一般有几种方式.

1.tcp/ip协议的每一层是一个单独进程.链路层是一个进程,ip层是一个进程,tcp层是一个进程.这样的好处是网络协
议的每一层都非常清晰,代码的调试和理解都非常容易.但是最大的坏处数据跨层传递时会引起上下文切换(context switch).
对于接收一个TCP segment要引起3次context switch(从网卡驱动程序到链路层进程,从链路层进程到ip层进程,从ip层进程
到TCP进程).通常对于操作系统来说,任务切换是要浪费时间的.过频的context swich是不可取的.

2.另外一种方式是TCP/IP协议栈在操作系统内核当中.应用程序通过操作系统的系统调用(system call)和协议栈来进行通讯.
这样TCP/IP的协议栈就限定于特定的操作系统内核了.如windows就是这种方式.
3.lwip的process model:所有tcp/ip协议栈都在一个进程当中,这样tcp/ip协议栈就和操作系统内核分开了.而应用层程序既可以
是单独的进程也可以驻留在tcp/ip进程中.如果应用程序是单独的进程可以通过操作系统的邮箱,消息队列等和tcp/ip进程进行通讯.
如果应用层程序驻留tcp/ip进程中,那应用层程序就利用内部回调函数口(Raw API)和tcp/ip协议栈通讯.对于ucos来说进程就是一个系统任务.lwip的process model请参看下图.在图中可以看到整个tcp/ip协议栈都在同一个任务(tcpip_thread)中.应用层程序既可以是独立的任务(如图中的 tftp_thread,tcpecho_thread),也可以在tcpip_thread中(如图左上角)中利用内部回调函数口(Raw API)和tcp/ip协议栈通讯

2 Port Lwip to uCos
在这个项目中我用的硬件平台是s3c44b0x+rtl8019.ucos在44b0上的移植在网上有很多大侠非常详尽的讲解和移植代码.我就不敢罗嗦 了.需要说明的一点是lwip会为每个网络连接动态分配一些信号量(semaphone)和消息队列(Message Queue),当连接断开时会删掉这些semaphone和Queue.而Ucos-2.0不支持semaphone和Queue的删除,所以要选择一些 较高版本的ucos.我用的是ucos-2.51.

2.1 Lwip的操作系统封装层(operating system.emulation layer)

Lwip 为了适应不同的操作系统,在代码中没有使用和某一个操作系统相关的系统调用和数据结构.而是在lwip和操作系统之间增加了一个操作系统封装层.操作系统 封装层为操作系统服务(定时,进程同步,消息传递)提供了一个统一的接口.在lwip中进程同步使用semaphone和消息传递采用”mbox”(其实 在ucos的实现中我们使用的是Message Queue来实现lwip中的”mbox”,下面大家可以看到这一点)

Operating system emulation layer的原代码在…/lwip/src/core/sys.c中.而和具体的操作系统相关的代码在../lwip/src/arch/sys_arch.c中.
操作系统封装层的主要函数如下:
void sys_init(void)//系统初始化
sys_thread_t sys_thread_new(void (* function)(void *arg), void *arg,int prio)//创建一个新进程
sys_mbox_t sys_mbox_new(void)//创建一个邮箱
void sys_mbox_free(sys_mbox_t mbox)//释放并删除一个邮箱
void sys_mbox_post(sys_mbox_t mbox, void *data) //发送一个消息到邮箱
void sys_mbox_fetch(sys_mbox_t mbox, void **msg)//等待邮箱中的消息
sys_sem_t sys_sem_new(u8_t count)//创建一个信号量
void sys_sem_free(sys_sem_t sem)//释放并删除一个信号量
void sys_sem_signal(sys_sem_t sem)//发送一个信号量
void sys_sem_wait(sys_sem_t sem)//等待一个信号量
void sys_timeout(u32_t msecs, sys_timeout_handler h, void *arg)//设置一个超时事件
void sys_untimeout(sys_timeout_handler h, void *arg)//删除一个超时事件

关于操作系统封装层的信息可以阅读lwip的doc目录下面的sys_arch.txt.文件.

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友