线程的堆栈

Posted 13572980562

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程的堆栈相关的知识,希望对你有一定的参考价值。

每当创建一个线程的时候,系统会为线程的堆栈保留一个栈区的空间区域,并将一些物理存储器提交给这个已保留的区域,我查看了VS2015,该默认设置大小是1MB。

它可以自己设置,在 项目-->属性-->链接器-->系统--->堆栈保留大小 这个地方填写自己希望的栈大小。

// 堆保留大小 1M    堆提交大小 4KB
// 栈保留大小 1M    栈提交大小 4KB

在页面大小是4KB的计算机上创建一个堆栈区域,其物理存储均具有页面保护属性,即PAGE_READWRITE。

线程访问时,从栈顶到栈底,页面状态不断变为已提交的页面,但是最底下的页面总是被保留的,从来不会被提交,这样做的目的是为了防止不小心改写进程所需要的其他数据。因为如果栈底下方的地址上,另一个地址空间区域已经提交了物理存储器,那么就有可能改写了其他数据,这是非常危险又隐蔽的错误。
结合SEH异常处理机制,可以写一个简单的求和函数,用递归的方法,让它发生栈溢出,并对异常进行捕获。

#include "stdafx.h"
#include <windows.h>

// 堆保留大小 1M 堆提交大小 4KB
// 栈保留大小 1M 栈提交大小 4KB

UINT Sum(UINT uNum);
LONG WINAPI FilterFunc(DWORD dwExceptionCode);
DWORD WINAPI SumThreadFunc(PVOID Param);


int main()
{

DWORD ThreadId;

UINT uSum = 5000; //大概可以计算4700以内的数

HANDLE ThreadHandle = CreateThread(NULL, 0,
SumThreadFunc, (PVOID)(UINT_PTR)uSum, 0, &ThreadId);


WaitForSingleObject(ThreadHandle, INFINITE);

GetExitCodeThread(ThreadHandle, (PDWORD)&uSum);
CloseHandle(ThreadHandle);

if (uSum == UINT_MAX)
{


printf("The number is too big, please enter a smaller number\r\n");
}
else
{
printf("%d\r\n", uSum);
}
return 0;
}

UINT Sum(UINT uNum)
{


return((uNum == 0) ? 0 : (uNum + Sum(uNum - 1)));
}

LONG WINAPI FilterFunc(DWORD dwExceptionCode)
{

return((dwExceptionCode == STATUS_STACK_OVERFLOW)
? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH);
}


DWORD WINAPI SumThreadFunc(PVOID Param)
{
UINT uSumNum = PtrToUlong(Param);  //指针转ULONG
UINT uSum = UINT_MAX; // 错误数字

__try
{

uSum = Sum(uSumNum);  //递归求和
}
__except (FilterFunc(GetExceptionCode())) //如果是EXCEPTION_EXECUTE_HANDLER  (1)
{
// 说明堆栈溢出
// 添加一些自己的处理
printf("堆栈溢出了\r\n");
}


return uSum;
}

创建独立线程的理由:
1.每个线程保证拥有自己的1MB堆栈空间,而不是和他人分享1MB

2.当发生溢出时,每个线程只得到一次通知

3.系统自动收回提交给堆栈的物理存储器。

 

以上是关于线程的堆栈的主要内容,如果未能解决你的问题,请参考以下文章

每个线程都有自己的堆栈吗?

当前逻辑线程增加/线程堆栈泄漏

评估堆栈和线程堆栈之间的区别

属于线程的堆栈与进程的堆栈有何不同

线程池的堆栈问题

记录不同线程的出错的堆栈