2.1.1 请求调页
因为物理内存比虚拟内存小得多,操作系统必须小心以高效地利用物理内存。一种节约
物理内存的方法是只装载被执行的程序当前正在使用的虚拟页。例如:一个数据库应用程序
可能被运行来查询一个数据库。这种情况下,并非数据库的全部都需要被装入内存,而只是
装入那些被检查的数据记录。如果数据库查询是一个搜索查询,那么把处理添加新记录的代
码从数据库程序中装载进来是毫无意义的。这种只在被访问时把虚拟页装入内存的技巧叫请
求调页。
当进程试图访问一个当前不在内存中的虚拟地址时,处理器将不能为被引用的虚拟页找
到页表项。例如:图 1-2-1中,进程X的页表中没有虚拟页帧号 2的页表项,所以当X试图从虚
拟页帧号2中读一个地址时,处理器将不能把该地址转换成物理地址。这时处理器通知操作系
统发生了页故障。
如果故障的虚拟地址无效,这意味着,该进程试图访问一个不该访问的地址。或许应用
程序出了些错误,比如写内存中的随机地址。这种情况下操作系统将终止它,保护系统中其
他进程不受此恶意进程破坏。
如果故障的虚拟地址有效,但它所引用的页当前不在内存中,操作系统就必须从磁盘上
的映像中把适当的页取到内存中,相对来说,磁盘访问需要很长时间,所以进程必须等待一
会儿直到该页被取出。如果还有其他可以运行的进程,操作系统将选择其中一个来运行。被
取出的页会被写到一个空闲的物理页帧,该虚拟页帧号的页表项也被加入到进程页表中。然
后进程从出现内存故障的机器指令处重新开始。这次进行虚拟内存访问时,处理器能够进行
虚拟地址到物理地址的转换,进程将继续执行。
Linux使用请求调页把可执行映像装入进程虚拟内存中。每当一个命令被执行时,包含该
命令的文件被打开,它的内容被映射到进程虚拟空间。这是通过修改描述进程内存映射的数
据结构来完成的,被称作“内存映射”。然后,只有映像的开始部分被实际装入物理内存,映
像其余部分留在磁盘上。随着进程的执行,它会产生页故障, Linux使用进程内存映射以决定
映像的哪一部分被装入内存去执行。
2.1.2 交换
如果一个进程想将一个虚拟页装入物理内存,而又没有可使用的空闲物理页,操作系统
就必须淘汰物理内存中的其他页来为此页腾出空间。
如果从物理内存中被淘汰的页来自于一个映像或数据文件,并且还没有被写过,则该页
不必被保存,它可以被丢掉。如果有进程再需要该页时就可以把它从映像或数据文件中取回
内存。
然而,如果该页被修改过,操作系统必须保留该页的内容以便晚些时候再被访问。这种
页称为“脏(dirty)”页,当它被从内存中删除时,将被保存在一个称为交换文件的特殊文件中。
相对于处理器和物理内存的速度,访问交换文件要很长时间,操作系统必须在将页写到磁盘
以及再次使用时取回内存的问题上花费心机。
如果用来决定哪一页被淘汰或交换的算法 (交换算法)不够高效的话,就可能出现称为“抖
动”的情况。在这种情况下,页面总是被写到磁盘又读回来,操作系统忙于此而不能进行真
正的工作。例如,如果图1-2-1中物理页帧号1经常被访问,它就不是一个交换到硬盘上的好的
候选页。一个进程当前正使用的页的集合叫做“工作集 (working set)”。一个有效的交换算法
将确保所有进程的工作集都在物理内存中。
Linux使用“最近最少使用 (Least Recently Used, LRU)”页面调度技巧来公平地选择哪个
页可以从系统中删除。这种设计中系统中每个页都有一个“年龄”,年龄随页面被访问而改变。
页面被访问越多它越年轻;被访问越少越年老也就越陈旧。年老的页是用于交换的最佳候选
页。
2.1.3 共享虚拟内存
虚拟内存使得几个进程共享内存变得容易。所有的内存访问都是通过页表进行,并且每
个进程都有自己的独立的页表。为了使两个进程共享—物理页内存,该物理页帧号必须在它
们两个的页表中都出现。
图1-2-1显示了共享物理页帧号4的两个进程。对进程X来说是虚拟页帧号4,而对进程Y来
说是虚拟页帧号6。这说明了共享页有趣的一点:共享物理页不必存在于共享它的进程的虚拟
内存的相同位置。
2.1.4 物理寻址模式和虚拟寻址模式
操作系统本身运行在虚拟内存中没有什么意义。操作系统必须维持自己的页表是件非常
痛苦的事情。大多数多用途处理器在支持虚拟寻址模式的同时支持物理寻址模式。物理寻址
模式不需要页表,在这种模式下处理器不会进行任何地址转换。 Linux内核就是要运行在物理
寻址模式下。
Alpha AXP处理器没有特殊的物理寻址模式。相反,它把地址空间分成几个区,指定其中
两个作为物理映射地址区。这种核心地址空间被称为 K S E G 地址空间,它包括所有
oxfffffc0000000000以上的地址。为了执行链接在 KSEG中的代码(定义为内核代码)或存取其中
的数据,代码必须执行于核心模式下。Alpha上的Linux内核被链接成从地址oxfffffc0000310000
开始执行。
2.1.5 访问控制
页表项中也包含访问控制信息。因为处理器要使用页表项来把进程虚拟地址映射到物理地
址,它可以方便地使用访问控制信息来检查并保证进程没有以其不应该采用的方式访问内存。
有许多原因导致限制访问内存区域。有些内存,比如那些包含可执行代码的,自然地就
是只读内存;操作系统不应该允许进程写数据到它的可执行代码中。相反,包含数据的则可
以被写,但是试图把它当作指令来执行就会失败。大部分处理器至少有两种执行模式:用户
态(用户模式)和核心态(核心模式)。我们不希望核心代码被用户执行或核心数据结构被访问,
除非处理器运行在核心态下。
访问控制信息保留在PTE(页表项)中,并且是处理器相关的。图1-2-2显示了Alpha AXP 的
PTE。其各字段意义如下:
javascript:window.open(this.src);" style="CURSOR: pointer" onload="return imgzoom(this,550)">screen.width-500)this.style.width=screen.width-500;}" border="0" />此主题相关图片如下:
screen.width-500)this.style.width=screen.width-500;}" border="0" />
" V 有效性。如果置位则PTE有效。
" FOE 执行时故障,当试图执行本页中的指令时,处理器报告页故障并将控制传给操作
系统。
" FOW 写时故障,当试图写本页时发出如上页故障。
" FOR 读时故障,当试图读本页时发出如上页故障。
" ASM 地址空间匹配,当操作系统想要仅清除转换缓冲区中一些项时用到。
" KRE 运行于核心态的代码可以读此页。
" URE 运行于用户态的代码可以读此页。
" GH 粒度暗示,当用一个而不是多个转换缓冲区项映射一整块时用到。
" KWE 运行于核心态的代码可以写此页。
" UWE 运行于用户态的代码可以写此页。
" PFN 页帧号。对于V位置位的PTE,本字段包括物理页帧号;对于无效 PTE,如果本字
段非0,则包含本页在交换文件中的位置的信息。
下面两个位在Linux中被定义并使用:
" _PAGE_DIRTY 如果置位,此页需要被写到交换文件中。
" _PAGE_ACCESSED Linux用来标识一个页已经被访问过。