没有合适的资源?快使用搜索试试~ 我知道了~
首页全面介绍Windows内存管理机制
以下为文章节选 本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制。 本文目的: 对Windows内存管理机制了解清楚,有效的利用C++内存函数管理和使用内存。 本文内容: 本文一共有六节,由于篇幅较多,故按节发表。 1. 进程地址空间 1.1地址空间
资源详情
资源评论
资源推荐
全面介绍 Windows 内存管理机制及 C++内存分
配实例
文章整理: www.diybl.com 文章来源: 网络 -
-
本文背景:
在编程中,很多 Windows 或 C++的内存函数不知道有什么区别,更别谈有效使
用;根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过
简单的总结描述,结合实例来阐明这个机制。
本文目的:
对 Windows 内存管理机制了解清楚,有效的利用 C++内存函数管理和使用内存。
本文内容:
本文一共有六节,由于篇幅较多,故按节发表。
1. 进程地址空间
1.1 地址空间
32|64 位的系统|CPU
操作系统运行在硬件 CPU 上,32 位操作系统运行于 32 位 CPU 上,
64 位操作系统运行于 64 位 CPU 上;目前没有真正的 64 位 CPU。
32 位 CPU 一次只能操作 32 位二进制数;位数多 CPU 设计越复杂,软件
设计越简单。
软件的进程运行于 32 位系统上,其寻址位也是 32 位,能表示的空间
是 2
32
=4G,范围从 0x0000 0000~0xFFFF FFFF。
NULL 指针分区
范围:0x0000 0000~0x0000 FFFF
作用:保护内存非法访问
例子:分配内存时,如果由于某种原因分配不成功,则返回空指针
0x0000 0000;当用户继续使用比如改写数据时,系统将因为发生访问违
规而退出。
那么,为什么需要那么大的区域呢,一个地址值不就行了吗?我在
想,是不是因为不让 8 或 16 位的程序运行于 32 位的系统上呢?!因为
NULL 分区刚好范围是 16 的进程空间。
独享用户分区
范围:0x0001 0000~0x7FFE FFFF
作用:进程只能读取或访问这个范围的虚拟地址;超越这个范围的行为
都会产生违规退出。
例子:
程序的二进制代码中所用的地址大部分将在这个范围,所有 exe 和
dll 文件都加载到这个。每个进程将近 2G 的空间是独享的。
注意:如果在 boot.ini 上设置了/3G,这个区域的范围从 2G 扩大为 3G:
0x0001 0000~0xBFFE FFFF。
共享内核分区
范围:0x8000 0000~0xFFFF FFFF
作用:这个空间是供操作系统内核代码、设备驱动程序、设备 I/O 高速
缓存、非页面内存池的分配、进程目表和页表等。
例子:
这段地址各进程是可以共享的。
注意:如果在 boot.ini 上设置了/3G,这个区域的范围从 2G 缩小为 1G:
0xC000 0000~0xFFFF FFFF。
通过以上分析,可以知道,如果系统有 n 个进程,它所需的虚拟空
间是:2G*n+2G (内核只需 2G 的共享空间)。
1.2 地址映射
区域
区域指的是上述地址空间中的一片连续地址。区域的大小必须是粒度
(64k) 的整数倍,不是的话系统自动处理成整数倍。不同 CPU 粒度大小
是不一样的,大部分都是 64K。
区域的状态有:空闲、私有、映射、映像。
在你的应用程序中,申请空间的过程称作保留(预订),可以用
VirtualAlloc;删除空间的过程为释放,可以用 VirtualFree。
在程序里预订了地址空间以后,你还不可以存取数据,因为你还没
有付钱,没有真实的 RAM 和它关联。
这时候的区域状态是私有;
默认情况下,区域状态是空闲;
当 exe 或 DLL 文件被映射进了进程空间后,区域状态变成映像;
当一般数据文件被映射进了进程空间后,区域状态变成映射。
物理存储器
Windows 各系列支持的内存上限是不一样的,从 2G 到 64G 不等。理论
上 32 位 CPU,硬件上只能支持 4G 内存的寻址;能支持超过 4G 的内存
只能靠其他技术来弥补。顺便提一下,Windows 个人版只能支持最大 2G
内存,Intel 使用 Address Windows Extension (AWE) 技术使得寻址范围为
2
36
=64G。当然,也得操作系统配合。
内存分配的最小单位是 4K 或 8K,一般来说,根据 CPU 不同而不同,
后面你可以看到可以通过系统函数得到区域粒度和页面粒度。
页文件
页文件是存在硬盘上的系统文件,它的大小可以在系统属性里面设置,
它相当于物理内存,所以称为虚拟内存。事实上,它的大小是影响系统
快慢的关键所在,如果物理内存不多的情况下。
每页的大小和上述所说内存分配的最小单位是一样的,通常是 4K 或
8K。
访问属性
物理页面的访问属性指的是对页面进行的具体操作:可读、可写、可执
行。CPU 一般不支持可执行,它认为可读就是可执行。但是,操作系统
提供这个可执行的权限。
PAGE_NOACCESS
PAGE_READONLY
PAGE_READWRITE
PAGE_EXECUTE
PAGE_EXECUTE_READ
PAGE_EXECUTE_READWRITE
这 6 个属性很好理解,第一个是拒绝所有操作,最后一个是接受收有操
作;
PAGE_WRITECOPY
PAGE_EXECUTE_WRITECOPY
这两个属性在运行同一个程序的多个实例时非常有用;它使得程序可以
共享代码段和数据段。一般情况下,多个进程只读或执行页面,如果要
写的话,将会 Copy 页面到新的页面。通过映射 exe 文件时设置这两个属
性可以达到这个目的。
PAGE_NOCACHE
PAGE_WRITECOMBINE
这两个是开发设备驱动的时候需要的。
PAGE_GUARD
当往页面写入一个字节时,应用程序会收到堆栈溢出通知,在线程堆栈
时有用。
映射过程
进程地址空间的地址是虚拟地址,也就是说,当取到指令时,需要把虚
拟地址转化为物理地址才能够存取数据。这个工作通过页目和页表进行。
从图中可以看出,页目大小为 4K,其中每一项(32 位)保存一个页表的物
理地址;每个页表大小为 4K,其中每一项(32 位)保存一个物理页的物理
地址,一共有 1024 个页表。利用这 4K+4K*1K=4.4M 的空间可以表示进
程的 1024*1024* (一页 4K) =4G 的地址空间。
进程空间中的 32 位地址如下:
高 10 位用来找到 1024 个页目项中的一项,取出页表的物理地址后,利
用中 10 位来得到页表项的值,根据这个值得到物理页的地址,由于一页
有 4K 大小,利用低 12 位得到单元地址,这样就可以访问这个内存单元
了。
每个进程都有自己的一个页目和页表,那么,刚开始进程是怎么找
到页目所在的物理页呢?答案是 CPU 的 CR3 寄存器会保存当前进程的页
目物理地址。
当进程被创建时,同时需要创建页目和页表,一共需要 4.4M。在进
程的空间中,0xC030 0000~0xC030 0FFF 是用来保存页目的 4k 空间。
0xC000 0000~0xC03F FFFF 是用来保存页表的 4M 空间。也就是说程序
里面访问这些地址你是可以读取页目和页表的具体值的(要工作在内核
方式下)。有一点我不明白的是,页表的空间包含了页目的空间!
至于说,页目和页表是保存在物理内存还是页文件中,我觉得,页
目比较常用,应该在物理内存的概率大点,页表需要时再从页文件导入
物理内存中。
页目项和页表项是一个 32 位的值,当页目项第 0 位为 1 时,表明页
表已经在物理内存中;当页表项第 0 位为 1 时,表明访问的数据已经在
内存中。还有很多数据是否已经被改变,是否可读写等标志。另外,当
页目项第 7 位为 1 时,表明这是一个 4M 的页面,这值已经是物理页地址,
用虚拟地址的低 22 位作为偏移量。还有很多:数据是否已经被改变、是
否可读写等标志。
1.3 一个例子
编写生成软件程序 exe
软件描述如下:
Main ()
{
1:定义全局变量
2:处理函数逻辑(Load 所需 DLL 库,调用方法处理逻辑)
3:定义并实现各种方法(方法含有局部变量)
4:程序结束
}
将程序编译,生成 exe 文件,附带所需的 DLL 库。
exe 文件格式
exe 文件有自己的格式,有若干节(section):.text 用来放二进制代
码(exe 或 dll);.data 用来放各种全局数据。
.text
指令 1:move a, b
指令 2:add a, b
…
.data
数据 1:a=2
数据 2:b=1
…
这些地址都是虚拟地址,也就是进程的地址空间。
运行 exe 程序
建立进程:运行这个 exe 程序时,系统会创建一个进程,建立进程控
制块 PCB,生成进程页目和页表,放到 PCB 中。
数据对齐:数据的内存地址除以数据的大小,余数为 0 时说明数据
是对齐的。现在的编译器编译时就考虑数据对齐的问题,生成 exe 文
件后,数据基本上是对齐的,CPU 运行时,寄存器有标志标识 CPU
是否能够自动对齐数据,如果遇到不能对齐的情况,或者通过两次
访问内存,或者通知操作系统处理。
要注意的是,如果数据没有对齐,CPU 处理的效率是很低的。
文件映射:系统不会将整个 exe 文件和所有的 DLL 文件装载进物理
内存中,同时它也不会装载进页面文件中。相反,它会建立文件映
射,也就是利用 exe 本身当作页面文件。系统将部分二进制代码装载
进内存,分配页面给它。
假设分配了一个页面,物理地址为 0x0232 FFF1。其中装载的一
个指令虚拟地址为 0x4000 1001=0100 0000 00 0000 0000 01 0000 0000
0001。一个页面有 4K,系统会将指令保存在低 12 位 0x0001 的地址
处。同时,系统根据高 10 位 0x0100 找到页目项,如果没有关联的页
表,系统会生成一个页表,分配一个物理页;然后,根据中 10 位
0x0001 找到表项,将物理地址 0x0232 FFF1 存进去。
执行过程:
执行时,当系统拿到一个虚拟地址,就根据页目和页表找到数据的
地址,根据页目上的值可以判断页表是在页文件中还是在内存中;
如果在页文件中,会将页面导入内存,更新页目项。读取页表项的
值后,可以判断数据页文件中还是在物理内存中;如果在页文件中,
会导入到内存中,更新页表项。最终,拿到了数据。
在分配物理页的过程中,系统会根据内存分配的状况适当淘汰
暂时不用的页面,如果页面内容改变了(通过页表项的标志位),
保存到页文件中,系统会维护内存与页文件的对应关系。
由于将 exe 文件当作内存映射文件,当需要改变数据,如更改全局变
量的值时,利用 Copy-On-Write 的机制,重新生成页文件,将结果保
存在这个页文件中,原来的页文件还是需要被其他进程实例使用的。
在清楚了指令和数据是如何导入内存,如何找到它们的情况下,
剩下的就是 CPU 不断的取指令、运行、保存数据的过程了,当进程
结束后,系统会清空之前的各种结构、释放相关的物理内存和删除
页文件。
2. 内存状态查询函数
2.1 系统信息
Windows 提供 API 可以查询系统内存的一些属性,有时候我们需要获取一
些页面大小、分配粒度等属性,在分配内存时用的上。
请看以下 C++程序:
SYSTEM_INFO sysInfo;
GetSystemInfo(&sysInfo);
cout<<"机器属性:"<<endl;
cout<<"页大小="<<sysInfo.dwPageSize<<endl;
cout<<"分配粒度="<<sysInfo.dwAllocationGranularity<<endl;
cout<<"用户区最小值="<<sysInfo.lpMinimumApplicationAddress<<endl;
cout<<"用户区最大值="
<<sysInfo.lpMaximumApplicationAddress<<endl<<endl;
结果如下:
剩余20页未读,继续阅读
没有蛀牙lm
- 粉丝: 3444
- 资源: 41
上传资源 快速赚钱
- 我的内容管理 收起
- 我的资源 快来上传第一个资源
- 我的收益 登录查看自己的收益
- 我的积分 登录查看自己的积分
- 我的C币 登录后查看C币余额
- 我的收藏
- 我的下载
- 下载帮助
会员权益专享
最新资源
- zigbee-cluster-library-specification
- JSBSim Reference Manual
- c++校园超市商品信息管理系统课程设计说明书(含源代码) (2).pdf
- 建筑供配电系统相关课件.pptx
- 企业管理规章制度及管理模式.doc
- vb打开摄像头.doc
- 云计算-可信计算中认证协议改进方案.pdf
- [详细完整版]单片机编程4.ppt
- c语言常用算法.pdf
- c++经典程序代码大全.pdf
- 单片机数字时钟资料.doc
- 11项目管理前沿1.0.pptx
- 基于ssm的“魅力”繁峙宣传网站的设计与实现论文.doc
- 智慧交通综合解决方案.pptx
- 建筑防潮设计-PowerPointPresentati.pptx
- SPC统计过程控制程序.pptx
资源上传下载、课程学习等过程中有任何疑问或建议,欢迎提出宝贵意见哦~我们会及时处理!
点击此处反馈
安全验证
文档复制为VIP权益,开通VIP直接复制
信息提交成功
评论0