LwIP协议栈学习之动态内存管理

前言

LWIP 的动态内存管理机制可以有三种:

  • C 运行时库自带的内存分配策略
  • 动态内存堆(HEAP)分配策略
  • 动态内存池(POOL)分配策略
    由于前两种分配策略具有很大的相似性,使用时只能从中选择一种,通过头文件
    lwippools.h 中的宏定义 MEM_LIBC_MALLOC 来实现的,当它被定义为 1 时则使用标准 C
    运行时库自带的内存分配策略,而为 0 时则使用 LWIP 自身的动态内存堆分配策略。
    一般情况下,选择使用LWIP自身的动态内存堆分配策略。

动态内存堆分配策略

动态内存堆分配策略可以有两种实现方式:

  1. 通过开辟一个内存堆,然后通过模拟 C 运行时库的内存分配策略来实现。
  2. 通过动态内存池的方式来实现,也即动态内存堆分配函数通过简单调用动态内存池(POOL)分配函数来完成其功能,此时,需要在头文件lwippools.h 中定义宏
    MEM_USE_POOLS 和 MEM_USE_CUSTOM_POOLS 为 1,同时还要开辟一些额外的缓冲池
    区,如下:
LWIP_MALLOC_MEMPOOL_START
LWIP_MALLOC_MEMPOOL(20, 256)
LWIP_MALLOC_MEMPOOL(10, 512)
LWIP_MALLOC_MEMPOOL(5, 1512)
LWIP_MALLOC_MEMPOOL_END

上述代码表示为动态内存堆相关功能函数分配 20 个 256 字节长度的内存块, 10 个 512 字节的内存块, 5 个 1512 字节的内存块。
内存池管理会根据以上的宏自动在内存中静态定义一个大片内存用于内存池。在内存分配申请的时候,自动根据所请求的大小,选择最合适他长度的池去申请。如 果 启 用 宏
MEM_USE_POOLS_TRY_BIGGER_POOL,那么,如果上述的最适合长度的池中没有空间
可以用了,分配器将从更大长度的池中去申请。

模拟C运行时库的分配策略实现

动态内存堆分配策略原理就是在一个事先定义好大小的内存块中进行管理,其内存分配的策略是采用最快合适(First Fit)方式,只要找到一个比所请求的内存大的空闲块,就从中切割出合适的块,并把剩余的部分返回到动态内存堆中。

  • 分配的内存块分配大小不能小于MIN_SIZE,一般MIN_SIZE为12字节,在这12个字节中前几个字节会存放内存分配器管理用的私有数据,不能被用户程序修改;
  • 内存释放的过程是相反的过程,但分配器会查看该节点前后相邻的内存块是否空闲,如果空闲则合并成一个大的内存空闲块。
  • 这种分配策略的优点是内存浪费小,简单,适用于小内存的管理,缺点是频繁的动态分配和释放,可能会造成严重的内存碎片。
  • 推荐的方法是:分配->释放->分配->释放,减少内存碎片。

LWIP中实现的函数解析

  1. mem_init( ) 内存堆的初始化函数,主要是告知内存堆的起止地址,以及初始化空闲列表,由 lwip 初始化时自己调用,该接口为内部私有接口,不对用户层开放。
  2. mem_malloc( ) 申请分配内存。将总共需要的字节数作为参数传递给该函数,返回值是指向最新分配的内存的指针。分配的空间大小会受到内存对齐的影响,可能会比申请的略大。
  3. mem_calloc( ) 是对 mem_malloc( )函数的简单包装,他有两个参数,分别为元素的数目和每个元素的大小,这两个参数的乘积就是要分配的内存空间的大小。
动态内存池(POOL)分配策略的实现
  • 此方式实现简单,内存的分配、释放效率高,可以有效防止内存碎片的产生,缺点是会浪费部分内存。
  • 某种类型的POOL其单个大小是固定的,而分配该类POOL的个数是可以用户配置的,用户应该根据协议栈实际使用状况进行配置。
  • 把协议栈中所有的 POOL 挨个放到一起,并把它们放在一片连续的内存区域,这呈现给用户的就是一个大的缓冲池。
  • 分配必须是以单个缓冲池为基本单位。

LWIP中的实现

static u8_t memp_memory [ MEM_ALIGNMENT - 1 
#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_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 被编译后就为一条一条的+(), +(), +(), +() ….所以最终的数组memp_memory等价定义为:

static u8_t memp_memory [ MEM_ALIGNMENT – 1
+()
+() ….]

至于MEM_ALIGNMENT-1是由于考虑了编译器的字对齐问题。
基于上面的数组建立方法,LWIP还建立了一些与缓冲池管理的全局变量:

  • memp_num:这个静态数组用于保存各种类型缓冲池的成员数目
  • memp_sizes:这个静态数组用于保存各种类型缓冲池的结构大小
  • memp_tab:这个指针数组用于指向各种类型缓冲池当前空闲节点

实现函数

  • memp_init():内存池的初始化,主要是为每种内存池建立链表 memp_tab,其链表是逆序
    的,此外,如果有统计功能使能的话,也把记录了各种内存池的数目。
  • memp_malloc(): 如果相应的 memp_tab 链表还有空闲的节点,则从中切出一个节点返回,否则返回空。
  • memp_free():把释放的节点添加到相应的链表memp_tab头上。

参考资料

老衲五木:TCP/IP协议栈LwIP的设计与实现

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值