本文转自公众号欢迎关注
基于dwc_ether_qos的以太网驱动开发-lwip的堆管理介绍 (qq.com)
https://mp.weixin.qq.com/s/omnn1wsbdvqeql6uogsqva
一. 前言
堆管理是重点的基础代码,需要重点关注,移植时也需要关注。所以这一篇就来讲讲lwip的堆管理。
二. lwip的堆管理实现
lwip实现了内部的堆管理,这样无os等环境也可以直接移植使用,不依赖系统的堆管理。
当然也可以配置为使用系统的堆管理。
源码位于mem.c,mem.h
如果使能mem_libc_malloc则使用系统的堆管理接口
需要配置以下宏
mem_clib_free
mem_clib_malloc
mem_clib_calloc
默认是
/* in case c library malloc() needs extra protection,
如果使能mem_use_pools则使用内存池实现,这个上一篇已经讲解了。
否则使用mem.c的实现,我们重点关注这一部分。
三. 堆的存储分配
如果没有定义lwip_ram_heap_pointer则
mem.c中定义一个大数组lwip_declare_memory_aligned
大小是mem_size_aligned + (2u * sizeof_struct_mem)
用户可用空间是#define mem_size_aligned lwip_mem_align_size(mem_size)
即用户定义的mem_size,默认值是1600
/**
这里多分配了2u * sizeof_struct_mem是一方面多一个ram_end标记末尾,作为末尾的边界节点,一方面预留对齐的浪费空间。
用户也可以直接定义宏lwip_ram_heap_pointer指定存储的位置。
个人觉得这里lwip_ram_heap_pointer静态指定,改为指针变量,初始化变量值为lwip_ram_heap_pointer,然后也可以通过接口设定会更好。这样用户可以根据实际情况动态分配出这部分存储来,否则静态分配,程序不运行也要占用空间比较浪费。
lwip_declare_memory_aligned
实现如下
/** if you want to relocate the heap to external memory, simply define
四. 源码分析
4.1数据结构
核心数据结构如下struct mem
/**
* the heap is made up as a list of structs of this type.
* this does not have to be aligned since for getting its size,
* we only use the macro sizeof_struct_mem, which automatically aligns.
*/
struct mem {
/** index (-> ram[next]) of the next struct */
mem_size_t next;
/** index (-> ram[prev]) of the previous struct */
mem_size_t prev;
/** 1: this area is used; 0: this area is unused */
u8_t used;
#if mem_overflow_check
/** this keeps track of the user allocation size for guard checks */
mem_size_t user_size;
#endif
};
一个双向链表来实现,该结构体描述某一个区块,used表示当前区块是否使用。
next和prev分别指向前后区块,用于入链表和出链表操作。
这里为什么没有描述本区块大小的字段呢?
因为可以直接从next减去当前区块的基地址得到,所以不需要额外的大小信息了。
比如当前区块基地址是ptr则当前区块可用于分配的有效空间如下计算
(mem->next - (ptr + sizeof_struct_mem))
即后一个区块的开始的地址-本区块开始地址-信息头。
这个实现其实和ucos的堆管理实现差不多。
4.2接口
mem_init
初始化时,初始全局变量ram即对齐后的存储空间,
lfree指向空闲块的开头,初始化时为ram,
lfree始终用于指向未分配的区块。
此时只有一个整的未分配的区块,next指向mem_size后,
mem_size外ram_end用于标记结束,这也是之前存储多分配的原因。
mem_malloc/mem_calloc
分配算法核心思想如下,
从lfree开始查找空闲空间大于等于需求size的块。
如果找到了就分配它。
这里有一个处理,如果本块比较大,则分配了size后还有剩余,所以要拆分,即分配出size后剩余的部分成为空闲块。
这个到底多大要拆分,标准是大于等于(size + sizeof_struct_mem + min_size_aligned)拆分,即可用空间是min_size_aligned才拆分。
分配完后,如果分配出的块刚好是lfree位置,则要更新lfree指向后续空闲块。
因为lfree始终是指向的空闲块,即lfree链接起来的都是空闲块。分配出的块就从lfree中去掉了。
比如分配黄色空间后如下
注意返回的是结构体之后的可用空间部分。
mem_free
将区块链接到lfree空闲块中去,如果该块前后为空闲则和前后拼接成大的空闲块。
mem = (struct mem *)(void *)((u8_t *)rmem - (sizeof_struct_mem + mem_sanity_offset));
先偏移结构到块头。
如果mem小于lfree则要更新lfree为mem。
核心处理是plug_holes,判断mem前后是否空闲,如果是空闲则和前后合并。
mem_trim
缩小
如果本块缩小后剩余的空间不够sizeof_struct_mem + min_size_aligned,则没有必要缩小,不处理。
如果缩小后剩余空间大于上述大小则看next是否空闲如果是则和next合并,否则单独独立出来一个空闲块。
五. 总结
lwip实现了小型的堆管理,这样无os也可以直接移植使用,另外也可以配置为实用内存池和系统实现,比较灵活。
【新专利介绍】基于关键器件的智能电能表加速退化试验方法
『这个知识不太冷』了解 RF 前端和滤波器
霍尔传感器在智能坐便器无刷电机上的应用解析
高频AD8331 VGA与 ADC AD9215互连
TPCAST推出适配HTC VIVE的无线套件
基于DWC_ether_qos的以太网驱动开发-LWIP的堆管理介绍
智慧照明与健康照明的现状及未来
微动开关范例 欧姆龙锁体微动开关满足市场新需求
了解2017年7大科技有哪些细节性的进展
传台积电正计划赴日本建设先进封装厂
特斯拉股价创新高峰,大众全球第二地位不保
Brookman将在2021年3月底成为Toppan的子公司
互联网信息技术打破疫情防控地理空间阻隔
MAX34440 6路复杂系统监测器(附图)
iQOO成功上位,超越小米成国内安卓一哥
紫外激光打标机在键盘和鼠标进行标记效果如何
iPhone8上市时间确定:iPhone8双卡双待来袭?已打包出货美国,腮红金成亮点
TCL洗衣机:坚守初心,矢志创新
PWM音频功放芯片WT1312在血氧仪的应用
易现蝉联两届金V奖“年度优秀XR融合创新奖”