好 接下来我们介绍中断异常和系统调用
那在这里呢 我们会说中断异常和系统调用
它到底是用来干什么 解决什么问题
然后这几个的应用场景
或者说他们之间有些什么样的区别和联系
它在用处的时候 它们之间会有一些差异
但是也有很多会共同的东西
再一个就是说我们需要来说明
中断异常和系统调用它的实现机制
首先我们说在上前边的内容里头已经说过
操作系统 计算机系统启来了之后
它会加载操作系统的内核
然后就把控制权转给操作系统内核了
在这前面这一段呢 是可以信任的
但是在操作系统内核之上呢
实际上我们还有很多的应用程序
这些应用程序我们没有办法
对它做到完全的信任
而这应用程序要使用操作系统内核提供的服务
那这时候怎么办呢
实际上我们就需要解决一个操作系统内核
和外界打交道的问题
那我们说操作系统内核是可以信赖的
我可以在里做对计算机系统里的任何内容的控制
可以执行它的特权指令 这种信任呢
并不是它跟外界完全的隔离
它还需要为上边的应用程序提供服务
也就是说我可以信任的内核
它必须对外界提供某种访问的接口
或者打交道的通道 然后再一个呢
我们除了会是跟应用程序打交道之外
我们在程序或者计算机系统
在运行过程当中会有各种各样的问题
比如说我在外设
你在跟他做交互的时候你敲键盘
我并不能确认 你会在什么时间
准确的一个什么时间点去敲键盘
你敲了键盘之后 可能我的系统正在干别的事
这时候我没办法给你做出响应
那这样的话就会让你感觉
这个计算机系统很不好用
所以为了能够让计算机系统
能够对外界做出适当的反映
我们需要提出中断机制
也就是说当外设与系统有交互的时候
我需要怎么来处理
还有一种情况呢
是说我的应用程序在执行过程当中
比如说我做除法 除了一个零
那这时候是要出问题呢
而是否会除零呢
我只有执行到那一步的时候我才知道
好 等到这个时候你再知道的话
那我已经没办法往下做了
所以总会有一些意外情况
我在事先写程序的时候我是没办法预料到的
对于这种意外的情况
我们怎么来处理 所以在我们这里呢
通常的做法是说应用程序提供你所需要的功能
那里头的异常呢 这种意外的情况呢
把它控制权转给操作系统
由操作系统来处理它
这是我们这里说到的应用程序执行当中意外
有异常来做处理 那还有一种情况呢
是说我们要解决用户程序如何来解决系统的服务
就好象说我们提供给银行对外提供服务
银行为了保证安全 所以它有很多的防护
这个防护又和对外提供服务这是有矛盾的
为了方便用户来使用银行的服务
你必须提供灵活的访问接口
但是这种灵活的接口 又不能影响到银行的安全
操作系统内核也是一样的
我们需要来通过系统调用来提供一个接口
让应用程序既方便的使用内核提供的服务
又不至于用户的行为对我内核的安全产生影响
那也就是说使用服务呢 我们有很多种方式
那在这里头呢 用内核提供服务
和我们通常写程序的时候用函数库
来提供服务有啥区别 那这是我们在这里呢
你在选择用系统调用 还是用函数库的时候
需要做出的判断
好 有了刚才这些问题我们来看
用下边这个图来看一下
内核和外界之间打交道的地方
这是我们内核 然后我们在里呢
提供了相应的一些内核服务 这些内核服务
首先会是说跟外界的硬件打交道
比如说我敲了键盘 那我在敲下一个键的时候
可能里头会有一个缓冲区
缓冲区的大小是有限制的
那存多了 前边的内容就会丢
好 那我在这里呢 有数据之后
我就必须告诉操作系统
那你在这里面及时把我的数据读走
否则的话
后边来的数据就会丢了 那这时候呢
有一条通道 那这就是我们这里的中断
好中断 通知内核 内核通过驱动
来与你的设备进行数据的交互
比如说键盘我是读进去 磁盘的话
即是会有读有写 然后还有一种情况是说
应用程序在执行的过程当中
好 正常执行 那跟内核没关系
好 执行到某一个位置的时候
比如说某条指令做除法 除了个零
或者说一条存储访问指令
访问到某一个存储单元
但这个存储单元是不允许你访问的
就好比说储户你想说银行里有你的钱
你就想直接去开银行的保险柜
把你的钱取出来 那这是不行的
对于这种情况呢
我们在这里跟操作系统是类似的
它会提供一个异常机制
那我们把这些情况都认为
是你代码执行过程当中的出错
有些错误呢 实际上是我们事先有预计的
好 那这时候呢 把空闲交到内核
内核可能做出的处理呢
要么说我把你遇到的问题解决掉
比如说你访问到的某一个存储单元
原来的存储单元不存在
好 那我把原来的存储单元的内容放到内存里头
OK 对于虚拟存储来讲
这时候你就可以执行了
而另外一些问题 比如说像是除零之类的
那你这时候除零 我就没有办法继续下去
这时候我就会把应用程序
所占这些资源还给操作系统
那这是我们异常 那最主要的一类呢
是正常情况下的使用
应用程序会使用到函数库
那这时候跟内核不打交道
但是内核 应用程序或者函数库
会间接的通过系统调用接口
使用到操作系统内核的服务
比如说我要读写文件
那我应用程序不能直接访问磁盘设备
它怎么办呢 发出操作系统
提供了一个磁盘读写的一个系统调用接口
好 那应用程序通过调用这个
读写接口所提供的这个函数
好 那这时候进到内核里头来
内核把相应的数据读出来
还给应用程序 那这个流程就结束了
所以从这我们可以看到
操作系统内核和外界打交道基本上就是中断
异常和系统调用这三个接口
这是我们在这里说到这三条
从这三条我们就可以看到
中断 异常和系统调用他们之间
到底有什么样的定义
系统调用是应用程序
主动向操作系统发出的服务请求
而异常呢 是非法指令或者其他原因
导致的指令执行失败之后的处理请求
那这种处理请求呢 可能会是说我终止程序
也可能会是说解决你遇到的问题
然后重新执行这条指令 而中断呢
是硬件设备对操作系统提出的处理请求
比如说我缓冲区里有数据
需要内核把它读走
或者说我缓冲区里的数据已经全部用完
需要内核补充新的数据
那这里我们说到的中断异常和系统调用
那这三者之间的区别到底怎么来描述呢
我们可以从这样几条来区别
第一个是它的源头 那在这呢
我们标出了系统调用的触发条件
中断的起头和异常的起头
这三个分别对应着我们这里的来源不同
外设 应用程序 实际上这个异常
在这个图里描述是适宜性的
我在执行内核代码的时候
也可能由于代码的执行出现问题
这时候呢 也可能是内部出现的
然后第二个呢 是他们的响应方式不一样
那这三个箭头分别对应着系统 调用
中断和异常的响应方式 那系统调用呢
通常情况下我们会是同步的或者异步的
也就是说应用程序发出系统调用请求
然后它就在那等着了
一直到你算完内核服务完成之后给它结果
那也可能是异步的
说我们在这里系统调用发出了之后
你内核在处理时候
处理的过程当中
我就切换过去干别的事情过去了
然后等到其他条件准备之后
你这才会回来 而中断的它是异步的
也就是说你上边应用程序
该怎么做处理怎么做处理
我不会感知到中断的存在
那中断只是在应用程序暂停执行
处理完中断所需要的服务之后
继续恢复你应用程序的执行
就跟没有这件事情一样
而异常是跟你的这个当前指令有关的
这个是同步的 也就是说必须处理完
当前这条异常所产生的指令所导致的问题
我才可以继续下去 那当然他们之间的处理呢
会都在内核里头来做
但他们的处理也会有一些区别
中断会持续的进行
而系统调用它会是用户提出之后会处理
等待然后再继续 而异常呢
是会处理当前所出现的问题
那具体说起来 中断的处理
那这个中断实际上我们把它理解为
系统调用异常和中断这三种情况的总称
对于硬件上的处理呢
这主要是在硬件中那个地方
在CPU做初始化的时候
它其中就有一项工作 要对中断使能
也就是说在许可外界打扰CPU的执行之前
CPU是不会对外界的任何中断请求发出响应的
那只有我CPU把相应的准备工作做完
外界来一个请求之后 我知道怎么处理了
我才会允许这种处理
如果我不知道怎么处理的话
你给我一个请求 我也不知道怎么办
所以在这 在初始化的时候
它有一个中断使能 使能之后
我才能够进行中断的处理
第二个是说这个事件产生了
产生了之后通常是一个电平的上升沿
或者说是一个高电平
那CPU会记录下这件事情
也就是说我有一个中断标志
表示出现了一个中断
然后这时候我需要知道中断
到底是由什么设备产生的
需要知道中断源的编号
这一部分工作是由硬件来做的
好 硬件做完这一部分工作之后
剩下的事情就是由内核的软件来做
那在这里头呢
也就是说这几个进来都到了这个中断向量表
中断向量表如果是中断
那它直接绕到这边的中断服务例程
驱动程序里来做出响应
如果是异常 它直接转到异常服务例程来做处理
如果说是系统调用 由于系统调用的量很大
那我们在这儿 系统调用之后
系统调用它之后总共占用一个中断编号
然后它自己的不同的系统调用的功能呢
是用系统调用表来表示的
好 在这呢 根据你系统调用表里的
功能的选择不同
我去选择不同的系统调用实现
那么在这个过程里头呢
我为了不影响程序的正常执行
我前边有一个保护现场和恢复现场
而在这里头呢 要做系统调用的交互
我还需要知道系统调用产生之前
我准备的上下文的信息 比如说你到底让我干啥
那这一部分工作呢 是会和你的编译有关系
然后中间这一部分到底如何实现
那这个是由操作系统来做的
然后如果是中断的话
那么你在执行的过程当中
需要清除这个中断标志
这个也是由你的中断服务例程来完成的
好 我们说中断可以满足应用程序
外部设备 或者程序执行异常的服务请求
那这时候可能会出现一种情况
我正在处理一个请求的时候
有来了一个请求这时候我怎么办
那我们说在操作系统的里头呢
它是硬件的中断 它是允许被打断的
也就是说我正在处理一个中断的时候
可以允许你再出现其他的中断
如果两个中断源不同 那这时候呢
我可以通过优先级的高低
让一个往后推一段
或者说让一个暂停下来
那使得我可以同时在做
交替在做处理 然后在中断服务例程里头
并不是说我任何一个时刻都可以做任何一个处理
它会在一定的时间里呢
禁止中断请求 比如说我电源有问题
那可能其他的问题就变得不重要了
这时候我在做电源的处理的时候
我就会禁止掉其他中断
然后中断服务请求会一直保持到CPU做出响应
然后对于异常呢 我也可以被打断
你比如说我在程序执行当中出现了异常
这时候正在做异常的处理
比如说我在这里头虚拟存储里头
它访问到的存储单元的数据不存在
我正在从硬盘上倒数据进来
倒的过程当中 它会用到磁盘I/O
这时候也会再有磁盘设备的中断
这时候是允许它可以做嵌套的
然后对于异常服务的嵌套呢
这是我们说到的 异常服务和缺页
这两个在异常服务里头还会再出现异常
也就是说我执行中断
执行异常处理例程里头
有一段存储访问它是缺页的
那这时候两个异常也是可以嵌套到一起