正文
异常(Exception)的处理过程
异常与中断不同,异常也是因为某个特定的指令触发了异常事件,比如除0操作。他也会有一个异常的ID,然后OS会根据这个ID保存现场(当前执行进度、这条指令地址、当前寄存器的内容等)。保存之后操作系统会根据ID进行相应处理,分为两种:
退出执行(杀死)或者重新执行[OS认为这个程序的异常原因是因为OS的服务不到位,OS会进行一定的弥补工作,然后根据刚才异常产生的现场进行恢复,让应用程序重新执行。由于OS已经将把他产生异常的原因补完,所以就不会再次产生异常,这个过程和中断相同,对应用程序是透明的。APP感受不到某条特殊指令会产生异常。
系统调用System Call
来源于APP,需要OS提供服务,而这些服务APP不能直接执行,需要OS来执行。这些过程需要一些接口,这些接口我们称为系统调用接口。有这些接口,OS就能给我们提供各种服务。
Ex)
…………………… printf("Helloworld!"); ……………………
这条指令是打印一串字符串。这条指令最终会触发一个write()系统指令。write系统调用会带一些参数,这些参数包含了要让哪个设备来显示这个字符串以及字符串的内容。很明显操作系统在获取这个参数后会访问对应的设备,比如这里就是我们的屏幕。着整个过程是操作系统完成的而不是应用程序完成的,应用程序只能发出请求。当OS完成这个请求,它会返回一个成功或者失败让APP知道,然后APP进行后续的执行工作。
上图中的右子图是一个通用接口,通过这个借口,APP可以完成各种各样的功能,对整个计算机系统进行间接的管理和控制。
为了方便应用程序能够使用OS的系统调用接口,产生了很多定义好的API。
详见下图
POSIX:通用可移植的标准。遵循这个标准的OS,同一个程序可以跨平台执行。
JAVA API:不是系统调用,那些API只是Java虚拟机提供的支持,是由库来实现的。最终还是根据环境不同(WIN/LINUX环境)应用其他API来实现OS功能。
系统调用的实现
对于应用程序(APP),它只需要知道这个系统调用能提供什么样的功能,他的接口是什么,参数是怎么定义的就可以了。但是从操作系统的角度来说。
英文原图:
APP会直接或者间接通过库(Library)来访问系统调用的接口,一旦访问API。会触发一个从用户态到内核态的转变。
用户态:应用程序 执行过程中,CPU处于一个特权级的状态。他的特权级非常低,不能够直接访问某些特殊的机器指令和直接访问IO。
内核态:OS运行过程中CPU所处于的一个状态,这个状态下OS可以执行任何一种指令,这使得安全性得到保障。
System Call Interface阶段:当我们应用程序调用一个系统调用时,完成一个用户态到内核态的装换,控制权从APP转到OS。
那么OS可以对应用程序发出的这些系统调用的参数(ID)做出标识,作出标识后,就可以对这个系统调用进行识别和完成具体的服务。
函数调用和系统调用的区别
当应用程序发出函数调用,其实是在一个栈空间完成的参数的传递和返回。
但是系统调用的执行过程中,我们的应用程序和操作系统拥有各自的堆栈,当APP发出系统调用之后,当他切换到内核里执行的时候,需要切换堆栈同时也需要完成特权级的转换 (用户态到内核态)。这些转换都会需要一定的开销,也就意味着当系统调用时,开销会比函数调用大很多。
开销
由图可知,三种操作都跨越了APP和OS和外设的边界,跨越边界都有一定的代价。跨越边界是为了更加安全、可靠地执行各种上级程序。
映射关系的开销
表:
指令 | 对应服务历程 |
中断号x | y1 |
异常号x | y2 |
系统调用号x | y3 |
为了让中断/异常/系统调用进行处理,首先要有一个对应的映射关系。(对应的系统调用号、中断号对应的哪一个服务历程,这个表要在能够让他正常工作之前就建好。OS初始环节就需要把这个表做好)
内核堆栈的开销
操作系统有自己的堆栈,不能和程序堆栈混淆。这样维护堆栈的开销也是必须的。(退出时保存堆栈,执行时堆栈恢复等)。同理,APP的执行到内核里执行时,需要保存APP的堆栈信息,当内核操作完成后,返回到APP又需要用到APP的堆栈信息。
验证参数的开销
操作系统不信任我们的应用程序,他会认为APP会有恶意程序的存在。所以OS在运行APP之前会进行一个安全检查,这个检查当然也会有开销。
内核态映射到用户态的开销
操作系统处理完某些数据后,会把这些数据从内核态拷贝到用户态,这个过程会有新的开销。不像应用程序内指针传递那样简单,他必须要把内核空间的数据拷贝到用户空间。
内核态的独立地址空间的开销
随着应用程序执行,有可能会引起内存状态的改变(后续课程详解)。比如页机制的转变,关于CPU的Cache(闪存)和TLB有可能会被刷新。这个刷新过程会导致额外的开销。
关于Cache和TLB相关内容请参考CSA(计算机组成原理对应章节)
小结:
这些都因为执行中断、异常、系统调用过程中可能会带来的开销,但是这些开销是必须的,有这些开销的存在才能保证OS在安全可靠的环境下运行。