【问题标题】:Implementing std::malloc in a C++ standard-compliant manner以符合 C++ 标准的方式实现 std::malloc
【发布时间】:2019-07-11 14:08:26
【问题描述】:

在回答问题之前进行快速的思考实验。想象一下有人正在实现 std::malloc(比如说,JEMalloc 或 TCMalloc 的人之一)。他们需要的最基本的东西之一是能够知道一旦执行进入 std::malloc 的实现,程序将不会回调到 malloc。

例如,

void* malloc(...) {
    auto lck = std::unique_lock{malloc_mutex};
    // .. memory allocation business logic
}

现在,如果在锁和分配的业务逻辑之间有信号,如果信号处理程序回调到 std::malloc,我们就会死锁。它不是为可重入而设计的,C++ 标准要求向 std::signal 注册的信号处理程序不回调到 operator new (可能回调到 malloc,因此需要用户定义的信号处理程序不会回调到 malloc,如果它被认为是可移植的语言的所有实现)。

最新版本的标准中的§[support.signal]p3 概述了这一要求

  1. 评估是信号安全的,除非它包含以下内容之一:

对任何标准库函数的调用,除了普通的无锁原子操作和明确标识为信号安全的函数。 [注意:这隐含地排除了依赖库提供的内存分配器的 new 和 delete 表达式的使用。 — 结束注释]


但是,C++ 标准似乎没有说明如何为执行线程实现函数堆栈(请参阅此问题:C++ threads stack address range),这意味着 std::malloc 实现中的函数调度可能会调用 @987654327 @如果程序是用分段堆栈编译的。

在这种情况下,如何实现std::malloc 之类的功能?如果 C++ 标准确实没有提供这样的保证,那又是什么呢?我们怎么知道一个正则函数的实现经过了正则的栈分配过程(栈指针递增)?哪个标准(例如 ABI、编译器、POSIX)涵盖了这一点?

【问题讨论】:

  • 既然您在谈论信号,那么最相关的标准就是 POSIX 标准。它没有在其异步信号安全功能中列出malloc。简而言之,malloc(或其兄弟)应该永远在信号处理程序中被调用。如果在信号处理程序中有这样的调用,那么这是编写的程序的错误,而不是作为malloc 的实现者应该打扰的任何事情。
  • 关于 POSIX,参见例如this official reference about signals.
  • @Someprogrammerdude 我的意思是少问一些关于 malloc 及其信号安全性的问题,但更多的是关于我在哪里可以保证函数堆栈不会对分配器进行一些疯狂的魔术调用,而只是用一个简单的堆栈指针递增/递减实现。似乎程序员应该能够以某种方式断言这个基本要求。但我正在阅读的任何标准似乎都没有在任何地方强制要求这一点。
  • 请注意,您可能无法在符合 C/C++ 代码(即不触发 UB)中实现 malloc()。参见例如1, 2.
  • @AlexeyFrunze 你的意思是没有 ABI 编译?严格的单独编译是针对 ABI。 ABI 中没有“有效类型”这样的东西。

标签: c++ multithreading stack language-lawyer abi


【解决方案1】:

在 C++ 标准的逻辑下,实现被视为一个整体。特别是,实现的任何部分都可以假设与实现的任何其他部分有关的任何事情。

这意味着对于这个问题,std::malloc 和信号处理程序可能会相互假设。一些实现可能会认为他们的std::malloc 实现是异步安全的,而另一些可能会认为不是。但是可能存在无数其他假设 - 对齐、连续性、空闲地址的回收等。由于这些都是实现内部的,因此没有标准来描述这一点。

这是“替换 malloc”的问题。您可以实现JE::malloc,但std:: 是特殊的。 C++ 至少承认了替换 operator new 的可能性,但即便如此,也从未指定到这个详细级别。

【讨论】:

    【解决方案2】:

    实现需要为其堆栈帧使用信号安全分配器。这是因为信号处理程序中的函数调用(对非库函数)是允许的。该实现可以使用mallocoperator new,但前提是这些分配器本身是信号安全的。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-28
      • 1970-01-01
      • 2015-02-21
      • 1970-01-01
      • 2012-11-22
      相关资源
      最近更新 更多