如果请求的内存空间很大,Malloc是否只使用堆?

时间:2013-10-10 19:43:51

标签: c pointers memory-management malloc heap

每当你研究进程的内存分配时,你通常会看到它如下所示:

enter image description here

到目前为止一切顺利。

但是你有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也有帮助。

1 个答案:

答案 0 :(得分:13)

首先,绝对确定发生的事情的唯一方法是阅读malloc的源代码。或者甚至更好,使用调试器逐步完成它。

但无论如何,这是我对这些事情的理解:

  1. 系统调用sbrk()用于增加数据部分的大小,可以。通常,您不会直接调用它,但它将由malloc()实现调用,以增加堆的可用内存。
  2. 函数malloc()不从OS分配内存。它只是将数据部分分成几部分,并将这些部分分配给任何需要它们的人。您使用free()将一件作品标记为未使用且可用于重新分配。
  3. 第2点过于简单化。至少GCC实现,对于大块,malloc()使用mmap()使用私有的非文件支持选项分配它们。因此,这些块在数据段之外。显然,在这样的区块中调用free()会调用munmap()
  4. 什么是大块取决于许多细节。有关详细信息,请参阅man mallopt

    由此可以猜出当你访问free'd memory时会发生什么:

    1. 如果块,内存仍然存在,所以如果你读不了就会发生。如果你写它,你可能会破坏内部堆结构,或者它可能已被重用,你可以破坏任何随机结构。
    2. 如果块很大,则内存已取消映射,因此任何访问都将导致分段错误。除非在过渡期间出现不可能的情况,否则会分配另一个大块(或另一个线程调用mmap()并且恰好使用相同的地址范围。
    3. <强>澄清

      术语数据部分使用两种不同的含义,具体取决于上下文。

      1. 可执行文件的.data部分(链接器角度)。它还可能包含.bss甚至.rdata。对于没有任何意义的操作系统,它只是将程序的各个部分映射到内存中,而不考虑除了标志之外的内容(只读,可执行......)。
      2. ,即每个进程拥有的内存块,不是从可执行文件读取的,可以使用sbrk()增长。
      3. 您可以看到使用以下命令打印简单程序(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,他们无法区分)。

        最后一行是堆栈!