Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
堆_heap_动态内存管理
小内存管理(1)--- 内存初始化
发布于 2024-05-11 14:47:37 浏览:402
订阅该版
[tocm] # RT-Thread小内存管理初始化 **结构体定义:** > **struct rt_small_mem_item**:用于管理每一个内存块 > > **struct rt_memory**:所有内存管理模块的父类 > > **struct rt_small_mem**:作为整块传入空间的管理结构体 ```c struct rt_small_mem_item { rt_ubase_t pool_ptr; /**< small memory object addr */ rt_size_t next; /**< next free item */ rt_size_t prev; /**< prev free item */ } struct rt_memory { struct rt_object parent; /**< inherit from rt_object */ const char * algorithm; /**< Memory management algorithm name */ rt_ubase_t address; /**< memory start address */ rt_size_t total; /**< memory size */ rt_size_t used; /**< size used */ rt_size_t max; /**< maximum usage */ }; struct rt_small_mem { struct rt_memory parent; /**< inherit from rt_memory */ rt_uint8_t *heap_ptr; /**< pointer to the heap */ struct rt_small_mem_item *heap_end; /**< 标记内存空间的末尾 */ struct rt_small_mem_item *lfree; /**< 指向管理内存空间中第一个可用的块 */ rt_size_t mem_size_aligned; /**< aligned memory size */ }; ``` 其中需要注意的是结构体**struct rt_small_mem_item** 这里很容易忽略的一点就是它的**next**和**prev**指针都是**rt_size_t(unsigned long)**类型的。(我就是一开始忽略了,一直把它当结构体指针看,导致后面看代码老是感觉不对劲)。 **宏定义:** ```c /* 内存保护字初始化 */ #define HEAP_MAGIC 0x1ea0 /* 掩码 实际值:0xFFFFFFFE */ #define MEM_MASK ((~(rt_size_t)0) - 1) /* 标记内存块为已使用状态 */ #define MEM_USED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x1) /* 标记内存块为未使用状态 */ #define MEM_FREED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x0) /* 判断内存块的使用状态 */ #define MEM_ISUSED(_mem) \ (((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (~MEM_MASK)) /* 获取整个内存池的管理结构体 */ #define MEM_POOL(_mem) \ ((struct rt_small_mem *)(((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (MEM_MASK))) /* 获取内存块的空间大小 */ #define MEM_SIZE(_heap, _mem) \ (((struct rt_small_mem_item *)(_mem))->next - ((rt_ubase_t)(_mem) - \ (rt_ubase_t)((_heap)->heap_ptr)) - RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE)) /* 对齐后最小的块大小 */ #define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) /* 内存池管理结构体的空间大小 */ #define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE) ``` 大部分宏都还是比较好理解的,这里稍微解释一下我花了比较多时间理解的几个宏。 内存块使用状态标记以及内存状态判断 ```c #define MEM_USED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x1) #define MEM_FREED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x0) #define MEM_ISUSED(_mem) \ (((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (~MEM_MASK)) ``` 其中**small_mem**是内存块的对齐后的起始地址,使用内存块起始地址的最后一位去标记内存状态其实是有妙用的。 一般来说我们在标记状态时会专门设置一个变量,例如:int flag或者uint8_t flag。也就是说我们至少要花费一个字节去存储状态。而RT-Thread并没有特地为内存状态去创建一个变量,它将内存状态以及内存的起始地址一同保存在了结构体**rt_small_mem_item**的**pool_ptr**变量中。我们在需要通过内存块去找到内存起始地址时只需要**pool_ptr & MEM_MASK**即可,对应宏**MEM_POOL**。一个变量两个用处,是不是很神奇? 当然,这种使用方法是有一个前提的,即我们使用的内存地址是8地址对齐的,它保证了地址的最后三位都是0,从而避免了出现内存起始地址的最后一位为1导致出错的情况。 **代码实现:** 我们先结合图片了解内存在初始化过程中的使用情况然后再带着理解去看代码就容易多了: 刚开始将指定内存块传入初始化函数: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240511/45e763c047ec7470aa19367098552f72.png) 为内存池管理结构体预留好空间并对齐地址之后: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240511/2ed0801d5c9442f24e941b0afa43d379.png) 初始化完成: ![screenshot_image.png](https://oss-club.rt-thread.org/uploads/20240511/2fb0b0ed0ea1d9786c8bda47c2c8341c.png) 我们在阅读时需要将heap_ptr当作一个uint8_t类型的数组。 ```c rt_smem_t rt_smem_init(const char *name, void *begin_addr, rt_size_t size) { struct rt_small_mem_item *mem; struct rt_small_mem *small_mem; rt_ubase_t start_addr, begin_align, end_align, mem_size; // 将传入的地址进行对齐 small_mem = (struct rt_small_mem *)RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); // 为内存池管理结构体预留空间 -- 此处类型转换非常重要,如果不转换就变成指针运算了 start_addr = (rt_ubase_t)small_mem + sizeof(*small_mem); // 对预留空间之后的地址进行再对齐 begin_align = RT_ALIGN((rt_ubase_t)start_addr, RT_ALIGN_SIZE); end_align = RT_ALIGN_DOWN((rt_ubase_t)begin_addr + size, RT_ALIGN_SIZE); // 计算出内存空间的末尾地址 /* 检查预留空间之后的剩余空间大小 * 然后再为起始内存块和结尾内存块预留出块结构体。 */ if ((end_align > (2 * SIZEOF_STRUCT_MEM)) && ((end_align - 2 * SIZEOF_STRUCT_MEM) >= start_addr)) { /* calculate the aligned memory size */ mem_size = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM; } else { rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n", (rt_ubase_t)begin_addr, (rt_ubase_t)begin_addr + size); return RT_NULL; } // 内存池管理结构体的初始化 rt_memset(small_mem, 0, sizeof(*small_mem)); rt_object_init(&(small_mem->parent.parent), RT_Object_Class_Memory, name); small_mem->parent.algorithm = "small"; small_mem->parent.address = begin_align; small_mem->parent.total = mem_size; small_mem->mem_size_aligned = mem_size; /* point to begin address of heap */ small_mem->heap_ptr = (rt_uint8_t *)begin_align; LOG_D("mem init, heap begin address 0x%x, size %d", (rt_ubase_t)small_mem->heap_ptr, small_mem->mem_size_aligned); /* initialize the start of the heap */ mem = (struct rt_small_mem_item *)small_mem->heap_ptr; mem->pool_ptr = MEM_FREED(); mem->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; mem->prev = 0; #ifdef RT_USING_MEMTRACE rt_smem_setname(mem, "INIT"); #endif /* RT_USING_MEMTRACE */ /* initialize the end of the heap */ // 当指向heap_end时,说明内存池的空间已经使用完了。 small_mem->heap_end = (struct rt_small_mem_item *)&small_mem->heap_ptr[mem->next]; small_mem->heap_end->pool_ptr = MEM_USED(); small_mem->heap_end->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; small_mem->heap_end->prev = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; #ifdef RT_USING_MEMTRACE rt_smem_setname(small_mem->heap_end, "INIT"); #endif /* RT_USING_MEMTRACE */ /* initialize the lowest-free pointer to the start of the heap */ small_mem->lfree = (struct rt_small_mem_item *)small_mem->heap_ptr; return &small_mem->parent; } ``` 对于小内存管理的了解先讲解到这里,大家先消化一下,后面还会继续编写内存申请以及内存释放的解析。
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
比特饼干
这家伙很懒,什么也没写!
文章
10
回答
1
被采纳
0
关注TA
发私信
相关文章
1
rt_malloc 申请内存失败
2
webnet heap最大使用量。
3
调度锁会引起线程内存不足
4
RT-Thread内存和字符串相关函数与C语言自带的内存和字符串相关函数冲突问题
5
webnet 是否可以做全动态网页,使用内存池来加快速度
6
boatload跳转到app反复重启,难道你们编译器有问题?
7
怎么释放动态线程占用的内存
8
内存堆使用时产生不对齐
9
pandora开发板使用cjson,内存不足。
10
小内存管理中rt_realloc的实现问题
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
USB
DMA
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
FAL
rt-smart
ESP8266
I2C_IIC
WIZnet_W5500
UART
ota在线升级
PWM
cubemx
freemodbus
flash
packages_软件包
BSP
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
中断
Debug
编译报错
msh
SFUD
keil_MDK
rt_mq_消息队列_msg_queue
ulog
C++_cpp
at_device
本月问答贡献
踩姑娘的小蘑菇
7
个答案
3
次被采纳
a1012112796
13
个答案
2
次被采纳
张世争
9
个答案
2
次被采纳
rv666
5
个答案
2
次被采纳
用户名由3_15位
11
个答案
1
次被采纳
本月文章贡献
程序员阿伟
8
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
Betrayer
1
篇文章
1
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部