内存分配本质就是在一个大数组(内存堆)里面分配合适的空间,然后将这个空间的开始地址返回给调用者,内存分配中最重要的就是分配的时间效率!

内存碎片:内存堆在经过频繁的malloc和free之后可能会产生很多的碎小的不能使用的内存碎片

内存池POOL

LWIP中的内存池(POOL)分配策略简单,但是内存的分配、释放效率高,可以有效的防止内存碎片的产生。

特点:

  • 在内存的策略下用户只能申请固定大小的空间
  • 内存池方法主要用于LWIP内核中固定数据结构的分配,比如UDP控制块,TCP控制块等。

LWIP内核在初始化的时候已经为每个数据结构类型都初始化了一定数量的POOL,文件memp.c和memp.h就是内存池相关内容。

如何建立内存池的多少

至于LWIP内核建立多少种POOL依赖于用户和系统配置,

  • 比如如果定义了宏LWIP_UDP为1
    • 那么在编译时与UDP控制块数据结构相关的内存池POOL就会被建立(MEMP_UDP_PCB),
  • 如果定义了宏LWIP_TCP为1,
    • 编译时与TCP数据结构相关的内存池就会被建立(MEMP_TCP_PCB、MEMP_TCP_SEG)等等!

:::info 每种类型的POOL大小都是固定的。 :::


内存池的数据结构

有6个与LWIP内存池有关的全局变量和数据结构

memp_t

memp_t为一个枚举类型变量,用来给每个POOL取个名字,或者说是编号。

  1. typedef enum
  2. {
  3. #define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
  4. #include "lwip/memp_std.h"
  5. MEMP_MAX
  6. } memp_t;
  1. typedef enum
  2. {
  3. MEMP_ RAW_PCB,
  4. MEMP_ UDP_PCB,
  5. MEMP_ TCP_PCB,
  6. MEMP_ TCP_PCB_LISTEN,
  7. MEMP_ TCP_SEG,
  8. MEMP_ REASSDATA,
  9. …….
  10. MEMP_MAX
  11. } memp_t;

其中MEMP_MAX代表memp_t代表枚举类型中元素总个数,并不代表任何类型的POOL 。

memp_tab[]

memp_tab为一个全局指针数组,指向每类POOL的第一个POOL,memp_tab在文件memp.c文件中定义,定义如下:

  1. static struct memp *memp_tab[MEMP_MAX];

memp_sizes[]

memp_sizes为一个全局数组,用来记录每个POOL的大小,memp_sizes在文件memp.c文件中定义,定义如下:

  1. const u16_t memp_sizes[MEMP_MAX] =
  2. {
  3. #define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
  4. #include "lwip/memp_std.h"
  5. };
  6. const u16_t memp_sizes[MEMP_MAX] =
  7. {
  8. LWIP_MEM_ALIGN_SIZE(sizeof(struct raw_pcb)),
  9. LWIP_MEM_ALIGN_SIZE(sizeof(struct udp_pcb)),
  10. LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb)),
  11. LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_pcb_listen)),
  12. LWIP_MEM_ALIGN_SIZE(sizeof(struct tcp_seg)),
  13. …….
  14. }

memp_num[]

memp_num为一个全局数组,用来记录每类POOL中POOL的个数,memp_num在文件memp.c文件中定义,定义如下:

  1. const u16_t memp_num[MEMP_MAX] =
  2. {
  3. #define LWIP_MEMPOOL(name,num,size,desc) (num),
  4. #include "lwip/memp_std.h“
  5. };
  6. const u16_t memp_num[MEMP_MAX] =
  7. {
  8. MEMP_NUM_RAW_PCB,
  9. MEMP_NUM_UDP_PCB,
  10. MEMP_NUM_TCP_PCB,
  11. MEMP_NUM_TCP_PCB_LISTEN,
  12. MEMP_NUM_TCP_SEG
  13. ……
  14. };

上面的MEMP_NUM_RAW_PCB、MEMP_NUM_UDP_PCB等等都是又用户定义的,用来记录对应的POOL的数量

用户可以在lwipopts.h文件中定义,LWIP在opt.h中已经配置了默认值。

memp_desc[]

memp_desc为一个全局型指针数组,指向每类POOL的描述符 ,memp_desc在文件memp.c文件中定义,

  1. static const char *memp_desc[MEMP_MAX] =
  2. {
  3. #define LWIP_MEMPOOL(name,num,size,desc) (desc),
  4. #include "lwip/memp_std.h"
  5. };
  6. static const char *memp_desc[MEMP_MAX] = {
  7. ("RAW_PCB"),
  8. ("UDP_PCB"),
  9. ("TCP_PCB"),
  10. ("TCP_PCB_LISTEN"),
  11. ("TCP_PCB_LISTEN"),
  12. …….
  13. };
  14. //memp_desc中的每个元素指向了一个字符串,这些字符串在统计信息输出中可能用到。

memp_memory[]

  1. static u8_t memp_memory[MEM_ALIGNMENT - 1
  2. #define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
  3. #include "lwip/memp_std.h"
  4. ];
  5. static u8_t memp_memory
  6. [
  7. MEM_ALIGNMENT 1
  8. +((MEMP_NUM_RAW_PCB) * (MEMP_SIZE +
  9. MEMP_ALIGN_SIZE(sizeof(struct raw_pcb)) ))
  10. +((MEMP_NUM_UDP_PCB) * (MEMP_SIZE +
  11. MEMP_ALIGN_SIZE(sizeof(struct udp_pcb)) ))
  12. +((MEMP_NUM_TCP_PCB) * (MEMP_SIZE +
  13. MEMP_ALIGN_SIZE(sizeof(struct tcp_pcb)) ))
  14. ……..
  15. ];

其中MEMP_SIZE表示需要在每个POOL头部预留的空间,LWIP中在某些特殊场合使用该空间中的值来对POOL进行特殊处理,这里不使用该项功能,所以MEMP_SIZE为0。
如果使用到MEMP_SIZE的话也需要对这个大小进行内存对齐!

内存池实现函数

函数 描述
memp_init() 内存池初始化
memp_malloc() 内存池分配
memp_free() 内存池释放
  • 优点
    • 在于速度快,效率高,不会产生内存碎片
  • 缺点
    • 只能分配各种固定大小的内存空间,
    • LWIP必须实现知道用户要使用哪些类型的POOL,每种类型的POOL数量,然后根据这个需求建立内存池。