【发布时间】:2014-10-17 17:09:49
【问题描述】:
我正在重新编码 malloc 使用 mmap 函数。我正在使用最适合的算法,并且能够在一页中分配和取消分配。当我想分配小于页面大小的空间时,我的 malloc 函数运行良好。
但我不明白我应该如何处理大于页面大小的东西的分配?
【问题讨论】:
标签: c memory memory-management malloc
我正在重新编码 malloc 使用 mmap 函数。我正在使用最适合的算法,并且能够在一页中分配和取消分配。当我想分配小于页面大小的空间时,我的 malloc 函数运行良好。
但我不明白我应该如何处理大于页面大小的东西的分配?
【问题讨论】:
标签: c memory memory-management malloc
默认的 libc malloc 实现已经使用 mmap 和 MAP_ANONYMOUS 分配大量的单一内存分配。
为了演示,编译这个:
#include <stdio.h>
#include <stdlib.h>
int
main (int argc, char **argv)
{
void *i = malloc (100 * 1024 * 1024);
exit (0);
}
然后在strace下运行:
$ strace ./x 2>&1
execve("./x", ["./x"], [/* 21 vars */]) = 0
brk(0) = 0x135d000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7ad0fbb000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=105169, ...}) = 0
mmap(NULL, 105169, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f7ad0fa1000
close(3) = 0
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\200\30\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1811128, ...}) = 0
mmap(NULL, 3925176, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f7ad09dc000
mprotect(0x7f7ad0b91000, 2093056, PROT_NONE) = 0
mmap(0x7f7ad0d90000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1b4000) = 0x7f7ad0d90000
mmap(0x7f7ad0d96000, 17592, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f7ad0d96000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7ad0fa0000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7ad0f9f000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7ad0f9e000
arch_prctl(ARCH_SET_FS, 0x7f7ad0f9f700) = 0
mprotect(0x7f7ad0d90000, 16384, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f7ad0fbd000, 4096, PROT_READ) = 0
munmap(0x7f7ad0fa1000, 105169) = 0
mmap(NULL, 104861696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7aca5db000
exit_group(0) = ?
这一行……
mmap(NULL, 104861696, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f7aca5db000
...是大块的分配。
这就是您问题的答案。你给它分配一个大尺寸和MAP_PRIVATE|MAP_ANONYMOUS。
来自malloc 的手册页
通常,
malloc()从堆中分配内存,并调整 根据需要使用sbrk(2) 的堆大小。分配块时 大于MMAP_THRESHOLD字节的内存,glibcmalloc()实现将内存分配为私有匿名映射 使用mmap(2)。MMAP_THRESHOLD默认为 128 kB,但 使用mallopt(3)进行调整。使用mmap(2) 执行的分配是 不受RLIMIT_DATA资源限制的影响(请参阅getrlimit(2))。
来自mallopt 的手册页
M_MMAP_THRESHOLD当一个大于给定值的分配请求不能被一个现有的空闲块满足时,使用mmap()保证获得内存。较小的请求可能会分配给
mmap()或sbrk()。mmap()-分配的内存在被释放时可以立即返回给操作系统,但并非所有使用sbrk()分配的内存都是如此;但是,mmap()分配并随后释放的内存既没有加入也没有重用,因此开销更大。默认值:128*1024。
M_MMAP_MAX给定值设置在给定时间允许使用的
mmap()分配块的最大数量(即使分配请求的大小超过M_MMAP_THRESHOLD参数的值)。这在mmap()实现扩展性较差的系统上很有用。值 0 将禁用mmap()。默认值:65536。
如果您将M_MMAP_TRESHOLD 调低到零(我不知道它是否能工作那么远),我猜想每次分配都将通过mmap 完成,而无需您更改超过一行代码.但无论如何,您的问题的答案是mmap,其参数与上述类似。
请注意,可能不希望有太多单独的mmap'd 区域,在这种情况下,您需要在单个区域或多个区域中分配。在不更改地址的情况下扩展mmap 区域可能会很困难,这可能会使分配碎片时难以将内存返回给操作系统。
【讨论】: