本博文很大程度上参考了,潘爱民先生的《Windows内核原理与实现》一书,在此对他表示感谢。

记得是在学C语言指针的时候,首次比较实际的使用内存寻址。也是在那个时候知道不能使用未初始化的指针,记得当时老师还说过,如果使用了未初始化的指针,轻则运行错误,重则操作系统崩溃。现在看起来那个重则系统崩溃还是比较可笑的,如果真的这么容易就让系统崩溃,那么Windows早就被用户抛弃了。而且我在调程序的时候,如果出现指针解引用错误,基本都是让系统直接终止掉我的程序,Windows一向安然无恙。当然,也许老师指的是DOS环境,不过我在dos下只写过汇编代码。

我在学C语言之后的较长一段时间,都天真的认为我的程序在运行时候是直接使用物理内存的。那个时候我做过这样的尝试:访问地址为0x0的内存值。结果可想而知。那个时候我只知道那块内存系统禁止访问,觉得也许是因为操作系统在时刻监视着所有进程代码的原因吧。再后来写C程序输出地址的时候,发现每次程序变量的内存地址都差不多,开始有点怀疑Windows是怎么让我的程序跑起来的。因为我觉得每次程序运行的变量地址应该有较大的偏差才是。而实际结果却是,不论我计算机运行程序的数量多少,当前物理内存的使用量是多少,地址范围始终都差不多,而且地址的位置比较低,好像二进制的高8位一直都是0。再后来看Windows的时候,才知道,我在程序中并非直接使用物理地址,而是一个虚拟地址。

所以,我找了本Windows内核的书好好看了看,并写了这篇博文。

因为内存管理的优劣会直接影响到系统本身的性能,所以操作系统管理内存的方法一般要根据处理器的硬件情况来决定。Windows的主要硬件体系结构就是Intel的架构,所以在内存管理上就受很多的x86架构的影响。另外为了能让多个进程之间不互相干扰破坏,并与操作系统隔离,Windows在处理器寻址的基础上,又应用了大量的内存管理技术来满足各种要求。在所有进程对于内存的要求已经超过了实际物理内存的时候,Windows还必须要平衡各种需求,借助如硬盘的外部存储,使计算机能够正常运行。由此可见,内存管理是所有操作系统非常重要的一部分。

首先,在x86体系中,内存地址有三类:

1 物理地址(直接对应物理内存的地址,一般为32位或36位的无符号整数)

2 虚拟地址(也称作线性地址,处理器使用前需要转换为物理地址,为32位无符号整数,因此地址空间范围为4GB)

3 逻辑地址(学过8086汇编的人都知道,逻辑地址分为两个部分:段地址和偏移地址,这里也是一样的)

然后再介绍一下两种比较主流的内存管理方式:页式内存管理,段式内存管理。

1 页式内存管理

如果直接让进程使用物理地址来访问内存将会使进程的动态内存分配难以有效实施,因为内存单元和进程紧密的联系在了一起,从而使内存的回收和再分配受限于特定的进程和物理地址。一种简单的思路就是让进城使用虚拟地址,在虚拟地址和物理地址之间建立一个映射表来完成转译。

页式内存管理中,虚拟地址空间是按照页来管理的,对应的物理内存同样是按照页来管理,二者的页大小是相同的。而因为这样的一个转译关系存在,在虚拟地址中连续的页面在物理地址中可以不连续。通过维护这样的一个转译关系,物理页面可以被动态的分配给虚拟页面,这样可以做到当虚拟页面真正被使用的时候才为其分配物理页面,这样的好处就是能够节省物理内存,使用效率更高。

另外需要注意的是,在一个系统中,物理地址空间只有一个,而虚拟内存空间可以有多个。而且每个虚拟内存空间都必须要维护一个映射关系,这样每个进程的地址空间都是独立的。进程A地址为0x00FF0000对应的物理地址和进程B的0x00FF0000地址对应的物理地址就不相同。另外,每个虚拟地址空间实际映射的物理页面很少。而物理页面一般只被映射到一个虚拟地址空间中。当然存在例外,比如DLL在被多个进程使用的时候,其函数所在的物理页面必然被映射到了多个进程的虚拟地址空间中。

在页面划分机制下,32位的虚拟地址被分为了两个部分:页索引+页内偏移。Intel x86下的页面大小标准为4KB,也就是2^12。因此,虚拟地址中的低12位可以用于页内偏移。前20位可以用于页索引部分,用于找到一个实际的物理页面。这样的页面映射表大小就是1M。若用线性关系表示的话,那么就需要用4M的内存,因为一个地址的大小是4Byte。不过这种方式浪费比较严重,毕竟Windows默认线程栈的大小也不过才1M。

另外在这1M的地址空间中,还有很大一部分的表项并未使用,浪费很严重。

所以Intel x86的虚拟地址解析过程如下:

32位的虚拟地址被分为了3个部分:31 - 22 页目录索引 21 - 12 页表索引 11- 0 页内偏移

1 对于一个虚拟地址,首先解析页目录索引,在也目录中查找。页目录的大小就是4KB。页目录索引的首地址位于CR3寄存器中。

2 在目录中找到后,再根据页表索引来找到页对应的物理地址。

3 最后加上页内偏移,就定位到了最终的物理地址。

可以知道,这样下来需要使用的空间达到了4KB+4MB的大小,但是这4MB的页表索引在对应页表不使用的时候完全可以不构造出来,从而节省大量的内存。

同时,因为二级页表的查找也需要付出一定性能上的代价,毕竟多了一次查找。对于这个问题,intel x86处理器缓存了地址转译信息,也就是虚拟地址到物理地址的映射关系。这样当处理器重复访问一个虚拟地址时,就不用再次进行转译了。此缓存被称为TLB,中文意思就是:地址转译快查缓冲区。

TLB是硬件级的地址查找支持电路,专门用来做这个,效率非常高。也因此,这一块是软件所无法触及的,知道优惠这么个东西就OK了。因为TLB的存在,二级查找引发的性能下降就不是很明显了。

另外当CR3寄存器的值改变的时候,TLB中的数据就全部无效了,因为这意味着进程的切换,之前的映射关系自然就不再成立了。

另外Intel x86 Pentium Pro还提供了成为PAE物理地址扩展的内存映射模式,支持36位物理地址,但虚拟地址仍旧是32位。而且PAE采用的是3级页表机制。但是基本原理一样。

段式内存管理还看得不是很清楚,看明白了再把整理篇博文发出来。

Windows内存管理的更多相关文章

  1. Windows内存管理[转]

    本文主要内容:1.基本概念:物理内存.虚拟内存:物理地址.虚拟地址.逻辑地址:页目录,页表2.Windows内存管理3.CPU段式内存管理4.CPU页式内存管理 一.基本概念1. 两个内存概念物理内存 ...

  2. windows内存管理方式以及优缺点

    Windows内存管理方式:页式管理,段式管理,段页式管理 页式管理 将各进程的虚拟空间(逻辑地址)划分为若干个长度相等的页,业内管理把内存空间(物理内存)按照页的大小划分为片或者页面,从而实现了离散 ...

  3. windows 内存管理的几种方式及其优缺点

    windows 内存管理方式主要分为:页式管理,段式管理,段页式管理. 页式管理的基本原理是将各进程的虚拟空间划分为若干个长度相等的页:页式管理把内存空间按照页的大小划分成片或者页面,然后把页式虚拟地 ...

  4. 全面介绍Windows内存管理机制及C++内存分配实例(四):内存映射文件

    本文背景: 在编程中,很多Windows或C++的内存函数不知道有什么区别,更别谈有效使用:根本的原因是,没有清楚的理解操作系统的内存管理机制,本文企图通过简单的总结描述,结合实例来阐明这个机制. 本 ...

  5. Windows内存管理的方式

    一.内存的概念 1. 物理内存:即插在主板上的内存条.他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外). 但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存占用,甚至导 ...

  6. 20140919 进程间通信 系统栈 用户栈 多级反馈队列 windows 内存管理

    1.进程间通信 共享内存(剪切板) 匿名管道只能实现父子进程间的通信(以文件系统为基础): 匿名管道是什么,有什么用,怎么用 1.创建父进程,也就是在解决方案中建立一个parent的工程 2.在par ...

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

    转自:http://blog.csdn.net/yeming81/article/details/2046193 本文基本上是windows via c/c++上的内容,笔记做得不错.. 本文背景: ...

  8. Windows内存管理简介:

    1:连续的内存空间分配: (1)单一连续分配:只能单作业,单任务运行: 分为系统和用户区:用户区是指除了系统需外左右的内存,由于单用户,单任务,要不都被占用,要不全空   (2):固定空间分配:固定分 ...

  9. Windows程序设计学习笔记(一)Windows内存管理初步

    学习Windows程序设计也有一些时间了,为了记录自己的学习成果,以便以后查看,我希望自己能够坚持写下一系列的学习心得,对自己学习的内容进行总结,同时与大家交流.因为刚学习所以可能有的地方写不不正确, ...

随机推荐

  1. img加载不出来,给个默认图片。

    忽然发现,jq里也有坑,很多东西莫名其妙的被废弃了……所以,只能用原生js来做了: $('img').each(function() { if (!this.complete || typeof th ...

  2. 通过url判断当前页,动态给导航加样式

    //通过url判断当前页,动态给导航加样式 var str =location.pathname; var index = str.lastIndexOf("\/"); str = ...

  3. 山东BOSS性能压力测试

    1. 概述 在山东BOSS性能压力测试过程中,发现脚本对于整个压力测试过程的重要性,一个压力测试脚本录制和编辑修改得怎么样直接影响后面压力测试的执行.通常情况下,脚本应尽可能的精简,就像写代码一样.针 ...

  4. Spring注解@Scope("prototype")

    spring 默认scope 是单例模式 这样只会创建一个Action对象 每次访问都是同一个Action对象,数据不安全 struts2 是要求 每次次访问 都对应不同的Action scope=& ...

  5. Sqlite的导入导出功能

    导出,使用dump命令 导入,使用read命令 可以直接执行,类似 sqlite xxx.db3 ".read ../sss.sql"

  6. HDU 2089 不要62【数位DP入门题】

    不要62 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submis ...

  7. Bzoj3942 Censoring(KMP)

    \(KMP\)问题的核心在于数组\(next\)(或者\(pre\)/\(fail\),各种叫法),几乎所有的此类型题都是需要计算\(next\)的. 这里解释一波\(next\):即满足字符子串\( ...

  8. Codeforces 980 E. The Number Games

    \(>Codeforces \space 980 E. The Number Games<\) 题目大意 : 有一棵点数为 \(n\) 的数,第 \(i\) 个点的点权是 \(2^i\) ...

  9. MySql数据库理解

    在之前的面试过程中,有被问到很多次,关于MySQL数据库相关知识,其中有问到了解存储引擎,数据库优化等问题,问得一脸懵X,确实以前在学习的时候没有去深入了解过这一块儿,今天找到了相应的数据库视频,稍稍 ...

  10. 通过scrapy内置的ImagePipeline下载图片到本地、并提取本地保存地址

    1.通过scrapy内置的ImagePipeline下载图片到本地 2.获取图片保存本地的地址 1.通过scrapy内置的ImagePipeline下载图片到本地 1)在settings.py中打开  ...