Lwip内存管理策略

01内存池

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)等等!每种类型的POOL大小都是固定的。用户可以在lwipopts.h文件中定义,LWIP在opt.h中已经配置了默认值。

有6个与LWIP内存池有关的全局变量和数据结构:memp_t、memp_tab[]、memp_sizes[]、memp_num[]、memp_desc[]和memp_memory[]。

1.1memp_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编译之后如下:

typedefenum
{    
    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.2memp_tab全局指针数组

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

1.3memp_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文件中定义,定义如下:

编译之后

constu16_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.5memp_desc[]全局型指针数组

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

编译之后

staticconst char *memp_desc[MEMP_MAX] =
{   
    ("RAW_PCB"), 
    ("UDP_PCB"),   
    ("TCP_PCB"),  
    ("TCP_PCB_LISTEN"),   
    ("TCP_PCB_LISTEN"),  
    …….
};

memp_desc中的每个元素指向了一个字符串,这些字符串在统计信息输出中可能用到。

1.6memp_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中开辟一些用于内存堆分配函数的内存池。

代码和工程已经开源

代码和KeilIAR工程开源地址:

https://github.com/strongercjd/STM32F207VCT6

 

点击查看本文所在的专辑,STM32F207网络开发

 

posted @ 2021-04-26 20:34  Fireflycjd  阅读(33)  评论(0编辑  收藏  举报