标签:
从给处理器加电开始,直到断电为止,PC(程序计数器)都在不间断的读取并执行指令。
最简单的一种控制流是一个”平滑的”序列,其中每个
instk 和instk+1 在存储器中都是相邻的。典型地,这种平滑流的突变,也就是instk? 和instk+1? 不相邻,是由诸如跳转(jump)、调用(call)和返回(ret)这样一些熟悉的程序指令造成的。这样一些指令都是必要的机制,使得程序能够对由程序变量表示的内部程序状态中的变化做出反应。
但是系统也必须能够对系统状态的变化做出反应,这些系统状态不是被内部程序变量捕获的,而且,也不一定要和程序的执行相关。比如,一个硬件定时器定期产生信号,这个事件必须得到处理。包(package)到达网络适配器后,必须存放在存储器中。程序向磁盘请求数据,然后休眠,直到被通知数据己就绪。当子进程终止时,创造这些子进程的父进程必须得到通知。
现代系统通过使控制流突变来对这些情况做出反应。一般而言,我们把这些突变称为异常控制流(Exceptional Control Flow, ECF)异常控制流发生在计算机系统的各个层次。比如:
硬件层: 硬件检测到的事件(events)会触发控制突然转移到异常处理程序(exception handler)。
操作系统层: 内核(kernel)通过上下文转换(context switch)将控制从一个用户进程转移到另一个用户进程。
应用层: 一个进程可以发送信号(signals)到另 一个进程,而接收者会将控制突然转移到它的一个信号处理程序(signal handler)。一个程序可以通过回避通常的栈规则,并执行可以跳转到其他函数中任意位置的非本地跳转(nonlocal jumps)来对错误做出反应。
一、ECF的作用
- ECF 是操作系统用来实现I/O 、进程和虚拟存储器的基本机制。
- 应用程序通过使用一个叫做陷阱(trap) 或者系统调用(system call) 的ECF 形式,向操作系统请求服务。(比如,向磁盘写数据、从网络读取数据、创建一个新进程,以及终止当前进程,都是通过应用程序调用系统调用来实现的。)
- 操作系统为应用程序提供了强大的ECF 机制,用来创建新进程、等待进程终止、通知其他进程系统中的异常事件,以及检测和响应这些事件。(如果你理解这些ECF 机制,那么你就能用它们来编写诸如Unix 外壳和Web 服务器之类的有趣程序了。)
- ECF 是计算机系统中实现并发的基本机制。(中断应用程序、进程和线程执行的异常处理程序和中断应用程序执行的信号处理程序都是并发的例子。)
- 像C++ 和Java 这样的语言通过try、catch 以及throw 语句来提供软件异常机制。软件异常允许程序进行非本地跳转(违反通常的调用/返回栈规则的跳转)来响应错误情况。非本地跳转是一种应用层ECF ,在C 中是通过setjmp和longjmp 函数提供的。理解这些低级函数将帮助你理解高级软件异常如何得以实现。
二、Exceptions(异常) and System Call(系统调用)
异常: 位于硬件和操作系统交界的部分,它一部份是由硬件实现的,一部分是由操作系统实现的。
系统调用: 它们是为应用程序提供到操作系统的人口点的异常。在处理器中,状态(state)被编码为不同的位和信号。状态变化称为事件(event) 。程序在执行过程中遇到了事件就会触发相应的异常,进而跳转到相应的异常处理程序中去。
系统中可能的每种类型的异常都分配了一个唯一的非负整数的异常号(exception number )1。其中一些号码是由处理器的设计者分配的,其他号码是由操作系统内核(操作系统常驻存储器的部分)的设计者分配的。前者的示例包括被零除、缺页、存储器访问违例、断点以及算术溢出。后者的示例包括系统调用和来自外部I/O设备的信号。
在系统启动时(当计算机重启或者加电时),操作系统分配和初始化一张称为异常表(exception table)2的跳转表,使得条目k包含异常k的处理程序的地址。
异常向量表:
Intel: IDT(中断描述表)
异常表的起始地址放在一个叫做异常表基址寄存器(exception table base register)的特殊CPU寄存器里面3。
异常号k左移两位(x4),然后加上异常表基地址得到异常表中对应异常号k的异常向量4的地址。
异常类似于普通过程调用,涉及到与普通过程调用类似的返回地址压栈,一些额外的处理器状态压栈。如果控制从一个用户程序转移到内核,那么所有这些项目都被压到内核栈中。异常处理程序运行在内核模式下,对系统资源有完全的访问权限。
一旦硬件触发了异常,剩下的工作就由异常处理程序在软件中完成。在处理程序处理完事件之后,它通过执行一条特殊的“从中断返回”指令,可选地返回到被中断的程序,该指令将适当的状态弹回到处理器的控制和数据寄存器中,如果异常中断的是一个用户程序,就将状态恢复为用户模式,然后将控制返回给被中断的程序。
2.1 异常的类别
异常可以分为四类:
- 中断(interrupt)
- 陷阱(trap)
- 故障(fault)
- 终止(abort)
2.1.1 中断
中断是异步发生的,是外部的I/O 设备的信号的结果。硬件中断不是由任何一条专门的指令造成的,从这个意义上来说它是异步的。剩下的异常类型(陷阱、故障和终止)是同步发生的,是执行当前指令的结果。我们把这类指令叫做故障指令(faulting instruction)。
I/O设备,例如网络适配器、磁盘控制器和定时器芯片,通过向处理器芯片上的一个引脚发信号(这个信号就是中断请求信号),并将异常号放到系统总线上,以触发中断,这个异常号标识了引起中断的设备。
2.1.1.1 设备的中断号
2.1.1.2 I/O接口的基本组成
2.1.1.3 中断请求接口电路5
记CPU的中断查询信号为R。
设备中断请求信号INTR的产生过程是这样的:当设备完成任务就会处于完成状态,设置完成触发器的输出D=1,如果相关掩码MASK=0,那么得到中断请求触发器的输出INTR=DR 。当MASK=0, R=1, D=1时,INTR=1, 设备成功发出中断请求,否则INTR=0,没有发出中断请求。
- 当MASK=1时,中断屏蔽触发器的输出是MASK取反的结果
MASKˉˉˉˉˉˉˉˉˉˉˉ=0 , 这时候无论完成触发器的输出D是什么,D和MASKˉˉˉˉˉˉˉˉˉˉˉ 在与非门的作用下输出为0Dˉˉˉˉˉ=0ˉ=1 ,下一步在非门的作用下输出为1ˉ=0 。此时,中断请求触发器的输出为INTR=0R=0 。所以MASK=1时,相关设备是触发不了中断请求信号的。相当于这个设备暂时被屏蔽了、忽略了、就算它想请求中断,CPU也不理睬它。- 当MASK=0时,中断请求触发器的输出
INTR=DR 。这时候如果设备触发完成触发器,同时又有来自CPU的中断查询信号,就会产生中断请求。所以MASK=0时,只要设备完成任务,触发了完成触发器,剩下的就是等待CPU的中断查询,查询一到,就会产生中断请求。
2.1.1.4 排队器
排队器的作用是选择当前所有中断请求中优先级最高的一个中断请求。
优先级高的请求会将优先级低的请求屏蔽掉,排队器实现了一个函数映射:
f(n):{0?1(0|1)?}→{0?10?}
(因为硬件是有限位的,所以用这个函数来描述不准确,不过大概就是这么个意思)
2.1.1.5 中断向量地址(中断号)的形成
中断号是设备编码器根据排队器的输出号(属于集合
{0?10?} )进行转换得到,之后,处理器根据中断号找到中断向量。(中断向量使得指令跳转到服务程序指令集的入口)
2.1.1.6 I/O中断处理过程
在当前指令完成执行之后,处理器注意到中断引脚的电压变高了(标志着排队器中至少有一个中断请求等待处理),就从系统总线读取异常号,然后调用适当的中断处理程序,当处理程序返回时,它就将控制返回给下一条指令(即如果没有发生中断,当前指令之后的那条指令)。结果是程序继续执行,就好像没有发生过中断一样。
标签:
原文地址:http://blog.csdn.net/unclerunning/article/details/51190040