【问题标题】:Non-blocking mlock()非阻塞 mlock()
【发布时间】:2014-07-24 17:09:46
【问题描述】:

有非阻塞mlock()这样的东西吗?在交通繁忙的情况下,我不希望我的线程阻塞等待 I/O。我宁愿使用 mlock() 从 mmap() 的文件中告诉 Linux 内核我需要哪个区域,然后在获取页面时得到通知。 (据我所知,标准的 mlock() 调用是阻塞的。)

【问题讨论】:

    标签: c++ c linux file-io


    【解决方案1】:

    mlock 接口似乎没有任何你想要的内置功能,所以我认为实现它的唯一方法是使用单独的线程来执行mlock 并让该线程通知你(通过条件变量、信号量或其他机制)当mlock 返回时。显然这会产生一些开销,但如果您的目标是获得实时延迟保证而不是改善整体运行时间/平均延迟,那么它仍然是一个明显的胜利。

    当然,除非您使用mlockall,否则很难做出任何实时假设,因为您的代码可能会被换出。因此,使用mlockall 和 POSIX AIO(或类似但更清洁的 API 系统在线程方面自己实现)进行读取而不是使用mmap 可能更有意义。那么你就有了一个硬性保证,一旦你的数据被提取,它就不能被换出。

    【讨论】:

      【解决方案2】:

      我相信您想要madvise()posix_madvise()mincore() 的组合。

      您将使用madvise 调用向内核请求MADV_WILLNEED。然后您必须使用mincore 进行轮询,以检查页面是否已读入内存。

      如果系统的内存负载过重,madvise 调用可能永远不会读入页面,因此您需要超时并退回到阻塞读取模式。

      【讨论】:

      • +1 我什至会说单独使用madvise 是最好的选择,没有mincore。在大多数情况下,让操作系统提前几毫秒加载页面应该可以工作,如果它不这样做,那么你无论如何都会感到不知所措(那么页面错误也可以,效果相同)。只需要记住之后将页面重置为MADV_NORMAL,否则一段时间后您的完整地址空间将设置为“将需要”,并且它变得无用。
      • @Damon:但这是对 mlock 的参考,这是一种最终的“将需要”。此外,我相当确定一旦页面加载到 RAM 中,Linux 内核就会忽略/丢弃 WILLNEED 标志。我不确定这是否适用于所有 POSIX 系统。
      【解决方案3】:

      如果您 mmap() 一个文件,那么您将该文件视为内存,VM 页面输入/输出文件取决于您的使用情况,这可能受益于预读。

      有多种方法可以进行非阻塞 I/O(asyncio、poll、select、epoll),但 mlock() 是在 RAM 中保留一个内存区域,不允许它被分页到交换分区;尽管在严重的内存压力下内核可能不会遵守这一点。

      最有可能,mmap(2) 就足够了,就好像您正在使用内存页面一样,它们不会被选择用于分页,但无论如何都会保留在内存中,因此请考虑这个问题是否是过早的优化,内核会努力提供(默认)良好的性能。

      【讨论】:

      • mlock 的内存永远不会去交换。机器将首先崩溃。这是限制用户帐户可以锁定多少的原因之一。
      • 根据 LWN.net 最近的文章,这并不完全正确,如果内存压力足够高,内核可能会忽略“请求”,尽管有一种方法可以保证锁定区域永远不会无论内核多么绝望,需要非默认调用,都会被选中。
      • 你的意思是mlock会失败吗?是的,它可以。但如果它成功了,它就不会交换它。
      • 不,我的意思是它看起来会成功,但是如果内存压力足够高,操作系统仍然可以驱逐页面,除非选择了特定的选项。将其视为软锁与硬锁,无论如何都会真正占用内存资源。
      • 嗯。证据?因为 mlock 保证不会那样做,而我从未见过。我可以看到一些不寻常的情况导致它。虚拟主机可能会换出锁定的来宾。程序员也可以搞砸。如果您或释放 munmap,然后在不使用 MCL_FUTURE 的情况下进行更多映射,则新内存将不会被锁定。
      猜你喜欢
      • 2016-07-06
      • 1970-01-01
      • 1970-01-01
      • 2019-03-17
      • 2012-11-13
      • 1970-01-01
      • 1970-01-01
      • 2015-05-02
      • 2018-03-21
      相关资源
      最近更新 更多