线程的堆栈

每当创建一个线程的时候,系统会为线程的堆栈保留一个栈区的空间区域,并将一些物理存储器提交给这个已保留的区域,我查看了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 ");
}
else
{
printf("%d ", 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("堆栈溢出了 ");
}


return uSum;
}

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

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

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

原文地址:https://www.cnblogs.com/kekoukele987/p/7428471.html