c++ - std::condition_variable 的内存位置可能导致 futex 错误
问题描述
我们的软件中有一个错误,结果令人恐惧:
futex 工具返回了意外的错误代码。
我们将其追溯到一个问题,即 std::condition_variable 在 malloc 的内存区域内的位置导致 futex 错误。如果 std::condition_variable 未在 16 字节字上对齐 - 那么当您尝试wait
. 在示例中,前两个wait_for
调用有效,但最后一个调用因 futex 错误而中止程序。
void futex_error()
{
/* init */
std::mutex mtx;
/* Normal one works */
std::cout << "Doing normal" << "\n";
std::condition_variable* con_var = (std::condition_variable*)malloc(sizeof(std::condition_variable));
new (con_var) std::condition_variable{};
{
std::unique_lock<std::mutex> lck(mtx);
con_var->wait_for(lck, std::chrono::seconds(1));
}
/* Clean */
con_var->std::condition_variable::~condition_variable();
free(con_var);
std::cout << "Doing 16 bytes" << "\n";
/* Works on 16 byte alignment */
uint8_t* ptr_16 = (uint8_t*)malloc(sizeof(std::condition_variable) + 16);
std::condition_variable* con_var_16 = new (ptr_16 + 16) std::condition_variable{};
{
std::unique_lock<std::mutex> lck(mtx);
con_var_16->wait_for(lck, std::chrono::seconds(1));
}
/* Clean */
con_var_16->std::condition_variable::~condition_variable();
free(ptr_16);
std::cout << "Doing 1 byte" << "\n";
/* Futex error */
uint8_t* bad_ptr = (uint8_t*)malloc(sizeof(std::condition_variable) + 1);
std::condition_variable* bad = new (bad_ptr + 1) std::condition_variable{};
{
std::unique_lock<std::mutex> lck(mtx);
bad->wait_for(lck, std::chrono::seconds(1)); //<--- error here?
}
/* Clean */
bad->std::condition_variable::~condition_variable();
free(con_var);
}
我似乎找不到有关 futex 错误的文档以及对齐为何会导致这种情况。有谁知道为什么会发生这种情况?这是在使用 gcc 9.3 的 linux(Arch 和 Ubuntu)上。
解决方案
为什么对齐会导致这种情况
来自C++ 对齐草案 p1:
对象类型具有对齐要求([basic.fundamental]、[basic.compound]),这些要求限制了可以分配该类型对象的地址。
表达方式:
new (bad_ptr + 1) std::condition_variable{};
bad_ptr + 1
在未对齐的系统上调用未定义的行为alignof(std::condition_variable)
. 使用 gcc10在Godboltalignof(std::confition_variable)
上测试等于8
.
两种bad->
访问都是未对齐的访问,并且都是未定义的行为。
有谁知道为什么会发生这种情况?
检查strace
可执行文件的执行输出,我们可以看到:
futex(0x557da3e262e9, FUTEX_WAIT_BITSET_PRIVATE, 0, {tv_sec=2439, tv_nsec=619296657}, FUTEX_BITSET_MATCH_ANY) = -1 EINVAL (Invalid argument)
因为uaddr
应该是调用指针int
的第一个参数futex
没有对齐_Alignof(int)
,内核在这里检测到它并 futex 返回EINVAL
。标准库然后退出应用程序,这是未定义行为的完美行为。
推荐阅读
- c++ - 我编写了一个用于检查括号的代码,但编译器向我显示了这些错误
- linux - 我可以使用哪个 linux 命令检查 VM 的完整大小?
- nlp - 在使用 ThreadPoolExecutor 时,如何在不影响两个大子列表中的每个子列表的情况下加入两个大子列表?
- bert-language-model - 这些是 PyTorch 中 Bert 预训练模型推理的正常速度吗
- jmeter - 测量仪 | 在较新的 Jmeter 版本 5.x 中重用 Jmeter3.0 脚本时未生成负载
- android - Android 改造读取 ErrorBody.String()
- python - 如何从 Keras 对 model.predict 函数的输出进行排序?
- openstack-nova - 哪个计算节点实例正在处理
- react-native - 异常 '-[__NSCFString containsObject:]: unrecognized selector sent to instance 0x60000140a440' 在目标上调用 canOpenURL 时被抛出
- reactjs - “react-infinite-scroll-component” 一次调用后停止工作(loadMore 只被调用一次)