在/proc/pid/maps中缺少[heap]部分。

10

我正在尝试一个简单的C程序,它不断地分配内存:

for ( i = 0; i < 10000; i ++ )
{
    array[i] = malloc( 1024*1024 );
    if ( array[i] == NULL )
    {
        break;
    }
    printf("Sleeping...%d\n", i);
    sleep( 60 );
}

完整代码在这里粘贴:http://tny.cz/2d9cb3df

但是,当我执行cat /proc/pid/maps时,我没有得到[heap]部分。为什么呢?

08048000-08049000 r-xp 00000000 08:11 17         /data/a.out
08049000-0804a000 r--p 00000000 08:11 17         /data/a.out
0804a000-0804b000 rw-p 00001000 08:11 17         /data/a.out
0804b000-0805e000 rw-p 00000000 00:00 0 
b74c6000-b75c8000 rw-p 00000000 00:00 0 
b75c8000-b7775000 r-xp 00000000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7775000-b7777000 r--p 001ad000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7777000-b7778000 rw-p 001af000 08:01 3539272    /lib/i386-linux-gnu/libc-2.17.so
b7778000-b777b000 rw-p 00000000 00:00 0 
b7797000-b779a000 rw-p 00000000 00:00 0 
b779a000-b779b000 r-xp 00000000 00:00 0          [vdso]
b779b000-b77bb000 r-xp 00000000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
b77bb000-b77bc000 r--p 0001f000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
b77bc000-b77bd000 rw-p 00020000 08:01 3539289    /lib/i386-linux-gnu/ld-2.17.so
bff21000-bff42000 rw-p 00000000 00:00 0          [stack]

编译器推断出for循环没有执行任何操作(即无操作),并将其优化掉了吗? - user529758
@H2CO3,我已经修改了代码以打印出分配的地址,但结果仍然相同。 - lang2
1
@H2CO3 看起来即使对于一个过度激进的优化编译器来说,这也是一个很大的飞跃,因为该循环包含三个调用具有相当大副作用的函数。 - Some programmer dude
@JoachimPileborg 嗯,好吧,说得也对。(但我也见过GCC和clang产生的“更糟糕”的优化...) - user529758
@Mat代码链接已添加。 - lang2
3个回答

10

考虑到您在谈论/proc/pid/maps,我猜您正在使用linux系统。

这是我所运行的linux发行版上man malloc给出的解释:

malloc()通常从堆中分配内存,并根据需要调整堆的大小,使用sbrk(2)

当分配大于MMAP_THRESHOLD字节的内存块时,glibc malloc()实现将该内存作为私有匿名映射分配,使用mmap(2)MMAP_THRESHOLD默认为128 KB,但可使用mallopt(3)进行调整。

使用mmap(2)执行的内存分配不受RLIMIT_DATA资源限制的影响(参见getrlimit(2))。

如果您真的想在/proc/pid/maps中看到[heap],请确保每次对malloc的调用分配的内存小于128k。


1

这个部分并未声明为“堆”,因为它是通过mmap()调用进行分配的。

在我的64位系统上,分配的内存来自不断增长的这个部分:

7f7fbda7a000-7f7fbdc7c000 rw-p 00000000 00:00 0
...
7f7fbc868000-7f7fbdc7c000 rw-p 00000000 00:00 0
...
7f7fbc363000-7f7fbdc7c000 rw-p 00000000 00:00 0

这个部分在开头进行扩展,新分配的内存总是从起始地址 + 0x10 开始。

内存分配器在这里的作用是

mmap(NULL, 1052672, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7fbb353000

奇怪的事情是,我本以为每个mmap()的内存区段会单独显示出来。但如果我让程序执行中间的free()调用,内存分配就会如下所示:
7f16be944000-7f16bec47000 rw-p 00000000 00:00 0
7f16bed48000-7f16bee49000 rw-p 00000000 00:00 0

在本来连续的内存中已经产生了一个洞。

还有一件事情发生了:在这个修改后的程序中,确实有一个[heap]部分。由于我无法理解的原因,程序会切换到通过brk()进行分配,这可能是堆的来源:

每次brk()调用时,相应的部分都会在其末尾扩展:

01183000-029a4000 rw-p 00000000 00:00 0                                  [heap]

我不知道是什么改变了malloc()的想法,使其使用与brk()相关联的“真实”堆(连接到“real” heap)进行分配。第一个munmap()调用与相应的free()有关,似乎会产生这种情况。
根据thismmap()的块似乎有一个最大值。如果超过了这个最大值,gnulibc会回退到在“常规堆区域”上操作的brk()/sbrk()
因此,简短的答案是:malloc()通过brk()获取的内存位于“real”[heap]部分,而mmap()的内存部分没有标记为[heap]

0

使用brk()调用进行动态分配。在程序加载期间计算.brk部分的起始位置(请查看/linux/fs/binfmt_elf.c中的load_elf_binary())。但是,在实际分配请求时,直到运行时才映射页面。下面是x86_64系统上进行分配之前和之后的/proc/pid/maps屏幕截图

调用calloc(8032,1)之前:

$ cat /proc/$(pid)/maps

00400000-00401000 r-xp 00000000 08:02 1314308             /testheap

00600000-00601000 r--p 00000000 08:02 1314308             /testheap

00601000-00602000 rw-p 00001000 08:02 1314308             /testheap

7fafeea3e000-7fafeebfb000 r-xp 00000000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

7fafeebfb000-7fafeedfb000 ---p 001bd000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

调用calloc(8032,1)后:

$cat /proc/$(pid)/maps

00400000-00401000 r-xp 00000000 08:02 1314308             /testheap

00600000-00601000 r--p 00000000 08:02 1314308             /testheap

00601000-00602000 rw-p 00001000 08:02 1314308             /testheap

**021ea000-0220c000 rw-p 00000000 00:00 0                   [heap]**

7fafeea3e000-7fafeebfb000 r-xp 00000000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so

7fafeebfb000-7fafeedfb000 ---p 001bd000 08:02 198420      /lib/x86_64-linux-gnu/libc-2.17.so


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接