我正在研究mmap()。从手册中,我只知道在概念上文件被映射到虚拟地址空间,因此支持随机访问。但是,我也想知道映射文件是否立即加载到内存中?我猜这个内核按页面管理映射内存,并且按需加载它们,如果我只进行一些读写操作,只加载几页。这是对的吗?
答案 0 :(得分:22)
不,是的,也许吧。这取决于。
通常只调用mmap
意味着,对于您的应用程序,映射文件的内容将映射到其地址空间,就好像文件已加载到那里一样。或者,好像该文件确实存在于内存中,就好像它们是同一个(包括更改被写回磁盘,假设您具有写访问权限)。
不多也不少。它没有加载某些东西的概念,应用程序也不知道这意味着什么。
虽然虚拟内存系统让它看起来像那样,但应用程序并不真正了解内存等任何内容。应用程序可以“看到”(和访问)的内存可能或可能不对应于实际的物理内存,原则上这可以在任何时候更改,而无需事先警告,并且没有明显的原因(对您的应用程序来说很明显)。登记/> 除了由于页面错误而可能经历一小段延迟之外,应用程序(原则上)完全没有意识到发生任何此类事情,并且很少或根本没有控制它 1 。
应用程序通常会根据需要从映射文件(包括主可执行文件!)加载页面,因为遇到了故障。但是,操作系统通常会尝试推测性地预取数据以优化性能。
实际上,调用mmap
将立即从映射的开头开始到(异步)预取页面,直到某个实现指定的大小。原则上,对于小文件,这意味着答案是“是”,对于较大的文件,这将是“否”
但是,mmap
不会阻止等待readahead的完成,这意味着您无法保证在mmap
返回后立即将任何文件存入RAM中(并非您在任何情况下都有此保证)时间无论如何!)。就此而言,答案是“也许”。
在Linux下,我上次看的时候,默认的预取大小是31个块(~127k) - 但这可能已经改变,加上它是一个可调参数。触摸预取区域附近或末尾的页面时,会异步预取更多页面。
如果你暗示MADV_RANDOM
到madvise
,预取“不太可能发生”,在Linux下完全禁用预取。
另一方面,给出MADV_SEQUENTIAL
提示将从映射的开始开始异步预取“更积极”(并且可以更快地丢弃被访问的页面)。在Linux下,“更积极”意味着正常数量的两倍。
给出MADV_WILLNEED
提示建议(但不保证)尽快加载给定范围内的所有页面(因为您说要访问它们)。操作系统可能会忽略这一点,但在Linux下,它被视为一个订单而不是一个提示,直到进程的最大RSS限制和一个实现指定的限制(如果我没记错的话,是物理内存量的1/2) )。
请注意,MADV_DONTNEED
可以说是在Linux下错误地实现的。提示不是按照POSIX指定的方式解释的,即您可以暂时将页面分页,而是您的意思是丢弃它们。这对于只读映射的页面没有什么大的区别(除了一个小的延迟,你说没关系),但确定对其他一切都很重要。
特别是,使用MADV_DONTNEED
认为Linux会在操作系统将它们懒惰地写入磁盘后发布不需要的页面不是如何工作!您必须明确同步,或准备一个惊喜。
在调用readahead
之前调用了文件描述符mmap
(或者之前已经读过/写过文件),文件的内容实际上 立即进入RAM
但是,这只是一个实现细节(统一虚拟内存系统),并受系统内存压力的影响。
调用mlock
将 - 假设成功 2 - 立即将请求的页面加载到RAM中。它会阻塞,直到所有页面都存在,并且您可以保证页面将保留在RAM中,直到您解锁它们为止。
<小时/> 1 存在查询(
mincore
)特定范围内的任何或所有页面是否实际存在的功能,以及提示操作系统关于您希望在没有任何硬保证(madvise
)的情况下看到正在发生的事情,最后是强制有限的页面子集出现在内存(mlock
)中以实现特权进程的功能。
2 可能没有,因为缺乏特权和超额配额或物理RAM的数量。
答案 1 :(得分:2)
是的,mmap会创建一个映射。它通常不会读取您映射到内存中的任何内容。如果你希望这样做,你可以使用mlock / mlockall系统调用来强制内核将映射的内容读入RAM中。(/ p>
答案 2 :(得分:1)
是。 mmap
的整个点就是更有效地管理内存,而不仅仅是将所有东西都插入内存。
当然,任何给定的实现都可能在某些情况下决定一次性读取整个文件的效率更高,但对于调用mmap
的程序来说应该是透明的。
答案 3 :(得分:1)
默认情况下,mmap()仅配置映射并返回(快速)。
Linux(至少)具有选项MAP_POPULATE(请参阅“ man mmap”),该选项完全可以解决您的问题。