Mmap()一个完整的大文件

Eme*_*mer 64 c mmap

我正在尝试使用以下代码(test.c)"mmap"二进制文件(~8Gb).

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#define handle_error(msg) \
  do { perror(msg); exit(EXIT_FAILURE); } while (0)

int main(int argc, char *argv[])
{
   const char *memblock;
   int fd;
   struct stat sb;

   fd = open(argv[1], O_RDONLY);
   fstat(fd, &sb);
   printf("Size: %lu\n", (uint64_t)sb.st_size);

   memblock = mmap(NULL, sb.st_size, PROT_WRITE, MAP_PRIVATE, fd, 0);
   if (memblock == MAP_FAILED) handle_error("mmap");

   for(uint64_t i = 0; i < 10; i++)
   {
     printf("[%lu]=%X ", i, memblock[i]);
   }
   printf("\n");
   return 0;
}
Run Code Online (Sandbox Code Playgroud)

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

虽然这适用于小文件,但是当我尝试加载一个大文件时,我会遇到分段错误.该程序实际返回:

Size: 8274324021 
mmap: Cannot allocate memory
Run Code Online (Sandbox Code Playgroud)

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

bdo*_*lan 63

MAP_PRIVATE映射需要内存预留,因为写入这些页面可能会导致写入时复制分配.这意味着你不能映射比物理ram + swap大得多的东西.请尝试使用MAP_SHARED映射.这意味着对映射的写入将反映在磁盘上 - 因此,内核知道它总是可以通过写回来释放内存,所以它不会限制你.

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

至少PROT_WRITE,这恰好适用于x86,因为x86不支持只写映射,但可能导致其他平台上的段错误.请求PROT_READ|PROT_WRITE- 或者,如果您只需要阅读,PROT_READ.

在我的系统(VPS与676MB RAM,256MB交换),我再现了你的问题; 更改为MAP_SHARED导致EPERM错误(因为我不允许写入打开的支持文件O_RDONLY).更改为PROT_READMAP_SHARED允许映射成功.

如果您需要修改文件中的字节,一个选项是将您要写入的文件的范围设为私有.也就是说,munmap并重新映射MAP_PRIVATE您要写入的区域.当然,如果您打算写入整个文件,那么您需要8GB内存才能这样做.

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


dco*_*les 6

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

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

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