1 异常
异常是异常控制流的一种形式,它一部分是由硬件实现的,一部分是由操作系统实现的。异常就是控制流中的突变,用来响应处理器状态中的某些变化。在处理器中,状态被编码为不同的位和信号。状态变化称为事件event,事件可能与当前指令的执行直接相关。比如发生虚拟存储器的换页,算数溢出,或者一条指令试图除以零。另一方面,事件也可能和当前指令的执行没有关系。比如一个系统定时器产生信号或者一个I/O请求完成。
在任何情况下,当处理器检测到有事件发生的话,它就会通过一张叫做异常表exception table的跳转表,进行一个间接的过程调用(异常),到一个专门设计用来处理这类事件的操作系统子程序(异常处理程序)。系统为每一种异常都分配了一个唯一的非负整数的异常号。其中一些号码是由处理器的设计者分配的,其他的号码是由操作系统内核的设计者分配的。处理器的设计者负责的异常有除以零,缺页,存储器访问违例,算术溢出等。操作系统内核的设计者负责的异常有系统调用system call,和来自外部I/O设备的信号。
2 异常的类别
异常可以分为四类:中断interrupt,陷阱trap,故障fault,和终止abort。
中断:中断是异步发生的,是来自处理器外部的I/O设备的信号的结果。硬件中断不是由任何一条专门的指令造成的,从这个意义上来说,它是异步的。I/O设备,比如网络适配器,磁盘控制器等通过处理器芯片上的一个引脚发送信号,并将异常号放到系统总线上,用来触发中断,这个异常号标识了引起中断的设备。
陷阱:陷阱是有意识的异常,是执行一条指令的结果。上面的中断不是程序执行指令导致的,而是外部I/O设备的信号导致的。这就是区别。陷阱最重要的用途就是在用户程序和内核之间提供一个像过程一样的接口,叫做系统调用。用户经常要向系统请求服务,比如read,fork等等。为了允许对这些内核服务的受控的访问,处理器提供了一条特殊的指令syscall n,当用户想要请求服务n的时候,可以执行syscall n这条指令,然后将会导致一个到异常处理程序的陷阱,这个处理程序并调用适当的内核程序。其实从程序员的角度来看,系统调用和普通函数之间是没有区别的,然而他们的实现确实不同的。普通的函数运行在用户模式(user mode)中,用户模式限制了函数执行的类别,而且他们只能访问用户栈。而系统调用是运行在内核模式中的,内核模式允许系统调用执行指令,并访问内核中的栈。等会我还会继续说一下内核模式和用户模式的区别。
故障:由错误情况引得,能够被故障处理程序修正。当故障发生的会后,处理器将控制转移给了故障处理程序。如果这个故障处理程序能够修理这个故障的话,它就将控制返回到引起故障的指令,重新执行它。否则的话将返回到内核中的abort,abort会终止引起故障的应用程序。一个经典的故障就是缺页异常,当指令引用了一个虚拟地址,而与该虚拟地址对应的物理页面此时并不在内存中,因此必须从磁盘中取出来,这时就会发生故障(缺页故障)。
终止:终止是不可恢复的致命错误造成的结果,通常是一些硬件错误。终止处理程序从不将控制再返回给应用程序。
3中断的基本知识
中断的概念:
所谓中断,是指CPU在正常运行程序时,由于程序的预先安排或内外部事件,引起CPU中断正在运行的程序,而转到发生中断事件程序中。这些引起程序中断的事件称为中断源。
其实从物理学的角度看,中断是一种电信号,由硬件设备产生,并直接送入中断控制器(如 8259A)的输入引脚上,然后再由中断控制器向处理器发送相应的信号。处理器一经检测到该信号,便中断自己当前正在处理的工作,转而去处理中断。此后,处理器会通知 OS 已经产生中断。这样,OS 就可以对这个中断进行适当的处理。不同的设备对应的中断不同,而每个中断都通过一个唯一的数字标识,这些值通常被称为中断请求线。
那么当产生一个中断时,CPU是如何识别的呢?
在Intel X86中可以支持256种向量中断,为了使处理器能使别每种中断源,给它们进行了编号----->叫做中断向量
这些中断向量在Linux中是如何分配的:
编号0~31的向量对应于异常和非屏蔽中断
编号32~47的向量(即由IO设备引起的中断)分配给屏蔽中断。
编号48~255的向量用来标示软中断。Linux用其中的128或0x80来实现系统调用
非屏蔽中断的向量和异常的向量是固定的。
4 用户模式和内核模式
为了使操作系统内核提供一个无懈可击的进程抽象,处理器必须提供一种机制,限制一个应用可以执行的指令以及它可以访问的地址空间范围。
处理器通常是用某个控制寄存器中的一个模式位mode bit来提供这种功能的,当设置了模式位的时候,进程就可以再内核模式中运行。一个运行在内核模式的进程可以执行任何指令集合中的指令,并且可以访问系统中任何存储器的位置。当没有设置模式位的时候,进程就运行在用户模式中。用户模式中的进程不允许执行特权指令,比如停止处理器,改变模式位等操作,也不能发起一个I/O操作。更不允许用户模式中的进程直接饮用地址空间中内核区内的代码和数据。
那么进程从用户模式变成内核模式的唯一途径就是通过诸如中断,故障,陷阱来陷入系统调用。当这些异常发生的时候,控制就传递到了异常处理函数中,处理器将模式位从用户模式变为内核模式。异常处理程序是运行在内核模式中的,当他返回到应用程序代码时,处理器就把模式从内核模式改回到用户模式。