zoukankan      html  css  js  c++  java
  • 存储管理与linux内存寻址(2)

    操作系统基础课存储管理2

    一.加速分页过程

    虚拟内存和分页技术从原理上实现了多道程序并行的内存管理,但还要考虑几个效率问题:

    1,  虚拟地址到物理地址的映射要尽可能快。

    2,  虚拟空间很大会造成页表很大,大页表造成内存开销很大。

    二.转换检测缓冲区TLB

    为加快分页速度,就要减少内存访问次数,一种解决方案是,大多数程序总是对少量的页面进行多次访问,因此只有很少的页表项会被反复读取。而其他页表项很少被访问。具体实现是可以为计算机设置一个小型的硬件设备,将虚拟地址直接映射到物理地址,而不必在访问页表,这种设备就是转换检测缓冲区Translation Lookaside Buffer,也即关联存储器。通常在MMU中,包含少量表项,每个表项纪录一个页面的相关信息,包括虚拟页号,页面修改位等。注意虚拟页号是此种特有的,而真实页表中不一定需要此项

     

    工作过程:当虚拟地址进入MMU进行转换时,先查找TLB中匹配的虚拟页号,如果存在一个有效的匹配,查看其保护位以确定当前操作是否有访问权限,如果不违反保护位,则取出表项中的页框号映射而不必访问内存。如果没有匹配的项,则要进行正常的页表查询,进入内存。然后从TLB中删除一个表项,用新找到的表项替代它,同时将删除表项的信息复制到内存页表对应的页表项中,其实就是修改页表项修改位和访问位(目的是更新页表项实用信息,为页表置换算法提供支持)。

    三.大内存页表

    TLB提高了虚拟地址到物理地址的映射,到虚拟内存的另外一个问题是,如果对于大的内存,虚拟内存可能是更大的地址空间,页表会有更多的表项,16位虚拟地址用4位来索引页表,因此页表共有16项.如果是32位地址,用20位索引,就有2^20个表项,一个巨大的虚拟地址空间。

    像计算机很多地方的处理方式一样,采用多级目录,这里采用多级页表,32位地址被分为10位PT1域,10位PT2域,和12位编移量,PT1域对应顶级页表,PT2域对应二级页表,一起被用于确定物理存储块的基地址,然后在基地址上通过12位偏移量确定相应的存储单元。

    但是事实一个进程或程序并不需要用到全部的地址空间,所以一般只用到顶级页表的三项,进程抽象了内存,所以程序在进程地址空间的存储就类似于前文所诉的,程序正文段位于顶级页表底部,上面是数据段,顶部是堆栈段,空闲区域为扩展备用:

     

    如图,顶级页表中只有没有阴影的三项使用了映射,从上到下分别对应堆栈段,数据段和正文段,其他表项的Present/absent位都被置为0.访问这些项讲产生缺页中断,操作系统准备负责新的映射。

    考虑另一个问题,这样的页表需要多大的空间,顶级页表共2^10=1024个表项,如果每个表项1字节,共1KB,每个表项对应一个二级页表索引,每个二级页表大小和顶级页表大小相同,也是1KB,共1024个二级页表,共1MB,所以共1025KB,可以接受。

    但是如果是64位计算机,使用64位地址空间,除去12位偏移量,用52位映射物理页框,52位就有2^52个虚拟页面,也就是2^52个页表项,即使最小每个页表项1字节,可以计算这样的开销有多大。即使使用多级页表,对大小的帮助也不明显。

    解决的办法是使用倒排页表。针对每一个物理页框建立表项,而不是对虚拟页面。对1GB的RAM,4KB的页,只需2^18项,每项是一个页框和页面的映射,类似键值对。每次访问都查找这样的键值对是否存在。带来的问题是以前查找是使用虚拟地址索引到对应的页面,是随机访问。但如果用这样的键值对,就没有了这样的索引必须从头开始逐个检索,这必然是个耗时的操作。要提高这个速度同样可以使用TLB,但TLB失效时,还是要逐个检索页表,为了避免这个操作,可以使用一个比较好的数据结构--哈希(个人觉得哈希表真是最优雅的数据结构)。根据虚拟页面进行哈希(即对虚拟页面地址),将相同散列值的虚拟页面链接在一起,哈希表长度为2^18(针对1GB内存),这样哈希后每个链表的平均长度为1,减少链式访问次数,尽量随机访问到。

     

    四.页面置换算法Page Replacement Algorithms

    发生缺页中断时,操作系统必须将在内存中的一个页面换出,以便为即将调入的页面腾出空间,如果换出的页面在内存驻留期间已经被修改过(查看其修改位),必须把它写回磁盘(即更新磁盘副本),如果没有没修改过,则磁盘副本依然有效,可以直接从磁盘调入新的页面覆盖之。选择哪个页面换出需要一个策略,如果一个频繁使用的页面被换出,很快就有需要再此换入,应该尽量减少这样的操作。为提高性能,选择最合适的页面换出就需要页面置换算法。

    最优页面置换算法:

    基本思想:内存中的每个页面可能在一个指令后被访问,也可能在很多个指令后才被访问,如果多少个指令之后被访问作为标记标记每一个页面,则在缺页中断发生时,选择那些标记最大的页面淘汰,也就是淘汰那些在很多个指令之后才会被使用或访问的页面。在需要时再将其换回。

    但这个思想是难以实现的,原因在于操作系统无法预先知道某个页面在何时会被访问。

    最近未使用页面置换算法:

    每个页面大都有一个访问位和修改位,简称R位和M位,当一个进程启动时,其所有页面的这两个位都被置为0,运行过程总如果访问到该页面,访问位置1,如果对该页面进行了修改修改位置1,R位被定期(每个时钟中断)的清零,但M位不清零,以便在页面换出时判断是否应该写回磁盘。因此每一个时刻,一个页面根据这两位被分为4个状态:

    第0类:没有被访问,没有被修改

    第1类:没有被访问,已被修改

    第2类:已被访问,没有被修改

    第3类:已被访问,已被修改

    第三类在时钟中断清零后就变成第一类,页面置换算法随机的从编号最小的类中选择一个页面将其换出。

    先进先出页面置换算法:

    将访问过的页面排成一个队列,先访问的页面在队头,后访问的页面在队尾。其实队头的页面是进入内存最久的,而队尾的页面是刚访问的也就是刚进入内存的。缺页中断发生时,淘汰队头的页面,并将换入的页面插入队尾。但这样做可能会淘汰一个队头页面,虽然这个页面最先被访问,但在整个过程中访问很频繁,队列只对页面最早被访问的时间进行了区分,而未考虑页面访问频率,因此会因为换出一些比较重要的页面而引起性能问题。

    第二次机会页面置换算法:

    FIFO算法可能会换出队头经常被使用的页面,对其进行修改:检查最老页面的R位,如果是0,则该页面就是最老有最没用(未使用)的页面,将其换出,如果R位是1,则虽然该页面最老,但老当益壮,经常被使用,因此将此页面放到队尾,并修改R位为0.这是一个比较好的算法,可能的缺陷在于将队头换到队尾的操作。完全可以想到一个更好的数据结构代替这样的队列,即环形队列,队头接队尾,这样就只需修改队头指针就可以了。修改后的算法成为时钟页面置换算法。

    最近最少使用页面置换算法:

    在前面几条指令中频繁使用的页面很可能在后面几条指令中继续被使用,而很久没被使用的页面很可能在未来很长一段时间内也不会被使用。所以当缺页中断发生时,置换未使用时间最长的页面,这就是LRU(Least Recently Used),这个算法的实现非常精彩和巧妙,第一次看到时心情相当兴奋:

     

    假设有4个页面0,1,2,3,访问顺序为0,1,2,3,2,1,0,3,2,3.这些页面被排在4×4的矩阵中,每行代表一个页面。当一个页面a被访问时,将矩阵中对应第a行所有列置1,然后将第a列全部置0,每行中1的个数代表了该页面的活跃程度,所以如图,但0页面一开始被访问是,被置3个1,然后之后三个时间都没有被访问,所以在第三个页面访问时,0页面又全部恢复为0,即最近不活跃。行列的设计非常巧妙,暂时没有想到用一种言语或模型来描述行列之间的微妙关系,只能意会了。

    五.分页系统设计问题之局部分配和全局分配策略

    之前讨论的分页,页面置换,以及页面置换算法貌似都是针对一个进程讨论的,那么发生缺页中断时,换出的页面是来自该进程本身还是从内存所有进程中选取呢。

    从内存所有页面中选择淘汰页面是一种全局的算法,而只是从进程自己的地址空间中选取页面是一种局部的算法。称全局置换算法和局部置换算法。

    局部算法可以有效的为每个进程分配固定的内存片段(可以理解为页框),而全局算法在可运行进程之间动态的分配页框,因此各个进程的页框数是动态变化的。

    解释两个名词:

    工作集:一个进程当前使用的所有页面集合。随着进程的运行,进程会加载更多的页面,换入换出操作会使工作集扩展或缩小。

    在全局算法下进程加载新的页面,操作系统选择空闲页框建立映射,工作集增长,局部算法也可以在分配给进程的内存片段中寻找空闲页框而使工作集增长,如果没有空闲页框,执行页面置换算法,不过有无空闲页框,都会产生陷阱,使CPU陷入操作系统,即缺页中断建立新的映射。这是一个系统及的调用,会使当前进程运行速度减慢,即进程抖动,也称颠簸。

    为一个进程分配多少的页框合适,在局部方法中,因为进程的内存片段是固定的,所以进程运行过程中,如果工作集减小,局部方法会有较多页框空闲而浪费资源,工作集满则每次缺页中断都执行换入换出操作。(此处不明白什么原因会导致工作集减小,局部方法应该一开始就为所有的页框执行好映射,即工作集满,然后缺页中断只是换入换出,并不会导致工作集减少。可能是一些像垃圾回收或局部变量失效的操作主动回收不会再使用的页面)。而如果是全局方法,一种好的做法是位进程大小按比例分配页框,同时规定一个最小页框数。所有空闲页框组成一个公用池,同时检测所有进程运行状况,分配在进行运行过程中动态更新。

    这就要有一个动态内存管理机制,一种方法是PFF(page fault frequency)缺页中断率算法,它指出了何时为一个进程分配页面,合适回收分配给该进程的页面。(这里适当的时候回收页面,那么在缺页时只需换入而不用换出,换出在一个之前的时间完成)。缺页中断率为每秒缺页中断的次数除以该进程所分配的页框数(也可以认为是进程使用中的页面数)。缺页中断率会随着分配页框的增大而减小。

    六.页面大小

    页面的大小也需要权衡,偏向于选择小页面,好处是首先,产生较少的内部碎片(internal fragmentation),因为大页面而数据小会空闲该页面,而该空闲无法被继续使用。另外,更好的适应各种数据结构和程序,如果程序分段,小页面可以在内存中保留较少的无用程序段。但小页面的缺陷是会加大页表,同时在换入换出是增大磁盘寻道时间。

    从数学上分析,假设进程的大小是s字节,页面大小p字节,每个页表项e字节,那么每个进程需要的是s/p个页项,页表和内部碎片开销为se/p+p/2

    页面比较小时,第一项较大,页面较大时第二项较大,最优解是取中间的某个值,对p求导,求极值。

     

  • 相关阅读:
    拓扑检查(ArcEngine)
    IMap.SelectByShape 方法选择要素
    根据图层获取要素的渲染颜色
    ArcEngine(AE)符号选择器及符号恢复功能【转载】
    版面元素介绍IElement
    示例:由线段生成一个环,由环构成一个多边形(可以包含多个环的多边形集)
    AO的Display对象简介二【转载】
    显示要素层中的某些要素
    示例:从GraphicsContain中取出一个元素
    示例:从Table中获取SelectionSet
  • 原文地址:https://www.cnblogs.com/brucemu/p/3127848.html
Copyright © 2011-2022 走看看