Windows的内存管理机制

Windows下的内存是如何管理的?

Windows内存的管理可以分为两个层面:物理内存虚拟内存

其中物理内存由系统管理,不允许应用程序直接访问,应用程序可见的只有一个2G的地址空间,而内存分配是通过堆进行的。
对于每个进程都有自己的默认堆,当一个堆创建后,就通过虚拟内存操作保留了相应大小的地址块(不占有实际内存,系统消耗很小)。
当在堆上分配一块内存时,系统在堆的地址表里找到一个空闲块(如果找不到,且堆创建属性是可扩充的,则扩充堆的大小),为这个空闲块所包含的所有内存页提交物理对象(在物理内存上或硬盘的交换文件上),这时就可以访问这部分地址。
提交时,系统将对所有进程的内存统一调配,如果物理内存不够,系统会试图把一部分进程暂时不访问的页放入交换文件中以腾出部分物理内存。
释放内存时,只在堆中将所在的页解除提交(相应的物理对象被解除),继续保留地址空间。

如果要知道某个地址是否被占用/可否访问,只要查询此地址的虚拟内存状态即可

关于内存管理的一些基本概念

32 位 Microsoft Windows 上的每个进程都有自己的虚拟地址空间,可以寻址多达 4 GB 的内存。64 位 Windows 上的每个进程都有 8 TB 的虚拟地址空间。进程的所有线程都可以访问其虚拟地址空间。但是,线程无法访问属于另一个进程的内存,这可以防止一个进程被另一个进程损坏(这个地址空间仅是内存地址空间不是物理内存,并不代表真的有这么大的内存可以用)

windows中 我们一般编程时接触的都是线性地址 也就是我们所说的虚拟地址,它不是数据真实存在的地方.换句话说我们在 0 x 80000000 ( 虚 拟 地 址 ) 0x80000000(虚拟地址) 0x80000000()的地方写入了 " U E S T C " "UESTC" "UESTC"这个字符串,但是我们这个字符串并不真实存在于物理地址的 0 x 80000000 0x80000000 0x80000000这里.再说了真实的物理地址是利用一段N长的数组来定位的(额~看不懂这句话没关系,一会看到物理地址那你就明白了).
但是为什么windows乃至linux都需要采取这种方式来寻址呢?原因很简单 为了安全.听说过保护模式吧?顾名思义就是这个模式下加入了保护系统安全的措施,同样采用线性地址也是所谓的安全措施之一.
我们假设下如果没有使用线性地址,那么我们可以直接访问物理地址,但是这样的话当我们往内存写东西的时候操作系统无法检查这块内存是否可写,换句话说操作系统无法实现对页面访问控制.这点是很可怕的事情,就如win9x那样可以在应用态往内核地址写东西,还有没有天理了
由于操作系统的安全需要,催生了虚拟地址的应用.在CPU中有个叫 M M U ( M e m o r y M a n a g e U n i t 内 存 管 理 单 元 ) MMU(Memory Manage Unit 内存管理单元) MMU(MemoryManageUnit)的东西,专门负责线性地址和物理地址之间的转化.我们每次读写内存,从CPU的结构看来都要经过ALU,ALU拿到虚拟地址后扔给MMU转化成物理地址后再把数据读入寄存器中.

物理内存

物理内存:人尽皆知,就是插在主板上的内存条。他是固定的,内存条的容量多大,物理内存就有多大(集成显卡系统除外)。但是如果程序运行很多或者程序本身很大的话,就会导致大量的物理内存占用,甚至导致物理内存消耗殆尽

虚拟内存

虚拟内存:简明的说,虚拟内存就是在硬盘上划分一块页面文件,充当内存。当程序在运行时,有一部分资源还没有用上或者同时打开几个程序却只操作其中一个程序时,系统没必要将程序所有的资源都塞在物理内存中,于是,系统将这些暂时不用的资源放在虚拟内存上,等到需要时在调出来用。

物理地址

物理地址(physical address):用于内存芯片级的单元寻址,与处理器和CPU连接的地址总线相对应。

——这个概念应该是这几个概念中最好理解的一个,但是值得一提的是,虽然可以直接把物理地址理解成插在机器上那根内存本身,把内存看成一个从0字节一直到 最大空量逐字节的编号的大数组,然后把这个数组叫做物理地址,但是事实上,这只是一个硬件提供给软件的抽象,内存的寻址方式并不是这样。所以,说它是“与 地址总线相对应”,是更贴切一些,不过抛开对物理内存寻址方式的考虑,直接把物理地址与物理的内存一一对应,也是可以接受的。

逻辑地址

逻辑地址(logical address):是指由程序产生的与段相关的偏移地址部分。

例如,你在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址,不和绝对物理地址相干。只有在Intel实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,Cpu不进行自动地址转换);逻辑也就是在Intel 保护模式下程序执行代码段限长内的偏移地址(假定代码段、数据段如果完全一样)。应用程序员仅需与逻辑地址打交道,而分段和分页机制对您来说是完全透明的,仅由系统编程人员涉及。应用程序员虽然自己可以直接操作内存,那也只能在操作系统给你分配的内存段操作。

页表、页目录概念

使用了分页机制之后,4G的地址空间被分成了固定大小的页,每一页或者被映射到物理内存,或者被映射到硬盘上的交换文件中,或者没有映射任何东西。对于一般程序来说,4G的地址空间,只有一小部分映射了物理内存,大片大片的部分是没有映射任何东西。物理内存也被分页,来映射地址空间。对于32bit的 Win2k,页的大小是4K字节。CPU用来把虚拟地址转换成物理地址的信息存放在叫做页目录和页表的结构里。
物理内存分页,一个物理页的大小为4K字节,第0个物理页从物理地址 0x00000000 处开始。由于页的大小为4KB,就是0x1000字节,所以第1页从物理地址 0x00001000处开始。第2页从物理地址0x00002000处开始。可以看到由于页的大小是4KB,所以只需要32bit的地址中高20bit来寻址物理页。
页目录: 一个页目录大小为4K字节,放在一个物理页中。由1024个4字节的页目录项组成。页目录项的大小为4 个字节(32bit),所以一个页目录中有1024个页目录项。页目录中的每一项的内容(每项4个字节)高20bit用来放一个页表(页表放在一个物理页中)的物理地址,低12bit放着一些标志。
页表: 一个页表的大小为4K字节,放在一个物理页中。由1024个4字节的页表项组成。页表项的大小为4个字节 (32bit),所以一个页表中有1024个页表项。页表中的每一项的内容(每项4个字节,32bit)高20bit用来放一个物理页的物理地址,低 12bit放着一些标志。
对于x86系统,页目录的物理地址放在CPU的CR3寄存器中。


用户操作内存

Windows提供了3种方法来进行内存管理:

  1. 虚拟内存:最适合用来管理大型对象或者结构数组
  2. 内存映射文件:最适合用来管理大型数据流(通常来自文件)以及在单个计算机上运行多个进程之间共享数据
  3. 内存堆栈:最适合用来管理大量的小对象

虚拟内存操作方式

windows 提供了一组用来操控虚拟内存的函数,我们可以通过他们直接预定地址空间区域,给区域调拨物理存储器,设置保护属性等。

具体的API可以参看 官方虚拟内存接口文档

内存映射文件的操作方式

内存映射文件与虚拟内存的不同之处在于给预定的地址空间区域调拨的物理存储器。
虚拟内存调拨的物理存储器是内存或者来至系统的页交换文件;
内存映射文件调拨的是磁盘上的一个文件。
内存映射文件主要用在一下3个方面,即加载可执行文件、访问数据文件、多进程共享数据。

详细代码可以参考官方文档:
window创建内存映射文件

堆的操作方式

堆是对虚拟内存的封装,可以使我们专注于解决实际业务问题,而不是考虑分配粒度、页面边界等问题。在系统内部,堆就是一块预定好的地址区域,随着不断的分配堆内存,堆管理器不断的调拨物理存储设备,释放堆内存也就释放已经调拨的存储器。

详细代码可以参考官方文档:
windows获取进程堆


总结

内存管理方式建议大家多看看计算机组成原理,回过头来看windows的内存管理机制就能更好的明白windows为啥这么做~而不是直接让你操作物理内存

参考

windows官方文档对内存管理的描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AlbertOS

还会有大爷会打钱?

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值