广告

原创 LwIP源代码解读[2]

2009-8-28 13:29 4694 1 1 分类: MCU/ 嵌入式

第二章基础组件<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />


内存管理


LwIP内存管理部分mem.h mem.c比较灵活,支持多种分配策略,有运行时库自带的内存分配(MEM_LIBC_MALLOC),有内存池分配(MEM_USE_POOLS),有动态内存堆分配,这些分配策略可以通过宏定义来更改。在嵌入式系统里面,C运行时库自带的内存分配一般情况下很少用,更多的是后面二者,下面就这两种分配策略进行简单的分析:


动态内存堆分配


其原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First Fit[user1] )方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。在分配的内存块前大约有12字节会存放内存分配器管理用的私有数据,该数据区不能被用户程序修改,否则导致致命问题。


内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。


采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。对于动态内存的使用,比较推荐的方法就是分配->释放->分配->释放,这种使用方法能够减少内存碎片。


mem_init( ) 内存堆的初始化,主要是告知内存堆的起止地址,以及初始化空闲列表,由lwip初始化时自己调用,该接口为内部私有接口,不对用户层开放。


mem_malloc( ) 申请分配内存。将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是NULL,分配的空间大小会收到内存对齐的影响,可能会比申请的略大。返回的内存是没有初始化的。这块内存可能包含任何随机的垃圾,你可以马上用有效数据或者至少是用零来初始化这块内存。内存的分配和释放,不能在中断函数里面进行。内存堆是全局变量,因此内存的申请、释放操作做了线程安全保护,如果有多个线程在同时进行内存申请和释放,那么可能会因为信号量的等待而导致申请耗时较长。


mem_calloc( ) 是对mem_malloc( )函数的简单包装,他有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小,与mem_malloc()不同的是它会把动态分配的内存清零。有经验的程序员更喜欢使用mem_malloc (),因为这样的话新分配内存的内容就不会有什么问题,调用mem_malloc ()肯定会清0,并且可以避免调用memset()


mem_realloc( )  函数


Opt.h文件的宏MEM_SIZE 是表示初始内存堆的大小。


动态内存池分配


说实话,我也不知道这个说法对不对,反正从源代码里面看,前者叫heap,后者叫pool。欢迎专家指正。


动态内存分配方式只能在内存池与内存堆中二选一。他们对外部的接口都是一样,只不过内部工作原理不太一样。动态内存池分配部分底层实现是在memp.cmemp.h 文件里面实现。


采用内存池进行内存管理可以有效防止内存碎片的产生,而且相比之下内存的分配、释放效率更高,不过,他会浪费部分内存。


需要启用宏MEM_USE_POOLS MEM_USE_CUSTOM_POOLS,另外还要做类似如下定义:


 LWIP_MALLOC_MEMPOOL_START


 LWIP_MALLOC_MEMPOOL(20, 256)


LWIP_MALLOC_MEMPOOL(10, 512)


 LWIP_MALLOC_MEMPOOL(5, 1512)


LWIP_MALLOC_MEMPOOL_END


上面的意思就是分配20256字节长度的内存块,10512字节的内存块,51512字节的内存块。内存池管理会根据以上的宏自动在内存中静态定义一个大片内存用于内存池。在内存分配申请的时候,自动根据所请求的大小,选择最适合他长度的池里面去申请,如果启用宏 MEM_USE_POOLS_TRY_BIGGER_POOL,那么,如果上述的最适合长度的池中没有空间可以用了,分配器将从更大长度的池中去申请,不过这样会浪费更多的内存。


此外,LwIP为内部的一些结构设计了专用的内存池,比如netconn,协议控制块,数据包等,这些都是在memp.c/memp.h里用内存池进行管理的。


这个模块里面LwIPC语言的宏用到了极致,它大量采用了C语言的宏特性,设计上面也非常的精妙,看上去也很优雅,不过对于初学者来说猛的看上去很头大,下面就且听我给你介绍,我们先看几个静态变量数组:


memp_memory[]


这是内存池容器,他的大小由编译期决定,他是各个组件的结构用量的累加。


我们来看代码:



这是内存池的具体定义,通过147行,我们可以看出内存池的大小由各个组件的 num*size的累加。如下图所示,每个组件的num 就是下列阴影区的宏定义(memp_std.h文件)



size的大小就是各个结构的大小,如下图阴影区所示。



整个内存池的大小又可以根据组件的需要而调整,比如,如果你不需要UDP,那么只要把LWIP_UDP定义为0。用宏定义来实现用起来方便,改起来容易,就是看起来头大。


memp_num


这个静态数组用于保存各个组件的成员数目,与memp_memory类似也是用宏实现的。


memp_sizes


这个静态数组用于保存各个组件的结构大小,与memp_memory类似也是用宏实现的。


 


memp_init


内存池的初始化,主要是为每种内存池建立链表memp_tab,其链表是逆序的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。


memp_malloc


如果相应的memp_tab链表还有空闲的节点,则从中切出一个节点返回,否则返回空。


memp_free


把释放的节点添加到相应的链表memp_tab头上。


 


pbuf


pbuflwIP包的内部表示,被设计为最小化栈的特殊需要。pbufs类似于BSD实现中的mbufspbuf结构支持为包内容动态分配内存和让包数据驻留在静态内存中。pbufs能被一个称为pbuf链的链接到一个链表中,以至一个包能跨越多个pbufs





广告

文章评论 0条评论)

登录后参与讨论
相关推荐阅读
oldtom 2009-11-10 22:11
nucleus底半中断设计
中断是提供队内部或外部事件立刻反应的机制。当中断发生时,处理器悬挂当前执行路径,把控制流转移到中断服务程序(ISR)。一个中断的精确操作是跟处理器相关。 NP支持可管理或不可管理的ISRs。可管理IS...
oldtom 2009-10-25 20:33
想搞个开源的工控板!不知道有没有人一起合作搞搞?
开源工控板需求设计 目标定位 主要应用于工业自动化测控相关产品的核心CPU模块,他是以ARM为核心的嵌入式工控平台。模块提供完善的外围硬件接口,内嵌RT-Thread实时操作系统以及其他相关的软件资源...
oldtom 2009-08-28 13:32
LwIP源代码解读[1]
LwIP协议栈的学习与应用前言<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />LWI...
我要评论
0
1
广告
关闭 热点推荐上一条 /1 下一条