每当你研究进程的内存分配时,你通常会看到它如下所示:
到目前为止一切顺利。
但是你有sbrk()系统调用允许程序改变其数据部分的上限,它也可以用来简单地检查sbrk的限制在哪里( 0)。使用该功能,我发现了以下模式:
模式1 - 小型malloc
我在Linux机器上运行以下程序:
#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;
}
输出如下:
localVar address (i.e., stack) = 0xbfe34058
globalVar address (i.e., data section) = 0x804a024
Limit of data section = 0x91d9000
ptr address (should be on stack)= 0xbfe3405c
ptr points to: 0x91d9008
Limit of data section after malloc= 0x91fa000
正如您所看到的,已分配的内存区域位于旧数据部分限制之上,并且在向上推动限制的malloc之后,因此分配的区域实际上位于新数据部分内。
问题1 :这是否意味着小型malloc将在数据部分分配内存而根本不使用堆?
模式2 - Big Malloc
如果在第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:罗德里戈的回答解决了这个谜团。 This Wikipedia link也有帮助。
答案 0 :(得分:13)
首先,绝对确定发生的事情的唯一方法是阅读malloc
的源代码。或者甚至更好,使用调试器逐步完成它。
但无论如何,这是我对这些事情的理解:
sbrk()
用于增加数据部分的大小,可以。通常,您不会直接调用它,但它将由malloc()
的实现调用,以增加堆的可用内存。malloc()
不从OS分配内存。它只是将数据部分分成几部分,并将这些部分分配给任何需要它们的人。您使用free()
将一件作品标记为未使用且可用于重新分配。malloc()
使用mmap()
使用私有的非文件支持选项分配它们。因此,这些块在数据段之外。显然,在这样的区块中调用free()
会调用munmap()
。什么是大块取决于许多细节。有关详细信息,请参阅man mallopt
。
由此可以猜出当你访问free'd memory时会发生什么:
mmap()
并且恰好使用相同的地址范围。<强>澄清强>
术语数据部分使用两种不同的含义,具体取决于上下文。
.data
部分(链接器角度)。它还可能包含.bss
甚至.rdata
。对于没有任何意义的操作系统,它只是将程序的各个部分映射到内存中,而不考虑除了标志之外的内容(只读,可执行......)。sbrk()
增长。您可以看到使用以下命令打印简单程序(cat
)的内存布局:
$ 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]
第一行是可执行代码(.text
部分)。
第二行是只读数据(.rdata
部分)和其他一些只读部分。
第三行是.data
+ .bss
以及其他一些可写部分。
第四行是堆!
下一行,具有名称的行是内存映射文件或共享对象。那些没有名字的人可能是大型的malloc'ed内存块(或者也许是私人匿名mmap,他们无法区分)。
最后一行是堆栈!