Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
RT-Thread一般讨论
关于rt-thread内核mem.c程序的疑问
发布于 2010-02-02 15:03:12 浏览:7730
订阅该版
我最近在看rt-thread的动态内存管理函数的实现,好像是从lwip哪里移植过来的,有一个疑问。 ```c void rt_system_heap_init(void* begin_addr, void* end_addr) { struct heap_mem *mem; /* alignment addr */ begin_addr = (void*)RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE); /* calculate the aligned memory size */ mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * sizeof(struct heap_mem); /* point to begin address of heap */ heap_ptr = begin_addr; #ifdef RT_MEM_DEBUG rt_kprintf("mem init, heap begin address 0x%x, size %d
", (rt_uint32_t)heap_ptr, mem_size_aligned); #endif /* initialize the start of the heap */ mem = (struct heap_mem *)heap_ptr; mem->magic= HEAP_MAGIC; mem->next = mem_size_aligned; mem->prev = 0; mem->used = 0; /* initialize the end of the heap */ heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; heap_end->magic= HEAP_MAGIC; heap_end->used = 1; heap_end->next = mem_size_aligned; heap_end->prev = mem_size_aligned; rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct heap_mem *)heap_ptr; } ``` 我的理解是其中`mem->next`是mem的下一个内存管理块基于heap_ptr偏移值,而 ```c mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * sizeof(struct heap_mem); ``` 已经为头尾内存块管理结构留足了空间,所以mem_size_aligned应该是初始内存块实际可用的内存空间,如果`mem->next = mem_size_aligned;`那么初始内存块实际可用的内存只有`mem_size_aligned-SIZEOF_STRUCT_MEM`了,这样不是不合理吗? 所以我觉得应该写成下面这样的 ```c void rt_system_heap_init(void* begin_addr, void* end_addr) { struct heap_mem *mem; /* alignment addr */ begin_addr = (void*)RT_ALIGN((rt_uint32_t)begin_addr, RT_ALIGN_SIZE); /* calculate the aligned memory size */ mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * SIZEOF_STRUCT_MEM; /* point to begin address of heap */ heap_ptr = begin_addr; #ifdef RT_MEM_DEBUG rt_kprintf("mem init, heap begin address 0x%x, size %d
", (rt_uint32_t)heap_ptr, mem_size_aligned); #endif /* initialize the start of the heap */ mem = (struct heap_mem *)heap_ptr; mem->magic= HEAP_MAGIC; mem->next = mem_size_aligned+SIZEOF_STRUCT_MEM; mem->prev = 0; mem->used = 0; /* initialize the end of the heap */ heap_end = (struct heap_mem *)&heap_ptr[mem->next]; heap_end->magic= HEAP_MAGIC; heap_end->used = 1; heap_end->next = mem_size_aligned+SIZEOF_STRUCT_MEM; heap_end->prev = mem_size_aligned+SIZEOF_STRUCT_MEM; rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); /* initialize the lowest-free pointer to the start of the heap */ lfree = (struct heap_mem *)heap_ptr; } ``` 同时将mem.c文件中的所有 ```c if (mem2->next != mem_size_aligned)改为if (mem2->next != (mem_size_aligned+SIZEOF_STRUCT_MEM)) ``` 不知道这样理解对不对,希望哪位高人能帮忙解答一下。
查看更多
11
个回答
默认排序
按发布时间排序
bernard
2010-02-02
这家伙很懒,什么也没写!
是的,这份文件在文件头上已经专门标记了申明。 这里为什么用了这么多个aligned,因为是出于对齐考虑的。`mem_size_aligned`是总的可用大小 ```c mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * SIZEOF_STRUCT_MEM; ``` 这个确实合理些,因为`SIZEOF_STRUCT_MEM`是对`sizeof(struct heap_mem)`做了对齐,比单独使用`sizeof(struct heap_mem)`要好。 后来的 `mem->next = mem_size_aligned+SIZEOF_STRUCT_MEM;` 这个则不对,因为mem_size_aligned就是边缘点,而不是再+SIZEOF_STRUCT_MEM。开始留的`2 * SIZE_STRUCT_MEM`,即`2 * sizeof(struct heap_mem)`,两个heap_mem结构体已经分离出来,相当于做内存池的前和尾,相当于在这两个位置打了两个庄,使得代码的实现不会越界。所以这里应该是指向mem_size_aligned,而不需要再+`SIZEOF_STRUCT_MEM`。
wzt324
2010-02-03
这家伙很懒,什么也没写!
抱歉,我还是有点不理解,我知道这个程序运行了很久,而且还是从LWIP上面移植过来的(lwip上面好像也是这么处理的),应该是稳定的,但是我始终觉得这个逻辑上有点问题。这里举个实际的数值分析一下。 假定函数`rt_system_heap_init`的参数`begin_addr==0x0, end_addr==0x100, SIZEOF_STRUCT_MEM`的值为12(0x0C),那么程序希望得到的内存管理块分布情况应该是 头内存管理块(头桩)heap_head地址为0x00, 尾内存管理块(尾桩)heap_end的地址为`(0x100-0x0c)=(0xf4)`,中间的初始化内存块的实际可用内存是`(0x100-2*12) = 0xe8`。 而实际rt_system_heap_init函数的运行结果分析如下: ```c mem_size_aligned = RT_ALIGN((rt_uint32_t)end_addr - (rt_uint32_t)begin_addr, RT_ALIGN_SIZE) - 2 * sizeof(struct heap_mem); ``` 得到的结果mem_size_aligned == 232(0xE8),所以我觉得mem_size_aligned表示的就是初始化内存块的实际可用内存(没有算上头桩所占用的空间),同时heap_ptr = begin_addr,则 ```c heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; ``` 得到的结果是heap_end的地址为0xE8,这不是和期望的`(heap_end==0xf4)`不一致吗? 我感觉是不是程序遗忘了头桩所需要的12字节的空间,造成heap_end的地址前移了12字节,就像bernard你说的那样,在堆的头尾打了两个桩,但是不是在打尾桩的时候没有考虑头桩也是要占空间的,所以尾桩打得超前了点。
bernard
2010-02-03
这家伙很懒,什么也没写!
尾桩肯定是不能分配出去的,因为分配出去就可能导致最后寻址出问题 而分配时,头桩肯定是存在的,因为它是一个索引。所以两个桩去掉后就是剩余可用的可分配内存。 而按照你的意思似乎是,能够把一桩分配出去,所以应该加上SIZEOF_STRUCT_MEM。
wzt324
2010-02-03
这家伙很懒,什么也没写!
>“尾桩肯定是不能分配出去的,因为分配出去就可能导致最后寻址出问题” 你这里的“尾桩”应该是heap_end所在的地址吧? >“而分配时,头桩肯定是存在的,因为它是一个索引。所以两个桩去掉后就是剩余可用的可分配内存” “而按照你的意思似乎是,能够把一桩分配出去,所以应该加上SIZEOF_STRUCT_MEM” 我明白头桩是不能分配出去的,所以实际可分配给用户的最大内存应该是头桩的下一个字节(heap_head+SIZEOF_STRUCT_MEM=0x0c)到heap_end的前一个字节的内存区域,大小应该是(0xe8-0x0c)=0xdc字节,理论上这个堆最大一次应该可以分配mem_size_aligned=0xe8字节的内存,程序实际能分配的最大内存(0xdc)比理论上可分配的最大内存(0xe8)不是少了一个桩的空间吗?我之前所加的SIZEOF_STRUCT_MEM的空间就是为不能分配出去的heap_head加的。
bernard
2010-02-03
这家伙很懒,什么也没写!
头桩“能够”被分配出去,但是头桩的头部应该排除掉。所以最大可用的字节数就是 total - 2 * 桩头大小
bernard
2010-02-03
这家伙很懒,什么也没写!
>程序实际能分配的最大内存(0xdc)比理论上可分配的最大内存(0xe8)**不是少了一个桩的空间吗**?我之前所加的SIZEOF_STRUCT_MEM的空间就是为不能分配出去的heap_head加的。 --- 嗯,关键就在这句上面。实际上没少,因为可以把头桩分配出去,这种情况是,头桩+数据。假设是分配0xe8的数据空间,那么分配出去的是头桩,数据部分即头桩后紧接着的区域。但这个区域依然只能最大是total - 2 * sizeof(heap_mem) 假定sizeof(heap_mem)已经做对齐,即你说的SIZEOF_STRUCT_MEM大小。
wzt324
2010-02-04
这家伙很懒,什么也没写!
头桩是和内存一起分配出去的,但是头桩对用户是不可见的, rt_malloc函数返回的是 ```c /* return the memory data except mem struct */ return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; ``` rt_free函数会通过要释放的内存指针得到对应的桩指针 ```c /* Get the corresponding struct heap_mem ... */ mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); ``` 所以如果用户申请0xe8长度的内存,返回的用户可用内存指针应该是`(0x00+0x0c=0x0c)`, 用户以为自己可用的内存空间是`0x0c~(0x0c+0xe8=0xf4)`, 如果用户对这片内存区域全部操作了一遍,势必会更改`0xe8~0xf4`这段内存区域, 而这回内存区域是`heap_end`结构所占的空间,不就产生了越界访问吗?
bernard
2010-02-04
这家伙很懒,什么也没写!
>头桩是和内存一起分配出去的,但是头桩对用户是不可见的, > rt_malloc函数返回的是 > /* return the memory data except mem struct */ > return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; > rt_free函数会通过要释放的内存指针得到对应的桩指针 > /* Get the corresponding struct heap_mem ... */ > mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); > 所以如果用户申请0xe8长度的内存,返回的用户可用内存指针应该是(0x00+0x0c=0x0c), 用户以为自己可用的内存空间是0x0c~(0x0c+0xe8=0xf4),如果用户对这片内存区域全部操作了一遍,势必会更改0xe8~0xf4这段内存区域,而这回内存区域是heap_end结构所占的空间,不就产生了越界访问吗? --- 呵呵,“用户以为自己可用的内存空间是0x0c~(0x0c+0xe8=0xf4)”。这段空间总长是0xe8,如果他仅限于这段空间中操作(0x0c - (0x0c + 0xe8)),他如何要访问到尾桩?
wzt324
2010-02-04
这家伙很懒,什么也没写!
这就是问题所在啊, ```c heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; ``` `mem_size_aligned = 0xe8`, heap_ptr的地址是0x0, 那么上面的程序所得到的结果heap_end所在的地址不就是0xe8嘛, 这个地址在(0x0c-0xf4)之间.是会被用户更改掉的。
bernard
2010-02-04
这家伙很懒,什么也没写!
>这就是问题所在啊, 搞了半天,原来我理解错了你提出问题的点,原来是这个地方: `heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned];` 这么做确实是把尾桩插到前面一个位置去了,然后总的可用空间就少了1部分。不过好像不会越界把最后的桩擦除掉,相当于少了12字节的内存。谢谢指正,我会尽快修复这个问题。
撰写答案
登录
注册新账号
关注者
0
被浏览
7.7k
关于作者
wzt324
这家伙很懒,什么也没写!
提问
6
回答
9
被采纳
0
关注TA
发私信
相关问题
1
有关动态模块加载的一篇论文
2
最近的调程序总结
3
晕掉了,这么久都不见layer2的踪影啊
4
继续K9ii的历程
5
[GUI相关] FreeType 2
6
[GUI相关]嵌入式系统中文输入法的设计
7
20081101 RT-Thread开发者聚会总结
8
嵌入式系统基础
9
linux2.4.19在at91rm9200 上的寄存器设置
10
[转]基于嵌入式Linux的通用触摸屏校准程序
推荐文章
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组件
最新文章
1
开源共生 商业共赢 | RT-Thread 2024开发者大会议程正式发布!
2
【24嵌入式设计大赛】基于RT-Thread星火一号的智慧家居系统
3
RT-Thread EtherKit开源以太网硬件正式发布
4
如何在master上的BSP中添加配置yml文件
5
使用百度AI助手辅助编写一个rt-thread下的ONVIF设备发现功能的功能代码
热门标签
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
rt_mq_消息队列_msg_queue
keil_MDK
ulog
MicroPython
C++_cpp
本月问答贡献
a1012112796
20
个答案
3
次被采纳
张世争
12
个答案
3
次被采纳
踩姑娘的小蘑菇
7
个答案
3
次被采纳
rv666
9
个答案
2
次被采纳
用户名由3_15位
13
个答案
1
次被采纳
本月文章贡献
程序员阿伟
9
篇文章
2
次点赞
hhart
3
篇文章
4
次点赞
RTT_逍遥
1
篇文章
6
次点赞
大龄码农
1
篇文章
5
次点赞
ThinkCode
1
篇文章
1
次点赞
回到
顶部
发布
问题
分享
好友
手机
浏览
扫码手机浏览
投诉
建议
回到
底部