操作系统-虚拟内存
基本概念
虚拟内存:计算机系统的用户可将其视为可寻址的主存储器的一种存储空间。在这种计算机系统中,虚地址可以映射到实地址。虚拟内存的大小受限于计算机系统的编址方案以及可用辅助存储器的大小,而与主存储器单元的实际数目无关。
虚拟地址:虚拟内存中的存储位置的地址。在虚拟内存中分配给某一位置的地址使该位置可以被访问,仿佛它是主内存中的一部分。
实地址:内存中的物理地址
缺页中断(Page fault): 当包含被访问字的页不在内存中的时候就会发生缺页。这会引发一个中断,要求正确的页被取入内存。
抖动(thrashing):在更换页面时,如果更换页面是一个很快会被再次访问的页面,则再次缺页中断后又很快会发生新的缺页中断。会导致整个系统的效率急剧下降。如果一个进程在换页上用的时间多于执行时间,这个进程就是在抖动。
工作集/驻留集:在某段时间间隔内,进程要访问的页面集合。
常驻集:进程执行的任何时候都在内存的部分。
局部性原理:时间局部性(temporal locality) 空间局部性(spatial locality) 顺序局部性(sequence locality)
虚拟内存技术
基于局部性原理,在程序装入的时候,可以将程序的一部分装入内存,而将其余部分留在外存,就可以启动程序执行。在执行过程中,当所访问的信息不在内存的时候,由操作系统将所需要的部分调入内存,然后继续执行程序。另一方面,操作系统将内存中暂时不适用的内容换出到外存上,从而腾出空间存放要调入的信息,这样,系统就为用户提供了一个比实际内存大的存储器,称为虚拟存储器。
这种存储器并不存在,只是由于操作系统提供了部分装入、请求调入和置换功能后,给用户的感觉好像是存在一个比实际物理内存大得多的存储器。其大小由计算机的地址结构决定,并非是内外存的简单相加。
虚拟内存中,允许一个作业分多次调入内存(分页和虚拟分页的主要区别,分段同理)。采用连续分配方式的时候,会使得相当一部分内存空间都处于暂时或永久的空闲状态,造成内存资源的严重浪费,而且也无法从逻辑上扩大内存容量。因此,虚拟内存的实现需要建立在离散分配的内存管理方式的基础上。
虚拟内存的实现方式有分页、分段、段页式。不管哪种方式,都需要硬件的支持,一般包括:一定容量的内存和外存;页表机制(或段表机制),作为主要的数据结构;中断机构:当用户程序要访问的部分尚未调入内存的时候,则产生中断;地址变换机构:逻辑地址到物理地址的转换。软件部分需要操作系统实现内存管理,使得段页能够在主存和外存之间移动。
分页
请求分页系统的页表机制不同于基本分页,它允许在一个作业运行之前不要求全部一次性调入内存,因此在作业的运行过程中,必然会出现要访问的页面不在内存的情况。
页表项PTE结构:
Page frame number:外存地址--用来指出该页在外存上的地址,通常是物理块号,供调入该页时参考。
Present Bit:存在位--用来指示该页是否已调入内存,供程序访问时参考。
Modified/Dirty Bit:修改位--标识该页在调入内存后是否被修改过
Caching enabled/disabled bit -:是否缓存
Protection bit - 保护位:选择保护策略
Referenced bit - 访问位:记录本页在一段时间内被访问的次数,或记录本页最近已有多长时间未被访问,供置换算法参考。
缺页中断机制:
每当要访问的页面不在内存的时候,便产生一个缺页中断,请求操作系统将所缺的页调入内存。缺页中断作为一个中断同样要经历:保护cpu、分析中断、转入缺页中断处理程、恢复cpu等几个步骤。但与一般的中断相比,他有两个明显的区别:(1)在执行执行期间产生和处理中断信号,而非一条指令执行完后,属于内部中断 (2)一条指令在执行期间,可能产生多次缺页中断
地址变换机构:
所有的页面都在页表中,因此是否命中会由valid bit来决定。
页表大小与虚拟地址空间成比例增加,可能会很大。页表自身在虚存,但是在进程运行的时候,部分页表在内存中。
二级页表:
虚拟地址的前10位用来检索根页表,用接下来的十位来检索用户页表,查找引用的页表项,最后根据偏移量来获取物理地址。
TLB(转移后备缓冲器/旁视缓冲器/页表缓存):
原则上,每个虚存访问都会引起两次的物理内存访问:一次读取相应的页表项,一次读取需要的数据。因此,为页表项建立一个高速缓存,能够节省时间。这个缓存包含有最近用过的页表项。
TLB的工作流程:
- 给定一个虚拟地址,处理器首先检查TLB。
- 如果需要的页表在其中,即TLB命中。则检索页框号形成实地址。
- 如果未找到,则处理器检索页表,并检查相应的页表项。
- 首先检查存在位,判断该页是否在内存中。如果在,处理器从页表项中检索页框号形成实地址并更新TLB
- 如果不在,则发生一次缺页中断,由操作系统通知CPU从硬盘进行读取,并更新页表(如果内存满了,则置换)
页面大小的一些结论:
页越小,内部碎片越少
页越小,每个进程需要的页越多,进程的页表就越大
页表越大,占用虚存越多
分段
分段与分页的最大区别就在于分段是大小不等的。它的优势在于能够简化不断增长的数据结构处理、允许程序独立的修改和编译、有助于进程间共享、有利于保护。
段表项的结构:
段在内存中的起始地址+段的长度+P(状态位)+M(修改位)
地址变换机构
段页式
段表项此时不需要存在位和修改位,相关问题会在页一级进行处理。
此外,还可能需要用于基于保护和共享目的的其他控制位。
页表项本质上与纯粹的分页系统是一样的。
保护和共享
分段有利于保护与共享,因为在段表项中有一个长度,能够控制程序的访问区域。分页由于对程序员不可见,使得保护和共享的要求难以实现。
读取策略
确定一个页何时取入内存,常用的是请求分页和预先分页。
请求分页:只有当访问到某页的一个单元的时候才将该页取入内存。
预先分页:读取的页并不是缺页中断请求的页,利用了大多数辐存设备的特性(寻道时间和合理延迟)。如果一个进程的页被连续存储在辅存中,则一次读取许多连续的页比隔一段时间读取一页更加有效。
放置策略
决定一个进程块驻留在实存中的什么地方。如最佳适配、首次适配等等算法。
放置策略对分段系统和NUMA系统影响大.放置策略对分页和段页式系统影响不大
置换策略
用来处理在必须读取新页的时候,应该置换内存中的哪一页。
置换策略的一个约束:内存中的某些页框可能是被锁定的,则当前保存的页就不能被置换。锁定是通过给每个页框关联一个lock位实现的,这一位可以包含在页框表或者当前页表中。
基本算法
Optimal policy( OPT, 最佳 )
置换下次访问距当前时间最长的那些页。但是这要求操作系统必须知道将来的事情,显然这是不可能的。
Least Recently Used (LRU, 最近最少使用)
堆栈类算法,上次访问距当前时间最长的页。性能好,需要寄存器和栈的硬件支持。
First-in, first-out (FIFO, 先进先出)
基于队列实现,把分配给进程的页帧看做是一个循环缓冲区。与实际运行的规律不适应。FIFO算法会产生当物理块数增加而缺页故障数不减反增的异常现象。(Bleady异常)
Clock Policy(时钟策略)/NRU(最近未用)
给每一帧关联一个附加位,称之为使用位。当某一页首次装入主存的时候,使用为设置为1,当该页随后再被访问到,还是被设置为1。当需要替换页的时候,操作系统扫描整个缓冲区,查找使用位为0的那一帧,每当遇到一个使用位为1的,则将其设置为0,并跳过。
改进型的时钟策略在此基础上增加了一个修改位。在替换的时候首选没有变化的页:如果有未使用过的页面,则首先换出,如果全部页面都使用过,则换出未修改过的。
图上有个问题:任何没有成功命中的都属于缺页,所以前四次访问都是缺页F的。
算法比较