c - malloc_chunk 的布局到底是什么样的?
问题描述
(更新了我在底部的一些解释)
我正在研究 malloc 是如何工作的,在对 chunk 的结构有了基本的了解之后,我决定写一个小程序来仔细看看到底是怎么回事。
#include <stdio.h>
#include <stdlib.h>
/*
An allocated chunk looks like this:
chunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of previous chunk, if unallocated (P clear) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of chunk, in bytes |A|M|P|
mem-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| User data starts here... .
. .
. (malloc_usable_size() bytes) .
. |
nextchunk-> +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| (size of chunk, but used for application data) |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| Size of next chunk, in bytes |A|0|1|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
int main()
{
void *a = malloc(24);
void *b = malloc(16);
void *chunk_b = NULL, *chunk_after_b = NULL, *chunk_a = NULL;
chunk_a = a - 0x8; //8 byte before a (user data)
chunk_b = a + malloc_usable_size(a);
chunk_after_b = b + malloc_usable_size(b);
*(int *)a = 0xa;
*(int *)b = 0xb;
printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));
printf("chunk_a[0]:0x%x value:0x%04x \n", chunk_a, ((int *)chunk_a)[0]);
printf("chunk_a[1]:0x%x value:0x%04x \n", &((int *)chunk_a)[1], ((int *)chunk_a)[1]);
printf("chunk_a[2]:0x%x value:0x%04x \n\n", &((int *)chunk_a)[2], ((int *)chunk_a)[2]);
printf("chunk_b[0]:0x%x value:0x%04x \n", chunk_b, ((int *)chunk_b)[0]);
printf("chunk_b[1]:0x%x value:0x%04x \n", &((int *)chunk_b)[1], ((int *)chunk_b)[1]);
printf("chunk_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_b)[2], ((int *)chunk_b)[2]);
printf("chunk_after_b[0]:0x%x value:0x%04x \n", chunk_after_b, ((int *)chunk_after_b)[0]);
printf("chunk_after_b[1]:0x%x value:0x%04x \n", &((int *)chunk_after_b)[1], ((int *)chunk_after_b)[1]);
printf("chunk_after_b[2]:0x%x value:0x%04x \n\n", &((int *)chunk_after_b)[2], ((int *)chunk_after_b)[2]);
free(a);
free(b);
return 0;
}
这就是我得到的:
a:0x625ca2a0 malloc_usable_size:24(0x18)
b:0x625ca2c0 malloc_usable_size:24(0x18)
chunk_a[0]:0x625ca298 value:0x0021
chunk_a[1]:0x625ca29c value:0x0000
chunk_a[2]:0x625ca2a0 value:0x000a -->mem (user data)
chunk_b[0]:0x625ca2b8 value:0x0021
chunk_b[1]:0x625ca2bc value:0x0000
chunk_b[2]:0x625ca2c0 value:0x000b -->mem (user data)
chunk_after_b[0]:0x625ca2d8 value:0x0411
chunk_after_b[1]:0x625ca2dc value:0x0000
chunk_after_b[2]:0x625ca2e0 value:0x6e756863
(gcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04) Target: x86_64-linux-gnu)
据我了解, malloc() 将返回“用户数据”部分的地址,因此减去 8 表示“块大小”的 8 个字节,“前一个块的大小”应该是该块的开始(chunk_a 在例如),另一方面,加上 malloc_usable_size() 应该是 nextchunk 的开始(示例中的 chunk_b )。
结果证明有些东西对我来说没有意义。
我认为“块的大小”应该是 chunk_a[1] 和 chunk_b[1],但它们的值都只是 0。但是,我认为“前一个块的大小”应该是 chunk_a[0 ]和chunk_b[0],实际得到的值是0x21,即十进制的33,LSB P减去1后可以得到32。
这个数字对我来说确实有点像,因为 malloc_usable_size(a) 在十进制中是 24 表示用户数据的大小,加上 8,这也表示“块大小”和“前一个块的大小”的 8 个字节,块的实际大小也会得到 32。
那么“块的大小”部分到底应该在哪里,我误解了什么吗?
感谢评论中的所有建议,我做了更多的测试,现在对我来说似乎是合理的。
关键是我错误地将两者都Size of previous chunk
视为Size of chunk
4 字节,因为我正在为 x86_64 系统进行编译,所以它们都应该是 8 字节。
按照这种方法,我重写了实验程序。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int i=0;
void *a = malloc(24);
void *b = malloc(16);
void *chunk_b = NULL, *chunk_a = NULL;
chunk_a = (u_int64_t *)a - 2;
chunk_b = a + malloc_usable_size(a);
((u_int8_t *)a)[0] = 0xa0;
((u_int8_t *)a)[23] = 0xa1;
((u_int8_t *)b)[0] = 0xb0;
printf("a:0x%x malloc_usable_size:%d(0x%x)\n", a, malloc_usable_size(a), malloc_usable_size(a));
printf("b:0x%x malloc_usable_size:%d(0x%x)\n\n", b, malloc_usable_size(b), malloc_usable_size(b));
printf("chunk_a[0]:0x%x value:0x%016x \n", chunk_a, ((u_int64_t *)chunk_a)[0]);
printf("chunk_a[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[1], ((u_int64_t *)chunk_a)[1]);
printf("chunk_a[2]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_a)[2], ((u_int64_t *)chunk_a)[2]);
for(i = 0; i<24;i++)
{
printf("a[%d]:0x%x value:0x%02x \n", i, &((u_int8_t *)a)[i], ((u_int8_t *)a)[i]);
}
printf("\n");
printf("chunk_b[0]:0x%x value:0x%016x \n", chunk_b, ((u_int64_t *)chunk_b)[0]);
printf("chunk_b[1]:0x%x value:0x%016x \n", &((u_int64_t *)chunk_b)[1], ((u_int64_t *)chunk_b)[1]);
printf("chunk_b[2]:0x%x value:0x%016x \n\n", &((u_int64_t *)chunk_b)[2], ((u_int64_t *)chunk_b)[2]);
free(b);
free(a);
return 0;
}
这是这次的结果:
a:0xc2a4b2a0 malloc_usable_size:24(0x18)
b:0xc2a4b2c0 malloc_usable_size:24(0x18)
chunk_a[0]:0xc2a4b290 value:0x0000000000000000
chunk_a[1]:0xc2a4b298 value:0x0000000000000021
chunk_a[2]:0xc2a4b2a0 value:0x00000000000000a0
a[0]:0xc2a4b2a0 value:0xa0
a[1]:0xc2a4b2a1 value:0x00
a[2]:0xc2a4b2a2 value:0x00
a[3]:0xc2a4b2a3 value:0x00
a[4]:0xc2a4b2a4 value:0x00
a[5]:0xc2a4b2a5 value:0x00
a[6]:0xc2a4b2a6 value:0x00
a[7]:0xc2a4b2a7 value:0x00
a[8]:0xc2a4b2a8 value:0x00
a[9]:0xc2a4b2a9 value:0x00
a[10]:0xc2a4b2aa value:0x00
a[11]:0xc2a4b2ab value:0x00
a[12]:0xc2a4b2ac value:0x00
a[13]:0xc2a4b2ad value:0x00
a[14]:0xc2a4b2ae value:0x00
a[15]:0xc2a4b2af value:0x00
a[16]:0xc2a4b2b0 value:0x00
a[17]:0xc2a4b2b1 value:0x00
a[18]:0xc2a4b2b2 value:0x00
a[19]:0xc2a4b2b3 value:0x00
a[20]:0xc2a4b2b4 value:0x00
a[21]:0xc2a4b2b5 value:0x00
a[22]:0xc2a4b2b6 value:0x00
a[23]:0xc2a4b2b7 value:0xa1
chunk_b[0]:0xc2a4b2b8 value:0x0000000000000021
chunk_b[1]:0xc2a4b2c0 value:0x00000000000000b0
chunk_b[2]:0xc2a4b2c8 value:0x0000000000000000
起初,我认为两者都Size of previous chunk
应该Size of chunk
是 4 个字节,因为 malloc_usable_size() 返回的大小与我从(本例中为 0xa77ef2a0 nextchunk
)开始添加返回值(本例中为 0x18)的位置不匹配。user data
我的误解是,如果 LSB P 的设置表明前一个块正在使用,那么它也Size of previous chunk
将被视为前一个块的数据字段。Size of chunk
所以对于实验,我将分配内存的最后一个字节(0xc2a4b2b7)设置为“0xa1”,下面应该是Size of chunk
chunk_b(0xc2a4b2b8),它确实如此!
现在这对我来说完全合理,但如果我仍然误解某些事情,请告诉我,并再次感谢所有评论。
解决方案
推荐阅读
- vba - 跨平台的 VBA 和 Excel
- javascript - AngularJS - 更改 ID 以匹配硬编码值
- spring - redisTemplate.opsForZSet().reverseRange 返回类型与预期不符
- java - 无法部署快照工件
- python - 在 PyQt 中调整窗口大小后如何继续在图像上绘图?
- node.js - 如何使用 Lambda 函数对 Alexa Skill 应用程序进行异步 api 调用?
- python - AttributeError:“Mob”对象没有属性“_Sprite__g”
- java - 隐藏 Jetty 服务器标头
- google-chrome - 处理自签名证书时,HSTS 在浏览器上不起作用
- reactjs - vscode 和 eslint 错误缩进
| JSX | 反应原生