【发布时间】:2014-07-24 17:09:46
【问题描述】:
有非阻塞mlock()这样的东西吗?在交通繁忙的情况下,我不希望我的线程阻塞等待 I/O。我宁愿使用 mlock() 从 mmap() 的文件中告诉 Linux 内核我需要哪个区域,然后在获取页面时得到通知。 (据我所知,标准的 mlock() 调用是阻塞的。)
【问题讨论】:
有非阻塞mlock()这样的东西吗?在交通繁忙的情况下,我不希望我的线程阻塞等待 I/O。我宁愿使用 mlock() 从 mmap() 的文件中告诉 Linux 内核我需要哪个区域,然后在获取页面时得到通知。 (据我所知,标准的 mlock() 调用是阻塞的。)
【问题讨论】:
mlock 接口似乎没有任何你想要的内置功能,所以我认为实现它的唯一方法是使用单独的线程来执行mlock 并让该线程通知你(通过条件变量、信号量或其他机制)当mlock 返回时。显然这会产生一些开销,但如果您的目标是获得实时延迟保证而不是改善整体运行时间/平均延迟,那么它仍然是一个明显的胜利。
当然,除非您使用mlockall,否则很难做出任何实时假设,因为您的代码可能会被换出。因此,使用mlockall 和 POSIX AIO(或类似但更清洁的 API 系统在线程方面自己实现)进行读取而不是使用mmap 可能更有意义。那么你就有了一个硬性保证,一旦你的数据被提取,它就不能被换出。
【讨论】:
我相信您想要madvise() 或posix_madvise() 和mincore() 的组合。
您将使用madvise 调用向内核请求MADV_WILLNEED。然后您必须使用mincore 进行轮询,以检查页面是否已读入内存。
如果系统的内存负载过重,madvise 调用可能永远不会读入页面,因此您需要超时并退回到阻塞读取模式。
【讨论】:
madvise 是最好的选择,没有mincore。在大多数情况下,让操作系统提前几毫秒加载页面应该可以工作,如果它不这样做,那么你无论如何都会感到不知所措(那么页面错误也可以,效果相同)。只需要记住之后将页面重置为MADV_NORMAL,否则一段时间后您的完整地址空间将设置为“将需要”,并且它变得无用。
如果您 mmap() 一个文件,那么您将该文件视为内存,VM 页面输入/输出文件取决于您的使用情况,这可能受益于预读。
有多种方法可以进行非阻塞 I/O(asyncio、poll、select、epoll),但 mlock() 是在 RAM 中保留一个内存区域,不允许它被分页到交换分区;尽管在严重的内存压力下内核可能不会遵守这一点。
最有可能,mmap(2) 就足够了,就好像您正在使用内存页面一样,它们不会被选择用于分页,但无论如何都会保留在内存中,因此请考虑这个问题是否是过早的优化,内核会努力提供(默认)良好的性能。
【讨论】: