使用`madvise`将大内存映射归零

时间:2013-09-03 14:41:29

标签: c mmap virtual-memory

我有以下问题:

我通过mmapMAP_ANONYMOUS分配了一大块内存(多个GiB)。该块包含一个大的哈希映射,需要时不时地归零。并非整个映射可以在每一轮中使用(并非每个页面都有错误),因此memset不是一个好主意 - 需要太长时间。

快速完成此任务的最佳策略是什么?

madvise(ptr, length, MADV_DONTNEED);

保证我后续的任何访问都会提供新的空页?

来自Linux man madvise页面:

  

此调用不会影响应用程序的语义( MADV_DONTNEED 除外),但可能会影响其性能。内核可以自由地忽略这些建议。

     

...

     

MADV_DONTNEED

     

此范围内页面的后续访问将成功,但会导致从底层映射文件重新加载内存内容(请参阅mmap(2))或零填充按需页面以获取没有基础文件的映射

     

...

     

当前的Linux实现(2.4.0)将此系统调用视为命令而不是建议......

或者我是否需要munmap并重新重新映射该区域?

它必须在Linux上工作,理想情况下在OS X上具有相同的行为。

3 个答案:

答案 0 :(得分:8)

对于您的问题,有一个更容易解决的问题是相当便携:

mmap(ptr, length, PROT_READ|PROT_WRITE, MAP_FIXED|MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);

由于MAP_FIXED被允许因相当任意的特定于实现的原因而失败,因此如果返回memset,则回到MAP_FAILED是明智的。

答案 1 :(得分:1)

这种madvise行为当然不是标准行为,因此不可移植。

如果您想要归零的部分恰好位于映射的末尾,则可以使用ftruncate。你必须再介绍一步:

  1. shm_open为您的数据提供“持久”文件描述符
  2. ftruncate达到所需的尺寸
  3. mmap的FD
  4. 然后你总是可以

    1. munmap
    2. ftruncate简短
    3. ftruncate到你需要的实际长度
    4. mmap再次
    5. 然后你“重新映射”的部分将被初始化为零。

      但是还要记住,系统必须对页面进行归零。这可能比编译器为memset生成的内联函数更有效,但这不确定。

答案 2 :(得分:1)

在Linux上,您可以依赖MADV_DONTNEED对映射进行归零的匿名映射。但这并不便携 - madvise()本身并未标准化。 posix_madvise()是标准化的,但POSIX_MADV_DONTNEED 与Linux MADV_DONTNEED标志具有相同的行为 - posix_madvise()始终是建议性的,而不是影响应用程序的语义。