为什么从 mmap 中取消引用指针会导致 top 报告的内存使用量增加?

Why does dereferencing pointer from mmap cause memory usage reported by top to increase?

我正在使用 MAP_SHARED 和 PROT_READ 调用 mmap() 以访问大小约为 25 GB 的文件。我注意到推进返回的指针对应用程序顶部的 %MEM 没有影响,但是一旦我开始在不同位置取消引用指针,内存就会急剧增加并达到 55% 的上限。一旦调用 munmap,该值将回落至 0.2%。

我不知道我是否应该相信 55% 的价值最高报告。它似乎并没有实际使用可用的 16 GB 中的 8 GB。我应该担心吗?

当你第一次映射文件时,它所做的只是保留地址space,如果你不传递MAP_POPULATE(OS,它不一定从文件中读取任何内容=] 可能会做一些预取,这不是必需的,而且通常直到你开始 reading/writing).

当您第一次读取给定的内存页时,会触发 page fault。这个“无效页面错误”大多数人听到这个名字想到的是:

  1. 一个小错误-数据已经加载到内核中,但是需要建立该地址到加载数据的用户space映射(快速)
  2. 一个重大错误-数据根本没有加载,内核需要为数据分配一个页面,从磁盘填充它(慢),然后执行相同的映射到用户space如小故障案例

您看到的行为可能是由于映射文件太大而无法与其他想要驻留的所有内容一起放入内存,因此:

  1. 首次映射时,初始页面尚未映射到进程(其中一些可能在内核缓存中,但除非它们链接到进程的地址,否则不会向进程收费space 由轻微的页面错误)
  2. 你从文件中读取,导致了小错误和大错误,直到你填满主 RAM
  3. 一旦您填满了主 RAM,新页面中的错误通常会导致旧页面之一被丢弃(您没有像 OS 那样使用所有页面,而其他进程正在使用它们的页面,因此低 activity 页面,尤其是那些可以免费删除而不是写入 page/swap 文件的页面,是丢弃的理想页面),因此您的内存使用量稳定(对于读入的每个页面,你再丢一个)
  4. 当您 munmap 时,针对您的进程的记帐将被删除。许多页面可能仍在内核缓存中,但除非它们很快被重新映射和再次访问,否则它们很可能首先在斩波块上丢弃,如果其他东西请求内存

正如评论者所指出的,共享内存映射文件统计变得 很奇怪 ;每个进程都对内存“收费”,但即使没有其他进程映射它,它们也会将其全部报告为共享,因此区分“共享”是不切实际的,因为它是 MAP_SHARED 并由内核缓存支持,但没有其他人将其映射,因此它实际上由该进程唯一拥有”来自“共享,因为 N 个进程正在映射相同的数据,累积报告 shared_amount * N 使用情况,但实际上只消耗 shared_amount 内存总量(加上一个微不足道的维护每个映射的每个进程页表)。如果计数不一致,没有理由担心。