至于LWIP内核建立多少种POOL依赖于用户和系统配置,比如如果定义了宏LWIP_UDP为1,那么在编译时与UDP控制块数据结构相关的内存池POOL就会被建立(MEMP_UDP_PCB),如果定义了宏LWIP_TCP为1,编译时与TCP数据结构相关的内存池就会被建立(MEMP_TCP_PCB、MEMP_TCP_SEG)等等!每种类型的POOL大小都是固定的。用户可以在lwipopts.h文件中定义,LWIP在opt.h中已经配置了默认值。
有6个与LWIP内存池有关的全局变量和数据结构:memp_t、memp_tab[]、memp_sizes[]、memp_num[]、memp_desc[]和memp_memory[]。
1.1、memp_t数据类型
memp_t为一个枚举类型变量,用来给每个POOL取个名字,或者说是编号。memp_t在文件memp.h文件中定义,定义如下:
- #define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
这句代码意思是:
遇到LWIP_MEMPOOL(name,num,size,desc) 换成MEMP_##name
例如:在memp_std.h的34行
- LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(structraw_pcb), "RAW_PCB")
变成了
- MEMP_RAW_PCB
所以memp_t编译之后如下:
- typedef enum
- {
- MEMP_ RAW_PCB,
- MEMP_ UDP_PCB,
- MEMP_ TCP_PCB,
- MEMP_ TCP_PCB_LISTEN,
- MEMP_ TCP_SEG,
- MEMP_ REASSDATA,
- …….
- MEMP_MAX
- } memp_t;
其中MEMP_MAX代表memp_t代表枚举类型中元素总个数(C语言基础知识),并不代表任何类型的POOL
1.2、memp_tab全局指针数组
memp_tab为一个全局指针数组,指向每类POOL的第一个POOL,memp_tab在文件memp.c文件中定义,定义如下:
1.3、memp_sizes全局数组
memp_sizes为一个全局数组,用来记录每个POOL的大小,memp_sizes在文件memp.c文件中定义,定义如下:
编译之后
- 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(structtcp_pcb_listen)),
- LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)),
- …….
- }
memp_sizes中保存了每种类型POOL的大小,这里的大小都是进行了内存对齐的。
这个宏定义,MEM_ALIGNMENT为4,也就是4字节对齐。
分析这个宏定义,也就说,当MEM_ALIGNMENT为4时,传入的size为3时,变为4
申请3个字节,实际申请4个字节
申请6个字节,实际申请8个字节
申请18个字节,实际申请20个字节
1.4、 memp_num[]全局数组
memp_num为一个全局数组,用来记录每类POOL中POOL的个数,memp_num在文件memp.c文件中定义,定义如下:
编译之后
- const u16_t memp_num[MEMP_MAX] =
- {
- (MEMP_NUM_RAW_PCB),
- (MEMP_NUM_UDP_PCB),
- (MEMP_NUM_TCP_PCB),
- (MEMP_NUM_TCP_PCB_LISTEN),
- (MEMP_NUM_TCP_SEG),
- ……
- };
上面的MEMP_NUM_RAW_PCB、MEMP_NUM_UDP_PCB等等都是由用户定义的,用来记录对应的POOL的数量,用户可以在lwipopts.h文件中定义,LWIP在opt.h中已经配置了默认值。
1.5、memp_desc[]全局型指针数组
memp_desc为一个全局型指针数组,指向每类POOL的描述符,memp_desc在文件memp.c文件中定义,定义如下:
编译之后
- static const char *memp_desc[MEMP_MAX] =
- {
- ("RAW_PCB"),
- ("UDP_PCB"),
- ("TCP_PCB"),
- ("TCP_PCB_LISTEN"),
- ("TCP_PCB_LISTEN"),
- …….
- };
memp_desc中的每个元素指向了一个字符串,这些字符串在统计信息输出中可能用到。
1.6、memp_memory[]数组
memp_memory为一个数组,这个数组才是真正的内存池!!!这个数组在文件memp.c文件中定义,定义如下:
编译之后
- static u8_t memp_memory
- [
- MEM_ALIGNMENT – 1
- +((MEMP_NUM_RAW_PCB) * (MEMP_SIZE +
- MEMP_ALIGN_SIZE(sizeof(struct raw_pcb)) ))
- +((MEMP_NUM_UDP_PCB) * (MEMP_SIZE +
- MEMP_ALIGN_SIZE(sizeof(struct udp_pcb)) ))
- +((MEMP_NUM_TCP_PCB) * (MEMP_SIZE +
- MEMP_ALIGN_SIZE(sizeof(struct tcp_pcb)) ))
- ……..
- ];
其中MEMP_SIZE表示需要在每个POOL头部预留的空间,LWIP中在某些特殊场合使用该空间中的值来对POOL进行特殊处理,这里不使用该项功能,所以MEMP_SIZE为0,。如果使用到MEMP_SIZE的话也需要对这个大小进行内存对齐!
1.7、与内存池管理相关的函数:
使用内存池分配内存的优点在于速度快,效率高,不会产生内存碎片,但是缺点在于只能分配各种固定大小的内存空间,LWIP必须实现知道用户要使用哪些类型的POOL,每种类型的POOL数量,然后根据这个需求建立内存池。
02内存堆
LWIP还提供了另外一种内存策略—内存堆,使用内存堆策略就可以随便申请任意大小的内存了。但是这种方法效率和速度会有所下降。
使用内存堆策略的话用户申请的内存大小有最小限制,所申请的内存大小不能小于MIN_SIZE,LWIP默认的MIN_SIZE为12个字节,在mem.c文件中。该值用户可以自行定义。
使用内存堆策略,其有点事内存浪费小,比较简单,适合于小内存的管理,但是缺点就是如果频繁的进行动态内存申请和释放的话,可能会造成严重的内存碎片,如果碎片严重的话可能会导致内存分配失败!
内存堆策略下的内存空间是数组:ram_heap[]。在mem.c文件中。
与内存堆有关的函数有3个:
- mem_init()
- mem_malloc()
- mem_free()
03其他内存策略
前面讲的内存池(POOL)和内存堆(HEAP)这两个内存策略都是LWIP默认的内存策略,LWIP内核中大量的使用了这两个策略,不过LWIP也给我们提供了其他可选的内存策略。
(1)、当定义宏MEM_LIBC_MALLOC为1,那么与内存堆相关的代码就不会被编译的,内存堆中的mem_malloc()和mem_free()就会被ANSIC编译器自带的malloc()和free()替代。
(2)、当定义宏MEMP_MEM_MALLOC为1,那么内存池文件memp.c就不会被编译。
(3)、当定义宏MEM_USE_POOLS定义为1,那么内存堆分配相关的函数及全局变量不会被编译,这个时候就用内存池分配方式来实现内存堆的的分配方式,因为内存池的分配策略效率很高。
但是(3)中的方法使用起来比较麻烦,需要在lwipopts.h中定义宏MEM_USE_POOLS和MEM_USE_CUSTOM_POOLS都为1,还需要在另外一个头文件lwippools.h中开辟一些用于内存堆分配函数的内存池。