Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/visual-studio/7.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 用visualstudio确定堆栈空间_C_Visual Studio_Memory_Memory Management_Stack - Fatal编程技术网

C 用visualstudio确定堆栈空间

C 用visualstudio确定堆栈空间,c,visual-studio,memory,memory-management,stack,C,Visual Studio,Memory,Memory Management,Stack,我在VisualStudio2005中用C语言编程。我有一个多线程程序,但这在这里并不特别重要 如何确定(大约)线程使用的堆栈空间 我计划使用的技术是将堆栈内存设置为某个预定值,比如0xDEADBEEF,长时间运行程序,暂停程序,然后检查堆栈 如何使用Visual Studio读取和写入堆栈内存 编辑:例如,你看,这个问题是关于嵌入式系统的,但在这里我试图在普通PC上确定答案。堆栈的工作方式也不符合你的预期。堆栈是页面的线性序列,最后一个(顶部)页面用页面保护位标记。当触摸此页面时,保护位被移除

我在VisualStudio2005中用C语言编程。我有一个多线程程序,但这在这里并不特别重要

如何确定(大约)线程使用的堆栈空间

我计划使用的技术是将堆栈内存设置为某个预定值,比如0xDEADBEEF,长时间运行程序,暂停程序,然后检查堆栈

如何使用Visual Studio读取和写入堆栈内存


编辑:例如,你看,这个问题是关于嵌入式系统的,但在这里我试图在普通PC上确定答案。

堆栈的工作方式也不符合你的预期。堆栈是页面的线性序列,最后一个(顶部)页面用页面保护位标记。当触摸此页面时,保护位被移除,页面可以使用。为了进一步增长,将分配一个新的保护页

因此,您想要的答案是gaurd页面的分配位置。但是你提出的技术会触及到有问题的页面,因此它会使你试图测量的东西失效


确定(堆栈)页面是否具有保护位的非侵入性方法是via。

您可以利用

当您想在线程中找出它使用了多少堆栈空间时,可以执行以下操作:

#include <windows.h>
#include <winnt.h>
#include <intrin.h>

inline NT_TIB* getTib()
{
    return (NT_TIB*)__readfsdword( 0x18 );
}
inline size_t get_allocated_stack_size()
{
    return (size_t)getTib()->StackBase - (size_t)getTib()->StackLimit;
}

void somewhere_in_your_thread()
{
    // ...
    size_t sp_value = 0;
    _asm { mov [sp_value], esp }
    size_t used_stack_size = (size_t)getTib()->StackBase - sp_value;

    printf("Number of bytes on stack used by this thread: %u\n", 
           used_stack_size);
    printf("Number of allocated bytes on stack for this thread : %u\n",
           get_allocated_stack_size());    
    // ...
}
#包括
#包括
#包括
内联NT_TIB*getTib()
{
返回(NT_TIB*)读取(0x18);
}
内联大小\u无法获取\u分配的\u堆栈\u大小()
{
return(size_t)getTib()->StackBase-(size_t)getTib()->StackLimit;
}
在你的线程()中的某个地方无效
{
// ...
大小\u t sp\u值=0;
_asm{mov[sp_值],esp}
size\u t used\u stack\u size=(size\u t)getTib()->StackBase-sp\u值;
printf(“此线程使用的堆栈字节数:%u\n”,
使用的(堆栈大小);
printf(“堆栈上为此线程分配的字节数:%u\n”,
获取分配的堆栈大小();
// ...
}

Windows不会立即提交堆栈内存;相反,它为它保留地址空间,并在访问时逐页提交。阅读更多信息

因此,堆栈地址空间由三个连续区域组成:

  • 保留但未提交的内存,可用于堆栈增长(但从未被访问)
  • 保护页,也从未被访问过,当被访问时,它会触发堆栈增长
  • 提交内存,即线程曾经访问过的堆栈内存
这允许我们构造一个函数来获取堆栈大小(页面大小粒度):


需要考虑的一点是:
CreateThread
允许指定初始堆栈提交大小(当
stack\u size\u参数为\u保留时,通过
dwStackSize
参数)。如果此参数非零,则只有当堆栈使用率大于
dwStackSize
值时,我们的函数才会返回正确的值。

您可以使用GetThreadContext()函数确定线程的当前堆栈指针。然后使用VirtualQuery()查找此指针的堆栈基。减去这两个指针将得到给定线程的堆栈大小。

您的评论不完全正确。触摸有问题的页面是可以的,真的。该技术是用一个特定的值写入所有相关内存,然后在长时间运行后,查看有多少内存不再具有该值。Qupting Microsoft:“尝试读取或写入保护页会导致系统引发状态\访问\违规异常并关闭保护页状态。因此,保护页充当一次性访问警报。“。不,阅读也不例外。我想我们是在扯淡。但如果我理解正确,你的解决方案只有页面分辨率。你的答案很有帮助,但它并没有给我我所希望的那样具体的答案。事实上,这是正确的答案,因为分配给堆栈的页面是专门分配给该堆栈和线程的。因此,堆栈大小始终与页数相同。另请参阅MSVC编译器选项-像“初始堆栈空间”这样的选项是以页面大小的倍数指定的。堆栈不是变小了吗?为什么要将RegionSize添加到基址而不是减去它?@Philip-堆栈确实会增长(至少在x86上)。我之所以添加,是因为
VirtualQuery
返回内存分配区域的基址——向下增长堆栈中最后一个(理论上)可用字节的地址。在堆栈向上增长的平台上,第一个
VirtualQuery
调用将给出所需的结果。我想我可以用一幅画来说明它;当我有更多的时间时,我甚至可能稍后再做。@atzz我对这个解决方案有点担心(这非常有用)。我们如何知道,在执行此函数或它发出的某个VirtualQuery调用时,我们没有运行到保护页,因此导致实际堆栈状态在我们下面发生更改?保护页不能移动吗?@acm它不能(如果您愿意接受关于
VirtualQuery
内部和编译器代码生成的一些合理假设,堆栈增长应该在第一次调用
VirtualQuery
时完成)。尽管您可以将此fn twise(或n次)调用,并将最后一个结果作为额外的保证。(但这也不是100%;例如,另一个进程可能会对我们造成
写进程内存
,我们会被搞砸:)。堆栈使用的概念只对运行状况监视或调试有意义,因此fn应该是正常的。
static size_t GetStackUsage()
{
    MEMORY_BASIC_INFORMATION mbi;
    VirtualQuery(&mbi, &mbi, sizeof(mbi));
    // now mbi.AllocationBase = reserved stack memory base address

    VirtualQuery(mbi.AllocationBase, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe reserved (uncommitted) portion of the stack
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the guard page
    // skip it

    VirtualQuery((char*)mbi.BaseAddress + mbi.RegionSize, &mbi, sizeof(mbi));
    // now (mbi.BaseAddress, mbi.RegionSize) describe the committed (i.e. accessed) portion of the stack

    return mbi.RegionSize;
}