hur.cn - 华软网

 热门搜索

进程地址空间和虚拟内存

  作者:未知    来源:网络    更新时间:2009/6/19
我对于虚拟地址空间和虚拟内存的理解:32位的CPU中,一个进程都有个4G的虚拟地址空间;虚拟内存是存在于硬盘上的页交换文件。
前段时间听了一个报告,报告人竟然一再强调说MMU管理的是虚拟内存,个人认为MMU管理的应该是进程的虚拟地址空间吧?
回来查了下《WINODWS核心编程》,发现第五版的中文版本中对这两个概念说的也不清楚,如P362页:磁盘上的文件一般被称为页交换文件,其中包含虚拟内存。难道页交换文件不是等价于虚拟内存吗?

欢迎大家讨论,最后能得出个一致的结论。
---华软 网友回答---
就没哥哥给我回答个,是不是太EASY,呵呵
---华软网友回复---
硬盘上的文件是虚拟内存交换到磁盘的结果,虚拟内存就是你的程序能访问到的那些内存,这是相对于真正的内存物理地址而言,这是我的理解。我觉得,windows核心已经解释得足够清晰了,如果认为模糊,那么多读几次,另外补下基础知识,其实,有几个人完全清楚捏-_-
---华软网友回复---
第一个问题:
    虚拟内存是一个4G的地址空间,是一个空间,有部分对应实在的代码和数据,还有很多是空的地址,不对应实际的代码和数据。
第一个疑点:
    MMU管理的确实是虚拟内存,它是CPU的一个物理部件。
第二个问题:
    虚拟内存对应实在的代码和数据存在两个地方,一个是按页存入物理内存(以下简称内存),另一个是按页存入硬盘。在硬盘上的部分是以文件形式存在,目的是为了换入换出内存,所以叫页交换文件。
    说明白点:页交换文件不是虚拟内存(白马非马),是其中的一种存在形式,另一种形式是内存(黑马也是马)。
问题扩充:
    举例,可执行文件运行时要先装载,但并没有把所有代码和数据从硬盘复制到内存(内存和硬盘中各有部分,并且以页形式存在,一般页有4KB大小),当程序执行到某个代码或数据时,如果内存中没有,那么CPU就产生一个异常(因为CPU只能直接存取在内存中的代码和数据,不能直接访问硬盘中的),然后通过MMU控制把对应的代码或数据的页从硬盘调入内存,再回到产生异常的指令,重新执行(当然代码或数据已经在内存,这次不产生异常了)。
问题溯源:
    那为什么不全部调入内存?
    因为要在我的32M的物理内存的586电脑中执行大于32M的程序(比如WIN2000)。
    还有一个好处,可以把物理内存中长时间没用到的数据和代码页移出到硬盘,腾出内存空间我再开始运行另一个新的程序。
    还有一个好处,....虚拟内存的用处很多。
---华软网友回复---
虚拟内存就是用页面文件来充当内存使用.
某种意义上是一致的,但不是绝对相等的
---华软网友回复---
“还有一个好处,可以把物理内存中长时间没用到的数据和代码页出到硬盘中的页交换文件,腾出内存空间我再开始运行另一个新的程序。”

这样改一下更好理解。
---华软网友回复---
虚拟内存是一个Windows内存管理的概括
非要说是由什么组成的:物理内存、页文件和映射文件都是虚拟内存机制的一部分

说虚拟内存就是页文件并不正确,映射文件的部分后援存储器都是直接对应磁盘文件的,而不是页文件
---华软网友回复---
非常感谢以上各位的回答,谈谈自己的看法。
microyzy:虚拟内存就是你的程序能访问到的那些内存,我认为这句话是不确切的。程序的进程地址空间的用户区所有映射的内存,我的程序应该都能访问,包括实际的物理内存。
tumen:虚拟内存是一个4GB的地址空间,-----。我认为虚拟内存理论上是最大值是4GB的地址空间(32位寻址),但实际上虚拟内存仅仅是进程地址中映射到页交换文件中的那部分。
wocow3:说虚拟内存就是页文件并不正确,映射文件的部分后援存储器都是直接对应磁盘文件的,而不是页文件。文件映射部分绝对不是虚拟内存机制。

综合以上各位的回答,有几个相互混淆的概念:
1、进程地址空间和虚拟内存应该不是同一概念,在tumen的叙述中我觉得存在将两者等价的嫌疑;
2、虚拟内存和实际的物理内存绝对不是一个谁包含于谁的概念。在tumen、wocow3的叙述中都有将物理内存划入到是虚拟内存机制的一部分之嫌疑。我觉得虚拟内存应该是相对于物理内存而言的。

目前我的理解:
每个进程都有个4G的进程虚拟地址空间,虚拟地址空间和实际的物理存储介质(包括物理内存、硬盘等)具有映射关系,而映射到页交换文件的那部分即为虚拟内存。

请大家批评修正。

---华软网友回复---
引用 4 楼 sandyandy 的回复:
虚拟内存就是用页面文件来充当内存使用. 
 某种意义上是一致的,但不是绝对相等的
某种意义上是一致的?哪种意义上是一致的?
但不是绝对相等的?什么时候体现出区别?
还请sandyandy兄详细指教。
---华软网友回复---
代码和数据要放到内存中才能跑
虚拟内存就是一种机制,让有限的物理内存运行更大更多的程序。
当物理内存中不够时,需要将内存的页换出或是丢弃。
那么对于映射文件,直接在内存中清除就可以了,而不是换出到页文件保存。
---华软网友回复---
搅胡了~~
虚拟内存空间就是操作系统让你觉得你能使用的内存空间吧。
---华软网友回复---
引用 9 楼 wocow3 的回复:
代码和数据要放到内存中才能跑 
 虚拟内存就是一种机制,让有限的物理内存运行更大更多的程序。 
 当物理内存中不够时,需要将内存的页换出或是丢弃。 
 那么对于映射文件,直接在内存中清除就可以了,而不是换出到页文件保存。
如果说虚拟内存是一种机制的话,我同意,但是,虚拟内存机制和文件映射的机制明显不是一样的。
虚拟内存机制应该只是内存管理机制中的一种。
虚拟内存机制是在给进程地址空间划拨实际物理存储器时,用的是页交换文件。不知这样wocow3兄是否同意?
---华软网友回复---
虚拟内存机制是在给进程地址空间划拨实际物理存储器时,用的是页交换文件。
-------------------
当然不全是,可执行程序的代码段多是直接映射内存的。在划拨实际物理存储器不用页交换文件
---华软网友回复---
每个进程都有独立的虚拟内存
---华软网友回复---
引用 12 楼 wocow3 的回复:
虚拟内存机制是在给进程地址空间划拨实际物理存储器时,用的是页交换文件。 
 ------------------- 
 当然不全是,可执行程序的代码段多是直接映射内存的。在划拨实际物理存储器不用页交换文件
可执行程序的代码段是用的直接映射,划拨存储器时也是不用页交换文件.但是这种方式就不是
虚拟内存机制,而是文件映射.
---华软网友回复---
    搞深了。如果不怕不明白,我就往深处说说。

    虚拟内存世上本不存在,在8086,或者说80186(不要说没听过,后来多用于单片机了)以前,程序对内存的访问,不管是代码地址还是数据地址,都是直来直去,是几号单元就去几号内存单元找,当然,最复杂的也就算 段地址×16+偏移地址了。
    从286开始引入虚拟内存,当然,我们现在很少见到针对286虚拟内存的程序。
    经过完善,386时虚拟内存全面登场,“虚拟内存”加上“32位数据和地址”两大特性成为划时代的经典CPU,这也是现在很多操作系统文件中残留i386、386、32等字样的历史原因。
    386的一大突破就是设计了一个功能强大(相对286)可以使用虚拟内存地址寻址的MMU(内存管理单元),把物理内存(简称内存)划分成一页一页来管理,一页内存大小是固定的(一般是4KB并按4KB地址对齐),把每页的首地址按顺序排成一个数组放在内存,称为页表,页表的大小也是固定的,再把页表的首地址排成数组放在内存,称为页目录表,再把页目录表的首地址用一个专用CPU寄存器保存。
    程序代码通过MMU进行虚拟内存寻址时,先取出专用寄存器中保存的页目录表首地址,再用32位虚拟地址的高20位作为索引来一层一层的查目录表查页表找到物理内存页的首地址,用虚拟地址低12位(对于4kb页)做页内偏移来找到指定单元。

如果非要说的再明白一点,那么:虚拟内存就是程序代码不能直接访问物理内存的一种间接访问物理内存的机制。或者说是把程序中的地址值x和物理内存单元地址值y用y=f(x)的方式架开分隔。

每个进程都有自己的一套独立的页表和页目录(在进程创建时建立),CPU切换进程时,只要把下一个进程的页目录表的首地址写入专用寄存器,就开始寻址该进程的代码和数据了(页目录表的首地址是领导,领导换了,下面的页目录和页表这些小头目也变成了另一套班子了)。

对于一个进程来说,它能见到和使用的一切内存地址都是虚拟内存地址(不管访问最终目标的是在内存中,还是硬盘上),物理内存地址也就只有操作系统偶尔私自在背后把玩把玩,什么时候都不要认为你的程序中的地址是物理内存地址,它会去直接访问物理内存地址。(除非你是在做操作系统)

在物理内存中只有一套操作系统的代码,把所有操作系统代码页的页首地址在每个进程的页表和页目录表中都抄一份,那么每个进程就可以共享同一个操作系统的代码了。

虚拟内存地址通常绝不等于物理内存地址,隔了十万八千里,风牛马不相及。

当然,页表和页目录中有很多不对应物理内存页的空项,这些空项要么是程序没用到那么多的内存,要么该页放在了硬盘上,这些信息在页表项和页目录表项的指定位上有标记指示,当MMU查到这些页表项或页目录表项时,会根据标记确定是出错(因为访问了程序使用范围外的虚拟内存地址)或者调入硬盘中对应的映射页到内在存并登记页表和页目录更改对应项的标记。

同样,即使没有映射到硬盘上一个字节,你的程序全部装载到物理内存,进程使用的还是虚拟内存地址,因为虚拟内存和是否使用硬盘没有必然关系,说的直白一点,即使一台没有硬盘的电脑,它也能使用虚拟内存。

系统并不机械地建立一个从00000000h到0ffffffffh的4GB虚拟内存映射的页表和页目录,只对那些隔三插王的使用了的虚拟地址才建立对应的页表和页目录,否则的话,小小的内存全被一个个进程的页表和页目录填满了。



这些基本原理明白后就可以清醒了。
---华软网友回复---
在WindowXP上编程,就是在使用虚拟内存。
---华软网友回复---
引用 16 楼 tumen 的回复:
在WindowXP上编程,就是在使用虚拟内存。
tumen:
            您好!
感谢你给出的很多原理性的解释,通俗易懂,让我学到了
很多东西.但是,我始终认为你把进程的虚拟地址空间和虚拟
内存的概念搞混淆了,呵呵,也许我太犟,不过纯粹是学术讨论,
还请不吝赐教.
---华软网友回复---
我觉得,虚拟内存与虚拟内存管理技术是不同的概念

虚拟内存是与物理内存对应的一个概念,指的是有假包换的假内存,也就是用硬盘充当(冒充)的内存

至于虚拟内存管理技术,进程空间那是另外一套符号体系:)
---华软网友回复---
引用 18 楼 ok1234567 的回复:
我觉得,虚拟内存与虚拟内存管理技术是不同的概念 
  
 虚拟内存是与物理内存对应的一个概念,指的是有假包换的假内存,也就是用硬盘充当(冒充)的内存 
  
 至于虚拟内存管理技术,进程空间那是另外一套符号体系:)
感谢OK1234567的回复,我非常赞同你的解释.也就是说,虚拟内存和虚拟内存管理技术、进程地址
空间是不同的概念。并且虚拟内存就是硬盘充当的那部分内存,即是页交换文件的一部分。希望大家能得
出个统一的认识,求同存异。
---华软网友回复---
我理解:进程地址是一个程序的运行空间,程序的运行需要在内存中进行,虚拟内存技术实现了进程地址到内存地址的映射。
---华软网友回复---
虚拟地址空间包括已分配和未分配的空间,已分配的就是虚拟内存,虚拟内存的储存单元可能是物理内存,也可能是磁盘文件。
---华软网友回复---
记得以前的286,386 CPU内存是分开的,一个是代码段,另外一个数据段,后来到MMX 就改了,好像说用线性内存管理,
我觉得tumen兄,讲的是CPU对内存寻址技术(虚拟内存管理技术)好像是单片机8031+RAM
而所谓"虚拟内存"是在Win系统下建立,有相应多的硬件I/O支持,有不经CPU的大流量数据块传输(DMA),有硬盘..等等!

随便说说而已,错了多多包涵!


---华软网友回复---
我在楼主的帖子上耗费的时间已有5个小时,(不要认为仅仅是打几十个字的时间,我要针对问题去长时间思考和组织语言,目的是用简单的语言说明白,我从不喜欢从GOOGLE的搜索结果中粘贴,我认为我知道的我一定能说明白),但我认为这点时间还是值得。很大的原因在于我不想让曾经困惑我很久的问题让后来者再踏上同一条曲折的弯路。
引用 7 楼 jcsnwpu 的回复:
非常感谢以上各位的回答,谈谈自己的看法。 
microyzy:虚拟内存就是你的程序能访问到的那些内存,我认为这句话是不确切的。程序的进程地址空间的用户区所有映射的内存,我的程序应该都能访问,包括实际的物理内存。 
tumen:虚拟内存是一个4GB的地址空间,-----。我认为虚拟内存理论上是最大值是4GB的地址空间(32位寻址),但实际上虚拟内存仅仅是进程地址中映射到页交换文件中的那部分。 
wocow3:说虚拟内存就是页文件并不正确,映射文件的部分后援存储器都是直接对应磁盘文件的,而不是页文件。文件映射部分绝对不是虚拟内存机制。 

综合以上各位的回答,有几个相互混淆的概念: 
1、进程地址空间和虚拟内存应该不是同一概念,在tumen的叙述中我觉得存在将两者等价的嫌疑; 
2、虚拟内存和实际的物理内存绝对不是一个谁包含于谁的概念。在tumen、wocow3的叙述中都有将物理内存划入到是虚拟内存机制的一部分之嫌疑。我觉得虚拟内存应该是相对于物理内存而言的。 

目前我的理解: 
每个进程都有个4G的进程虚拟地址空间,虚拟地址空间和实际的物理存储介质(包括物理内存、硬盘等)具有映射关系,而映射到页交换文件的那部分即为虚拟内存。 

请大家批评修正。 

    对于以上问题和疑惑我认为我已经说明白了,但又为什么不能让人明白,我只好模拟通常学东西的方法,上GOOGLE搜了一下“虚拟内存”,我明白了,我也想起了我以前走的弯路。
    又是微软,又是WINDOWS,还是微软,还是WINDOWS。
    再说两段,能明白就明白吧。
    
    1.如果是网管,或者经常修机装系统,能把虚拟内存理解成你设置在硬盘的C盘或D盘或E盘等逻辑盘上一个多达几百MB或几个GB的交换文件,没错,按照微软或者WINDOWS的定义没错,你是合格的,胜任自己的工作。
    2.如果是编程,特别是中低层相关的系统编程(相对于网页、PHOTOSHOP\3DMAX等),按MS的定义你必将走上和我曾经一样的充满困惑和疑虑的弯路。

    建议两条路可走:A.抱紧MS的教条,继续疑惑别人的一些观点并一直疑惑下去,成为程序生涯中一个不愿面对的死胡同。B.让MS的教条去死吧,去接受一个真正的、开放意义上的虚拟内存,让自己对电脑软硬件的认识有一个更高视野。低眉信首,疑云随风散。

    也许我的效率低了,近两个小时,但我还是希望都能明白。
---华软网友回复---
真是无语了,刨根问底是好的,但是不能像孔乙己那样追究茴香豆茴字的写法,那就变成了掉书袋了,毫无意义。

---华软网友回复---
其实“虚拟内存”是宏观概念!就好像两父子,父亲(CPU有虚拟的技术),到儿子(系统)继承了该技术,再发扬光大应用到硬盘上!只不过一些人讲他父亲的属性,一些讲儿子的属性,一些讲他家族的属性!
大家都有道理!!

---华软网友回复---
引用 24 楼 codewarrior 的回复:
真是无语了,刨根问底是好的,但是不能像孔乙己那样追究茴香豆茴字的写法,那就变成了掉书袋了,毫无意义。 
 
感谢您的回复.其实虚拟内存对WINDOWS编程人员来是说,是相当重要的一个概念.也许有很多人像我这样
地在困惑,也许是我的领悟能力太差,但绝不是孔乙己的茴香豆,谢谢.
---华软网友回复---
感谢大家,感谢microyzy,tumen,wocow3,ok1234567,cnzdgs,zufei.
讨论是必要的,争议也是将认识和技术升华的一种的动力.或许最终没有得出一个
统一的结论,但是我还是从这个帖子中受益良多.
最后尤其要感谢tuemn,在这个帖子上花费了很多宝贵的时间,甚至让我一度不能赞成
他的观念,但脑子里却认为他应该是对的,自己尽量朝他的方向去想.但遗憾的是,他最终
未能说服我,也许他的观点是对的.最后,也想给tumen一点建议:你说的很多,也很详细,
从原理上讲也是对的,但更多的是缺少回答问题的针对性,比如我一再和你强调的进程地址
空间和你说的虚拟内存到底是什么区别等,你从未正面回答这些问题.
最后,谢谢大家.祝大家工作顺利,月月加薪!
---华软网友回复---
引用 26 楼 jcsnwpu 的回复:
引用 24 楼 codewarrior 的回复:
真是无语了,刨根问底是好的,但是不能像孔乙己那样追究茴香豆茴字的写法,那就变成了掉书袋了,毫无意义。 


感谢您的回复.其实虚拟内存对WINDOWS编程人员来是说,是相当重要的一个概念.也许有很多人像我这样 
地在困惑,也许是我的领悟能力太差,但绝不是孔乙己的茴香豆,谢谢.


    本来是在讨论编程方面宏观虚拟内存概念,却把MS狭义的教条往上套,驴头非要对上马嘴,肯定说不清楚了,怨不得会搞出点茴香豆的味道来。

    怨不得,怨不得的。实情确如此,当年曾断断续续地困惑我有半年,闷头苦想,总要把MS的虚拟内存概念生拉硬扯进去,越想越乱,后来不知何时总算有了宏观意义上的概念,现在细想,当时是一点点褪去MS的影响后才建立的正确观念。

     如果我本来就不知道有MS的虚拟内存概念,我想我一个月内肯定会小有成果的。

     楼主现在要做的首要工作是忘掉你所知道的MS的能设置的虚拟内存的知识。
     忘的越彻底,学的越快。
---华软网友回复---
引用 27 楼 jcsnwpu 的回复:
比如我一再和你强调的进程地址空间和你说的虚拟内存到底是什么区别等,你从未正面回答这些问题. 

本来是想回答的,但我发现真正的原因后,觉得已经不需要再重复已经回答的话了,如果一直有MS障目的话,你是看不见我描绘的大山的。
建议不要去刻意进程地址空间和虚拟内存(宏观的)到底是什么区别,应该看它们的深刻联系,本就是鱼与水。善藏此贴,一个月后再看。

---华软网友回复---
lz有兴趣的话重新开个帖子或者加我msn讨论,codewarrior2000@msn.com。
lz压根就没有抛开他原有的对虚拟内存的错误认识,在错误认识的基础上尝试解释别人正确的观点,试图将正确的观点融入自己错误的体系,怪不得搞得解释的人口干舌燥,你自己会头昏脑胀。
---华软网友回复---
引用 30 楼 codewarrior 的回复:
lz有兴趣的话重新开个帖子或者加我msn讨论,codewarrior2000@msn.com。 
 lz压根就没有抛开他原有的对虚拟内存的错误认识,在错误认识的基础上尝试解释别人正确的观点,试图将正确的观点融入自己错误的体系,怪不得搞得解释的人口干舌燥,你自己会头昏脑胀。
谢谢codewarrior2000兄弟.一定加你MSN,请多多指教.我说话有不当之处,多多包涵.
---华软网友回复---
页文件有什么作用啊?
---华软网友回复---
我也有和楼主一样的疑问,刚找到一篇能解答楼主问题的文章,阐述的生动而且详细,希望对你有帮助
前面讲“库”文件时说到:动态库的“函数”,有“函数地址”。其实不仅函数有地址,程序中的各种数据,都有地址。
5.4.1.什么叫进程
当程序安静地躺在硬盘上时,它是一个“文件”,非要给点区别的话,它是一个“可执行文件”;而当程序运行起来,它就从硬盘上一跃进入内存,这时它就被叫做一个“进程/process”了。
请同时按下Ctrl + Alt + Del 键,或者在任务栏空白处点右键,弹出菜单中选“任务管理器”,并切换到“进程”页,我们可以看到各个进程的一些指标。提示:通过菜单“查看->选择列”,可以配置更多观察选项,下图是一个示例: 
图 5-6 通过“任务管理器”,观察进程
5.4.2.进程的内存空间
硬盘上的文件,是“死”的,内存的进程,才是“活”的,可以说,内存就是程序生存的天地,那么每个进程的拥有天地有多大呢?回答是:“4G”。
或许有些读者想打开机箱:明明只插了1G,甚至只有512兆的内存啊?操作系统如何给每个进程都分配出4 G的内存空间呢?
在本章前部的《从代码到程序》一节,我们已经说过,程序运行在操作系统之上,要访问硬件,通过要假手操作系统。内存可是最重要的硬件了,元老级的操作系统确实允许我们直接操纵内存,但现代的操作系统都不允许了。这样,操作系统就有了玩猫腻的机会——当然,是在硬件支持的情况下。
首先,预备的并不是真实的内存空间大小,而是“内存地址”大小,更严格地说,是内存的“虚拟地址”(真实内存地址称为“物理地址”)。
如果我们把内存空间的最小单位想像成一个格子。那么内存地址就是格子的门牌号码。这里的4G,是指有4G个内存“虚拟地址”——再直白一点,就是每个进程可以获得4G个门牌号,而不是4G个真实的内存空间。
房子少,门牌却多,这些门牌最终要挂在哪里呢?答曰:一部分挂到真实的物理地址上去,一部分可能被挂到“虚拟内存(硬盘空间)”上,还有很大一部分,哪儿也不挂,整个儿就是操作系统给进程开的白条。
“虚拟内存”和“虚拟地址”没多大关系。后者是指操作系统分配给每个进程一致的,从“0”到“4294967295”的内存地址。前者则是指操作系统经常性地拿硬盘空间假装成内存空间欺骗进程。
右键点击“我的电脑”,弹出菜单选“属性”,出现“系统属性”对话框中,切换到“高级”页;找到“性能”分组框,点击其内“设置”按钮,出现“性能选项”对话框,同样切换到“高级”页。请在上面查找有关“虚拟内存”的配置。
有关4G内存空间的几个迷底我们一并揭开:
第一、“内存不够,硬盘来凑”。但是,请注意:访问真实内存的速度,是“纳秒(十亿分之一秒)/ns”级,访问硬盘的速度,却是“毫秒(千分之一秒)/ms”级。
程序需要读写(物理)文件很正常,但如果一个程序需要非常频繁地读写文件,就需要考虑其设计思路是否有问题。
比如读取配置文件,一般被设计成在程序启动期间,读入内存,之后一直使用内存数据。如果在程序运行过程中,配置文件会被外部修改,则应争取设置成“通知”机制,即外部有修改后,程序能够收到一个通知,然后重读文件,而不应设计成每次需要配置,就从文件中读取(配置数据的使用频度往往很高)。
第二、 虽然承诺是4G,但实际上这是一个按需分配的过程,绝大多数进程,需要的最多不过数百兆。操作系统分给每个进程是4G的内存虚拟地址(门牌),绝不是直接就给出4G内存(那样的话,系统直接挂掉了)。
第三、 在4G里,还需要分出2G用来预留和其操作系统、和其它进程共享使用,这部分内存,当前进程默认状态下没有访问权限。
第四、 最后一迷底很重要:谁负责内存“虚拟地址”与“物理地址”的变换呢?是硬件,主要是CPU。物理内存是兵家必争之地——至少未来十年之内还是——读写内存必须非常迅速,这件要求软件(包括操作系统)实现不了,只能交给硬件去做。而操作系统所做的,是屏蔽掉多数应用程序直接读写物理内存的权限。 
上面的迷底一开,会不会让读者您很鄙视“4G空间”这一说了?且莫!
通过“虚拟地址”的内存访问模式,称为“保护模式”,对应的,允许程序直接访问物理内存,称为“实模式”。“实模式”存在不少问题。
比如,随便一个程序对物理内存胡作非为了,整个操作系统——甚至就是整台机器, 直接就宕掉了。
再如,没有系统实现的虚拟内存时,程序必须自己处理如何将暂时不用的数据,存到硬盘上,等需要时,又如何读出来,令程序员烦不胜烦;现在有了4G空间,程序员一口气写6个程序,脸不红心不跳的,真是方便多了!
5.4.3. 内存分配测试程序
下面我们写一个程序,以观察一个C++程序大致能向操作系统要到多少内存。这是一很小的,同时也很不准确的程序。仅粗略测试程序运行期,能够动态分配到多少内存,程序自身已经占用的内存并不计算。
打开Code::Blocks,新建一个控制台项目,项目名称为“MemoryAllocTest”。打开项目中默认存在的main.cpp文件,完成以下代码:#include <iostream> using namespace std; int main() { 007 unsigned int bytes = 0; 009 while(true) { 011 try { 013 new char [1024 * 4]; //每次分配4K内存 014 bytes += 1024 * 4; } 016 catch(std::bad_alloc const & e) { 018 std::cout << e.what() << std::endl; 019 break; } } 023 std::cout << bytes << "bytes" << std::endl; return 0; } 
切莫着急编译,然后运行这个程序。因为它会尽全力去吞噬你的内存(物理的,或者虚拟的),直到操作系统忍无可忍地拒绝了它。在此过程中,你的电脑会变得反应迟钝,很多其它进程变得不能及时响应,甚至有些进程会出错。所以,请在执行本程序之前,先保存你修改中任何内容,并尽量关闭一些不需要的程序。
内存的最小单位,叫“字节/byte”。在007行,我们定义一个“正整数/unsigned int”的变量,我们就把它取名为bytes了。这个变量一会儿用来记录我们分配了多少个字节的内存。现在,它被初始化为0。
009行是我们熟悉的“死循环”,既然是死循环,通常就得有个出口来解救:在019行,我们看到一个break。
这个break什么时候起作用呢?这里我们要认识新朋友了:异常。请看从011行和016行,组成这样的一个语句结构
011 try { //代码块-1 } 016 catch(std::bad_alloc const & e) { //代码块-2 } 
在try{} 的范围内的代码(代码块-1),如果发生C++定义的异常,程序将跳转catch() {}范围内的代码(代码块-2)继续执行。
异常是C++支持的一种程序流程结构化跳转,我们将在以后学习,本例中将要发生的事情是:
013 行的代码,将在循环内一次又一次分配内存,(并且后面从不释放),终有一次,这个贪婪的行为将会失败。C++规定,出现内存分配失败时,默认行为是抛出一个C++的异常数据,并且中止其后代码运行,然后跳转到某一种catch代码块中,在本例,就是018行和019行。
018行输出所捕获到异常的简单说明。019行则是重要的“break”,没错,程序内存被耗尽,循环进行不下去,此时不break,更待何时?
下面是该程序在我的电脑上运行的结果,费时超过6分钟,分配了近2 G字节的内存。
图 5-7 C++程序内存分配试验结果
读者可能对上述代码中,C++如何申请内存有兴趣。我们也简单说一下。
C++中的“new”命令,可以向系统申请分配内存,并且它根据其后所接的“数据类型”,来决定要申请多个字节。“char/字符”类型在C++中的大小正好是1个字节,所以,如果013行代码写的是:
013 new char;
那么每一次循环步骤中,将只分配1个字节,这会让这个测试更准确一些,但代价可能是变成费时1个小时,所以本例每次申请4K 个字节(1K是1024字节)。在C++,要一次分配连续的,多个char大小的内存,方法是使用中括号,正如本例所示:
new char [N];
其中N是所要分配的个数。
内存最小单位为:字节/byte。
1K:2的10次方,1024个字节。
1M(兆):2的20次方,1024*1024个字节。 
1G:2的30次方:1024*1024*1024个字节。
前面描述内存地址时的4G,即2的32次方(1024*1024*1024 * 4)。 
5.4.4.32位机器
现在,本小节最后一个问题是:为什么系统给每个进程,预备的是4G个字节,而不是更多或更少?
以下是回答过程: 我们现在常用的电脑,是32位机。这32位,对应到硬件上,就是32条电路,这32路要么通电,要么断电,对应成二进制,就是32个1或0。 
用这32位来表达内存地址,最大数是一个32位全为1的二进制数,即2的32次方,正好是4G。
哪一天我们全用上64位机,这个4G的数值,可就要改成多大2的64次方: 4G个4G!
#include <iostream> using namespace std; int main() { 007 unsigned int bytes = 0; 009 while(true) { 011 try { 013 new char [1024 * 4]; //每次分配4K内存 014 bytes += 1024 * 4; } 016 catch(std::bad_alloc const & e) { 018 std::cout << e.what() << std::endl; 019 break; } } 023 std::cout << bytes << "bytes" << std::endl; return 0; } 
切莫着急编译,然后运行这个程序。因为它会尽全力去吞噬你的内存(物理的,或者虚拟的),直到操作系统忍无可忍地拒绝了它。在此过程中,你的电脑会变得反应迟钝,很多其它进程变得不能及时响应,甚至有些进程会出错。所以,请在执行本程序之前,先保存你修改中任何内容,并尽量关闭一些不需要的程序。
内存的最小单位,叫“字节/byte”。在007行,我们定义一个“正整数/unsigned int”的变量,我们就把它取名为bytes了。这个变量一会儿用来记录我们分配了多少个字节的内存。现在,它被初始化为0。
009行是我们熟悉的“死循环”,既然是死循环,通常就得有个出口来解救:在019行,我们看到一个break。
这个break什么时候起作用呢?这里我们要认识新朋友了:异常。请看从011行和016行,组成这样的一个语句结构
011 try { //代码块-1 } 016 catch(std::bad_alloc const & e) { //代码块-2 } 
在try{} 的范围内的代码(代码块-1),如果发生C++定义的异常,程序将跳转catch() {}范围内的代码(代码块-2)继续执行。
异常是C++支持的一种程序流程结构化跳转,我们将在以后学习,本例中将要发生的事情是:
013 行的代码,将在循环内一次又一次分配内存,(并且后面从不释放),终有一次,这个贪婪的行为将会失败。C++规定,出现内存分配失败时,默认行为是抛出一个C++的异常数据,并且中止其后代码运行,然后跳转到某一种catch代码块中,在本例,就是018行和019行。
018行输出所捕获到异常的简单说明。019行则是重要的“break”,没错,程序内存被耗尽,循环进行不下去,此时不break,更待何时?
下面是该程序在我的电脑上运行的结果,费时超过6分钟,分配了近2 G字节的内存。
图 5-7 C++程序内存分配试验结果
读者可能对上述代码中,C++如何申请内存有兴趣。我们也简单说一下。
C++中的“new”命令,可以向系统申请分配内存,并且它根据其后所接的“数据类型”,来决定要申请多个字节。“char/字符”类型在C++中的大小正好是1个字节,所以,如果013行代码写的是:
013 new char;
那么每一次循环步骤中,将只分配1个字节,这会让这个测试更准确一些,但代价可能是变成费时1个小时,所以本例每次申请4K 个字节(1K是1024字节)。在C++,要一次分配连续的,多个char大小的内存,方法是使用中括号,正如本例所示:
new char [N];
其中N是所要分配的个数。
内存最小单位为:字节/byte。
1K:2的10次方,1024个字节。
1M(兆):2的20次方,1024*1024个字节。 
1G:2的30次方:1024*1024*1024个字节。
前面描述内存地址时的4G,即2的32次方(1024*1024*1024 * 4)。 
5.4.4.32位机器
现在,本小节最后一个问题是:为什么系统给每个进程,预备的是4G个字节,而不是更多或更少?
以下是回答过程: 我们现在常用的电脑,是32位机。这32位,对应到硬件上,就是32条电路,这32路要么通电,要么断电,对应成二进制,就是32个1或0。 
用这32位来表达内存地址,最大数是一个32位全为1的二进制数,即2的32次方,正好是4G。
哪一天我们全用上64位机,这个4G的数值,可就要改成多大2的64次方: 4G个4G!
---华软网友回复---
 刚才我也有楼主jcsnwpu的困惑, 也怀疑 tumen 的回答牛头不对马嘴, 后来看了btstt的回答, 好像有点明白了。 根据各位大牛们的回答, 我的理解是,  MS 的虚拟内存管理技术应该是建立在 MMU 的内存管理基础之上, MS 的虚拟内存终究本质还是MMU的虚拟内存管理。
---华软网友回复---
看一下下面这个帖子的讨论 或许对大家的理解有帮助
http://topic.csdn.net/u/20080905/21/49c6f5fa-8be5-4074-ae7e-d55c5f0570da.html
---华软网友回复---
 
btstt的帖子讲的挺清晰的。非常幽默。不知何道是何书讲的啊?我们一般编程人员都这样的疑惑和焦虑。我都搞不懂什么是虚拟内存,到底OS是怎么管理的。真是惭愧极了。感谢楼上各位大侠的指导。
---华软网友回复---
简单的说

    Win32所指的“虚拟内存”=主板上插的内存+磁盘上的交换文件
    (以下描述均针对Win32系统)

“虚拟内存”
    “虚拟内存”是为多进程提出的一个概念,他给进程造成一种假象:每个进程都单独拥有操作系统所支持的4G最大内存(32位系统),即使你主板上实际没有那么多内存,甚至你的内存+交换文件都不足4G。

“4G地址空间”
    每个进程所拥有的4G空间是一个“地址的空间”,并不是一个已经分配了“虚拟内存”的空间,所以这个空间的大小是0,它只是一组从 00000000-ffffffff 的地址序列而已。

为地址空间分配“虚拟内存”
    进程如果想通过4G地址空间内的“地址”访问“虚拟内存”,必须通过调用操作系统的API为这些地址分配实际的“虚拟内存”,就是用那些Malloc函数去Reserve&Commit内存,这时你的进程才真正的占用到了“虚拟内存”,而具体占用的是主板上插的内存还是磁盘交换文件,这是由操作系统决定的。

分配内存还是交换文件
    为进程分配主板上的内存,还是磁盘交换文件,这不是你来决定的,你能申请到的永远是“虚拟内存”。这个“虚拟内存”到底分配的是真正的内存还是磁盘交换文件,这是由操作系统的内存管理机制和页表决定的。

    当然,“内存映射文件”是个例外。(File-Mapping)
---华软网友回复---
所有 32 位应用程序都有 4 GB 的进程地址空间(32 位地址最多可以映射 4 GB 的内存)。对于 Microsoft Windows 操作系统,应用程序可以访问 2 GB 的进程地址空间,称为用户模式虚拟地址空间。应用程序拥有的所有线程都共享同一个用户模式虚拟地址空间。其余 2 GB 为操作系统保留(也称为内核模式地址空间)。所有操作系统版本(从 Windows 2000 Server 开始,包括 Windows Server 2003)都有一个 boot.ini 开关,可以为应用程序提供访问 3 GB 的进程地址空间的权限,从而将内核模式地址空间限定为 1 GB。

详细回答:http://technet.microsoft.com/zh-cn/library/ms189334.aspx

---华软网友回复---
引用 13 楼 qq14923349 的回复:
每个进程都有独立的虚拟内存

这位兄弟就是典型的将虚拟内存和虚拟内存空间弄混的例子,每个进程都有一个4G的虚拟内存空间,其中使用的是虚拟地址,在映射的过程中首先是通过段寄存器转换为线性地址,然后通过页表转换为物理地址,最后通过物理地址的划分找到具体的存储单元。而虚拟内存则是为了弥补物理内存的空间不足而利用硬盘充当内存使用的一种概念。
---华软网友回复---
btstt讲的真好,今天看到这里,终于听明白了!

---华软网友回复---
好,又清晰了很多!      
华软声明:本内容来自网络,如有侵犯您版权请来信指出,本站立即删除。