Linux下对mmap映射的大文件进行窗口控制。

4

当我们映射一个大文件时,如何控制RSS中的窗口?现在让我解释一下我的意思。

例如,我们有一个超过RAM几倍的大文件,我们为几个进程进行共享内存映射,如果我们访问一些虚拟地址位于此映射内存中的对象并捕获页面错误,那么从磁盘读取,子问题是,如果我们不再使用给定的对象,是否会发生相反的情况?如果这种情况像LRU一样发生,那么LRU的大小是多少,如何控制它?在这种情况下,页面缓存如何参与?

RSS图表

这是测试实例(2个线程,8 GB RAM)上80 GB tar文件的RSS图表。这个3800 MB的值从哪里来,并且在我映射文件后运行时保持稳定?我如何控制它(或建议内核控制它)?

1个回答

2
只要您没有采取明确的行动来锁定内存中的页面,它们应该最终会自动交换出去。内核基本上使用内存压力启发式来决定将多少物理内存用于换入的页面,并根据需要频繁地重新平衡。
如果您想在控制此过程方面发挥更积极的作用,请查看{{link1:madvise()系统调用}}。
这使您可以为您的mmap调整分页算法,采取以下操作:
  • MADV_FREE(自Linux 4.5起)

    • 应用程序不再需要addr和len指定的范围内的页面。内核因此可以释放这些页面,但释放可能会延迟到发生内存压力时。
  • MADV_COLD(自Linux 5.4起)

    • 停用给定范围的页面。这将使页面成为更可能的回收目标,如果存在内存压力,则会回收。
  • MADV_SEQUENTIAL

    • 期望按顺序引用页面。(因此,给定范围内的页面可以被积极地预读,并且在访问后不久可能会被释放。)
  • MADV_WILLNEED

    • 期望在不久的将来进行访问。(因此,最好提前读取一些页面。)
  • MADV_DONTNEED

    • 不期望在不久的将来进行访问。(暂时,应用程序已经完成了给定范围的操作,因此内核可以释放与之相关的资源。)
在创建了 mmap 之后发出 madvise(MADV_SEQUENTIAL) 可能足以获得可接受的行为。如果不行,您还可以在遍历时穿插一些 MADV_WILLNEED/MADV_DONTNEED 访问提示(和/或 MADV_FREE/MADV_COLD),以便在通过页面组时进行操作。

2
谢谢你的回答!但是我不能控制窗口本身的宽度(例如,最大值或最小值),对吧?(是的,C++开发人员想要控制一切)也就是说,我的例子中的这3800 MB,由于不同线程/进程频繁访问,可能会变成4500 MB甚至更多。理想情况下,我想设置一个上限,比如5000 MB,并确保它不会超过这个值,同时设置一个下限,比如3500 MB,知道在这个值以下不会进行页面上传。 - Vladislav Markov
如果我们谈论一个真实的例子,那么我观察到其中一台服务器上存在一种内存“积累”,有时候当页面加载到RAM中并且之后没有被加载回来时会出现急剧上升,但是该问题服务器的RPS并没有改变(相对于其他类似的服务器,该问题服务器的RPS通常较高)。我认为为服务器分配更多的内存不是合理的选择,相反,我需要限制分页。 - Vladislav Markov
2
@VladislavMarkov 内核管理物理内存的详细信息非常复杂,并且严重依赖于内核版本。请参阅此文章以获取一些示例。 - Dan Bonachea
@VladislavMarkov,你想要详细的系统范围控制有具体原因吗?从用户级别编程上来说,madvise调用可能是你能做到的最好的。如果你想改变整个系统的分页行为,你可能需要root权限重新配置内核,这种类型的问题更适合在http://serverfault.com上讨论。 - Dan Bonachea
1
明白了,我有一个关于 madvice 的小问题 - 我应该在 mmap 后调用 MADV_DONTNEED/MADV_FREE 一次来设置某些策略,还是只有当我不需要这些页面时才调用它们? 至于控制 - 我理解我的程序通常有足够的内存,但页缓存占用了太多的内存,并且存储了通常情况下可能不会很快需要的内存。因此,在我的情况下,具有最大缓存大小的 LRU 算法很适合。 - Vladislav Markov
1
通常在程序使用完页面后,会调用MADV_DONTNEED/MADV_FREE来释放一系列页面。因此,在您的情况下,程序可以读取前N个页面,然后通过madvise命令内核来释放这些页面。如果您正在寻找一次性在mmap时间调用的内容,则应该使用MADV_SEQUENTIAL,它告诉内核为顺序访问调整分页启发式算法。 - Dan Bonachea

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接