mmap问题,分配大量内存

mon*_*ing 13 c c++ memory mmap

我得到了一些我需要解析的大文件,人们一直在推荐mmap,因为这样可以避免在整个内存中分配整个文件.

但是看看'top'看起来我确实打开整个文件进入内存,所以我觉得我一定做错了.'顶级秀> 2.1演出'

这是一段代码片段,展示了我正在做的事情.

谢谢

#include <stdio.h>
#include <stdlib.h>
#include <err.h>
#include <fcntl.h>
#include <sysexits.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <cstring>
int main (int argc, char *argv[] ) {
  struct stat sb;
  char *p,*q;
  //open filedescriptor
  int fd = open (argv[1], O_RDONLY);
  //initialize a stat for getting the filesize
  if (fstat (fd, &sb) == -1) {
    perror ("fstat");
    return 1;
  }
  //do the actual mmap, and keep pointer to the first element
  p =(char *) mmap (0, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
  q=p;
  //something went wrong
  if (p == MAP_FAILED) {
    perror ("mmap");
    return 1;
  }
  //lets just count the number of lines
  size_t numlines=0;
  while(*p++!='\0')
    if(*p=='\n')
      numlines++;
  fprintf(stderr,"numlines:%lu\n",numlines);
  //unmap it
  if (munmap (q, sb.st_size) == -1) {
    perror ("munmap");
    return 1;
  }
  if (close (fd) == -1) {
    perror ("close");
    return 1;
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

pax*_*blo 39

不,你正在做的是文件映射到内存中.这与将文件实际读入内存有所不同.

如果你读它,你将不得不将整个内容传输到内存中.通过映射,您可以让操作系统处理它.如果您尝试读取或写入该存储区中的某个位置,操作系统将首先为您加载相关部分.除非需要整个文件,否则它不会加载整个文件.

这是您获得性能提升的地方.如果您映射整个文件但只更改一个字节然后取消映射它,您会发现根本没有太多的磁盘I/O.

当然,如果你触摸文件中的每个字节,那么是的,它将在某个时刻加载,但不一定在物理RAM中同时加载.但即使您预先加载整个文件也是如此.如果没有足够的物理内存来包含所有数据,操作系统将交换部分数据,以及系统中其他进程的内存.

内存映射的主要优点是:

  • 你推迟阅读文件部分直到需要它们(如果它们从不需要,它们就不会被加载).因此,加载整个文件时没有大的前期成本.它分摊了装载成本.
  • 写入是自动的,您不必写出每个字节.只需关闭它,操作系统就会写出更改的部分.我认为这也会在内存被换出时发生(在低物理内存情况下),因为你的缓冲区只是文件的一个窗口.

请记住,您的地址空间使用量与物理内存使用量之间很可能存在脱节.您可以在仅具有1G RAM的32位计算机中分配4G的地址空间(理想情况下,尽管可能存在操作系统,BIOS或硬件限制).操作系统处理来往磁盘的分页.

并回答您的进一步澄清要求:

只是为了澄清.那么如果我需要整个文件,mmap会实际加载整个文件吗?

是的,但它可能不会同时存在于物理内存中.操作系统会将位交换回文件系统以引入新位.

但如果你手动阅读整个文件,它也会这样做.这两种情况之间的区别如下.

手动将文件读入内存后,操作系统会将部分地址空间(可能包括数据或可能不包括)交换到交换文件中.完成后,您需要手动重写文件.

通过内存映射,您已经有效地告诉它将原始文件用作该文件/内存的额外交换区域.并且,当数据写入交换区域时,它会立即影响实际文件.因此,当您完成时不必手动重写任何内容,并且不会影响正常交换(通常).

它实际上只是文件的一个窗口:

                        内存映射文件映像


osg*_*sgx 5

您还可以使用 fadvise(2) (和 madvise(2),另请参阅 posix_fadvise 和 posix_madvise )将 mmaped 文件(或其部分)标记为只读。

#include <sys/mman.h> 

int madvise(void *start, size_t length, int advice);
Run Code Online (Sandbox Code Playgroud)

该建议在advice参数中指示,可以是

MADV_SEQUENTIAL 
Run Code Online (Sandbox Code Playgroud)

期望页面引用按顺序排列。(因此,可以积极地提前读取给定范围内的页面,并且可以在访问它们后立即释放它们。)

可移植性:posix_madvise 和 posix_fadvise 是 IEEE Std 1003.1, 2004 的 ADVANCED REALTIME 选项的一部分。常量为 POSIX_MADV_SEQUENTIAL 和 POSIX_FADV_SEQUENTIAL。