Windows内存管理机制

上传人:m**** 文档编号:561627480 上传时间:2023-12-14 格式:DOCX 页数:10 大小:101.96KB
返回 下载 相关 举报
Windows内存管理机制_第1页
第1页 / 共10页
Windows内存管理机制_第2页
第2页 / 共10页
Windows内存管理机制_第3页
第3页 / 共10页
Windows内存管理机制_第4页
第4页 / 共10页
Windows内存管理机制_第5页
第5页 / 共10页
点击查看更多>>
资源描述

《Windows内存管理机制》由会员分享,可在线阅读,更多相关《Windows内存管理机制(10页珍藏版)》请在金锄头文库上搜索。

1、Windows内存管理机制在编程中,很多Windows或C+的内存函数不知道有什么区别,更别谈有效使 用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过 简单的总结描述,结合实例来阐明这个机制。本文目的:对Windows内存管理机制了解清楚,有效的利用C+内存函数管理和使用内 存。本文内容: 本文一共有六节,由于篇幅较多,故按节发表。其他章节请看本人博客的 Windows内存管理及C+内存分配实例(一)(二)(三)(四)和(五)。1. 进程地址空间2. 内存状态查询函数3. 内存管理机制-虚拟内存 (VM)4. 内存管理机制-内存映射文件 (Map)5. 内存管理机制-堆 (

2、Heap)使用场合操作系统为每个线程都建立一个默认堆栈,大小为1M。这个堆栈是供函 数调用时使用,线程内函数里的各种静态变量都是从这个默认堆栈里分配的。默认1M的线程堆栈空间的结构举例如下,其中,基地址为0x0004 0000, 刚开始时, CPU 的堆栈指针寄存器保存的是栈顶的第一个页面地址 0x0013 F000。第二页面为保护页面。这两页是已经分配物理存储器的可用 页面。随着函数的调用,系统将需要更多的页面,假设需要另外5页,则给这5 页提交内存,删除原来页面的保护页面属性,最后一页赋予保护页面属 性。当分配倒数第二页 0x0004 1000时,系统不再将保护属性赋予它,相反,它会产生堆

3、栈溢出异常STATUS_STACK_OVERFLOW,如果程序没有处 理它,则线程将退出。最后一页始终处于保留状态,也就是说可用堆栈数 是没有1M的,之所以不用,是防止线程破坏栈底下面的内存(通过违规 访问异常达到目的)。0x0013 F0004K栈顶0x0004 10004K保护页面4K0x0OCX 00004K栈底当程序的函数里分配了临时变量时,编译器把堆栈指针递减相应的页数目,堆栈指针始终都是一个页面的整数倍。所以,当编译器发现堆栈指针位于保护页面之下时,会插入堆栈检查函数,改变堆栈指针及保护页面。这样,当程序运行时,就会分配物理内存,而不会出现访问违规。使用例子改变堆栈默认大小:有两个

4、方法,一是在CreateThread()时传一个参数进去改变;二是通过链接命令:#pragma comment(linker,/STACK:102400000,1024000)第一个值是堆栈的保留空间,第二个值是堆栈开始时提交的物理内存大小。本文将堆栈改变为 100M。堆栈溢出处理:如果出现堆栈异常不处理,则导致线程终止;如果你只做了一般处理,内 存结构已经处于破坏状态,因为已经没有保护页面,系统没有办法再抛出堆 栈溢出异常,这样的话,当再次出现溢出时,会出现访问违规操作STATUS_ACCESS_VIOLATION,这是线程将被系统终止。解决办法是,恢复堆栈的保护页面。请看以下例子:C+程序

5、如下:bool handle=true;static MEMORY_BASIC_INFORMATION mi;LPBYTE lpPage;/得到堆栈指针寄存器里的值_asm mov lpPage, esp;/ 得到当前堆栈的一些信息VirtualQuery(lpPage, &mi, sizeof(mi);/输出堆栈指针printf(堆栈指针=%xn,lpPage);/ 这里是堆栈的提交大小printf(已用堆栈大小=%dn,mi.RegionSize);printf(堆栈基址=%xn,mi.AllocationBase);for(int i=0;i2;i+)_try_try_try厂厂Hit/

6、 如果是这样静态分配导致的堆栈异常,系统默认不 抛出异常,捕获不到/char a1024*1024;/动态分配栈空间,有系统调用 Alloca 实现,自动 释放Add(1000);/ 系统可以捕获违规访问int * p=(int*)0xC00000000;*p=3;cout 执行结束 endl;_except(GetExceptionCode()=STATUS_ACCESS_VIOLATION ? EXCEPTION_EXECUTE_HANDLER :EXCEPTION_CONTINUE_SEARCH)coutExcpetion 1endl;except(GetExceptionCode()=

7、STATUS_STACK_OVERFLOW ? EXCEPTION_EXECUTE_HANDLER :EXCEPTION_CONTINUE_SEARCH)coutException 2endl;if(handle)/ 做堆栈破坏状态恢复LPBYTE lpPage;static SYSTEM_INFO si;static MEMORY_BASIC_INFORMATION mi;static DWORD dwOldProtect;/ 得到内存属性GetSystemInfo(&si);/ 得到堆栈指针_asm mov lpPage, esp;/ 查询堆栈信息VirtualQuery(lpPage,

8、&mi, sizeof(mi);printf(坏堆栈指针=%xn,lpPage);lpPage = (LPBYTE)(mi.BaseAddress)-si.dwPageSize;printf(已用堆栈大小=%dn,mi.RegionSize);printf(坏堆栈基址=%xn,mi.AllocationBase);/释放准保护页面的下面所有内存if (!VirtualFree(mi.AllocationBase,(LPBYTE)lpPage -(LPBYTE)mi.AllocationBase,MEM_DECOMMIT)exit(1);/ 改页面为保护页面if (!VirtualProtect

9、(lpPage, si.dwPageSize,PAGE_GUARD | PAGE_READWRITE,&dwOldProtect)exit(1);printf(Exception handler %lXn, _exception_code();_except(EXCEPTION_EXECUTE_HANDLER)coutDefault handlerendl;cout 正常执行 endl;/ 分配空间,耗用堆栈char c1024*800;printf(c0=%xn,c);printf(c1024*800=%xn,&c1024*800-1);void ThreadStack:Add(unsign

10、ed long a)/ 深递归,耗堆栈char b1000;if(a=0)return;Add(a-1);程序运行结果如下:可以看见,在执行递归前,堆栈已被用了 800多K,这些是在编译时就静态决 定了。它们不再占用进程空间,因为堆栈占用了默认的1M进程空间。分配是 从栈顶到栈底的顺序。当 第一次递归调用后,系统捕获到了它的溢出异常,然后堆栈指针自动恢复到 原来的指针值,并且在异常处理里,更改了保护页面,确保第二次递归调用时 不会出现访 问违规而退出线程,但是,它仍然会导致堆栈溢出,需要动态的增 加堆栈大小,本文没有对这个进行研究,但是试图通过分配另外内存区,改变 堆栈指针,但是没有 奏效。注意:在一个线程里,全局变量加上任何一个函数里的临时变量,如果超过堆 栈大小,当调用这个函数时,都会出现堆栈溢出,这种溢出系统不会抛出堆栈 溢出异常,而直接导致线程退出。对于函数1调用函数2,而函数n-1又调用函数n的嵌套调用,每层调用不算临 时变量将损失 240字节,所以默认线程最多有 1024*(1024-2)/240=4360次调 用。加上函数本身有变量,这个数目会大大减少。

展开阅读全文
相关资源
正为您匹配相似的精品文档
相关搜索

最新文档


当前位置:首页 > 学术论文 > 其它学术论文

电脑版 |金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号