【问题标题】:Is there really no mremap in Darwin?达尔文真的没有mremap吗?
【发布时间】:2011-04-01 01:57:33
【问题描述】:

我正在尝试找出如何在 Mac 上重新映射内存映射文件(当我想扩展可用空间时)。

我看到我们在 Linux 世界的朋友有 mremap,但我在我的 Mac 上的标题中找不到这样的功能。 /Developer/SDKs/MacOSX10.6.sdk/usr/include/sys/mman.h 有以下内容:

  • mmap
  • mprotect
  • msync
  • munlock
  • munmap
  • 但没有mremap

man mremap 证实了我的恐惧。

如果我想调整映射文件的大小,我目前不得不munmapmmmap,这涉及使所有加载的页面无效。一定会有更好的办法。确定吗?

我正在尝试编写可在 Mac OS X 和 Linux 上运行的代码。如果我不得不,我可以选择一个宏来在每种情况下使用最好的功能,但我宁愿正确地做。

【问题讨论】:

    标签: c macos mmap memory-mapped-files libc


    【解决方案1】:

    如果您需要缩小地图,只需munmap末尾要删除的部分即可。

    如果你需要放大地图,你可以mmapMAP_FIXED到旧地图正上方的地址,但你需要注意不要映射到其他东西那已经在那里了……

    删除线下的上述文字是一个糟糕的主意; MAP_FIXED 根本上是错误的,除非您已经知道目标地址的内容并希望自动替换它。如果您试图在地址范围空闲的情况下随机映射新的东西,您需要使用mmap 与请求的地址但没有 MAP_FIXED 并查看它是否成功并为您提供请求的地址;如果它成功但使用不同的地址,您将需要取消映射刚刚创建的新映射,并假设无法在请求的地址进行分配。

    【讨论】:

    • 添加额外的映射区域是否存在无法在该地址分配的风险?
    • 是的,这就是我所警告的。但我相信MAP_FIXED 将始终映射到现有映射之上(破坏现有映射)而不是失败,这更糟。
    【解决方案2】:

    如果您以足够大的块进行扩展(例如,64 MB,但这取决于它的增长速度),那么使旧地图失效的成本可以忽略不计。与往常一样,在假设问题之前进行基准测试。

    【讨论】:

      【解决方案3】:

      您可以将文件 ftruncate 到一个大尺寸(创建一个孔)并将其全部映射。如果文件是持久的,我建议使用写入调用来填充漏洞,而不是写入映射,否则文件的块可能会在磁盘上不必要地碎片化。

      【讨论】:

      • 所以你的意思是我应该分配我可能想要的最大尺寸并填补这个洞?这是一个有趣的想法,但要么我绘制了最大可能的地址范围,并且不再为其他任何东西留下地址,要么我使用较少的地址并冒着用完的风险。此外,这不会是跨平台的(如我的问题所示),因为我不能保证某些文件系统实际上不会将文件的整个范围归零并浪费千兆字节。
      • 您甚至不需要使磁盘上的文件那么大。只是mmap 大于文件大小。超过文件末尾的访问将导致SIGBUS,因此在尝试通过mmap 访问新部分之前,您需要更长的时间ftruncate,否则没关系。
      • @R..GitHubSTOPHELPINGICE 你确定这适用于达尔文吗? POSIX 需要在使用 ftruncate 增长后再次执行 mmap
      • @osvein: 哦,确实没有说明会发生什么。使用ftruncate 新创建的部分的MAP_FIXED 应该可以工作。
      【解决方案4】:

      我没有内存映射的经验,但看起来您可以临时映射同一个文件两次,以扩展映射而不会丢失任何内容。

      int main() {
          int fd;
          char *fp, *fp2, *pen;
      
            /* create 1K file */
          fd = open( "mmap_data.txt", O_RDWR | O_CREAT, 0777 );
          lseek( fd, 1000, SEEK_SET );
          write( fd, "a", 1 );
      
            /* map and populate it */
          fp = mmap( NULL, 1000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
          pen = memset( fp, 'x', 1000 );
      
            /* expand to 8K and establish overlapping mapping */
          lseek( fd, 8000, SEEK_SET );
          write( fd, "b", 1 );
          fp2 = mmap( NULL, 7000, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 );
      
            /* demonstrate that mappings alias */
          *fp = 'z';
          printf( "%c ", *fp2 );
      
            /* eliminate first mapping */
          munmap( fp, 1000 );
      
            /* populate second mapping */
          pen = memset( fp2+10, 'y', 7000 );
      
            /* wrap up */
          munmap( fp2, 7000 );
          close( fd );
          printf( "%d\n", errno );
      }
      

      输出是zxxxxxxxxxyyyyyy....

      我想,如果您对此有所了解,可能会比使用mremap 更快地耗尽地址空间。但无论哪种方式都无法保证,另一方面它可能同样安全。

      【讨论】:

      • 如果我错了,请纠正我,但这不会导致 mmap MAP_PRIVATE 和 mremap 产生大量额外的 IO 吗?由于 MAP_SHARED 刷新写入磁盘?
      • @Eloff:唯一的办法就是尝试看看。这篇文章很老了,我从来没有太多的动力去做这个实验:vP。看到过多的刷新会有点令人惊讶,因为所有内存总是至少映射一次。操作系统应该只在最后一个映射被消除时才刷新,对吧?
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-12-05
      • 2015-12-04
      • 2016-05-11
      • 1970-01-01
      • 2018-01-05
      • 2017-11-05
      相关资源
      最近更新 更多