malloc()是否使用brk()或mmap()?

时间:2015-05-30 04:56:51

标签: c memory-management malloc mmap sbrk

c code:

// 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:进程在启动时是否会在堆上分配内存以供将来使用?或者编译器改变分配的时间点?否则,为什么?

[update]摘要:brk()或mmap()

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

mallopt()可以设置参数来控制malloc()的行为,并且通常会有一个名为M_MMAP_THRESHOLD的参数:

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

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

书中提到TLPI第147页和第1035页,但我没有仔细阅读该部分。

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

4 个答案:

答案 0 :(得分:12)

如果我们更改程序以查看malloc&#39; 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执行分配。

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

  

格式'%p'需要'void *'类型的参数,但参数2的类型为'int'

对问题1的回答:编译器告诉你参数应该是void *,但你提供的是int。如果你花五秒钟阅读并理解错误,这应该是显而易见的。你有什么不明白的地方吗?如果是这样,请问一个更加详细的问题,让您感到困惑,而不是&#34;为什么会这样?&#34; ......

printf("%x\n", sbrk(0));应发生类似警告,因为根据the manual%x预计会对应unsigned参数。另外,根据手册:

  

如果任何参数不是相应转换规范的正确类型,则行为未定义。

通常,我们应该努力编写在任何系统上以相同方式工作的程序。为此,我们需要建立一套规则。因此,会提供警告,告诉您违反规则并调用未定义的行为。尽管您的行为未定义,但您的代码可能正如您希望的那样在您的系统上,此时 ...但是,这不应该是可以依赖,因为在未来的某个时候,您的计算机可能会获取一个更新,导致您的代码以微妙但破坏性的方式破解,或者可能无法在其他计算机上运行...或者可能只是选择那个时间

问题2,3和4的答案:

  

进程在开始供将来使用时是否在堆上分配内存?

没有要求堆积&#39;存在于标准C的规则之内,因此,至少,直到你告诉我们你正在使用哪个编译器/标准库。

  

或者编译器改变分配的时间点?

可能。允许编译器执行甚至可能消除您的分配的优化,只要他们可以推断它是安全的(例如可观察行为不是&#39;改变了。)

  

否则,为什么?

好问题。

为什么我们有规则可以自由地考虑某些未定义的行为并允许它运行,即使在程序员的危险中也是如此?优化

为什么分配的内存是在上还是其他方面?你为什么要关心?只要你的记忆被分配,对吗?只要你能使用它,它就会相当快。优化

为什么编译器会执行优化?我会留下那个给你回答;)

答案 3 :(得分:0)

如果您在代码中使用malloc,它将在开头调用brk(),从堆中分配了0x21000字节,这是您打印的地址,因此问题1:可以满足以下mallocs要求:分配的空间,因此这些malloc实际上并未调用brk,这是malloc中的优化。如果下次您要分配超出该范围的大小,则将调用一个新的brk(如果不大于mmap阈值)。