9.3 中断向量表的发展

计算机组成

9 中断和异常

9.3 中断向量表的发展

Screen Shot 2018-09-30 at 9.40.46 am

现在这个手册的制造者已经说了,他在第一页就留好了一个表格,一共有256行。虽然现在没有填写完全,但是后面,随着不断的升级,推出新的手册的时候呢?会继续补充后面的一些条款。

我们现在就来看一看,历代的这个手册它是怎么补充这个条款的。

Screen Shot 2018-09-30 at 9.40.53 am

这是8086的总共一兆的地址空间,在这其中最低的1K字节固定用来放置中断向量表。这个中断向量表当中包括256个中断向量,每个中断向量为4个字节(256*4=1K)。这4个字节就指向了对应的中断服务程序的入口地址。

Screen Shot 2018-09-30 at 9.41.02 am

在8086CPU中,已经规定了前5个类型的中断。类型0为除法错,也就当除法运算出错时,CPU就会来取得这个中断向量,并调用对应的中断服务程序。类型1是单步,类型2是非屏蔽中断,类型3是断点中断,类型4就是我们最开始提到的运算溢出的中断。

从类型5到类型31在8086当中并没有定义,但它说明了这些中断类型是CPU保留的。随着以后CPU功能的丰富,将会使用这些中断类型号来提供新的中断服务。

那我们就来简单的看一看后来的CPU又有了哪些新的中断类型。

Screen Shot 2018-09-30 at 9.41.10 am

在8086之后,新的中断类型在不断地添加进来。

比如说类型6,叫作未定义的操作码。CPU从内存当中取回一条指令编码后,发现这个编码不属于当前指令系统当中定义的任何一条指令,CPU自然就不知道该做对应的什么样的操作。那最开始并没有约定遇到这样的情况应该怎么办?后来增加了这个类型的中断之后,CPU只要遇到未定义的指令操作码,就可以产生类型6的中断,并调用对应的中断服务程序。这个中断服务程序有可能就会在屏幕上打出一行字,说遇到了一条未定义的指令,然后就停止当前运行的程序,或者跳过这条指令继续执行。这就取决于中断服务程序如何去编写了。

那么后面还增加了一些其他类型的中断,有一些标了一个星号的是从386开始才有的,标了两个星号的是从486开始才有的。这里有一些中断类型是对原先没有处理的异常情况进行了处理,更多的则是根据CPU新增的功能添加了新的中断类型。随着指令系统体异结构进一步升级和CPU的改进,以后还会定义新的中断类型。

这是中断向量表内容的变化,我们再来看一看中断向量表存放的位置。

Screen Shot 2018-09-30 at 9.41.18 am

这是现在比较流行的个人计算机的内部结构。它如果运行在实模式下,就可以认为它是一个非常快的8086。因此,当CPU复位之后,也会去1兆地址空间的最高的16个Byte的位置去取第一条指令,这个地址会被南北桥芯片组引导到BIOS芯片。CPU执行BIOS芯片当中的指令,对主板上的各个设备进行基本的配置。其中一项工作,就是在主存地址0的地方构建出中断向量表,所谓构建中断向量表,也就向主存中最低的那1K的字节填写那些中断向量。构建好了中断向量表,又准备好了中断服务程序之后,CPU再遇到中断,就可以访问主存中的中断向量表,并调用对应的中断服务程序了。

但是我们也知道,现在的CPU其实主要不是运行在实模式下,内存也并不是仅仅只有一兆了。所以,CPU对存储器的访问方式也发生了变化。

Screen Shot 2018-09-30 at 9.41.26 am

我们以指令的寻址为例,在实模式下是用代码段寄存器CS指令指针计存器IP进行组合。这两个寄存器都是16位的,它们的组合用段加偏移的方式产生一个20位的地址。但是从386开始,指令指针计存器就从16位扩展到了32位,也就是EIP计存器。那么它的寻址能力就有了2的32次方也就是4G个字节单元。而从386开始,32位的CPU对外也就是32位的地址线,能够寻址的范围也是2的32次方。在这时候,指令指针寄存器(EIP)的宽度和实际需要寻址的范围已经是一一对应的了。 所以,在保护模式下,虽然逻辑地址还是写成CS寄存器和EIP寄存器的形式,但物理地址的产生方式已经和实模式完全不同了。

Screen Shot 2018-09-30 at 9.41.35 am

在保护模式下,段基址并不是存放在CS寄存器当中,而是存放在内存中的。那么来看一看这时候CPU是怎么进行存储器寻址的。

在内存当中的某个地方,存放着一张表,称为描述符表。这张表一共有8192(\(2^{13}\))个表项,每个表项由8(\(2^3\))个字节构成,就被称为一个描述符。这个描述符当中,第2,3,4个字节和第7个字节这一共4个字节是基地址,这个地址就对应了实模式下保存在CS寄存器当中的内容。那这个描述符当中,除了基地址之外,还有一些别的内容。例如段界限,它指明了这个段究竟有多长;还有权限这里面有若干个比特,指明了这个段的内容是否可读,是否可写等等。这些描述符是存放在内存中的,CPU又是如何能访问到呢?所以,在 CPU当中实际上是修改了CS寄存器的使用方式。我们可以想一想为什么总共有8192个描述符? 这和CS计存器的宽度是有关系的,CS计存器是16位宽的。所以,它一共可以寻址2的16次方个内存单元,这就是64K。而因为每个描述符是8个字节,所以8192个描述符刚好是64K(8192*8=\(2^{13}\)*\(2^3\)=\(2^{16}\)=64K)。因此,用CS寄存器就正好可以查找这么多个描述符。

但是这个描述符表存放的地址并不是从0开始的,所以想要找到对应的描述符,CPU还得知道这个描述符表存放的起始地址。所以,在CPU当中还需要再设置一个新的寄存器,叫GDTR,它用于保存这张描述符表的起始地址。那么又得问了?这个GDTR的内容是从哪里来的呢?实际是因为x86的CPU在启动的时候都会先进入实模式。在实模式下,会在内存的某一个地方,先把这张描述符表都填好,然后将这张表的起始地址填到GDTR计存器当中去。这(GDTR)也是CPU内部的一个计存器,只不过它不像EX,EDX这些寄存器可以用作数据的运算,它是一个特殊的寄存器,但也是可以用特定的指令进行访问的。所以,在保护模式下,CPU每次要访问存储器,都得先用CS寄存器的内容,加上GDTR计存器的内容得到一个地址。用这个地址去访问存储器,然后取出这个描述符,再把这个描述符当中4个字节的基地址提取出来,然后再和指令指针计存器EIP的内容进行组合,从而得到要访问的存储器地址,然后再用这个地址去访问存储器,得到想要的指令编码。

既然在保护模式下,每一次存储器的访问都必须要经历这个过程,那访问中断向量表也就不例外。而且不但如此,中断向量表的位置也发生了变化。

Screen Shot 2018-09-30 at 9.41.43 am

在保护模式下,中断向量表也就没有放在地址为0的存储器区域了,而是可以放在内存的任意地方。而且它的名字也有了一些变化,叫作中断描述符表。所以,现在在内存的某一个地方,存放的这张中断描述符表总共有256个描述符,每个描述符是 8个字节。这8个字节当中,字节0和字节1,以及字节6,字节7组合起来,一共是32位的地址;而字节2和字节3 则是一个段选择符。当CPU发生中断时,还是会根据中断类型号来查找这个中断描述符表,因为现在中断描述符表的起始地址不是0,所以CPU必须要先知道这个中断描述符表的起始地址,这起始地址也保存在CPU当中的一个计存器,称为IDTR,就是中断描述符表的地址寄存器。这个寄存器的内容也需要由系统初始化软件在建立好中断描述符表后填写进去。

现在,CPU就需要将中断类型号以8再上IDTR寄存器中的内容,就可以得到对应的中断描述符的地址。然后把这个描述符取回之后,将段选择符这16位存放到CS寄存器当中,然后将地址对应的这32位存放到EIP寄存器当中。我们注意这个操作的动作实际上和实模式下是类似的,只不过实模式下每个中断向量是4个字节。然后把其中两个字节放到CS寄存器中,两个字节放到IP寄存器中。现在呢?每个中断描述符是8个字节,我们也是将其中的一部分放到CS寄存器中,另一部分放到EIP计存器中。在完成了这个动作之后,如果在实模式下,下一个周期就直接可以从新的地址开始取址了;而在保护模式下,则没有那么简单,我们还需要按照刚才讲的那样,用CS计存器和GDTR计存器配合,去内存中找出对应的段基值。然后再和EIP计存器组合,才能得到对应的内存地址。这个内存地址,才是我们需要调用的中断服务程序的入口地址。直到这时,CPU才会从中断服务程序的入口,取回指令,真正开始中断的处理。

Screen Shot 2018-09-30 at 9.41.54 am

现在我们已经知道了,这个表格随着这个手册的更新换代,也在不断的补充进新的内容。而且在后来还觉得这个表放在第一页不太好,给它放到了这个手册当中的其他的位置,这就需要用别的方法,来标记这个表到底在什么位置,不然我们遇到异常情况不就找不着了嘛。当然了,这部分内容不是我们的重点,只是简单地给大家讲解了一下。如果有兴趣,可以去深入地分析相关的资料。

posted @ 2018-10-04 19:46  houhaibushihai  阅读(607)  评论(0编辑  收藏  举报