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