malloc() 使用 brk() 还是 mmap()?

Posted

技术标签:

【中文标题】malloc() 使用 brk() 还是 mmap()?【英文标题】:Does malloc() use brk() or mmap()? 【发布时间】:2015-08-13 01:53:11 【问题描述】:

c 代码:

// program break mechanism
// TLPI exercise 7-1

#include <stdio.h>
#include <stdlib.h>

void program_break_test() 
    printf("%10p\n", sbrk(0));

    char *bl = malloc(1024 * 1024);
    printf("%x\n", sbrk(0));

    free(bl);
    printf("%x\n", sbrk(0));



int main(int argc, char **argv) 
    program_break_test();
    return 0;

编译以下代码时:

 printf("%10p\n", sbrk(0));

我收到警告提示:

format ‘%p’ expects argument of type ‘void *’, but argument 2 has type ‘int’

问题 1: 为什么会这样?


在我malloc(1024 * 1024)之后,似乎程序中断没有改变。

这是输出:

9b12000
9b12000
9b12000

问题 2: 进程启动时是否在堆上分配内存以供将来使用?还是编译器改变了分配的时间点?否则,为什么呢?


[更新] 摘要:brk() 或 mmap()

在查看了 TLPI 并检查了手册页(在 TLPI 的作者的帮助下),现在我明白了malloc() 是如何决定使用brk()mmap() 的,如下所示:

mallopt()可以设置参数来控制malloc()的行为,有一个参数叫M_MMAP_THRESHOLD,一般来说:

如果请求的内存小于它,将使用brk(); 如果请求的内存大于或等于它,将使用mmap()

参数的默认值为128kb(在我的系统上),但是在我的测试程序中我使用了1Mb,所以选择了mmap(),当我将请求的内存更改为32kb时,我看到brk()会是用过。

这本书在 TLPI 第 147 页和第 1035 页中提到了这一点,但我没有仔细阅读那部分。

参数的详细信息可以在mallopt()的手册页中找到。

【问题讨论】:

#include &lt;unistd.h&gt;? @JS1 是的,解决了这个问题,你能解释一下吗,我是 linux 编程新手... 您需要sbrk() 的原型,它位于unistd.h 中。如果没有原型,编译器会假定未知函数返回 int @JS1 是的,我以为sbrk() 是在stdlib.h 中声明的,谢谢! @EricWang:如果您使用 -Wall 编译(假设您使用 gcc 或 clang),它会发出警告。您应该始终使用 -Wall 进行编译。 【参考方案1】:

如果我们更改程序以查看malloc'd 的内存在哪里:

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>

void program_break_test() 
  printf("%10p\n", sbrk(0));

  char *bl = malloc(1024 * 1024);
  printf("%10p\n", sbrk(0));
  printf("malloc'd at: %10p\n", bl);

  free(bl);
  printf("%10p\n", sbrk(0));



int main(int argc, char **argv) 
  program_break_test();
  return 0;

sbrk 不会改变可能更清楚一点。 malloc 给我们的内存被映射到一个完全不同的位置。

您还可以在 Linux 上使用 strace 查看进行了哪些系统调用,并发现 malloc 正在使用 mmap 执行分配。

【讨论】:

我发现有一个THRESHOLD来控制是使用brk()还是mmap(),我在问题中更新了。【参考方案2】:

malloc不限于使用sbrk分配内存。例如,它可能使用mmap 映射一个大的MAP_ANONYMOUS 内存块;通常mmap 会分配一个远离数据段的虚拟地址。

还有其他可能性。特别是malloc,作为标准库的核心部分,本身并不局限于标准库函数;它可以利用操作系统特定的接口。

【讨论】:

您是说mmap 可以分配比sbrk 更多的内存吗?如果是这样,sbrk 在哪里分配内存,mmap 在哪里分配内存? +1 -- 很好的答案。 sbrk 在特定位置分配内存;每次调用sbrk 时,您都会获得与上一次调用连续的一大块内存。从历史上看,这是堆和堆栈之间的边界(堆从数据段向上生长,堆栈从进程地址空间的末端向下生长)。 mmap 通常不能分配更多内存,但它绝对可以分配不同的地址和更多的选项(内存保护标志、后备存储、巨大的虚拟块(以减少页表大小)等) @petercordes:是的,我确实是这个意思。谢谢。【参考方案3】:

如果你在代码中使用malloc,它会在开头调用brk(),从堆中分配0x21000字节,就是你打印的地址,所以问题1:下面mallocs的要求可以从预先分配的空间相遇,所以这些mallocs实际上并没有调用brk,它是malloc中的优化。如果下次您想将 malloc 大小超出该边界,则会调用新的 brk(如果不大于 mmap 阈值)。

【讨论】:

以上是关于malloc() 使用 brk() 还是 mmap()?的主要内容,如果未能解决你的问题,请参考以下文章

malloc和free,brk和sbrk和mmap和munmap的使用和关系以及内存分配的原理

Linux 内核 内存管理内存管理架构 ② ( 用户空间内存管理 | malloc | ptmalloc | 内核空间内存管理 | sys_brk | sys_mmap | sys_munmap)

Linux 内核 内存管理内存管理架构 ④ ( 内存分配系统调用过程 | 用户层 malloc free | 系统调用层 brk mmap | 内核层 kmalloc | 内存管理流程 )

malloc是如何分配内存的

malloc是如何分配内存的

内存映射:小块内存申请brk和申请大块内存的Mmap分析