Mmap()整个大文件


69

我试图使用以下代码(test.c)“映射”一个二进制文件(〜8Gb)。

使用gcc -std=c99 test.c -o testfile返回的test编译test.c :test: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.15, not stripped

尽管这对于较小的文件来说效果很好,但在尝试加载较大的文件时却出现了分段错误。该程序实际上返回:

我设法使用boost :: iostreams :: mapped_file映射了整个文件,但我想使用C和系统调用来实现。我的代码有什么问题?


4
您需要打开带有O_LARGEFILE标志的文件。检查手册。不确定是否可以mmap编辑4GB以上的文件。
phoxis

2
在这里无法复制。您的代码可以在9G文件上正常运行。您有多少(RAM + SWAP)?您当前的/ proc / sys / vm / overcommit_memory策略是什么?

@Mat $ free -m #Mem {总计:1984,使用的:1923,免费60}交换{总计:2021,使用的:0,免费:2021} $ cat / proc / sys / vm / overcommit_memory #returns 0
Emer

1
@phoxis在32位计算机上需要该标志。参考
Emer

@Emer:对不起,我刚刚注意到x86_64
phoxis

Answers:


72

MAP_PRIVATE映射需要保留内存,因为写入这些页面可能会导致写时复制分配。这意味着您不能映射比物理ram + swap更大的东西。尝试改用MAP_SHARED映射。这意味着对映射的写入将反映在磁盘上-因此,内核知道通过执行回写操作始终可以释放内存,因此它不会限制您。

我还注意到,您正在使用进行映射PROT_WRITE,但是接着继续阅读内存映射。您还打开了文件O_RDONLY-这本身可能是您遇到的另一个问题;你必须指定O_RDWR,如果你想使用PROT_WRITEMAP_SHARED

至于PROT_WRITE只,这种情况发生在x86的工作,因为86不支持只写映射,但可能会导致在其他平台上段错误。请求PROT_READ|PROT_WRITE-或(如果您只需要阅读)PROT_READ

在我的系统上(具有676MB RAM的VPS,256MB交换空间),我重现了您的问题;更改为会MAP_SHARED导致EPERM错误(因为不允许我写入以开头的备份文件O_RDONLY)。更改为PROT_READMAP_SHARED允许映射成功。

如果您需要修改文件中的字节,一种选择是仅将要写入的文件范围设为私有。也就是说,munmap然后重新映射MAP_PRIVATE您要写入的区域。当然,如果要写入整个文件,则需要8GB的内存。

或者,您可以写信1/proc/sys/vm/overcommit_memory。这将使映射请求成功;但是,请记住,如果您实际上尝试使用完整的8GB COW内存,则OOM杀手会杀死您的程序(或其他程序!)。


嗯。Mat设法将8 * 3G(23G)与16G虚拟空间映射在一起。因此,它并没有真正的保留内存..
卡罗利·霍瓦斯

1
@yi_H,默认情况下,Linux内核允许您超出物理+交换一定百分比:opsmonkey.blogspot.com/2007/01/linux-memory-overcommit.html根据您的系统配置,您可能会将其设置为更严格的配置。或者,如果总共只有4G,则可能会超出限制。
bdonlan

我知道,检查他写了什么。
卡洛里·霍瓦斯

是的,我注意到了O_RDONLY和PROT_WRITE。但是,我需要交换映射文件的字节而不修改它。我尝试使用PROT_READ和MAP_PRIVATE,但没有用。谢谢您的回答
Emer

@Emer,请参阅我的编辑。如果您需要交换8GB的文件,则需要8GB的内存+交换来存储结果。
bdonlan

4

您没有足够的虚拟内存来处理该映射。

举例来说,我在这里有一台具有8G RAM和〜8G交换空间(因此总可用虚拟内存为16G)的机器。

如果我在〜8G的VirtualBox快照上运行您的代码,则可以正常工作:

现在,如果我放弃交换,那么我剩下的总内存为8G。(不要在活动服务器上运行它。)结果是:

因此,请确保您有足够的虚拟内存来保存该映射(即使您仅触摸该文件中的几页)。


您可以检查是否可以映射两个具有8 + 8虚拟空间的8G文件吗?即使某些页面可能永远都不会加载或修改(在这种情况下,可以将它们简单地删除),但它需要空间还是有点奇怪。
卡罗里·霍瓦斯

我可以mmap使用(8G + 8G)虚拟内存提供许多小于16G的文件(三个文件最多可以存储约23G)。我无法仅使用8G映射单个> 8G文件。

感谢您对其进行测试。有没有一种方法可以计算出映射文件所需的虚拟内存的确切数量?还是取决于许多因素?
Emer

2
@ Mat,0并非“总是过量使用”;1是
bdonlan

@bdonlan:您是对的,对不起-整个评论都错了
Mat

4

Linux(显然还有其他一些UNIX系统)具有mmap(2)MAP_NORESERVE标志,该标志可用于显式启用交换空间过量使用。当您希望映射的文件大于系统上可用的可用内存量时,这很有用。

当与一起使用MAP_PRIVATE并且仅打算写入内存映射范围的一小部分时,这特别方便,因为否则这将触发整个文件的交换空间保留(或者ENOMEM,如果尚未进行系统范围的过量使用,则将导致系统返回)已启用,则超出了系统的可用内存)。

需要注意的问题是,如果您确实写入了该内存的很大一部分,则惰性交换空间保留可能会导致您的应用程序消耗所有可用的RAM并在系统上进行交换,最终触发OOM杀手(Linux)或导致您的应用收到SIGSEGV

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.