3.6.3 指令备份

当程序访问不在内存中的页面时,引起缺页中断的指令会半途停止并引发操作系统的陷阱。在操作系统取出所需的页面后,它需要重新启动引起陷阱的指令。但这并不是一件容易实现的事。

我们在最坏情形下考察这个问题的实质,考虑一个有双地址指令的CPU,比如Motorola 680x0,这是一种在嵌入式系统中广泛使用的CPU。例如,指令


MOVE.L#6(A1),2(A0)

广告:个人专属 VPN,独立 IP,无限流量,多机房切换,还可以屏蔽广告和恶意软件,每月最低仅 5 美元


为6字节(见图3-28)。为了重启该指令,操作系统要知道该指令第一个字节的位置。在陷阱发生时,程序计数器的值依赖于引起缺页中断的那个操作数以及CPU中微指令的实现方式。

阅读 ‧ 电子书库
图 3-28 引起缺页中断的一条指令

在图3-28中,从地址1000处开始的指令进行了3次内存访问:指令字本身和操作数的2个偏移量。从可以产生缺页中断的这3次内存访问来看,程序计数器可能在1000、1002和1004时发生缺页中断,对操作系统来说要准确地判断指令是从哪儿开始的通常是不可能的。如果发生缺页中断时程序计数器是1002,操作系统无法弄清在1002位置的字是与1000的指令有关的内存地址(比如,一个操作数的位置),还是一个指令的操作码。

这种情况已经很糟糕了,但可能还有更糟的情况。一些680x0体系结构的寻址方式采用自动增量,这也意味着执行这条指令的副作用是会增量一个或多个寄存器。使用自动增量模式也可能引起错误。这依赖于微指令的具体实现,这种增量可能会在内存访问之前完成,此时操作系统必须在重启这条指令前将软件中的寄存器减量。自动增量也可能在内存访问之后完成,此时,它不会在陷入时完成而且不必由操作系统恢复。自动减量也会出现相同的问题。自动增量和自动减量是否在相应访存之前完成随着指令和CPU模式的不同而不同。

幸运的是,在某些计算机上,CPU的设计者们提供了一种解决方法,就是通过使用一个隐藏的内部寄存器。在每条指令执行之前,把程序计数器的内容复制到该寄存器。这些机器可能会有第二个寄存器,用来提供哪些寄存器已经自动增加或者自动减少以及增减的数量等信息。通过这些信息,操作系统可以消除引起缺页中断的指令所造成的所有影响,并使指令可以重新开始执行。如果该信息不可用,那么操作系统就要找出所发生的问题从而设法来修复它。看起来硬件设计者是不能解决这个问题了,于是他们就推给操作系统的设计者来解决这个问题。