清华大学操作系统公开课(一)系统启动、中断、异常及系统调用
1.操作系统的启动
计算机操作系统最基本的三部分:
启动大致顺序:DISK:存放OS ===> POST(加电自检):寻找显卡和执行BIOS ===> BIOS:基本I/O处理系统 ===> BootLoader:加载OS程序
2.操作系统与设备和程序的交互
操作系统存在的意义是向外部提供interface。主要包括三种:
面向外设提供 中断
面向应用程序提供 异常 和 系统调用
定义
- 系统调用(来源于应用程序):系统程序主动向操作系统发出服务请求。
- 异常(来源于不良的应用程序):非法指令或者其他坏的处理状态(如:内存出错)。
- 中断(来源于外设):来自不同的硬件设备的计时器和网络中断 。
为什么应用程序不能直接去访问外设,而要通过操作系统?
- 在计算机运行中,内核是被信任的第三方,而应用程序可能是恶意的,不会被信任。
- 只有内核可以执行特权指令。
- 为了方便应用程序,只需要调用操作系统的接口,不太需要过于关注底层实现。
三者的区别和特点
源头:
- 系统调用:应用程序请求操作系统提供服务
- 异常:应用程序意想不到的行为
- 中断:外设
处理时间:
- 系统调用:异步或同步 (系统调用可能会异步发生,调用的过程是同步的,但返回结果的过程可能是异步的,中间可能执行了其他调用的指令,不知道什么时候返回)
- 异常:同步
- 中断:异步
异步事件:操作系统不知道何时产生的事件。
响应:
- 系统调用:等待和持续
- 异常:杀死或者重新执行意想不到的应用程序指令
- 中断:持续,对用户应用程序是透明的(用户操作的时候不会感觉到中断的发生)
3.中断和异常的处理过程
首先,产生一个中断或者异常的时候,需要知道这种中断或者异常应该由哪一个特定的服务例程来服务,这就需要建立一个表,一侧(key)是中断号或者异常号(标志到底是硬盘中断还是键盘中断),另一侧(value)是特定服务例程的地址,直接转跳执行。
除此之外,打断了一个程序的正常执行,我们需要对这个程序进行保护,保护手段称为保存与恢复机制。
中断和异常分硬件和软件两部分来完成。
中断处理过程
硬件首先依据自己的需求设置一个中断标记,CPU根据标记能得出是是哪种中断,并把对应的中断号发给操作系统。
软件:保存现场 ===> 中断服务处理 ===> 清除中断标记 ===>恢复 现场 === > 继续执行
异常处理过程
硬件首先依据自己的需求设置一个异常标记,CPU根据标记能得出是是哪种异常,并把对应的异常号发给操作系统。
软件:保存现场 ===> 异常处理(杀死产生异常的程序、重新执行异常指令) ===> 清除异常标记 ===>恢复 现场 === > 继续执行
异常处理过程和中断的不同在于,异常可能会重新执行出错指令,这是因为有可能是操作系统出现问题产生了异常,等操作系统将产生异常的问题修补好以后就可以正常执行了,同样的重新执行得到正确结果后,用户不会感觉到异常的发生。
4.系统调用处理过程
一个简单地系统调用例子:
可以看出程序访问主要通过通过高层次的API接口(write()系统调用)而不是直接进行系统调用。
java API 用于JAVA 虚拟机,也是要通过虚拟机调用操作系统的接口实现功能调用。
我们的应用程序会通过函数库找到系统调用接口,一旦访问系统调用接口之后,会触发一个用户态到内核态的转换,从而使得控制权从应用程序交到操作系统,之后操作系统对系统调用进行标识,并完成相应的服务。
用户态:应用程序在执行过程中CPU所处的特权级的一个状态,它的特权级比较低,不能直接访问一些特殊的机器指令和直接访问I/0。
内核态:操作系统运行过程中CPU所处的一个状态,在这种状态下,CPU可以执行任何一条指令。
当执行系统调用的时候,开销会比执行函数调用大很多,这种从用户态到内核态的转换会带来额外的开销,但回报就是安全、可靠。
5.跨越操作系统边界的开销
系统调用、中断和异常这些跨越操作系统边界的交互会带来额外开销,主要包括以下几点:
- 建立中断/异常/系统调用号与对应服务例程映射关系的初始化开销:这种映射的表需要在正常工作之前就建立好并加载到内存中。
- 建立内核堆栈:操作系统有自己的堆栈,不能和应用程序的堆栈混为一谈,这样就增加了维护堆栈(保存和恢复)的开销。
- 验证参数:操作系统不信任应用程序,会对应用程序发来的参数做检查,这部分是安全保障的开销。
- 内核态映射到用户态的地址空间:操作系统有可能要把内核态的数据导入到用户态,导入的过程不能像应用程序一样利用指针传递的方式实现,需要进行拷贝,这就带来了开销。
- 更新页数据TLB:刷新过程会导致额外的开销。