内存是现代计算机运行的中心(竟然不是CPU!)。内存有很大的一组字或字节组成,每一个字或字节都有地址。html
CPU根据程序计数器从内存中提取指令。CPU所能直接访问的存储器只有处理器内的寄存器和内存。一般,程序存储在磁盘上,执行的时候,程序被调入内存。那么CPU怎么找到它们呢?程序员
一般,将指令与数据绑定到内存地址有如下几种状况:算法
1)编译时,进程在内存中的驻留地址就已经肯定,生成的是绝对代码数据结构
2)加载时,编译器生成的是重定位代码,绑定延迟到加载时才进行spa
3)执行时,绝大多数操做系统采用操作系统
1、背景指针
一、逻辑地址与物理地址htm
CPU所生成的地址称为逻辑地址,而内存单元对应的地址(即加载到内存地址寄存器的地址)称为物理地址。从逻辑地址到物理地址的映射由硬件设备(内存管理单元,MMU)完成。用户只生成逻辑地址,这些地址在使用前必须映射到物理地址。用户程序决不会看到真正的物理地址。对象
二、动态加载及DLLblog
若是一个进程的整个程序和数据必须处于物理内存中,那么进程的大小会受物理内存大小所限制。可使用动态加载,子程序只在调用时才被加载。动态加载无须操做系统特别支持,这是程序员的工做。
除此而外,可使用DLL。DLL与动态加载不一样,一般须要操做系统的帮助,由于DLL能够被多个进程共用,只有操做系统能够检查其余进程的内存空间。
DLL有版本的区别,不一样版本的库均可以装入内存。
2、交换
进程须要在内存中以便执行,但进程能够暂时从内存中换出(swap)到备份存储上,当须要再次执行时再回到内存中。
交换有一些策略,好比基于优先级。
3、连续内存分配
内存一般分为两个区域:驻留操做系统 + 用户进程。
一般须要将多个进程同时放在内存中,所以须要考虑如何为输入队列中的进程分配内存空间。采用连续内存分配时,每一个进程位于一个连续的内存区域。
最简单的内存分配方法之一就是将内存分为多个固定大小的分区,每一个分区只能容纳一个进程。这种方法如今已再也不使用。
有一种固定分区的升级版是可变分区,操做系统有一个表记录内存的使用状况。一开始,全部内存均可用于用户进程,做为一大块可用内存,称为“孔”。当有新进程须要内存时,就为它寻找一块足够大的孔。若是找到,从该孔中进行分配,孔剩余部分能够下次使用。
寻找孔的算法有
1)首次适应。找到第一块足够大的孔就中止。
2)最佳适应,全表扫描,找到最小且合适的一块。
3)最差适应,全表扫描,找到最大且合适的一块。
三种算法中,最差适应最差,首次适应比最佳适应快一点点。可是,后两者产生的外部碎片问题(分区方法产生外部碎片,分页产生内部碎片)也比最差适应要严重。解决之道是紧缩(靠,SQL SERVER也有碎片问题,也是靠索引重建或收缩来解决)。所谓的紧缩就是移动内存内容,以便将空闲空间合并成一块。但并不是全部状况都适用紧缩。若是内存物理地址重定位是静态的,在汇编或装入时进行,没问题,但若是是在运行时才进行就开销太大。
另外一种解决方案是容许物理地址为非连续。这种方案就是如下的:分页和分段。
4、分页
分页容许进程的物理地址空间非连续。
分页避免了将不一样大小的内存块匹配到交换空间上这样的麻烦。由于备份存储也有与内存相关的碎片问题,而且访问速度更慢,所以不适宜合并。而分页是将物理内存划分为固定大小的块,能够避免外部碎片。分页因为其优越性为绝大多数操做系统所采用。
分页由硬件支持。不过,最新的趋势是经过硬件和OS相配合,尤为是在64位微处理器上。
一、基本方法
分页方案中,物理内存划分为固定大小的的块,称为帧(frame);与之对应,逻辑内存也分为一样大小的块,称为页。一样,备份存储也有一样大小的块。大小由硬件决定,一般为2的幂,以方便将逻辑地址转换为页号和页偏移。
p:页号
d:页偏移量
f:帧号
由CPU生成的每一个地址分为2部分:页号(p)和页偏移(d)。p做为页表的索引,页表包含每页所在物理内存的基地址,基地址 + 页偏移 = 物理地址。如图
采用分页技术不会产生外部碎片,但可能又内部碎片。由于进程所要求的内存不必定是页的整数倍。页究竟取多大的值,这是个问题。
分页的一个特色是用户视角的内存和实际的物理内存分离。从用户角度看,用户程序将内存做为一整块来处理,而且整个内存只有它本身存在。但事实上,上面有各类各样的进程,而且物理地址分布多是不连续的。
这种差别,经过硬件进行映射转换。而这一切,对用户来讲是透明的,但受操做系统控制。用户程序不能越界访问,没法访问其页表规定以外的内存。
因为操做系统管理物理内存,它必须清楚物理内存的分配细节,知道帧的使用状况,可用数,总数,等等。这些信息一般保存在帧表的数据结构中。
二、硬件支持
操纵系统如何维护页表?
绝大多数是为每一个进程分配一个页表,页表指针与其余寄存器一块儿存入进程控制块中。
页表能够用一组专用的寄存器来保存,这是效率最高的方法。但只适用于页表不大的状况。
若是页表很是大,如1百万个条目,就须要存放在内存,用页表基寄存器指向页表。改变基寄存器就能够切换页表,快得很。
不过由此而来的问题是,采用这种方案,访问一个字节如今须要2次内存访问,相对于原来速度减半。这种延迟是没法忍受的。对这个问题的解决方案是采用硬件缓冲:转换表缓冲区,TLB。TLB只维护页表中的一小部分条目,逻辑地址转换物理地址过程当中,先在TLB中查找,若是找到,那么物理地址唾手可得;若是TLB中没有,那么使用置换算法,将相关条目置换进TLB,而后再获得物理地址。
三、保护
分页环境下,内存保护经过每一个帧的关联保护位来实现。一般,这些位于页表中。
这个位,定义一个页是可读写仍是只读。
还能够定义一个有效-无效位。当该位为有效时,表示该页在进程的逻辑地址空间内,是合法的页,不然属非法地址。一个进程不多会使用其全部的地址空间,而只使用一小部分。
四、共享页
分页的另外一个好处是能够共享公共代码。
5、页表结构
一、层次页表
对于一个进程,一个页表已经能够对应很多的条目,好比说,100万条,那就是100万个内存地址。若是还不够,那么页表能够再分层级,将页表再分页。原先的逻辑地址是
页码 + 页偏移
,如今页表分层后,变为 页码 + 页偏移 + 页偏移
二、哈希页表
处理超过32位地址空间的经常使用方法是使用哈希页表。
哈希页表的条目包括一个链表的元素,每一个元素有3个域:1)虚拟页码 2)物理帧号 3)指向链表中下一个元素的指针。其中虚拟页码做为哈希值。(所谓虚拟页码,应该就是页码、页号)
算法主要是凭页码获得哈希值,在哈希表中得到对应条目,找到物理帧号。
三、反向页表
为避免页表条目是否必须,都罗列在页表中,从而形成页表臃肿庞大的毛病,也能够采用反向页表。真正使用的帧才在反向页表中有一个条目。整个系统只有一张页表,对每一个物理内存帧只有一个条目,条目包含对应逻辑内存页的地址,及进程号等。主要的问题是内存共享有点麻烦。
6、分段
分页的问题是用户视角的内存和实际物理内存分离,逻辑内存须要映射到物理内存。
但对于咱们程序员来讲,内存是一个集合,里面有各类变量、对象,经过名字、指针来调用,而不关心它们到底位于什么位置。
分段(segmentation)就是一种支持这种用户视角的内存管理方案。逻辑地址空间由一组段组成。与分页比较,其最大的不一样是不固定大小。其他根据段号 + 偏移来得到物理地址,与分页彷佛并没有大的不一样。
进程的地址空间被划分为若干个段,每一个段定义了一组逻辑信息。例程序段、数据段等。每一个段都从0开始编址,并采用一段连续的地址空间。所以,分段方案中,逻辑地址是二维的。
7、分页与分段的主要区别
分页和分段有许多类似之处,好比二者都不要求做业连续存放.但在概念上二者彻底不一样,主要表如今如下几个方面:
(1)页是信息的物理单位,分页是为了实现非连续分配,以便解决内存碎片问题,或者说分页是因为系统管理的须要.段是信息的逻辑单位,它含有一组意义相对完整的信息,分段的目的是为了更好地实现共享,知足用户的须要.
(2)页的大小固定,由系统肯定,将逻辑地址划分为页号和页内地址是由机器硬件实现的.而段的长度却不固定,决定于用户所编写的程序,一般由编译程序在对源程序进行编译时根据信息的性质来划分.
(3)分页的做业地址空间是一维的.分段的地址空间是二维的.
打个比方,好比说你去听课,带了一个纸质笔记本作笔记。笔记本有100张纸,课程有语文、数学、英语三门,对于这个笔记本的使用,为了便于之后复习方便,你能够有两种选择。
第一种是,你从本子的第一张纸开始用,而且事先在本子上作划分:第2张到第30张纸记语文笔记,第31到60张纸记数学笔记,第61到100张纸记英语笔记,最后在第一张纸作个列表,记录着三门笔记各自的范围。这就是分段管理,第一张纸叫段表。
第二种是,你从第二张纸开始作笔记,各类课的笔记是连在一块儿的:第2张纸是数学,第3张是语文,第4张英语……最后呢,你在第一张纸作了一个目录,记录着语文笔记在第三、七、1四、15张纸……,数学笔记在第二、六、八、九、11……,英语笔记在第四、五、12……。这就是分页管理,第一张纸叫页表。你要复习哪一门课,就到页表里查寻相关的纸的编号,而后翻到那一页去复习
四.段页式存储管理
1.基本思想:
分页系统能有效地提升内存的利用率,而分段系统能反映程序的逻辑结构,便于段的共享与保护,将分页与分段两种存储方式结合起来,就造成了段页式存储管理方式。
在段页式存储管理系统中,做业的地址空间首先被分红若干个逻辑分段,每段都有本身的段号,而后再将每段分红若干个大小相等的页。对于主存空间也分红大小相等的页,主存的分配以页为单位。
参考文章:
http://blog.sina.com.cn/s/blog_4692ea0a0101j4ss.html