首页 > 解决方案 > 以符合 C++ 标准的方式实现 std::malloc

问题描述

在回答问题之前进行快速的思想实验。想象一下,有人正在实现 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++ 线程堆栈地址范围),这意味着operator new如果程序在 std::malloc 的实现中调用函数调度可能会调用使用分段堆栈编译。

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

标签: c++multithreadingstacklanguage-lawyerabi

解决方案


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


推荐阅读