【问题标题】:Available stack size for C++ threadC++ 线程的可用堆栈大小
【发布时间】:2020-10-17 03:50:09
【问题描述】:

我使用的是 Windows 10、Visual Studio 2019 该程序生成线程。我需要添加功能,指出在任何执行时间内可用的堆栈大小。

#include <iostream>
#include <thread>

void thread_function()
{
    //AvailableStackSize()?  
    //Code1 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
    //Code2 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
    //Code3 //varibales on stack allocation + function call
    //AvailableStackSize()? it should decrease
}

int main()
{
    std::thread t(&thread;_function);
    std::cout << "main thread\n";
    std::thread t2 = t;

    t2.join();

    return 0;
}

我尝试使用 .但我不确定我该如何继续。我只能查询堆栈的总大小。没有可用的。

bool AvailableStackSize()
{
    // Get the stack pointer
    PBYTE pEsp;
    _asm {
        mov pEsp, esp
    };

    // Query the accessible stack region
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(pEsp, &mbi, sizeof(mbi)));

    return mbi.RegionSize;//This is a total size! what is an available? Where guard is located?

}

也许我还可以检查堆栈内是否有某个地址

PVOID add;
(add>= mbi.BaseAddress) && (add < PBYTE(mbi.BaseAddress) + mbi.RegionSize);

我看到了几个类似的问题,但没有人 100% 回答这个问题。 获取可用堆栈大小的正确方法是什么?

【问题讨论】:

  • 这是一个链接器选项,默认为每个线程 1M,请参阅:docs.microsoft.com/en-us/cpp/build/reference/… 另请参阅确定大约有多少可用堆栈空间 devblogs.microsoft.com/oldnewthing/20200609-00/?p=103847
  • 请注意,您的 cmets “它应该减少”是基于对堆栈如何工作的根本误解。 thread_function 的所有本地人都将在序言中保留空间。作为调用约定的一部分,所有被调用的函数都将在函数退出时恢复堆栈。只有在 that 函数中使用 _alloca() 时,堆栈的末尾才会在同一函数中的语句之间移动。当控制权返回到执行检查的函数时,即使是被调用函数中的 *_alloca()` 也不会影响可用的堆栈。

标签: c++ windows winapi stack virtualquery


【解决方案1】:

由于堆栈向下增长,它是堆栈的潜在底部和当前使用的末尾之间的差异。顺便说一句,您不需要任何内联汇编,因为您知道本地人被放置在堆栈上。

__declspec(noinline) size_t AvailableStackSize()
{
    // Query the accessible stack region
    MEMORY_BASIC_INFORMATION mbi;
    VERIFY(VirtualQuery(&mbi, &mbi, sizeof(mbi)));

    return uintptr_t(&mbi) - uintptr_t(mbi.AllocationBase);
}

这将与调用者中实际可用的内容略有不同,因为函数调用使用了一些(存储返回地址和任何保留的寄存器)。您需要调查最终保护页面是出现在相同的VirtualQuery 结果中还是相邻的结果中。

但这是一般方法。

【讨论】:

  • @RbMm:如果您不需要支持旧的 Windows 版本,那就更好了
  • 必须是mbi.AllocationBase 而不是mbi.BaseAddress
  • @dewaffled 还需要考虑到您的测试函数编译器(带有优化)可以替换递归到循环。为了防止这种情况,需要一些更复杂的......说void foo(int x) { size_t s = AvailableStackSize(); DbgPrint("%p\n", s); if (x) foo(x - 1); if (!s) __debugbreak(); }
猜你喜欢
  • 2011-01-29
  • 2011-07-27
  • 2011-05-08
  • 1970-01-01
  • 2021-03-17
  • 2015-02-13
  • 2010-12-22
  • 2017-12-11
相关资源
最近更新 更多