LWIP 的动态内存管理机制
文件mem.h mem.c memp.c memp.h学习
LWIP 的动态内存管理机制可以有三种:
C 运行时库自带的内存分配策略(MEM_LIBC_MALLOC);
动态内存堆(HEAP)分配策略;
动态内存池(POOL(MEM_USE_POOLS))分配策略。
动态内存堆分配策略和
通过自定义头文件lwipopt.h (原opt.h)中的宏定义 MEM_LIBC_MALLOC 来实现的,当它被定义为 1 时则使用标准 C运行时库自带的内存分配策略,而为 0时则使用 LWIP 自身的动态内存堆分配策略。一般情况下,我们选择使用
动态内存堆分配
其原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。在分配的内存块前大约有12字节会存放内存分配器管理用的私有数据,该数据区不能被用户程序修改,否则导致致命问题。
内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。
动态内存池分配
动态内存池(POOL)分配策略实现简单,内存的分配、释放效率高,可以有效防止内存碎片的产生。不过,他的缺点是会浪费部分内存。
用户需要在头文件 lwipopt.h (原opt.h)中定义宏 MEM_USE_POOLS和 MEM_USE_CUSTOM_POOLS 为
同时还要开辟一些额外的缓冲池区,创建文件lwippools.h 如下:
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(20, 256)
LWIP_MALLOC_MEMPOOL(10, 512)
LWIP_MALLOC_MEMPOOL(5, 1512)
LWIP_MALLOC_MEMPOOL_END
见 mem.c 文件头源码注释部分说明,内存池管理会根据以上的宏自动在内存中静态定义一个大片内存用于内存池。在内存分配申请的时候,自动根据所
请 求 的 大 小 , 选 择 最 适 合 他 长 度 的 池 里 面 去 申 请 , 如 果 启 用 宏MEM_USE_POOLS_TRY_BIGGER_POOL,那么,如果上述的最适合长度的池中没有空间可以用了,分配器将从更大长度的池中去申请。
Memp.c文件的数组定义,很是值得今后应用。
const u16_t memp_sizes[MEMP_MAX] = {
#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
#include "lwip/memp_std.h"
};
上面的代码定义了缓冲池所使用的内存缓冲区,问题的关键就在于头文件memp_std.h,它里面的东西可以被简化为诸多条LWIP_MEMPOOL(name,num,size,desc)。又由于用了define关键字将LWIP_MEMPOOL (name,num,size,desc)定义为+((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size))),所以,memp_std.h被编译后就为一条一条的+(),+(),+(),+()….
const u16_t memp_sizes[MEMP_MAX] = {
LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)),
LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen)),
...
};
数组要比实际需要的大MEM_ALIGNMENT – 1,作者考虑的是编译器的字对齐问题。详细说明可见《嵌入式网络那些事-LwIP协议深度剖析与实战演练》,朱老大说的很是明白。
协议栈还建立了一些与缓冲池管理的全局变量:
memp_num:这个静态数组用于保存各种类型缓冲池的成员数目
memp_sizes:这个静态数组用于保存各种类型缓冲池的结构大小
memp_tab:这个指针数组用于指向各种类型缓冲池当前空闲节点
实现函数:
memp_init():内存池的初始化,主要是为每种内存池建立链表memp_tab,其链表是逆序的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。
memp_malloc():如果相应的memp_tab链表还有空闲的节点,则从中切出一个节点返回,否则返回空。
memp_free():把释放的节点添加到相应的链表memp_tab头上。
《嵌入式网络那些事-LwIP协议深度剖析与实战演练》,这本书,值得拥有!