系统调用、异常和中断
System Call, Exception, Interrupt
目录
系统调用, 异常, 中断的特点
来源
- 系统调用: 应用程序
- 异常: 应用程序
- 中断: 外设
处理时间
- 系统调用: 异步或同步
- 异常: 同步
- 中断: 异步
响应
- 系统调用: 等待和持续
- 异常: 杀死或重新执行意想不到的应用程序指令
- 中断: 持续, 对用户应用程序是透明的
中断和异常的处理机制
- 中断陈外设的事件
- 异常是内部CPU的事件
- 中断和异常迫使CPU访问一些被中断和异常服务访问的功能
中断
硬件
- 设置中断标记**[CPU初始化]**
- 将内部、外部事件设置中断标记
- 中断时间的ID
软件
- 保存当前处理状态: 为了确保之后能从打断的地方能够继续执行
- 中断服务程序处理
- 清除中断标记
- 恢复之前保存的处理状态
异常
- 保存现场
- 异常处理
- 杀死产生异常的程序
- 重新执行异常指令 (这种情况下, 对于用户来说, 异常就是透明的)
- 恢复现场
系统调用
系统调用和前面的中断和异常从名字上看就不一样(认真脸). 应用程序需要系统提供一些服务, 而这些服务不能由应用程序直接执行, 需要由操作系统来执行, 这个过程就需要接口, 这个接口就称为系统调用接口.
举个🌰, C语言中的printf()
就会触发系统调用write()
.
-
程序访问主要是通过高层次的API接口, 而不是直接进行系统调用.
- Win32 API 用于 Windows
- POSIX API 用于 POSIX-based systems (包括UNIX, Linux, Mac OS 的所有版本)
- Java API 用于 JVM
-
通常, 每个系统调用相关的序号. 系统调用接口根据这些需要来维护表的索引.
-
系统调用接口调用内核态中预期的系统调用, 并返回系统调用的状态和其他任何返回值
-
用户不需要知道系统调用是如何实现的, 只需要获取API和了解操作系统将什么作为返回结果. 操作系统接口的细节大部分都隐藏在API中, 通过运行程序支持的库来管理(包括编译器的库来创建函数集).
-
系统调用和传统的函数调用是有区别的:
- 当应用程序发出函数调用的时候, 是在一个栈空间完成了参数的传递和参数的返回.
- 系统调用的执行过程中, 应用程序和OS是有各自的堆栈. 当应用程序发出系统调用的时候, 当切换到内核中执行之后也需要切换堆栈. 同时, 也需要完成特权级的转换(从用户态转为内核态).
也就是说系统调用的开销是比函数调用的开销大得多, 但是这样的做法也提高了安全性.
跨越操作系统边界的开销
通过上面可以了解到, 其实系统调用, 异常和中断就是操作系统和应用程序, 以及操作系统和外设之间跨越了边界.
- 在执行时间上的开销超过程序调用
- 开销:
- 建立中断/异常/系统调用号与对应服务例程映射关系的初始化开销
- 建立内核堆栈
- 验证参数, 因为操作系统是不信任应用程序的
- 内核态映射到用户态的地址空间, 更新页面映射权限. 这是一个拷贝的过程, 因为不能将内核态中的数据简单的用传递指针的方式传递给用户态.
- 内核态独立地址空间, TLB