完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
我最近在看rt-thread的动态内存管理函数的实现,好像是从lwip哪里移植过来的,有一个疑问。
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偏移值,而 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了,这样不是不合理吗? 所以我觉得应该写成下面这样的 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文件中的所有 if (mem2->next != mem_size_aligned)改为if (mem2->next != (mem_size_aligned+SIZEOF_STRUCT_MEM)) 不知道这样理解对不对,希望哪位高人能帮忙解答一下。 |
|
相关推荐
8个回答
|
|
是的,这份文件在文件头上已经专门标记了申明。
这里为什么用了这么多个aligned,因为是出于对齐考虑的。mem_size_aligned是总的可用大小 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。 |
|
|
|
抱歉,我还是有点不理解,我知道这个程序运行了很久,而且还是从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函数的运行结果分析如下: 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,则 heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; 得到的结果是heap_end的地址为0xE8,这不是和期望的(heap_end==0xf4)不一致吗? 我感觉是不是程序遗忘了头桩所需要的12字节的空间,造成heap_end的地址前移了12字节,就像bernard你说的那样,在堆的头尾打了两个桩,但是不是在打尾桩的时候没有考虑头桩也是要占空间的,所以尾桩打得超前了点。 |
|
|
|
你这里的“尾桩”应该是heap_end所在的地址吧?
我明白头桩是不能分配出去的,所以实际可分配给用户的最大内存应该是头桩的下一个字节(heap_head+SIZEOF_STRUCT_MEM=0x0c)到heap_end的前一个字节的内存区域,大小应该是(0xe8-0x0c)=0xdc字节,理论上这个堆最大一次应该可以分配mem_size_aligned=0xe8字节的内存,程序实际能分配的最大内存(0xdc)比理论上可分配的最大内存(0xe8)不是少了一个桩的空间吗?我之前所加的SIZEOF_STRUCT_MEM的空间就是为不能分配出去的heap_head加的。 |
|
|
|
头桩“能够”被分配出去,但是头桩的头部应该排除掉。所以最大可用的字节数就是
total - 2 * 桩头大小 关键就在这句上面。实际上没少,因为可以把头桩分配出去,这种情况是,头桩+数据。假设是分配0xe8的数据空间,那么分配出去的是头桩,数据部分即头桩后紧接着的区域。但这个区域依然只能最大是total - 2 * sizeof(heap_mem) 假定sizeof(heap_mem)已经做对齐,即你说的SIZEOF_STRUCT_MEM大小。 |
|
|
|
头桩是和内存一起分配出去的,但是头桩对用户是不可见的,
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)),他如何要访问到尾桩?
|
|
|
|
这就是问题所在啊,
heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; mem_size_aligned = 0xe8, heap_ptr的地址是0x0, 那么上面的程序所得到的结果heap_end所在的地址不就是0xe8嘛, 这个地址在(0x0c-0xf4)之间.是会被用户更改掉的。 |
|
|
|
搞了半天,原来我理解错了你提出问题的点,原来是这个地方:
heap_end = (struct heap_mem *)&heap_ptr[mem_size_aligned]; 这么做确实是把尾桩插到前面一个位置去了,然后总的可用空间就少了1部分。不过好像不会越界把最后的桩擦除掉,相当于少了12字节的内存。 |
|
|
|
你正在撰写答案
如果你是对答案或其他答案精选点评或询问,请使用“评论”功能。
764 浏览 0 评论
3847 浏览 0 评论
如何使用python调起UDE STK5.2进行下载自动化下载呢?
2479 浏览 0 评论
开启全新AI时代 智能嵌入式系统快速发展——“第六届国产嵌入式操作系统技术与产业发展论坛”圆满结束
2901 浏览 0 评论
获奖公布!2024 RT-Thread全球巡回线下培训火热来袭!报名提问有奖!
31185 浏览 11 评论
72822 浏览 21 评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-11-20 11:24 , Processed in 0.812002 second(s), Total 85, Slave 69 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号