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 <unistd.h>
?
@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:下面malloc
s的要求可以从预先分配的空间相遇,所以这些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 | 内存管理流程 )