




已阅读5页,还剩13页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
Windows内存机制解析By leezy_2000 03-9-3 9:38前言写这篇文章之前相当长的一段时间里,对windows内存机制是有着相当的困惑的。各个进程的内存空间是如何隔离和共享的?GDT(全局描述表)尚在,可分段机制去了那里?既然我们有虚拟的4G空间和结构化异常为何分配内存仍可能失败?在什么时候stack会溢出?当我把这些问题都弄清楚后,我写了这篇文章为自己做了个总结,希望对大家也有帮助。同时由于写Windows内存这块的文章比较多,我将尽力做到与别人的内容不重合。动笔后不久,我发现imquestion对于Windows内存写了几篇非常不错的文章,总题目叫JIURL玩玩Win2k内存篇,推荐阅读。一、总论Windows内存管理机制,底层最核心的东西是分页机制。分页机制使每个进程有自己的4G虚拟空间,使我们可以用虚拟线性地址来跑程序。每个进程有自己的工作集,工作集中的数据可以指明虚拟线性地址对应到怎样的物理地址。进程切换的过程也就是工作集切换的过程,如Matt Pietrek所说如果只给出虚拟地址而不给出工作集,那这个地址是无意义的。(见图一)在分页机制所形成的线性地址空间里,我们对内存进行进一步划分涉及的概念有堆、栈、自由存储等。对堆进行操作的API有HeapCreate、HeapAlloc等。操纵自由存储的API有VirtualAlloc等。此外内存映射文件使用的也应该算是自由存储的空间。栈则用来存放函数参数和局部变量,随着stack frame的建立和销毁其自动进行增长和缩减。说到这里,也许有人会提出疑问:对x86 CPU分段机制是必须的,分页机制是可选的。为什么这里只提到了分页机制。那么我告诉你分段机制仍然存在,一是为了兼容以前的16位程序,二是Windows毕竟要区分ring 0和ring 3两个特权级。用SoftIce看一下GDT(全局描述表)你基本上会看到如下内容:GDTbase=80036000 Limit=03FF0008 Code32 Base=00000000 Lim=FFFFFFFF DPL=0 P RE/内核态driver代码段0010 Data32 Base=00000000 Lim=FFFFFFFF DPL=0 P RW /内核态driver的数据段001B Code32 Base=00000000 Lim=FFFFFFFF DPL=3 P RE /应用程序的代码段0023 Data32 Base=00000000 Lim=FFFFFFFF DPL=3 P RW /应用程序的数据段这意味着什么呢?我们再看一下线性地址的生成过程(见图一)。从中我们应该可以得出结论,如果segmeng base address为0的话,那么这个段可以看作不存在,因为偏移地址就是最终的线性地址。此外还有两个段存在用于Kernel Processor Control Region和user thread environment block。所以如果你在反汇编时看到MOV ECX,FS:2C就不必惊讶,怎么这里使用逻辑地址而不是线性地址。在以后涉及异常处理的地方会对此再做说明。二、从Stack说开去从我个人的经验看,谈到内存时说堆的文章最多,说stack的最少。我这里反其道而行的原因是stack其实要比堆更重要,可以有不使用堆的程序,但你不可能不使用stack,虽然由于对stack的管理是由编译器确定了的,进而他较少出错。通过链接开关/STACK:reserve,commit可以指定进程主线程的stack大小,当你建立其他线程时如果不指定dwStackSize参数,则也将使用/STACK所指定的值。微软说,如果指定较大的commit值将有利于提升程序的速度,我没验证过,但理应如此。通常并不需要对STACK进行什么设定,缺省的情况下将保留1M空间,并提交两个页(8K for x86)。而1M空间对于大多数程序而言是足够的,但为防止stack overflow有三点需要指出一是当需要非常大的空间时最好用全局数组或用VirtualAlloc进行分配,二是引用传递或用指针传递尺寸较大的函数参数(这点恐怕地球人都知道),三是进行深度递归时一定要考虑会不会产生stack溢出,如果有可能,可以采用我在递归与goto一文中提到的办法来仿真递归,这时候可以使用堆或自由存储来代替stack。同时结构化异常被用来控制是否为stack提交新的页面。(这部分写的比较简略因为很多人都写过,推荐阅读Jeffery RitcherWindows核心编程第16章)下面我们来看一下stack的使用。假设我们有这样一个简单之极的函数:int _stdcall add_s(int x,int y)int sum;sum=x+y;return sum;这样在调用函数前,通常我们会看到这样的指令。mov eax,dword ptr ebp-8push eaxmov ecx,dword ptr ebp-4push ecx此时把函数参数压入堆栈,而stack指针ESP递减,stack空间减小。在进入函数后,你将会看到如下指令:push ebpmov ebp,espsub esp,44h这三句建立stack框架,并减小esp为局部变量预留空间。建立stack框架后,ebp+*指向函数参数,ebp-*指向局部变量。另外在很多情况下你会看到如下三条指令push ebxpush esipush edi这三句把三个通用寄存器压入堆栈,这样这三个寄存器就可以用来存放一些变量,进而提升运行速度。很奇怪,我这个函数根本用不到这三个寄存器,可编译器也生成了上述三条指令。对stack中内容的读取,是靠基址指针ebp进行的。所以对应于sum=x+y;一句你会看到mov eax,dword ptr ebp+8add eax,dword ptr ebp+0Chmov dword ptr ebp-4,eax其中ebp+8是x,ebp+0Ch是y,记住压栈方向为从右向左,所以y要在x上边。我们再看一下函数退出时的情况:pop edipop esipop ebxmov esp,ebppop ebpret 8此时恢复stack框架,使esp与刚进入这个函数时相同,ret 8使esp再加8,使esp与没调用这个函数的时候一致。如果使用_cdecl调用规则,则由调用方以类似add esp,8进行清场工作,使stack的大小与未进行函数调用时一致。Stack的使用就这样完全被编译器实现了,只要不溢出就和我们无关,也许也算一种内存的智能管理。最后要补充的两点是:首先stack不像heap会自动扩充,如果你用光了储备,他会准时溢出。其次是不要以为你使用了缺省参数进行链接,你就有1M的stack,看看启动代码你就知道在你拥有stack之前,C Run Time Library以用去了一小部分stack的空间。标题Windows内存机制解析(二)leezy_2000(原作)关键字Windows 内存机制by leezy_2000 2003-10-8 17:01三、浅谈一下Heap(鉴于Matt Pietrek在它的Windows 95 系统程式设计大奥秘对9x系统的heap做了非常详细的讲解,此处涉及的内容将仅限于Win2000)Heap与Stack正好相反,你需要手动来管理每一块内存的申请和释放(在没有垃圾收集机制的情况下),而对于C/C+程序员来说,操作Heap的方式实在是太多了点。下面是几乎所有可以操作堆内存的方法的列表:malloc/freenew/deleteGlobalAlloc/GlobalFreeLocalAlloc/LocalFreeHeapAlloc/HeapFree其中malloc/free由运行时库提供,new/delete为C+内置的操作符。他们都使用运行时库的自己的堆。运行时库的在2000和win9x下都有自己独立的堆。这也就意味着只要你一启动进程,你将至少有两个堆,一个作为进程缺省,一个给C/C+运行时库。GlobalAlloc/GlobalFree和LocalAlloc/LocalFree现在已失去原有的含义,统统从进程缺省堆中分配内存。HeapAlloc/HeapFree则从指定的堆中分配内存。单就分配内存而言(new/delete还要管构造和析构),所有这些方式最终要归结到一点2000和98下都是是HeapAlloc。所以微软才会强调GlobalAlloc/GlobalFree和LocalAlloc/LocalFree会比较慢,推荐使用HeapAlloc,但由于Global*和Local*具有较简单的使用界面,因此即使在微软所提供的源代码中他们仍被大量使用。必须指出的是HeapAlloc并不在kernel32.dll中拥有自己的实现,而是把所有调用转发到ntdll.RtlAllocateHeap。下面这张从msdn中截取的图(图2),应该有助于我们理解同堆相关的API。堆内部的运作同SGI STL的分配器有些类似,大体上是这样,OS为每个堆维护几个链表,每个链表上存放指定大小范围的区块。当你分配内存时,操作系统根据你所提供的尺寸,先确定从那个链表中进行分配,接下来从那个链表中找到合适的块,并把其线性地址返还给你。如果你所要求的尺寸,在现存的区块中找不到,那么就新分配一块较大的内存(使用VirtualAlloc),再对他进行切割,而后向你返还某一区块的线性地址。这只是一个大致的情形,操作系统总在不停的更新自己的堆算法,以提高堆操作的速度。堆本身的信息(包括标志位和链表头等)被存放在Heap Header中,而堆句柄正是指向Heap Header的指针,Heap Header的结构没有公开,稍后我们将试着做些分析。非常有趣的是微软一再强调只对toolhelp API有效的HeapID其实就是堆句柄。原来是准备分析一下堆内部的一些结构的,可后来一想这么做实用价值并不是很大,所需力气却不小。因此也就没具体进行操作。但这里把实现监测堆中各种变化的小程序的实现思路公开一下,希望对大家有所帮助。这个小程序非常的简单,主要完成的任务就是枚举进程内所有的堆的变化情况。由于涉及到比较两个链表的不同,这里使用了STL的vector容器和某些算法来减少编码。同时为了使STL的内存使用不对我们要监测的对象产生干扰,我们需要建立自己的分配器,以使用我们单独创建的堆。此外还需要特别注意的一点是由于toolhelp API Heap32Next在运行过程中不允许对任何堆进行扰动(否则他总返回TRUE),导致我们只能使用vector,并预先保留足够的空间。(访问堆内部某些信息的另一种方式是使用HeapWalk API,看个人喜好了)。程序的运行过程是这样的,先对当前进程中存在的堆进行枚举,并把结果存入一个set类型的变量heapid1,接下来创建自己的堆给分配器使用,并对进程中存在的堆再次进行枚举并把结果存入另一个set类型的变量heapid2,这样就可以调用set_difference求出我们新建堆的ID,再以后列举队内部的信息时将排除这个ID所表示的堆。接下来就可以在两点之间分别把堆内部的信息存入相应的vector,比较这两个vector,就可以得到对应于分配内存操作,堆内部的变化情况了。(图2 from msdn by Murali R. Krishnan)下面是一些悬而未决的问题,那位感兴趣可以自己探索。Heap Header结构是什么样的?堆内部对内存块的组织方式?(是链表么)每一个小块的描述信息在那里?(如果是链表,那就应该有指针使这些小块彼此相连。)标题Windows内存机制解析(二)源代码leezy_2000(原作)关键字Windows 内存机制 源代码/myallocator.h#ifndef _MYALLOCATOR_#define _MYALLOCATOR_#include #include namespace MyLib template class MyAlloc public: static HANDLE hHeap; / type definitions typedef T value_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef size_t size_type; typedef ptrdiff_t difference_type; / rebind allocator to type U template struct rebind typedef MyAlloc other; ; / return address of values pointer address (reference value) const return &value; const_pointer address (const_reference value) const return &value; /* constructors and destructor * - nothing to do because the allocator has no state */ MyAlloc() throw() MyAlloc(const MyAlloc&) throw() MyAlloc() throw() / return maximum number of elements that can be allocated size_type max_size () const throw() size_type N; N=(size_type)(-1)/ sizeof(T); return (0 N ? N : 1); / allocate but dont initialize num elements of type T pointer allocate (size_type num, const void* = 0) / print message and allocate memory with global new /*std:cerr allocate num element(s) of size sizeof(T) std:endl;*/ pointer ret = (pointer)(HeapAlloc(hHeap,0,num*sizeof(T); / std:cerr allocated at: (void*)ret T(); / deallocate storage p of deleted elements /原本应该为pointer void deallocate (void* p, size_type num) / print message and deallocate memory with global delete /* std:cerr deallocate num element(s) of size sizeof(T) at: (void*)p std:endl;*/ HeapFree(hHeap,0,(void*)p); ; / return that all specializations of this allocator are interchangeable template bool operator= (const MyAlloc&, const MyAlloc&) throw() return true; template bool operator!= (const MyAlloc&, const MyAlloc&) throw() return false; /end namespace MyLib#endif/teststlmem.cpp/*written by leezy_200003-9-5 15:12*/#include stdafx.h#pragma warning(disable:4786)/#define _STLP_USE_MALLOC#include myallocator.h#include #include #include #include #include #include typedef unsigned long ULONG_PTR, *PULONG_PTR;using namespace std;/* 本程序需要注意的几点: 1、在实现自己的分配器,这样可以使stl容器的变化不影响我们要监测的堆 2、容器只能用vector否则任何堆的任何变化将导致Heap32Next始终返回TRUE 这应该是微软的bug 3、分配内存失败的时候应该抛出std:bad_alloc内存,此处考虑不会出现低 内存的情况,没抛出此异常。即认定自编写分配器分配内存时不会失败。*/用于比较堆内存块的仿函数/以块大小来判定两个HEAPENTRY32的大小class HeapInfoComparepublic:bool operator() (const HEAPENTRY32& he1,const HEAPENTRY32& he2) constreturn (he1.dwBlockSize he2.dwBlockSize);typedef vector HEAPENTRY32, MyLib:MyAlloc HEAPENTRYSET;void heapinfo(HEAPENTRYSET& hset,ULONG_PTR heapid);void getheapid(set& heapid)HANDLE hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST,GetCurrentProcessId();HEAPLIST32 heaplist32;heaplist32.dwSize=sizeof(HEAPLIST32);BOOL bRet=Heap32ListFirst(hSnapShot,&heaplist32);while(bRet)heapid.insert(heaplist32.th32HeapID);coutheaplist32.th32HeapIDendl;bRet=Heap32ListNext(hSnapShot,&heaplist32);CloseHandle(hSnapShot);coutthe endendl;HANDLE MyLib:MyAlloc:hHeap=NULL;HANDLE hHeap;int main(int argc, char* argv)/枚举此时所有堆并在建立新堆后再次枚举这样从中剔除新建堆set heapid1,heapid2,heapid3;getheapid(heapid1);hHeap=HeapCreate(0,0,0);getheapid(heapid2);insert_iteratorset iter(heapid3,heapid3.begin();set_difference(heapid2.begin(),heapid2.end(),heapid1.begin(),heapid1.end(),iter);set:iterator pos;ULONG_PTR newheapid;for( pos=heapid3.begin(); pos !=heapid3.end(); +pos)coutThe new heap id ist(*pos)endl;newheapid=*pos;MyLib:MyAlloc:hHeap=hHeap;/vectorint, MyLib:MyAlloc v1;HEAPENTRYSET heapset1,heapset2,heapset3;heapset1.reserve(400);/保证vector不自动增长heapset2.reserve(400);heapset3.reserve(400);int size;heapinfo(heapset1,newheapid);sort(heapset1.begin(),heapset1.end(),HeapInfoCompare();size=heapset1.size();HANDLE hCurHeap=GetProcessHeap();/HeapAlloc(hCurHeap,HEAP_ZERO_MEMORY,4*1024);char* p=new char4*1024;/GlobalAlloc(GHND,4*1024);char* q=(char*)malloc(4*1024);cout the p is(int)pendl;heapinfo(heapset2,newheapid);sort(heapset2.begin(),heapset2.end(),HeapInfoCompare();size=heapset2.size();insert_iterator miter(heapset3,heapset3.begin();set_difference(heapset2.begin(),heapset2.end(),heapset1.begin(),heapset1.end(),miter,HeapInfoCompare();size=heapset3.size();HEAPENTRYSET:iterator mpos;for( mpos=heapset3.begin(); mpos !=heapset3.end(); +mpos)coutThe size of the different block ist(*mpos).dwBlockSizetand the addresss ist(*mpos).dwAddresstdwFlags ist(*mpos).dwFlags endl;coutThe heapid is:t(*mpos).th32HeapID endl;return 0;void heapinfo(HEAPENTRYSET& hset,ULONG_PTR hid)HANDLE hSnapShot=CreateToolhelp32Snapshot(TH32CS_SNAPHEAPLIST,GetCurrentProcessId();HEAPLIST32 heaplist32;heaplist32.dwSize=sizeof(HEAPLIST32);BOOL bRet=Heap32ListFirst(hSnapShot,&heaplist32);static int i=0;while(bRet)HEAPENTRY32 he32;DWORD totalsize=0,freesize=0;if(heaplist32.th32HeapID=hid) bRet=Heap32ListNext(hSnapShot,&heaplist32);continue;DWORD numbe
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 晋城口碑营销活动策划方案(3篇)
- 淄博隔声板施工方案(3篇)
- 印度农田施工方案(3篇)
- 取样员考试题库及答案
- 心理学考研题目及答案
- 小学整数加减法题目及答案
- 冬请允许我拥抱你250字12篇范文
- 数学课《几何图形变换与性质》教学实践
- 农村科技研发与应用推广合同
- 一条路到达一个地方(14篇)
- 2025年中国白胡椒行业市场运营现状及投资方向研究报告
- 2025年高考语文全国Ⅱ卷试卷评析及备考策略(课件)
- 通海翡翠华庭建设项目 水土保持方案报告表
- 乡村治理与乡村振兴规划
- T/CCMA 0206-2024混凝土机械液压平衡阀
- 奶茶店分红合同协议书
- 手阳明大肠经课件
- 职场高效沟通与结构化表达技巧培训
- 2025-2030中国红枣深加工行业市场深度调研及投资策略与投资前景预测研究报告
- 企业清洁生产审核手册
- 中国中煤华东分公司所属舟山公司招聘笔试题库2025
评论
0/150
提交评论