计算机组成
9 中断和异常
9.6 基于中断的功能调用
那现在,我在执行这些运算之后,也就是执行这些程序的过程中呢,又遇到了一个奇怪的现象。这里突然有一个地方写着,请查看紧急操作手册的第二百项,那我就翻到这个地方去了。翻到前面,然后找第二百项,然后找到了对应的操作——是让我在本子的某一个地方写一个数。这是怎么回事呢?一般来说,刚才我们学到的都是我在运算当中遇到了异常的情况,然后我主动去查找前面的对应的异常处理的方法,查找中断向量表。那么现在在这个程序当中,居然主动的写到了让我去查找相关的中断项量表。这个是怎么回事情呢?我们就来看看这个问题。
在x86指令系统当中,其实还提供了一条中断指令,它的格式是INT,加上一个操作数n。用这条指令,就可以直接调用对应的中断服务程序。这个n是0到255当中的一个数,对应的中断类型码。当CPU执行到指令时,首先会将标志寄存器压栈,然后清除IF和TF两个标志位,也就是关中断。再将CS和IP两个寄存器的内容压栈,然后根据指令中提供的类型码,也就是这个n,去查找中断向量表,找到对应的中断服务程序的入口地址。再将入口地址装到CS和IP寄存器当中去,这样下一步CPU就会到中断服务程序的入口取出下一条指令继续执行。
那么看到这一个步骤和我们之前介绍的,CPU处理中断的过程是一样的。但是区别在于,这是由运行指令主动触发的。
比如,之前CPU当中,如果发生的1号中断,CPU就会来中断向量表当中取出1号中断向量。然后,转向对应的中断服务程序开始执行。但如果当前CPU执行程序的过程当中,并没有满足触发1号中断的条件,而是直接写了一条指令 int 1
,CPU也会到中断向量表当中,取出1号中断向量,装入CS,IP寄存器,再转到1号中断服务程序开始执行。
那这样做有什么意义呢?明明没有发生中断,我们为什么要调用这个中断服务程序。这种中断指令的使用场景其实有两类。一类就是CPU的一些专用的中断,就是需要通过调用指令的方式来实现的。比如说3号中断,那就得写 int 3
这样的指令才可以产生。这种情况我们在之前已经介绍过了,现在我们来看另外一种情况。
比如说有一类,我们称为BIOS中断。BIOS就是基本输入输出系统,这是一套不大但是还挺复杂的程序,存放在ROM当中。寄存器在刚通电或者复位时,CPU就会从BIOS ROM当中取出第一条指令开始执行。所以,在BIOS当中会提供系统加电自检和主要的输入输出设备处理程序等功能模块。所谓的功能模块,也就是一个一个具有独立功能的程序,比如从键盘接收一个字符,或是在显示器上显示一个字符,这些工作不但在系统初始化时需要使用,在后来用户使用计算机的过程中,其实也会用到。而如果要用户自己去编写处理这些输入输出设备的程序,那就太麻烦了。所以,BIOS的设计者就将其中的这些功能各个包装起来,形成了很多个独立的功能模块。然后将这些独立功能模块的入口地址,放在中断的向量表当中,那如果我们想使用这些功能,比如说就像在显示器上显示一个字符,用户就不用去关心到底用的是什么显示器,要占用多少个像素,等等。只需要使用BOIS提供的功能模块,用INT指令调用对应的中断服务程序就可以了。而且我们可能希望向这些功能模块传递一些参数,那就可以通过寄存器进行传递。
我们不妨来看一个例子。
这个表是BIOS中断的一个片段。想使用BOIS中断,首先就得查找BIOS中断的手册,这个手册一般会提供这样一个表格,列出了BIOS这些功能模块所在用的中断号,比如说10H,就是用于在显示器上进行显示的一个中断服务程序;而1AH,则是设置系统时钟的一个中断服务程序。
我们以1AH为例。如果我们想要改变现在的系统时钟,当然我们可以去分析时钟管理芯片的功能,通过查找它的手册来分析如何去改变系统时钟的设置,这可能要花很多个步骤。那BIOS的设计者就帮我们封装好了这个功能,我们只要这样写代码就可以了(红色汇编代码)。因为我们通过这个表可以看到,如果要设置时钟,我们需要提供一个功能号为1,因为1AH这个中断里头,其实有多种功能,我们可能想读出当前时钟的值;也可能要改变当前时钟的值。这个中断服务程序怎么识别呢?
它就要求你在AH寄存器当中放入一个数,那么在中断服务程序的开始,会先检查AH寄存器。如果里面是0,那它就按照读时钟的操作运行后续的代码;如果AH里面的值是1,它就按照设置时钟的操作执行后续的代码。
现在我们要设置时钟。所以,在AH里面先放上1。然后我们查这张表,知道我们要设置的时间是放在这几个寄存器当中,CH放在要设置的小时数,CL放要上要设的分钟数,DH是秒,DL是百分之一秒。而CL和CH组成的寄存器是CX,DL和DH组成的寄存器是DX。所以,我们直接可以通过对CX和DX赋值,来设置这个时间。那么现在为了简单,我们就设成0点0分0秒,这些参数准备好以后,我们最后写 INT 1AH
。
接下来就像是之前介绍过发生中断的时候一样,CPU会去中断向量表当中找到1AH对应的中断向量,然后转移到对应的中断服务程序开始执行,而这段中断服务程序就是位于BIOS所在的存储区域。那在这个中断服务程序当中,就会去操作管理系统时钟的芯片或者部件完成时钟的更改,然后再返回到这个主程序当中继续执行下面的代码。
这样就把一个和底层硬件细节非常相关的操作给封装起来,让编程人员可以比较轻松的完成这样的工作。BIOS中断其实提供了很多种类型,可以完成相当丰富的功能,有兴趣的话,可以查找相关BIOS的手册。但是因为BIOS容量有限,因此我们还是可以利用这个方法,在上层的软件当中,提供更为丰富的功能。
那我们来介绍一个例子,就是DOS中断。
DOS是早期的一种操作系统。它占用了一个中断类型号,21H。和BIOS占据了多个中断类型号不同,DOS中断只有这一个类型号。但它的功能非常的丰富,常用的文件管理、 存储管理等很复杂的功能,都可以用这个中断服务程序来解决。那它怎么区分我们到底想使用哪个功能呢?其实刚才BOIS中断给我们提供了这样的思路。我们可以通过一个寄存器,传入一个参数,来告诉中断服务程序,我们到底想调用哪样的功能。所以,所有的DOS中断都只使用这一个中断入口。而且DOS是一个操作系统,它所提供的中断功能比BIOS中断更为齐全、完整,而且进一步屏蔽了设备的物理特性,让编程的使用变得更加的方便。
我们也来看一个例子。如果我们想在屏幕上输出一个字符,那我们可以查找DOS中断所提供的表格,DOS中断都是21H。所以,这个表里面只需要列出功能号。那么发现,6号功能是在进行输入输出的操作。所以,我们现在AH寄存器当中存入6。然后我们进一步发现,如果我们想输出一个字符,就在DL寄存器当中,放入我们想显示的这个字符;而如果我们想通过键盘输入这个字符,则只需要在DL寄存器当中,存入FF。而最后输入的字符,会放在AL寄存器当中。那我们现在还是来看输出,所以我们在DL寄存器当中存上这个字符,然后调用 INT 21H
, 这样CPU就会转向21H号中断所对应的中断服务程序。在这个服务程序当中,首先会检查AH里面的值,确定功能号,然后就进入到这个功能对应的程序代码段,再根据DL寄存器的内容,判断出这是一次输出。那这个服务程序接下来就会对显示器进行操作,让对应的字符显示在屏幕的合适的位置。
这些繁琐的工作,都不需要用户来关心了,只要简单的调用这个DOS中断就可以了。当然前提是这些操作都已经由其它的程序员帮你写好,并且封装起来还安装在了你这个电脑上,这样你才可以使用。归根到底,这些操作都还是要有人来写出程序,只不过一次写好之后,其他的用户就可以直接调用了,这样会很方便。所以说,无论是BIOS中断,还是DOS中断,或者是其它类似的中断方式,其本质并不是计算机运行当中发生了异常的情况,而是利用了现有的中断这种机制来实现一些系统函数,代码的调用,以便向高层的软件屏蔽底层硬件的细节,从而提高编程的便利性,正确性和可移植性。
那现在,我们的机制已经讲的很清除了。在程序执行的过程中遇到的任何异常情况,我们都可以自如的处理,而且还可以利用这个机制,完成更加丰富的功能。所以,可以说我们对任何情况,都已经是能够应对自如了。
原文地址:https://www.cnblogs.com/doctorx/p/9743786.html