Malloc是否仅在请求的内存空间较大时才使用堆?

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(){

每当您研究进程的内存分配时,您通常会看到如下概述:

到目前为止还不错

但是,还有一个sbrk()系统调用,它允许程序更改其数据部分的上限,也可以使用它简单地检查sbrk(0)的限制在哪里。使用该函数,我发现了以下模式:

模式1-小马洛克

我在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;
}
正如您所见,分配的内存区域正好位于旧数据段限制之上,在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()
    将一件物品标记为未使用且可重新分配
  • 第二点过于简单化。至少在GCC实现中,对于大的块,
    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]