深入理解内存(2):发展历史,内存地址
2013-01-15 20:08
218 查看
事物的发展总是从简单到复杂,当然我们学习应该是先从简单到复杂学习,然后慢慢思考融汇贯通,最后又把复杂化为简单.
我们先来看看在计算机刚出来不久内存是怎么被使用,然后随着技术的发展又是怎么演化的.当然这里的描述只是简单化的体现下那种思想,很多细节未必准确.
刚开始的时候电脑功能相当简单,而且用电脑的也都是些高手专家,对硬件相当的了解.此时也没有啥操作系统,(实际上可能显示器也可以不需要),电脑磁盘上除了保存一些供你调用的标准库函数外啥都没有.开机后CPU仍然静静的呆着不干嘛,内存里也是空的啥东东都没有(现在我们用的电脑一开机,内存至少会加载操作系统代码,所以永远不可能是空的).
此时你要做啥操作就敲入些命令,然后就调用保存的库函数,自然是先把库函数加载到内存,然后再在CPU上面跑,跑完了可能就是打印些啥信息出来(此时还没文件的概念,磁盘上不会保存你的结果数据).然后接下来CPU就休息去了,内存也空了.你不去调用库函数时,电脑就傻傻的呆了啥都不干.
2.批处理
像上面说的你敲入些命令时电脑才给你干活,这样效率太低了.于是做了些改进,首先是出现了操作系统的雏形,打开电脑后会加载一些操作系统的代码常驻在内存中.此时出现了文件,可以把很多待操作的命令事先写好,然后保存成一个个的文件.然后电脑可以按顺序给你从头跑到尾,就就是你启动一个操作后就可以走人了,然后计算机会全部给你按顺序处理完,一次处理一大批嘛.所以叫批处理.
此时内存会被分为两块,一小块给常驻内存的操作系统.其他的给用户程序跑(内存和CPU只能给单个用户程序用)
3.保存,切换
批处理只能从头到尾按顺序跑,如果中间某个程序要跑太久太久,后面的就没法跑了.一个程序一当跑起来你如果把它停了下次只能重新再跑.于是有人琢磨着如果程序在内存中跑了一段时间还没完,可以把内存中所有的信息切换出来保存到磁盘上.等下次再跑时可以不用从头跑,而是再把磁盘上的信息切换进内存接着上次的跑.
4.分时,多进程
前面说的三种情况都是一次只能一个用户程序在跑,此时有多少内存就用多少,不够用就报错呗,很简单,没涉及啥太复杂的技术.但是这样显然不能充分利用CPU,内存资源.因为内存变得越来越大,而一个程序可能只用占一点点内存,这样会空出一大片.而且CPU也是跑得贼快的.一眨眼就跑完了,只有你进行那些IO操作时可能就慢的像蜗牛了.于是有人就琢磨着,应该可以让几个程序同时跑,一个程序对应一个进程,都同时在内存中占一片地盘.然后就通过分时轮流使用CPU.
此时操作系统做的事就多很多了啊.首先是要保证每个进程的独立,不能让一个进程轻易使用别的进程的内存了.然后有时还得实现进程间的通信,要防止死锁之类的麻烦事.
1.合理的给每个进程分配一块内存.当内存满了时把一部分进程的内存切换出去放硬盘上.恰当的时候再从硬盘切换回内存
2.虚拟内存技术.实际上就是把硬盘也当内存用,只不过要多做些切换罢了,在硬盘和内存之间切换.
内存与硬盘间来回切换,会导致内存地址变化
如果只让一个进程在内存中,开始跑时全部加载,然后跑完了再释放内存.这样像硬盘一样直接通过内存的实际地址访问是没太多问题的. 但如果是多进程共用内存,并且还会在硬盘和内存间切换.此时就会有问题.假如你的程序跑了一部分,然后全部切换到硬盘,那些保存的信息里面有涉及到内存地址.等再切换到内存中时,你之前占的那内存可能被别的进程占用了,而系统分给你的是另外一块内存.那样的话你根据之前那些内存地址信息再读写数据时就会出错.咋整呢?
[b]内存物理地址与逻辑地址[/b]
像在C,C++中我们一般用指针来指向内存地址.那你可能会问指针指的内存地址是内存的物理地址吗 ?
如果是实际地址的话就出现上面说的问题了. 指针指向的实际上是一个逻辑地址.你可以这样想,在一个进程内我们先把所有指针指定一些固定的值(也就是逻辑地址),比如从0开始递增,然后实际上加载到内存中时(每次加载时的起始地址可能不一样),再用逻辑地址做偏移量加上内存中的实际起始地址就是实际的内存地址了. 你可能会奇怪那么每个进程的内存块的起始位置放哪里啊? 那个起始地址值是放在寄存器中.寄存器实际上可以看成一个速度更快的内存,在CPU呆一起,所以划分模块时我们一般分到CPU那一块去了.
当然从逻辑地址映射到物理地址肯定不止一种方法的,如果用到了分页技术,我们还可以把每个逻辑地址映射到物理地址中的不同页面(page)
不过上面说的也不是太准确,我们只能说我们用的绝大部分应用程序中涉及到的指针指向的内存地址是指向逻辑地址的.实际上也有些特殊的情况指针会指向内存物理地址,比如在嵌入式系统中,可能就一个进程用那内存,不用做啥切换,我们就可以用指针直接指向实际的物理内存地址.另外一般的桌面电脑中,内存中会划分出一块固定的内存来加载操作系统代码.操作系统中一部分内核程序会常驻内存,一直呆一个固定的地方,不会被切换到硬盘上去.此时指针也可以直接指向物理内存.
毫无疑问除了物理地址是真实存在的内存地址外.其他地址都是我们虚构出来的.我们知道操作系统把硬件封装抽象了,我们编程时基本不会直接面对硬件.那我们可以把虚拟地址空间看成是抽象出来的内存.当每个程序运行时(在windows中,linux或其他系统我还不确定)系统会给程序分配一个虚拟地址空间,就相当于给你一个内存,你尽量去用就是,不用管其他的事了.当你申请一块内存后会返回内存地址,这个就是逻辑地址,逻辑地址就是指向虚拟地址空间中某块地址
虚拟地址空间大小
在32位windows系统中,可用的虚拟地址空间是2的32次方,也就是4G. 你可能会奇怪这是怎么算出来的啊.因为32位系统顾名思义是因为处理器一次只能处理32位bit的信息,这样一来地址总线传的内存地址最多只能用32个bit来表示,所以只能表示4G的内存地址了.不过我们会把其中的2G分给系统空间,只留下2G给用户空间.所以任何一个win32应用程序运行时系统会给分配2G的虚拟地址空间.
不过你可以指定让用户空间超过2G,最大可增至3G,不过这样系统空间就仅有1G了.
在64们的windows系统中,可用的虚拟地址空间是2的64次方,貌似是40多亿G.但显然我们用不了这么多,用了也没意义.所以我们只用到这些虚拟地址空间的一部分.8T用户空间, 248T系统空间.所以任何一个win64应用程序运行时系统会给分配8T的虚拟地址空间
我们先来看看在计算机刚出来不久内存是怎么被使用,然后随着技术的发展又是怎么演化的.当然这里的描述只是简单化的体现下那种思想,很多细节未必准确.
简单的历史故事
1.单用户使用内存刚开始的时候电脑功能相当简单,而且用电脑的也都是些高手专家,对硬件相当的了解.此时也没有啥操作系统,(实际上可能显示器也可以不需要),电脑磁盘上除了保存一些供你调用的标准库函数外啥都没有.开机后CPU仍然静静的呆着不干嘛,内存里也是空的啥东东都没有(现在我们用的电脑一开机,内存至少会加载操作系统代码,所以永远不可能是空的).
此时你要做啥操作就敲入些命令,然后就调用保存的库函数,自然是先把库函数加载到内存,然后再在CPU上面跑,跑完了可能就是打印些啥信息出来(此时还没文件的概念,磁盘上不会保存你的结果数据).然后接下来CPU就休息去了,内存也空了.你不去调用库函数时,电脑就傻傻的呆了啥都不干.
2.批处理
像上面说的你敲入些命令时电脑才给你干活,这样效率太低了.于是做了些改进,首先是出现了操作系统的雏形,打开电脑后会加载一些操作系统的代码常驻在内存中.此时出现了文件,可以把很多待操作的命令事先写好,然后保存成一个个的文件.然后电脑可以按顺序给你从头跑到尾,就就是你启动一个操作后就可以走人了,然后计算机会全部给你按顺序处理完,一次处理一大批嘛.所以叫批处理.
此时内存会被分为两块,一小块给常驻内存的操作系统.其他的给用户程序跑(内存和CPU只能给单个用户程序用)
3.保存,切换
批处理只能从头到尾按顺序跑,如果中间某个程序要跑太久太久,后面的就没法跑了.一个程序一当跑起来你如果把它停了下次只能重新再跑.于是有人琢磨着如果程序在内存中跑了一段时间还没完,可以把内存中所有的信息切换出来保存到磁盘上.等下次再跑时可以不用从头跑,而是再把磁盘上的信息切换进内存接着上次的跑.
4.分时,多进程
前面说的三种情况都是一次只能一个用户程序在跑,此时有多少内存就用多少,不够用就报错呗,很简单,没涉及啥太复杂的技术.但是这样显然不能充分利用CPU,内存资源.因为内存变得越来越大,而一个程序可能只用占一点点内存,这样会空出一大片.而且CPU也是跑得贼快的.一眨眼就跑完了,只有你进行那些IO操作时可能就慢的像蜗牛了.于是有人就琢磨着,应该可以让几个程序同时跑,一个程序对应一个进程,都同时在内存中占一片地盘.然后就通过分时轮流使用CPU.
此时操作系统做的事就多很多了啊.首先是要保证每个进程的独立,不能让一个进程轻易使用别的进程的内存了.然后有时还得实现进程间的通信,要防止死锁之类的麻烦事.
内存分配
当出现了多进程的概念后.自然就需要想出很多花样出合理的分配和利用内存这个宝贵的东东.操作系统内存管理中涉及到的术语可能看得人头大.实际上用通俗点的话归纳下就做两方面的事1.合理的给每个进程分配一块内存.当内存满了时把一部分进程的内存切换出去放硬盘上.恰当的时候再从硬盘切换回内存
2.虚拟内存技术.实际上就是把硬盘也当内存用,只不过要多做些切换罢了,在硬盘和内存之间切换.
内存地址
先不管内存怎么分块.我们首先会想到要用内存该怎么去访问? 你可能说肯定是通过地址嘛.硬盘访问就是通过一个地址,然后用磁头去读写嘛.内存与硬盘间来回切换,会导致内存地址变化
如果只让一个进程在内存中,开始跑时全部加载,然后跑完了再释放内存.这样像硬盘一样直接通过内存的实际地址访问是没太多问题的. 但如果是多进程共用内存,并且还会在硬盘和内存间切换.此时就会有问题.假如你的程序跑了一部分,然后全部切换到硬盘,那些保存的信息里面有涉及到内存地址.等再切换到内存中时,你之前占的那内存可能被别的进程占用了,而系统分给你的是另外一块内存.那样的话你根据之前那些内存地址信息再读写数据时就会出错.咋整呢?
[b]内存物理地址与逻辑地址[/b]
像在C,C++中我们一般用指针来指向内存地址.那你可能会问指针指的内存地址是内存的物理地址吗 ?
如果是实际地址的话就出现上面说的问题了. 指针指向的实际上是一个逻辑地址.你可以这样想,在一个进程内我们先把所有指针指定一些固定的值(也就是逻辑地址),比如从0开始递增,然后实际上加载到内存中时(每次加载时的起始地址可能不一样),再用逻辑地址做偏移量加上内存中的实际起始地址就是实际的内存地址了. 你可能会奇怪那么每个进程的内存块的起始位置放哪里啊? 那个起始地址值是放在寄存器中.寄存器实际上可以看成一个速度更快的内存,在CPU呆一起,所以划分模块时我们一般分到CPU那一块去了.
当然从逻辑地址映射到物理地址肯定不止一种方法的,如果用到了分页技术,我们还可以把每个逻辑地址映射到物理地址中的不同页面(page)
不过上面说的也不是太准确,我们只能说我们用的绝大部分应用程序中涉及到的指针指向的内存地址是指向逻辑地址的.实际上也有些特殊的情况指针会指向内存物理地址,比如在嵌入式系统中,可能就一个进程用那内存,不用做啥切换,我们就可以用指针直接指向实际的物理内存地址.另外一般的桌面电脑中,内存中会划分出一块固定的内存来加载操作系统代码.操作系统中一部分内核程序会常驻内存,一直呆一个固定的地方,不会被切换到硬盘上去.此时指针也可以直接指向物理内存.
虚拟地址空间
虚拟地址与逻辑地址关系毫无疑问除了物理地址是真实存在的内存地址外.其他地址都是我们虚构出来的.我们知道操作系统把硬件封装抽象了,我们编程时基本不会直接面对硬件.那我们可以把虚拟地址空间看成是抽象出来的内存.当每个程序运行时(在windows中,linux或其他系统我还不确定)系统会给程序分配一个虚拟地址空间,就相当于给你一个内存,你尽量去用就是,不用管其他的事了.当你申请一块内存后会返回内存地址,这个就是逻辑地址,逻辑地址就是指向虚拟地址空间中某块地址
虚拟地址空间大小
在32位windows系统中,可用的虚拟地址空间是2的32次方,也就是4G. 你可能会奇怪这是怎么算出来的啊.因为32位系统顾名思义是因为处理器一次只能处理32位bit的信息,这样一来地址总线传的内存地址最多只能用32个bit来表示,所以只能表示4G的内存地址了.不过我们会把其中的2G分给系统空间,只留下2G给用户空间.所以任何一个win32应用程序运行时系统会给分配2G的虚拟地址空间.
不过你可以指定让用户空间超过2G,最大可增至3G,不过这样系统空间就仅有1G了.
在64们的windows系统中,可用的虚拟地址空间是2的64次方,貌似是40多亿G.但显然我们用不了这么多,用了也没意义.所以我们只用到这些虚拟地址空间的一部分.8T用户空间, 248T系统空间.所以任何一个win64应用程序运行时系统会给分配8T的虚拟地址空间
相关文章推荐
- 深入理解JVM学习笔记-自动内存管理机制
- (一)深入理解Java虚拟机-虚拟机内存区域与内存溢出
- 深入理解Java虚拟机----第2章 Java内存区域与内存溢出异常
- 深入理解JVM—JVM内存模型(很经典) (部分摘自 深入JAVA虚拟机 周志明著)
- 深入理解JAVA虚拟机---JAVA虚拟机的内存划分
- 深入理解linux内核之内存寻址
- 探索深入理解java虚拟机之java内存区域解析(1)
- 【深入理解Java虚拟机】Java内存区域模型、对象创建过程、常见OOM
- 深入理解JVM(一)--Java内存区域与内存溢出异常
- 深入理解计算机系统-之-内存寻址(六)--linux中的分页机制
- 深入理解Linux内存寻址的分段机制
- linux——深入理解linux内存分布—基础篇
- 深入理解Java虚拟机JVM高级特性与最佳实践阅读总结——第三章垃圾收集器与内存分配策略
- 深入理解java虚拟机【内存溢出实例】
- 读 - 深入理解java虚拟机 - 笔记(一) - java内存区域模型(2章)
- 深入理解.NET内存回收机制
- 深入理解Java虚拟机---(1)JVM五大内存区域划分
- 深入理解JVM之JVM内存区域与内存分配
- 深入理解JVM--java内存区域分析
- 深入理解Java虚拟机-3垃圾收集器与内存分配策略