4.重要的软件部分
第四章:软件
1.汇编过程中符号的替换
将汇编文件转换成有机器指令组成的目标这个过程称为汇编。当负责汇编的程序扫描汇编文件的时候,会将变量的名字和它对应的值都记录在一个符号表中,然后每当它发现一个变量的时候就用表中的值将它替换掉。有一个需要注意的地方是,如果一个变量在它被定义前被当作操作数使用那么就无法查表替换它的值,负责汇编的程序一般通过二次扫描的方法解决这个问题——第一次扫描整个程序将所有变量的值记录在表中,第二次再将汇编文件中的变量名从头到尾替换成对应的值。
2.链接程序,装载程序
当一个程序需要多个汇编文件汇编组成时,汇编程序会将每一个汇编文件中引用的外部名(就是对其他文件中变量和函数的引用)和使用着写外部名的指令组成一个列表,并把这个列表包含在生成的目标文件当中。
链接程序在链接目标文件成为目标程序的过程中,需要根据每个目标文件中列表包含的信息,确定各个地址标签(也就是被其他文件引用的函数,变量)的相对位置(也就是是在各自文件中的位置),进而决定合成目标程序时每一个地址标签的绝对位置,进而生成目标程序。
生成的目标程序存储在存储器当中,当我们需要执行它的时候,需要将它从存储器当中装载到主存当中,然后将要执行的第一条指令的地址装载到程序计数器当中,这一过程由装载程序负责进行。装载程序通过来自用户的输入知道要执行哪条程序并在存储器中找到它,进而通过程序的头部信息知道这个程序的长度和要将它存放哪里(这些信息在汇编时由汇编程序放在目标文件的头部位置)
3.调试器和中断的使用
当程序没有按照我们预期的方式运行的时候,我们通常会使用调试器(一个程序)来找出问题所在,而调试器是通过中断实现调试工作的。当进行调试时处理器处于跟踪模式,每执行一条指令就产生一次中断(就是单步执行),并调用调试器程序中的中断服务函数,这时执行过程由调试器控制,通过服务函数用户就能查看寄存器和存储单元的值。当用户允许继续执行时,中断返回指令被执行,处理器接着执行被调试程序的下一条程序,接着再次进入中断。
断点使得调试更加灵活可以让程序能够在用户想要的地方“停下”,它通过一条称为陷阱或者软件中断的特殊指令来实现,当你在i指令前设置断点时,调试器会将i
指令保存到一个临时的位置,并用一条中断指令替换它,当程序执行到断点的位置时就会触发中断,直到用户给出指令继续执行程序。当程序继续执行时,调试器还需要执行几个任务,首先它将i
指令放回原位这时i
指令将成为下一步第一条要执行的程序,然后调试器会在i+1
指令处打上一个临时断点,这样程序在执行完毕i
指令时就会再次进入中断当中,此时调试器会恢复i+1
指令并恢复i
指令处的断点,接着执行剩下的程序。
4.汇编语言与C语言交互
编译器不能根据高级语言的一个语句生成访问处理器控制寄存器的汇编指令,因此如果想用C语言程序实现访问控制寄存器的功能就需要将汇编语言嵌套在C语言中。我们可以通过一个特殊的指示符来告知编译器实现这个功能。
还有一个问题就是编译器会将所有的C函数以RETURN_FROM_SUBROUTINE
指令结尾(这是一个子程序调用的返回指令),但是由于中断服务函数的特殊性(它使用不同的堆栈,同时返回时需要恢复各个寄存器之前的状态),它需要以RETURN_FROM_INTERRUPT
专门的指令结尾,因此用C语言编写中断服务函数的时候需要在函数的最后加上下面的代码:
这样一来中断服务函数在执行到这条指令时,处理器就会返回到之前的状态(通过这种方法修改的中断服务函数在编译后最后一条指令依旧是Return-from-subroutine
但是当执行过到数第二条指令Return-from-interrupt
后函数就不会继续执行了)
还有一种中方法是在终端服务函数前添加关键字interrupt
来告诉编译器这是一个中断服务函数需要以Return-from-interrupt
结尾,这样编译器就会自行替换,但是并非是所有编译器都支持这样的关键字。
5.引导程序
操作系统是一个庞大的程序组合,计算机的正常运行需要依靠操作系统帮助。在计算机正常运行的过程中操作系统存放在主存中,但是主存掉电是会丢失数据的,因此操作系统是需要存放在存储器当中并在开机时由引导程序逐步从存储器中加载到主存当中的。引导程序存储在一个固定位置,当处理器上电后会自动读取该位置的指令,在执行的过程中不仅会将操作系统加载到主存当中,同时还会对IO设备和主存进行必要的初始化。