【问题标题】:Does malloc() use brk() or mmap()?malloc() 使用 brk() 还是 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 进行编译。

标签: c memory-management malloc mmap sbrk


【解决方案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 阈值)。

【讨论】:

    猜你喜欢
    • 2021-01-09
    • 2019-09-10
    • 1970-01-01
    • 2013-01-25
    • 2017-11-05
    • 2014-02-02
    • 2015-03-21
    • 2021-03-05
    • 2014-07-20
    相关资源
    最近更新 更多