Lwip 的动态内存管理机制可以有三种: C 运行时库自带的内存分配策略、动态内存堆(HEAP)分配策略和动态内存池(POOL)分配策略。
库自带的内存分配策略和动态内存堆(HEAP)分配策略这两种两种策略使用者只能从中选择一种。
在opt.h头文件中配置宏MEM_LIBC_MALLOC来配置是使用C库自带的内存分配还是使用使用lwip内部实现的内存分配。
动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适( First Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。分配的内存块有个最小大小的限 制,要求请求的分配大小不能小于 MIN_SIZE,否则请求会被分配到 MIN_SIZE 大小的内存空间。一般 MIN_SIZE 为 12 字节,在这 12 个字节中前几个字节会存放内存分配器管理用
的私有数据,该数据区不能被用户程序修改,否则导致致命问题。内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。lwip的具体实现为
1:先在通过全局变量数组定义申请一块连续的内存块作为连续的内存堆
具体如下:
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
MEM_ALIGNMENT根据内存对齐的方式来配置,若是字对齐则为 4 ,若是半字对齐则为2,若单字节则为1。
LWIP_MEM_ALIGN_SIZE(size) 是根据size计算 等于MEM_ALIGMENT整数倍和大于或等于size的最小整数。
例如 size = 9 MEM_ALIGNMENT 配置为1 则结果为9
若MEM_ALIGNMENT 配置为2 则结果10 size<10=5*MEM_ALIGNMEN
若MEM_ALIGNMENT 配置为4 则结果12 size<12=3*MEM_ALIGNMEN
库自带的内存分配策略和动态内存堆(HEAP)分配策略这两种两种策略使用者只能从中选择一种。
在opt.h头文件中配置宏MEM_LIBC_MALLOC来配置是使用C库自带的内存分配还是使用使用lwip内部实现的内存分配。
动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适( First Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。分配的内存块有个最小大小的限 制,要求请求的分配大小不能小于 MIN_SIZE,否则请求会被分配到 MIN_SIZE 大小的内存空间。一般 MIN_SIZE 为 12 字节,在这 12 个字节中前几个字节会存放内存分配器管理用
的私有数据,该数据区不能被用户程序修改,否则导致致命问题。内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。采用这种分配策略,其优点就是内存浪费小,比较简单,适合用于小内存的管理,其缺点就是如果频繁的动态分配和释放,可能会造成严重的内存碎片,如果在碎片情况严重的话,可能会导致内存分配不成功。lwip的具体实现为
1:先在通过全局变量数组定义申请一块连续的内存块作为连续的内存堆
具体如下:
#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
MEM_ALIGNMENT根据内存对齐的方式来配置,若是字对齐则为 4 ,若是半字对齐则为2,若单字节则为1。
LWIP_MEM_ALIGN_SIZE(size) 是根据size计算 等于MEM_ALIGMENT整数倍和大于或等于size的最小整数。
例如 size = 9 MEM_ALIGNMENT 配置为1 则结果为9
若MEM_ALIGNMENT 配置为2 则结果10 size<10=5*MEM_ALIGNMEN
若MEM_ALIGNMENT 配置为4 则结果12 size<12=3*MEM_ALIGNMEN
#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
#define MIN_SIZE 12
#define MEM_SIZE 16000
//堆的大小通过配置MEM_SIZE决定
u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
堆的大小为两个mem结构体的大小+空间
2:堆初始化void mem_init(void)
该函数的主要功能是初始化堆。将堆分成两块。
前面一块分MEM_SIZE_ALIGNED大小,余下的为第二块。并在两块堆的头部用mem结构进行初始化,记录堆的信息。第一块内存mem的used成员置为0,表示为空闲可用,第二块置为1,表示被占用。
全局变量空闲lfree指针指向栈的起始地址。
3:mem_malloc( ) 申请分配内存。
将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针,而如果内存没有分配好,则返回值是 NULL,分配的空间大小会收到内存对齐的影响,可能会比申请的略大。返回的内存是“没有“初始化的。这块内存可能包任何随机的垃圾,你可以马上用有效数据或者至少是用零来初始化这块内存。内存的分配和释放,不能在中断函数里面进行。内存堆是全局变量,因此内存的申请、释放操作做了线
程安全保护,如果有多个线程在同时进行内存申请和释放,那么可能会因为信号量的等待而导致申请耗时较长。
mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
//根据字节的对齐以及申请内存的大小,计算需要分配的内存大小。LWIP_MEM_ALIGN_SIZE宏在上面解释过。
遍历堆链表,找到第一块合适的内存块,分配合适的空间,并返回该内存块指针。
4:mem_calloc就是对mem_malloc的封装。同时将分配到的空间内存清零。
5:mem_free(void *rmem)释放指针指向的内存块,返回链表。
6:mem_trim(void *rmem, mem_size_t newsize)将rmem的内存块缩小为newsize大小。
若使用动态内存池分配 测量,则需要将opt.h头文件中的宏 MEMP_USE_CUSTOM_POOLS和 MEM_USE_POOLS置为1,同时要将lwippools.h(该文件在contrib版本中的ports/win32的目录下面)放在头文件包含路径里面。
在lwippools.h中
LWIP_MALLOC_MEMPOOL(100, 256)
LWIP_MALLOC_MEMPOOL(50, 512)
LWIP_MALLOC_MEMPOOL(20, 1024)
LWIP_MALLOC_MEMPOOL(20, 1536)
以上红定义表示100个256字节、50个512个字节,20个1024字节,20个1536字节的内存块。
内存池管理会根据上面的
宏自动在内存中静态定义一个大片内存用于内存池。
在使用内存池分配动态内存的时候,会根据大小分配合适长度的内存。若将宏MEM_USE_POOLS_TRY_BIGGER_POOL置为1,则在分配内存池中的内存块的时候,若是最合适的内存块用完了,则可以分配更大的内存块。
好吧,内存这一块实在乱,只能掌握一个大概。自己时间不够,先绕过吧。很多的地方。不如说我们mem.c
中定义了两个mem_malloc函数。一般对堆内存的分配方式相关函数定义在mem.c,同时在memp.c中,是关于内存池分配方式的相关函数。实在是乱,以后有时间在再回头来看,表红字提醒自己。不过pool的方式在注释里面提到,很少有用到。