序言:显存是程序得以运行的重要物质基础。怎样在有限的显存空间运行较大的应用程序,曾是困惑人们的一个困局。为解决这个问题,人们设计了许多的方案,其中最成功的当属虚拟显存技术。Linux作为一个以通用为目的的现代小型操作系统,其实也毫不例外的采用了优点颇多的虚拟显存技术。一,虚拟显存
为了运行比实际化学显存容量还要大的程序,包括Linux在内的所有现代操作系统几乎毫无例外的都采用了虚拟显存技术。虚拟显存技术,可让系统看起来具有比实际数学意义显存大的多的显存空间,并为实现多道程序的执行创造了条件。
(1)虚拟显存的概念
总所周知,为了对显存中的储存单元进行辨识,显存中的每一个储存单元都必须有一个准确的地址。而一台计算机的处理器能访问多大的显存空间就取决于处理器的程序计数器,该计数器的字长越长,能访问的空间就越大。
比如:对于程序计数器位数为32位的处理器来说,他的地址发生器所能发出的地址数量为2^32=4G个,于是这个处理器所能访问的最大显存空间就是4G。在计算机技术中,这个值就称作处理器的主存空间或主存能力。
照理说,为了充分借助处理器的主存空间,就应根据处理器的最大轮询来为其分配系统的显存。若果处理器具有32位程序计数器,这么就应当根据右图的方法,为其配备4G的显存:
这样,处理器所发出的每一个地址就会有一个真实的化学储存单元与之对应;同时,每一个化学储存单元都有惟一的地址与之对应。这其实是一种最理想的情况。
但遗憾的是,实际上计算机所配置显存的实际空间往往大于处理器的轮询范围,这是都会因处理器的一部份主存空间没有对应的数学储存单元,进而造成处理器轮询能力的浪费。诸如:如右图的系统中,具有32位轮询能力的处理器只配置了256M的显存储器,这都会导致大量的浪费:
另外,还有一些处理器因外部地址线的根数大于处理器程序计数器的位数,而使地址总线的根数不满足处理器的轮询范围,因而处理器的其余轮询能力也就被浪费了。诸如:Intel8086处理器的程序计数器位32位,而处理器芯片的外部地址总线只有20根,所以它所能配置的最大显存为1MB:
在实际的应用中,假如须要运行的应用程序比较小,所需显存容量大于计算机实际所配置的显存空间,自然不会出哪些问题。而且,目前好多的应用程序都比较大,计算机实际所配置的显存空间难以满足。
实践和研究都证明:一个应用程序总是逐段被运行的,并且在一段时间内会稳定运行在某一段程序里。
【文章福利】小编推荐自己的Linux内核技术交流群:【865977150】整理了一些个人认为比较好的学习书籍、视频资料共享在群文件上面,有须要的可以自行添加哦!!!前100名进群发放,额外附赠一份价值699的内核资料包(含视频教程、电子书、实战项目及代码)
这也就出现了一个方式:如右图所示redhat linux,把要运行的那一段程序自辅存复制到显存中来运行,而其他暂时不运行的程序段就让它依然留在辅存。
当须要执行另一端仍未在显存的程序段(如程序段2),如右图所示,就可以把显存中程序段1的副本复制回辅存,在显存腾出必要的空间后,再把辅存中的程序段2复制到显存空间来执行即可:
在计算机技术中,把显存中的程序段复制回辅存的做法称作“换出”,而把辅存中程序段映射到显存的做法称作“换入”。经过不断有目的的换入和换出,处理器就可以运行一个小于实际化学显存的应用程序了。或则说,处理器虽然是拥有了一个小于实际化学显存的显存空间。于是,这个储存空间称作虚拟显存空间,而把真正的显存称作实际化学显存,或简称为化学显存。
这么对于一台真实的计算机来说,它的虚拟显存空间又有多大呢?计算机虚拟显存空间的大小是由程序计数器的轮询能力来决定的。诸如:在程序计数器的位数为32的处理器中,它的虚拟显存空间就为4GB。
可见,假如一个系统采用了虚拟显存技术,这么它就存在着两个显存空间:虚拟显存空间和化学显存空间。虚拟显存空间中的地址称作“虚拟地址”;而实际化学显存空间中的地址称作“实际化学地址”或“物理地址”。处理器运算器和应用程序设计人员听到的只是虚拟显存空间和虚拟地址,而处理器片外的地址总线见到的只是数学地址空间和化学地址。
因为存在两个显存地址,因而一个应用程序从编撰到被执行,须要进行两次映射。第一次是映射到虚拟显存空间,第二次时映射到化学显存空间。在计算机系统中,第两次映射的工作是由硬件和软件共同来完成的。承当这个任务的硬件部份称作储存管理单元MMUlinux 文件空间容量,软件部份就是操作系统的显存管理模块了。
在映射工作中,为了记录程序段占用化学显存的情况,操作系统的显存管理模块须要构建一个表格,该表格以虚拟地址为索引,记录了程序段所占用的化学显存的化学地址。这个虚拟地址/化学地址记录表便是储存管理单元MMU把虚拟地址转化为实际化学地址的根据,记录表与储存管理单元MMU的作用如右图所示:
综上所述,虚拟显存技术的实现,是构建在应用程序可以分成段,但是具有“在任何时侯正在使用的信息总是所有储存信息的一小部份”的局部特点基础上的。它是通过用辅存空间模拟RAM来实现的一种使机器的作业地址空间小于实际显存的技术。
从处理器运算装置和程序设计人员的角度来看,它面对的是一个用MMU、映射记录表和化学显存封装上去的一个虚拟显存空间,这个储存空间的大小取决于处理器程序计数器的轮询空间。
可见,程序映射表是实现虚拟显存的技术关键,它可给系统带来如下特征:
二,Linux的虚拟显存技术
以储存单元为单位来管理其实不现实,因而Linux把虚存空间分成若干个大小相等的储存分区,Linux把这样的分区称作页。为了换入、换出的便捷,化学显存也就按也得大小分成若干个块。因为化学显存中的块空间是拿来容纳虚存页的容器,所以化学显存中的块称作页框。页与页框是Linux实现虚拟显存技术的基础。
虚拟显存的页、物理显存的页框及页表
在Linux中,页与页框的大小通常为4KB。其实,按照系统和应用的不同,页与页框的大小也可有所变化。
化学显存和虚拟显存被分成了页框与页然后,其储存单元原先的地址都被自然地分成了两段,但是这两段各自代表着不同的意义:低位段分别称作页框码和页脚,它们是辨识页框和页的编码;高位段分别称作页框偏斜量和页内偏斜量,它们是储存单元在页框和页内的地址编码。右图就是两段虚拟显存和化学显存分页然后的情况:
为了使系统可以正确的访问虚存页在对应页框中的映像,在把一个页映射到某个页框上的同时,就必须把页脚和储存该页映像的页框码填入一个称作页表的表项中。
这个页表就是之前提到的映射记录表。一个页表的示意图如下所示:
页模式下,虚拟地址、物理地址转换关系的示意图如下所示:
也就是说:处理器碰到的地址都是虚拟地址。虚拟地址和化学地址都分成页脚(页框码)和偏斜值两部份。在由虚拟地址转化成化学地址的过程中,偏斜值不变。而页脚和页框码之间的映射就在一个映射记录表——页表中。
三,请页与交换
虚存页面到化学页框的映射称作页面的加载。
当处理器企图访问一个虚存页面时,首先到页表中去查询该页是否已映射到化学页框中,并记录在页表中。假如在,则MMU会把页脚转换成页框码,并加上虚拟地址提供的页内偏斜量产生化学地址后去访问数学显存;假如不在,则意味着该虚存页面还没有被载入显存,这时MMU都会通知操作系统:发生了一个页面访问错误(页面错误),接出来系统会启动所谓的“请页”机制,即调用相应的系统操作函数,判定该虚拟地址是否为有效地址。
假如是有效的地址,就从虚拟显存上将该地址指向的页面读入到显存中的一个空闲页框中,并在页表中添加上相对应的表项,最后处理器将从发生页面错误的地方重新开始运行;假如是无效的地址,则表明进程在企图访问一个不存在的虚拟地址,此时操作系统将中止这次访问。
其实,也存在这样的情况:在请页成功以后,显存中已没有空闲化学页框了。这是,系统必须启动所谓地“交换”机制,即调用相应的内核操作函数,在化学页框中找寻一个当前不再使用或则近日可能不会用到的页面所抢占的页框。找到后,就把其中的页移出,以装载新的页面。对移出页面按照两种情况来处理:假如该页未被更改过红旗linux操作系统,则删掉它;假如该页以前被更改过,则系统必须将该页写回辅存。
系统请页的处理过程如下所示:
为了公正地选择即将从系统中抛弃的页面,Linux系统使用近来最少使用(LRU)页面的衰老算法。这些策略依据系统中每位页面被访问的频度,为化学页框中的页面设置了一个称作年纪的属性。页面被访问的次数越多,则页面的年纪最小;相反,则越大。而年纪较大的页面就是待换出页面的最佳候选者。
四,快表
在系统每次访问虚存页时,都要在显存的所有页表中找寻该页的页框,这是一个很费时间的工作。并且,人们发觉,系统一旦访问了某一个页,这么系统都会在一段时间内稳定地工作在这个页上。所以,为了提升访问页表的速率,系统还配备了一组刚好能容纳一个页表的硬件寄存器,这样当系统再访问虚存时,就首先到这组硬件寄存器中去访问,系统速率就快多了。这组储存当前页表的寄存器称作快表。
其实,使用虚拟储存技术时,处理器必须配备一些硬件来承当显存管理的一部份任务。承当显存管理任务的硬件部份称作储存管理单元MMU。储存管理单元MMU的工作过程如右图所示:
页的共享
在多程序系统中,往往有多个程序须要共享同一段代码或数据的情况。在分页管理的储存器中,这个事情挺好办:让多个程序共享同一个页面即可。
具体的方式是:使这种相关程序的虚拟空间的页面在页表中指向显存中的同一个页框。这样,当程序运行并访问那些相关页面时,就都是对同一个页框中的页面进行访问,而该页框中的页就被那些程序所共享。右图是3个程序共享一个页面的反例:
页的保护
由上可知,页表实际上是由虚拟空间转入数学空间的入口。因而,为了保护页面内容不被没有该页面访问权限的程序所破坏,就应在页表的表项中设置一些访问控制数组,用于指明对应页面中的内容容许何种操作,进而严禁非法访问。
右图是页表项中储存控制信息的一种可能的方式:
注意:其中的PCD位表示着是否容许高速缓存(cache)。
假如程序对一个页企图进行一个该页控制数组所不容许的操作,则会造成操作系统的一次中断——非法访问中断linux 文件空间容量,并拒绝这些操作,进而保护该页的内容不被破坏。
多级页表
须要注意的是,页表是操作系统创建的用于显存管理的表格。因而,一个程序在运行时,其页表也要储存到显存空间。假如一个程序只须要一个页表,则不会有哪些问题。但若果,程序的虚拟空间很大的话,还会出现一个比较大的问题。
例如:一个程序的虚拟空间为4GB,页表以4KB为一页,这么这个程序空间就是1M页。为了储存这1M页的页表针,这么这个页表的宽度就相当大了,对显存的负担也很大了。所以,最好对页表也进行分页储存,在程序运行时只把须要的页复制到显存,而暂时不须要的页就让它留在辅存中。为了管理这种页表页,还要构建一个记录页表页首地址的页目录表,于是单级页表就弄成了二级页表。二级页表的地址转换如右图所示:
其实,假如程序的虚拟空间更大,这么也可以用五级页表来管理。为了具有通用性,Linux系统使用了五级页表结构:页目录(PageDirectory,PGD)、中间页目录(PageMiddleDirectory,PMD)、页表(PageTable,PTE)。
Linux的页表结构
为了通用,Linux系统使用了五级页表结构:页目录、中间页目录和页表。PGD为顶尖页表,是一个pgd_t数据类型(定义在文件linux/include/page.h中)的字段,每位链表元素指向一个中间页目录;PMD为二级页表,是一个pmd_t数据结构的字段,每位链表元素指向一个页表;PTE则是页表,是一个pte_t数据类型的字段,每位元素中富含化学地址。
为了应用上的灵活,Linux使用一系列的宏来掩藏各类平台的细节。用户可以在配置文件config中按照自己的须要对页表进行配置,以决定是使用五级页表还是使用二级页表。
在系统编译时,会依照配置文件config中的配置,把目录include/asm符号联接到具体CPU专用的文件目录中。比如,对于i386386CPU,该目录符号会联接到include/asm-i386,并在文件pgable-2level-defs.h中定义了二级页表的基本结构,如右图:
其中还定义了:
#define PGDIR_SHIFT 22 //PGD在线性地址中的起始地址为bit22
#define PTRS_PER_PGD 1024 //PGD共有1024个表项
#define PTRS_PER_PTE 1024 //PTE共有1024个表项
#endif
在文件include/asm-i386/pgtable.h中定义了页目录和页表项的数据结构,如下:
typedof struct { unsigned long pte_low; } pte_t; //页表中的物理地址,页框码
typedof struct { unsigned long pgd; } pgd_t; //指向一个页表
typedof struct { unsigned long pgprot; } pgprot_t; //页表中的各个状态信息和访问权限
从定义可知,它们都是只有一个长整型类型(32位)的结构体。
注意:如上文的“页的保护”部分,页框码代表化学地址,只须要高20位就够了(由于页框的宽度为4KB,因而页内偏斜12位)。而后12位可以储存各个状态信息和访问权限。并且Linux并没有这样做,反倒重新定义了一个结构体来储存,通过“或”运算来将二者结合。