Malloc是否仅在请求的内存空间较大时才使用堆?
每当您研究进程的内存分配时,您通常会看到如下概述: 到目前为止还不错 但是,还有一个sbrk()系统调用,它允许程序更改其数据部分的上限,也可以使用它简单地检查sbrk(0)的限制在哪里。使用该函数,我发现了以下模式: 模式1-小马洛克 我在Linux机器上运行以下程序:Malloc是否仅在请求的内存空间较大时才使用堆?,c,pointers,memory-management,malloc,heap,C,Pointers,Memory Management,Malloc,Heap,每当您研究进程的内存分配时,您通常会看到如下概述: 到目前为止还不错 但是,还有一个sbrk()系统调用,它允许程序更改其数据部分的上限,也可以使用它简单地检查sbrk(0)的限制在哪里。使用该函数,我发现了以下模式: 模式1-小马洛克 我在Linux机器上运行以下程序: #include <stdio.h> #include <stdlib.h> #include <unistd.h> int globalVar; int main(){
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int globalVar;
int main(){
int localVar;
int *ptr;
printf("localVar address (i.e., stack) = %p\n",&localVar);
printf("globalVar address (i.e., data section) = %p\n",&globalVar);
printf("Limit of data section = %p\n",sbrk(0));
ptr = malloc(sizeof(int)*1000);
printf("ptr address (should be on stack)= %p\n",&ptr);
printf("ptr points to: %p\n",ptr);
printf("Limit of data section after malloc= %p\n",sbrk(0));
return 0;
}
正如您所见,分配的内存区域正好位于旧数据段限制之上,在malloc之后,该限制被向上推,因此分配的区域实际上位于新数据段内
问题1:这是否意味着小型malloc将在数据段中分配内存,而根本不使用堆
模式2-大马洛克
如果在第15行增加请求的内存大小:
ptr = malloc(sizeof(int)*100000);
现在,您将看到以下输出:
localVar address (i.e., stack) = 0xbf93ba68
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x8b16000
ptr address (should be on stack)= 0xbf93ba6c
ptr points to: 0xb750b008
Limit of data section after malloc= 0x8b16000
正如您所看到的,数据段的限制没有改变,相反,分配的内存区域位于数据段和堆栈之间的间隙部分的中间。 问题2:这是实际使用堆的大型malloc吗
问题3:对这种行为有何解释?我觉得这有点不安全,因为在第一个示例(小malloc)中,即使在释放分配的内存后,您仍然可以使用指针并使用该内存,而不会出现seg故障,因为它将位于数据部分中,这可能导致难以检测的错误 使用规范更新:Ubuntu 12.04,32位,gcc版本4.6.3,Linux内核3.2.0-54-generic-pae更新2:下面罗德里戈的答案解开了这个谜团。也有帮助。首先,要绝对确定发生了什么,唯一的方法就是阅读
malloc
的源代码。或者更好,使用调试器逐步完成它
但无论如何,以下是我对这些事情的理解:
sbrk()
用于增加数据段的大小,好的。通常,您不会直接调用它,但它将由malloc()
的实现调用,以增加堆的可用内存malloc()
不从操作系统分配内存。它只是将数据部分分成几部分,并将这些部分分配给需要它们的人。您可以使用free()
将一件物品标记为未使用且可重新分配malloc()
使用mmap()
和私有的、非文件备份的选项来分配它们。因此,这些块在数据段之外。显然,在这样的块中调用free()
将调用munmap()
manmallopt
从中,您可以猜测访问空闲内存时会发生什么:
mmap()
,并且碰巧使用了相同的地址范围).data
部分(链接器的角度)。它还可能包括.bss
甚至.rdata
。对于没有任何意义的操作系统,它只是将程序的各个部分映射到内存中,而不考虑除标志(只读、可执行…)以外的内容sbrk()
增长cat
):
第一行是可执行代码(.text
部分)
第二行是只读数据(.rdata
部分)和一些其他只读部分
第三行是.data
+.bss
和其他一些可写部分
第四行是堆
接下来的几行,那些有名称的是内存映射文件或共享对象。那些没有名称的可能是大内存块(或者可能是私有匿名mmap,它们无法区分)
最后一行是堆栈!所有这些“如果填充Y发生,填充X会发生吗”如果不提及具体的实现,这些问题在理论上和实践上都是无法回答的。什么Linux?哪个编译器?哪个标准库实现?哪个CPU?@H2CO3,那么你是说你确定上述行为依赖于实现,而不是Linux内核的标准?因为如果这是那么Linux内核规范就不重要了,对吧?不管怎样,我都是为了完整起见才包含它们的。@H2CO3我同意。尽管如此,我还是觉得这种行为很奇怪(你不觉得吗?),让我们看看是否有人对此有更多的线索。我的理解是,
malloc
对用户空间中的堆进行内存管理-根据需要释放或请求操作系统中的内存块(即尝试减少昂贵的上下文切换)。我还认为malloc确实请求了该OS/hardward可占用的内存块。请记住,许多堆管理器将放置非常大的分配(一般来说)
localVar address (i.e., stack) = 0xbf93ba68
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x8b16000
ptr address (should be on stack)= 0xbf93ba6c
ptr points to: 0xb750b008
Limit of data section after malloc= 0x8b16000
$ cat /proc/self/maps
08048000-08053000 r-xp 00000000 00:0f 1821106 /usr/bin/cat
08053000-08054000 r--p 0000a000 00:0f 1821106 /usr/bin/cat
08054000-08055000 rw-p 0000b000 00:0f 1821106 /usr/bin/cat
09152000-09173000 rw-p 00000000 00:00 0 [heap]
b73df000-b75a5000 r--p 00000000 00:0f 2241249 /usr/lib/locale/locale-archive
b75a5000-b75a6000 rw-p 00000000 00:00 0
b75a6000-b774f000 r-xp 00000000 00:0f 2240939 /usr/lib/libc-2.18.so
b774f000-b7750000 ---p 001a9000 00:0f 2240939 /usr/lib/libc-2.18.so
b7750000-b7752000 r--p 001a9000 00:0f 2240939 /usr/lib/libc-2.18.so
b7752000-b7753000 rw-p 001ab000 00:0f 2240939 /usr/lib/libc-2.18.so
b7753000-b7756000 rw-p 00000000 00:00 0
b7781000-b7782000 rw-p 00000000 00:00 0
b7782000-b7783000 r-xp 00000000 00:00 0 [vdso]
b7783000-b77a3000 r-xp 00000000 00:0f 2240927 /usr/lib/ld-2.18.so
b77a3000-b77a4000 r--p 0001f000 00:0f 2240927 /usr/lib/ld-2.18.so
b77a4000-b77a5000 rw-p 00020000 00:0f 2240927 /usr/lib/ld-2.18.so
bfba0000-bfbc1000 rw-p 00000000 00:00 0 [stack]