【问题标题】:Shouldn't malloc be asynchronous?malloc 不应该是异步的吗?
【发布时间】:2016-10-07 03:16:31
【问题描述】:

我是否正确假设当进程调用 malloc 时,可能涉及 I/O(交换缓存等)以使内存可用,这反过来意味着它可能会阻塞相当长的时间?因此,我们不应该在 linux 中有两个版本的 malloc,一个说“fast_malloc”,它适用于获取较小的块并保证不会阻塞(但当然可能仍然会因 OUT_OF_MEMORY 而失败)和另一个我们可以要求任意的 async_malloc -size 空间但需要回调?

示例:如果我需要一小块内存来为链表中的项目腾出空间,我可能更喜欢传统的内联 malloc,因为我知道操作系统应该能够在 99.999% 的时间内满足它,或者只是失败。另一个例子:如果我是一个数据库服务器,试图分配一个相当大的块来放入索引,我可能会选择 async_malloc 并处理“回调复杂性”。

我提出这个问题的原因是我希望创建每秒处理数十万个 Web 请求的高并发服务器,并且通常避免使用线程来处理这些请求。换句话说,无论何时发生 I/O,我都希望它是异步的(比如基于 libevent)。不幸的是,我意识到大多数 C API 缺乏对并发使用的适当支持。例如,无处不在的 MySQL C 库是完全阻塞的,而这只是我的服务器广泛使用的一个库。同样,我总是可以通过卸载到另一个线程来模拟非阻塞,但这远不如通过完成回调等待结果便宜。

【问题讨论】:

  • 调用malloc本质上不会导致更多的IO。也许您对返回的内存的使用与仅将内存分配给您感到困惑。仅仅因为你要求 100MB 并不意味着malloc 会立即触发 100MB 的交换。只有当您访问内存时才会发生这种情况。
  • C没有指定malloc()的计时性能。当然,这不是第一个有时间问题的应用程序。主要操作系统中的典型malloc() 确实避免长时间阻塞,而是仅在使用时分配。 stackoverflow.com/q/19991623/2410359.

标签: c++ c multithreading concurrency libevent


【解决方案1】:

正如凯勒姆在评论中所说:

调用malloc 本身不会导致更多的IO。也许您对返回的内存的使用与仅将内存分配给您感到困惑。仅仅因为你要求 100MB 并不意味着malloc 会立即触发 100MB 的交换。只有当你访问内存时才会发生这种情况。

如果您想在后续访问分配的内存期间防止长时间的交换等延迟,您可以在其上调用mlock在单独的线程中 (所以您的进程不会因为等待mlock 完成而停滞不前)。一旦mlock 成功,内存就会被物理实例化,直到munlock 才能换出。

【讨论】:

    【解决方案2】:

    请记住,调用 malloc() 并不一定会导致您的程序向操作系统请求更多内存。这取决于 C 运行时对 malloc() 的实现。

    对于 glibc malloc() 仅(取决于您要求多少)返回一个指向运行时已经从操作系统获得的内存的指针。同样 free() 不一定将内存返回给操作系统。这样会快很多。我认为 glibc 的 malloc() 也是线程安全的。

    有趣的是,这赋予了 C、C++(以及基于此构建的所有内容)与 Java 和 C# 等语言通常相关的相同类型的属性。可以说,在 glibc 这样的运行时之上构建像 Java 或 C# 这样的运行时意味着实际上有比必要更多的工作来管理内存......除非他们根本没有使用 malloc() 或 new。

    那里有各种各样的分配器,无论您的正常 C 运行时提供什么,您都可以将任何您想要的分配器链接到您的程序中。因此,即使在像 *BSD 这样的平台上(它们的内存分配方法通常要传统得多,每次调用 malloc() 或 new 时都会询问操作系统),您也可以使用相同的技巧。

    【讨论】:

    • 我认为 glibc 的 malloc() 也是线程安全的。 确实如此。实际上glibc的malloc实现为每个线程管理了一个线程本地(私有)arena,这样当多个线程同时调用malloc时几乎不会发生冲突。
    【解决方案3】:

    换句话说,无论何时发生 I/O,我都希望它是异步的(比如基于 libevent)。

    我有个坏消息要告诉你。任何时候访问内存都有可能因 I/O 而阻塞。

    malloc 本身不太可能被阻塞,因为它使用的系统调用只是在数据结构中创建一个条目,告诉内核“当它被访问时映射到这里的某个内存中”。这意味着malloc 只会在需要向下到内核映射更多内存并且内核内存不足时才会阻塞,因此它本身必须等待分配其内部数据结构(您可以等待相当长的时间)然后)或者你使用mlockall。直到您触摸内存后,才会发生可能导致交换的实际内存分配。而且您自己的内存可以随时换出(或者您的程序文本可能会被分页),而您几乎无法控制它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-08-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-11-30
      相关资源
      最近更新 更多