8

我最近发布了一个关于堆栈分段和提升协程的问题, 但似乎 -fsplit-stack 方法仅适用于使用该标志编译的源文件,当您分支到另一个尚未编译的函数时,运行时会中断 - fsplit 堆栈。例如_

这意味着运行时使用函数本地技术来检测当前堆栈何时被超越。而不是“保护页面信号”技巧,堆栈的末尾总是有一个保护页面,它将在写入或读取时发出信号,告诉运行时分配一个新的堆栈帧并分支到那个。

那么这个flag有什么用呢?如果我链接到任何其他尚未使用此构建的库,代码将中断(甚至是 libstdc++ 和 libc),那么人们如何在大型项目中实际使用它?


通过阅读有关拆分堆栈的gcc wiki,似乎从拆分堆栈函数调用非拆分堆栈函数会导致分配 64KB 堆栈帧。好的。

但似乎尚未实现从函数指针调用非拆分堆栈函数以遵循上述方案。

那这个flag有什么用呢?如果我继续调用任何虚函数,我的程序会中断吗?

下面的答案来看,clang 似乎还没有实现拆分堆栈?

4

2 回答 2

4

您必须使用分段堆栈支持和您的应用程序来编译 boost(至少 boost.context 和 boost.coroutine)。

  • 使用 b2 属性 segmented-stacks=on 编译 boost(boost.context 和 boost.coroutine)(在 boost.coroutine 和 boost.context 中启用特殊代码)。

  • 您的应用程序必须使用 -DBOOST_USE_SEGMENTED_STACKS 和 -fsplit-stack(boost.coroutines 标头要求)进行编译。

查看boost.coroutine 文档

boost.coroutine 包含一个演示分段堆栈的示例(在目录 coroutine/example/asymmetric/ call b2 toolset=gcc segmented-stacks=on 中)。

关于您的最后一个问题GCC Wiki 指出

对于从拆分堆栈代码到非拆分堆栈代码的调用,链接器将更改拆分堆栈(调用者)函数中的初始指令。这意味着链接器必须对编译器发出的指令有特殊的了解。更改的效果是将所需的帧大小增加一个足够大的数字,以合理地为非拆分堆栈工作。这将是一个依赖于目标的数字;默认值为 64K。请注意,当拆分堆栈函数返回时,这个大堆栈将被释放。请注意,我忽略了共享库中的拆分堆栈代码调用主可执行文件中的非拆分堆栈代码的情况;这似乎是一个不太可能的问题。

请注意:虽然 llvm 支持分段堆栈,但 clang 接缝不提供这些__splitstack_<xyz>功能。

于 2017-09-20T07:19:36.457 回答
1

首先,我想说拆分堆栈支持在本质上是实验性的。它不是一个被广泛支持的东西,也没有一个单一的实现被接受为要走的路。因此,它存在于编译器中的部分目的是支持实际使用中的研究。

也就是说,人们通常希望使用这样的特性来启用具有小堆栈的大量线程,但如果需要,它们可以变得更大。在某些应用程序中,可以严格控制在这些线程中运行的代码。例如,相当专业的请求处理程序不调用诸如 Boost 之类的通用库。高性能系统的工作通常涉及收紧对给定路径中使用什么代码的限制,这就是一个例子。它肯定会限制该功能的适用性,但如果有人以这种方式在生产中使用它,我不会感到惊讶。

请注意,类似的问题也存在于 -fno-exceptions 和 -fno-rtti 等标志中。通常,C++ 需要使用一组兼容的标志编译进入可执行文件的所有内容。有时可以混搭,但往往很脆弱。这是从源代码和封闭构建工具(如 bazel )构建所有内容的动机的一部分。其他语言对非源组件有不同的方法,尤其是基于虚拟机的语言,例如 Java 和 .NET 系列。在那些世界中,诸如拆分堆栈之类的事情是在较低级别的编译中决定的,但通常在源代码级别上无法控制或了解它们。

于 2017-09-29T02:05:53.503 回答