803-中断向量表的发展
# 803-中断向量表的发展
那好,现在这个手册的制造者已经说了, 他在第一页就留好了一个表格,一共有 256 行。 虽然现在没有填写完全,啊,但是后面呢,它随着不断的升级,推出新的手册的时候呢,会继续补充后面的一些条款。啊, 那我们现在呢,就来看一看,历代的这个手册它是怎么补充这个条款的。
这是 8086 的总共一兆的地址空间,那在这其中最低的 1K 字节固定用来放置中断向量表。 这个中断向量表当中包括 256 个中断向量,每个中断向量为 4 个字节。 这 4 个字节就指向了对应的中断服务程序的入口地址。
那在 8086CPU 中,已经规定了前 5 个类型的中断。 类型 0 为除法错,也就当除法运算出错时,CPU 就会来取得这个中断向量,并调用对应的中断服务程序。 类型 1 是单步,类型 2 是非屏蔽中断,类型 3 是断点中断, 类型 4 就是我们最开始提到的中断溢出的中断。
从类型 5 到类型 31 在 8086 当中并没有定义, 但它说明了这些中断类型是 CPU 保留的。随着以后 CPU 功能的丰富,将会使用这些中断类型号来提供新的中断服务。
那我们就来简单的看一看后来的 CPU 又有了哪些新的中断类型。 那在 8086 之后,新的中断类型在不断地添加进来。比如说类型 6, 叫作未定义的操作码。CPU 从内存当中取回一条指令编码后, 发现这个编码不属于当前指令系统当中定义的任何一条指令, 那 CPU 自然就不知道该做对应的什么样的操作。那最开始并没有约定遇到这样的 情况应该怎么办。后来增加了这个类型的中断之后,CPU 只要遇到未定义的指令操作码, 就可以产生类型 6 的中断,并调用对应的中断服务程序。 那这个中断服务程序有可能就会在屏幕上打出一行字, 说遇到了一条未定义的指令,然后就停止当前运行的程序,或者跳过这条指令继续执行。这就取决于中断服务程序如何去编写了。
那么后面还增加了一些其他类型的中断。 那有一些标了一个星号的是从 386 开始才有的,标了两个星号的是从 486 开始才有的。 这里有一些是对原先没有处理的异常情况进行了处理, 更多的则是根据 CPU 新增的功能添加了新的中断类型。 那随着指令系统体异结构进一步升级和 CPU 的改进,以后还会定义新的中断类型。 那这是中断向量表内容的变化。
我们再来看一看中断向量表存放的位置。这是现在比较流行的个人计算机的内部结构。
那它如果运行在实模式下,那就可以认为它是一个非常快的 8086。 因此当 CPU 复位之后,也会去 1 兆地址空间的最高的 16 个 byte 的位置 去取第一条指令。那么这个地址会被南北桥芯片组引导到 BIOS 芯片。 那 CPU 执行 BIOS 芯片当中的指令,对主板上的各个设备进行基本的配置。 其中一项工作,就是在组成地址 0 的地方构建出中断向量表。 那所谓构建中断向量表,也就向主存中最低的那 1K 的字节填写那些中断向量。 那构建好了中断向量表,又准备好了中断服务程序之后,CPU 再遇到中断就可以访问主存中的中断向量表,并调用对应的中断服务程序了。
但是我们也知道,现在的 CPU 其实主要不是运行在实模式下,内存也并不是仅仅只有一兆了, 所以 CPU 对存储器的访问方式也发生了变化。 那我们以指令的寻址为例,在实模式下是用代码断计存器 CS 和指令指针计存器 IP 进行组合。 这两个计存器都是 16 位的,它们的组合用断加偏移的方式产生一个 20 位的地址。
但是从 386 开始,指令指针计存器就从 16 位扩展到了 32 位。 也就是 EIP 计存器。那么它的寻址能力就有了 2 的 32 次方也就是 4G 个字节单元。 而从 386 开始,32 位的 CPU 对外也就是 32 位的地址线, 能够寻址的范围也是 2 的 32 次方。 那在这时候,指令指针计存器的宽度和实际需要寻址的范围已经是一一对应的了。
所以在保护模式下,虽然逻辑地址还是写成 CS 计存器和 EIP 计存器的形式, 但如果你地址的产生方式已经和实模式完全不同了。
在保护模式下,段基址并不是存放在 CS 计存器当中,而是存放在内存中的。 那么来看一看这时候 CPU 是怎么进行存储器寻址的。 那么在内存当中的某个地方,存放着一张表, 称为描述服务表。这张表一共有 8192 个表项, 每个表项由 8 个字节构成,就被称为一个描述符。这个描述符当中,第 2,3,4 个字节和第 7 个字节这一共四个字节是基地址, 这个地址就对应了实模式下保存在 CS 计存器当中的内容。那这个描述符当中, 除了基地址之外,还有一些别的内容。例如断界限,它指明了这个段究竟有多长。 还有权限,这里面有若干个比特,指明了这个段的内容,是否可读, 是否可写等等。
那这些描述符是存放在内存中的, CPU 又是如何能访问到呢?所以在 CPU 当中实际上是修改了 CS 寄存器的使用方式。 我们可以想一想为什么总共有 8192 个描述符? 这和 CS 计存器的宽度是有关系的,CS 计存器是 16 位宽的, 所以它一共可以寻址 2 的 16 次方个内存单元, 这就是 64K。而因为每个描述符是 8 个字节, 所以 8192 个描述符刚好是 64K。因此用 CS 计存器就正好可以查找这么多个描述符。
但是这个描述符表存放的地址并不是从 0 开始的, 所以想要找到对应的描述符,CPU 还得知道这个描述符表存放的起始地址。所以在 CPU 当中还需要再设置一个新的计存器,叫"GDTR", 它用于保存这张描述符表的起始地址。 那么又得问了,这个 GDTR 的内容是从哪里来的呢? 实际是因为 x86 的 CPU 在启动的时候都会先进入实模式。 在实模式下,就会在内存的某一个地方,先把这张描述符表都填好,然后将这张表的起始地址填到 GDTR 计存器当中去。 这也是 CPU 内部的一个寄存器。只不过它不像 EX,EDX 这些计存器可以用作数据的运算, 它是一个特殊的计存器, 但也是可以用特定的指令进行访问的。
所以在保护模式下,CPU 每次要访问存储器,都得先用 CS 计存器的内容,加上 GDTR 计存器的内容得到一个地址,用这个地址去访问存储器, 然后取出这个描述符,再把这个描述符当中 4 个字节的基地址提取出来, 然后再和指令指针计存器 EIP 的内容进行组合,从而得到要访问的存储器地址。 然后再用这个地址去访问存储器,得到想要的指令编码。
那既然在保护模式下,每一次存储器的访问都必须要经历这个过程,那访问中断向量表也就不例外。而且不但如此, 中断向量表的位置也发生了变化。 在保护模式下,中断向量表也就没有放在地址为 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 才会从中断服务程序的入口取回指令,真正开始中断的处理。
现在我们已经知道了, 这个表格随着这个手册的更新换代,也在不断的补充进新的内容。 而且在后来呢,还觉得这个表放在第一页不太好,给它放到了这个手册当中的其他的位置。 因此也用了用别的方法,来去标记这个表到底在什么位置。不然我们遇到异常情况不就找不着了嘛。当然了,这部分内容呢不是我们的重点,只是简单地给大家讲解了一下。 如果有兴趣呢可以去深入地分析相关的资料。
- 01
- 中国网络防火长城简史 转载10-12
- 03
- 公告:博客近期 RSS 相关问题10-02