`
webcenterol
  • 浏览: 950991 次
文章分类
社区版块
存档分类
最新评论

Windows内存机制解析(1)

 
阅读更多

Windows内存机制解析<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

By leezy_2000 03-9-3 9:38

前言

写这篇文章之前相当长的一段时间里,对windows内存机制是有着相当的困惑的。各个进程的内存空间是如何隔离和共享的?GDT(全局描述表)尚在,可分段机制去了那里?既然我们有虚拟的4G空间和结构化异常为何分配内存仍可能失败?在什么时候stack会溢出?―――

当我把这些问题都弄清楚后,我写了这篇文章为自己做了个总结,希望对大家也有帮助。同时由于写Windows内存这块的文章比较多,我将尽力做到与别人的内容不重合。

动笔后不久,我发现imquestion对于Windows内存写了几篇非常不错的文章,总题目叫《JIURL玩玩Win2k内存篇》,推荐阅读。

一、总论

Windows内存管理机制,底层最核心的东西是分页机制。分页机制使每个进程有自己的4G虚拟空间,使我们可以用虚拟线性地址来跑程序。每个进程有自己的工作集,工作集中的数据可以指明虚拟线性地址对应到怎样的物理地址。进程切换的过程也就是工作集切换的过程,如Matt Pietrek所说如果只给出虚拟地址而不给出工作集,那这个地址是无意义的。(见图一)

在分页机制所形成的线性地址空间里,我们对内存进行进一步划分涉及的概念有堆、栈、自由存储等。对堆进行操作的APIHeapCreateHeapAlloc等。操纵自由存储的APIVirtualAlloc等。此外内存映射文件使用的也应该算是自由存储的空间。栈则用来存放函数参数和局部变量,随着stack frame的建立和销毁其自动进行增长和缩减。

说到这里,也许有人会提出疑问:对x86 CPU分段机制是必须的,分页机制是可选的。为什么这里只提到了分页机制。那么我告诉你分段机制仍然存在,一是为了兼容以前的16位程序,二是Windows毕竟要区分ring 0ring 3两个特权级。用SoftIce看一下GDT(全局描述表)你基本上会看到如下内容:

GDTbase=80036000 Limit=03FF

0008 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 address0的话,那么这个段可以看作不存在,因为偏移地址就是最终的线性地址。

此外还有两个段存在用于Kernel Processor Control Regionuser thread environment block所以如果你在反汇编时看到MOV ECX,FS:[2C]就不必惊讶,怎么这里使用逻辑地址而不是线性地址。在以后涉及异常处理的地方会对此再做说明。

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" coordsize="21600,21600" o:spt="75" o:preferrelative="t" path="m@4@5l@4@11@9@11@9@5xe" filled="f" stroked="f"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:extrusionok="f" gradientshapeok="t" o:connecttype="rect"></path><lock v:ext="edit" aspectratio="t"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 465.75pt; HEIGHT: 392.25pt" type="#_x0000_t75"><img src="/Develop/ArticleImages/20/20854/CSDN_Dev_Image_2003-9-31653250.png" o:title="1"></shape>

二、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-8]

push eax

mov ecx,dword ptr [ebp-4]

push ecx

此时把函数参数压入堆栈,而stack指针ESP递减,stack空间减小。

在进入函数后,你将会看到如下指令:

push ebp

mov ebp,esp

sub esp,44h

这三句建立stack框架,并减小esp为局部变量预留空间。建立stack框架后,[ebp+*]指向函数参数,[ebp-*]指向局部变量。

另外在很多情况下你会看到如下三条指令

push ebx

push esi

push edi

这三句把三个通用寄存器压入堆栈,这样这三个寄存器就可以用来存放一些变量,进而提升运行速度。

很奇怪,我这个函数根本用不到这三个寄存器,可编译器也生成了上述三条指令。

stack中内容的读取,是靠基址指针ebp进行的。所以对应于sum=x+y;一句你会看到

mov eax,dword ptr [ebp+8]

add eax,dword ptr [ebp+0Ch]

mov dword ptr [ebp-4],eax

其中[ebp+8]x[ebp+0Ch]y,记住压栈方向为从右向左,所以y要在x上边。

我们再看一下函数退出时的情况:

pop edi

pop esi

pop ebx

mov esp,ebp

pop ebp

ret 8

此时恢复stack框架,使esp与刚进入这个函数时相同,ret 8使esp再加8,使esp与没调用这个函数的时候一致。如果使用__cdecl调用规则,则由调用方以类似add esp,8进行清场工作,使stack的大小与未进行函数调用时一致。Stack的使用就这样完全被编译器实现了,只要不溢出就和我们无关,也许也算一种内存的智能管理。最后要补充的两点是:首先stack不像heap会自动扩充,如果你用光了储备,他会准时溢出。其次是不要以为你使用了缺省参数进行链接,你就有1Mstack,看看启动代码你就知道在你拥有stack之前,C Run –Time

Library以用去了一小部分stack的空间。

分享到:
评论

相关推荐

    深入解析windows操作系统第6版下册-内存管理.zip

    虚拟内存是现代操作系统的关键特性,这部分详细阐述了Windows的虚拟内存机制。包括页面交换、页面缓存和工作集的概念,以及如何通过这些技术在物理内存有限的情况下,提供近乎无限的地址空间。同时,探讨了分页文件...

    全面介绍Windows内存管理机制及C++内存分配实例

    本文旨在深入解析Windows内存管理机制,并通过实例讲解如何在C++中有效地分配和使用内存。 首先,我们要了解的是进程的地址空间。在32位系统中,进程地址空间被分为几个主要部分。NULL指针分区从0x0000 0000到0x...

    全面介绍Windows内存管理机制.doc

    【Windows内存管理机制】 Windows内存管理机制是操作系统中至关重要的组成部分,它负责为应用程序分配、管理和回收内存资源。本文将详细解析Windows系统如何管理和利用内存,特别是针对32位和64位系统的区别。 1. ...

    深入解析Windows操作系统.第六版

    《深入解析Windows操作系统》第六版是一本权威且深入的技术书籍,专注于揭示Windows 7和Windows Server 2008 R2操作系统的内核层面的工作原理。本书由David A. Solomon和Mark Russinovich两位专家撰写,他们对于...

    Qt 获取Windows内存信息

    总之,获取Windows内存信息在Qt中可以通过多种方式实现,如使用`QProcess`执行`wmic`命令、非公开的`QMemoryInfo`类或者直接调用Windows API。选择哪种方法取决于项目需求和对稳定性的考虑。无论哪种方式,都要注意...

    windows内存学习总结

    以下是对Windows内存管理的深入解析: 一、内存分配 1. **基本概念**:内存是计算机用来存储数据和程序的地方,分为物理内存和虚拟内存。物理内存即RAM(随机存取存储器),是硬件层面的内存;虚拟内存则是硬盘上...

    windows内存编程讲解

    内存编程是计算机科学中的核心概念,特别是在开发操作系统和低...在Windows环境中,内存管理不仅涉及程序内部,还包括与操作系统交互的部分,如虚拟内存管理和内存保护机制,这些内容对于深入学习Windows编程至关重要。

    深入解析windows操作系统

    《深入解析Windows操作系统》是针对Windows Internals第四版的一份详细知识解读,旨在为读者揭示Windows操作系统的内部工作机制。这本书由权威专家编写,是IT专业人士深入理解Windows内核、系统服务、安全机制以及...

    深入解析Windows操作系统 卷1(英文版·第7版) 上册高清完整版.pdf

    这部分内容将介绍Windows如何分配和管理虚拟内存和物理内存,包括页表结构、分页机制、地址转换过程等。它还会探讨内存管理的一些高级特性,比如内存共享、大页面支持等。 - 更多内容:除了上述几个主题外,作为一...

    《深入解析Windows操作系统 第5版》 [PDF]

    《深入解析Windows操作系统 第5版》是一本专为IT专业人士和高级用户编写的经典书籍,旨在揭示Windows操作系统的深层机制和工作原理。该书详细介绍了Windows操作系统的核心组件、设计架构以及内部运作流程,帮助读者...

    深入解析WINDOWS操作系统(第4版).pdf

    《深入解析:Windows操作系统》(第4版)全书内容丰富、信息全面,主要包括的Windows操作系统深度知识有:理解Windows的关键机制,包括系统服务分发和调度机制、启动和停机,以及注册表;挖掘Windows的安全模型,包括...

    深入解析Windows操作系统中文.part2.rar

    深入解析WINDOWS操作系统(第4版) ISBN:9787121039690 本书是著名的操作系统内核专家Mark Russinovich和David Solomon撰写的Windows操作系统原理的最新版著作,全面和深入地阐述了Windows操作系统的整体结构以及...

    深入解析windows操作系统(英文版)

    《深入解析Windows操作系统》这本书籍可谓Windows操作系统的内核级指南,它不仅详细地解读了Windows操作系统的工作原理和系统架构,还深入地剖析了其底层机制。这本书籍以其英文版的形式,为国际上的IT专业人士,...

    9练习内存解析

    在实际操作中,内存解析工具是必不可少的,如windbg(Windows平台)、gdb(通用调试器)或者Volatility(内存取证框架)。这些工具可以帮助我们查看内存内容,跟踪指针,查找特定数据结构,甚至重建程序的执行流程。...

    Windows物理内存的取证

    1. **API调用分析**:Windows系统使用API(应用程序编程接口)执行各种任务,分析内存中的API调用历史可以帮助确定可疑行为。 2. **进程和线程分析**:通过识别并分析内存中的进程和线程,可以发现隐藏或异常活动,...

    内存映射机制

    #### 三、Windows内存映射机制的关键API 1. **CreateFileMapping**:用于创建一个内存映射文件对象。这个函数接收一个已打开文件的句柄作为参数,并返回一个内存映射文件对象的句柄。 - `HANDLE CreateFileMapping...

    Windows通信机制和DLL机制实验报告

    ##### 3.3 DLL机制解析 动态链接库(DLL)是Windows系统中一种常见的共享库形式,多个应用程序可以同时访问同一DLL中的函数,从而节省内存和提高效率。DLL机制的关键在于: - **加载和卸载**:通过`LoadLibrary`...

    [深入解析Windows操作系统].SysinternalsSuit.pdf文档

    5. 观察Windows内存管理机制。本书解释了Windows如何管理虚拟内存和物理内存,这对于优化内存使用、处理内存泄漏和提高系统整体性能至关重要。 6. 理解NTFS文件系统的操作和格式。Windows系统大多使用NTFS文件系统...

    《系统内存统计使用》实验报告

    - **了解Windows内存管理机制**:深入理解操作系统如何管理和分配内存资源。 - **理解页式存储管理技术**:学习并掌握基于页面的内存管理策略,包括页面置换算法等。 - **熟悉Windows内存管理基本数据结构**:...

Global site tag (gtag.js) - Google Analytics