mmap:将映射的文件立即加载到内存中吗?

我正在学习mmap()。 从手册中,我只知道在概念上文件映射到虚拟地址空间,所以支持随机访问。 但是,我也想知道映射文件是否立即加载到内存中? 我猜这个内核是通过页面来pipe理映射的内存的,而且它们是按需加载的,如果我只做几个读写操作,只加载几个页面。 这是对的吗?

不,是的,也许。 这取决于。

调用mmap 通常只意味着对于你的应用程序,映射文件的内容被映射到它的地址空间,就好像文件被加载到那里一样。 或者,如果文件真的存在于内存中,就好像它们是同一个文件(其中包括将更改写回到磁盘,假设您具有写访问权限)。

没有更多,不能少。 它没有载入内容的概念,应用程序也不知道这意味着什么。

尽管虚拟内存系统使得它看起来像这样,但是应用程序并没有真正的知道任何内存。 应用程序可以“看见”(和访问)的内存可能与实际的物理内存对应,也可能不对应实际的物理内存,原则上可以随时更改,无需事先警告,也没有明显的原因(对于您的应用程序来说显而易见)。
除了由于页面错误而可能经历的小的延迟之外,应用程序(原则上)完全不知道发生了这样的事情并且几乎没有或几乎没有对它的控制1

应用程序通常会根据需要从映射文件(包括主要可执行文件!)中加载页面,作为遇到错误的结果。 但是,操作系统通常会尝试预测数据来优化性能。

在实践中,调用mmap将立即开始 (异步)从映射的开始预取页面,直到某个实现指定的大小。 这意味着,对于小文件,原则上答案是“是”,而对于大文件,则是“不”。
但是, mmap不会阻塞等待readahead的完成,这意味着你不能保证任何文件在mmap返回后立即在RAM中(不是你在任何时候都有保证!)。 就此而言,答案是“也许”。

在Linux下,我上次查看时,默认的预取大小是31块(〜127k) – 但是这可能已经改变,加上它是一个可调参数。 当预读区域附近或结尾处的页面被触摸时,更多页面被异步地预取。
如果你已经暗示MADV_RANDOM了,预取“不太可能发生”,在Linux下,这完全禁用预取。

另一方面,提供MADV_SEQUENTIAL提示将从映射的开始开始“更积极地”异步预取(并且可以更快地丢弃访问的页面)。 在Linux下,“更积极”意味着正常金额的两倍。

给出MADV_WILLNEED提示建议(但不能保证)给定范围内的所有页面尽快加载(因为你说要访问它们)。 操作系统可能会忽略这个,但是在Linux下,它被视为一个命令而不是提示,直到进程的最大RSS限制和一个实现指定的限制(如果我没有记错的话,物理RAM的1/2 )。
请注意,在Linux下, MADV_DONTNEED可能被错误地执行。 这个提示并不是按照POSIX指定的方式来解释的,也就是说,你可以确定页面正在被分页,但是你的意思是丢弃它们 。 这对于只读映射的页面没有什么大的区别(除了一个小的延迟,你认为是可以的),但是对于其他所有的东西来说确实很重要
特别是在使用MADV_DONTNEED Linux会在操作系统懒洋洋地写入磁盘之后释放不需要的页面,这并不是怎么回事 ! 你必须明确地同步,或准备一个惊喜。

在调用mmap (或者之前已经读/写过文件)之前,在文件描述符上调用readahead ,实际上文件的内容实际上就是在RAM中。
但是,这只是一个实现细节(统一的虚拟内存系统),并且受到系统内存压力的影响。

调用mlock会 – 假设成功2 – 立即将请求的页面加载到RAM中。 它阻塞,直到所有的页面实际存在,并且你有保证,这些页面将留在内存,直到你解锁它们。


1有一些功能可以查询( mincore )在某一特定范围内的任何或所有页面是否实际存在,以及在没有任何硬性保证( madvise )的情况下向操作系统提示您希望看到的情况的功能,最后是强制页面的有限子集出现在内存( mlock )中的特权进程的功能。

2这可能不是由于缺乏特权,超出配额或物理内存的数量。

是的,mmap创建一个映射。 它通常不会读取您映射到内存中的所有内容。 如果你希望这样做,你可以使用mlock / mlockall系统调用来强制内核将映射的内容读入RAM(如果适用的话)。

是。 mmap在于,它能够更有效地管理内存,而不仅仅是将所有内容都存储在内存中。

当然,在某些情况下,任何给定的实现都可能决定一次读入整个文件更高效,但对于调用mmap的程序应该是透明的。