【操作系统】第二章:OS的启动和中断、异常、系统调用

本章学习目标

在这里插入图片描述

计算机体系结构

在这里插入图片描述
计算机启动,按下电源。这个过程从原理来看,电源启动后,操作系统是从硬盘上存储,然后点到CPU工作的。
BIOS:基本I/O处理系统,计算机开机后检测各种外设。检测外设之后才能去加载相应的软件进行执行。
DISK(硬盘):除去存储数据的功能,也拥有存放OS的功能。
Bootloader:启动时,加载OS。把OS从硬盘放到内存中,令CPU可以执行OS
在这里插入图片描述
在整个计算机内存里,一部分空间BIOS已经预先占用了。BIOS要做的第一步就是从某一个特定的地址开始执行,如果这个地址以x86为例,他的值是固定的(0xf000:fff0)。
两个寄存器合在一起,形成一个具体的内存地址(固定的)。一开始加电,从该地址开始执行,包括自检(检测自身设备是否可以正常工作)
POST(加电自检):寻找显卡和和执行BIOS(初始化检查,包括外设;把BootLoader从disk放到memory)。启动电源时,显示器会亮起,然后随着内部元件的工作,显示请开始显示不同的图形界面,所以加电过程中显卡是一定在工作的。除此之外,BIOS也会初始化检测对系统进行操作的外设(如键鼠等事件),以及数据存储(Disk硬盘等)。
BootLoader一般放在硬盘的第一个主引导扇区(一般512byte)。易于BIOS寻找。BIOS将BootLoader加载到内存中(BootLoader掌握CPU控制权),然后BootLoader 再加载OS到内存中,然后CPU控制权交给OS(跳到OS的起始地址,然后开始执行工作【前期的初始化工作、创建应用程序】)

OS与设备和程序的交互

操作系统的接口(interface):系统调用(system call)、异常(exception)、中断(interrupt)
外设:中断 IO 应用程序:系统调用、异常
·系统调用:APP(来源)主动向OS发出服务请求
·异常:由APP产生(来源),非法指令或者其他错的处理状态(内存出错)
·中断:来源于外设。来自不同的硬件设备的计时器和网络的中断
Q)为什么应用程序不能直接访问外设?
A)在计算机中内核是被信任的第三方,只有内核可以执行特权指令(安全角度:app恶意访问会破坏系统);同时操作系统方便了应用程序. 给上层应用提供一个简单的接口,令上层应用不需要针对下层不同device(设备)设置不同程序,让程序写得更加通用、可移植。
应用程序是不可信任的,可能会有恶意程序对整个系统进行破坏。

中断、异常、系统调用的区别

1.源头

中断:外设
键鼠的操作,产生字符事件、移动事件等
异常:app的意外
APP并不想主动产生,是意外发生。
Ex)除0操作,这是计算机系统会无法正常工作的指令个,这种情况下要求OS发现并及时处理。
Ex)恶意程序要越过权限去访问一个受保护的地址空间,这种情况下OS要及时截获
Ex)APP执行时,资源不足以满足需求,这时操作系统悄悄在后端分配资源,解决问题
总之,异常就是让OS能够应对和解决各种事件。
系统调用:应用程序请求OS提供服务。是APP主动提出的,有明确的指令和相应的参数。比如打开/关闭文,~读写文件、发送网络包等都是系统调用,这都是由OS来完成的,应用程序只需要设置好对应的接口和参数,即具体实现由OS完成。

2.处理时间

中断:一般是异步事件。

异步就是当事件发生时,APP并不知道这个事件什么时候发生的。异步不可预测
异常:同步,同步就是触发问题的时间点是固定的,比如a/0这个除零操作必然会发生异常。发生异常一定是指定某条特定指令时一定发生。

系统调用:同步或异步。系统调用发出指令后,读取指令的过程是同步的。每一条指令对应一种正常或异常的状态。但是返回数据的这个过程是可以异步的

系统调用后返回的时间:读数据操作
当程序发出请求后,OS就需要读取数据,数据读完之后,OS再将数据返回给系统调用并将之传给应用程序。这个过程中,应用程序一直属于等待状态,这种状态就是同步。
但是如果应用程序发出这个请求后马上去做其他事情,那么这个OS完成读数据后,会给应用程序发出一个异步消息告诉APP这个数据已经处理完成。也就这个指令发出请求的过程是同步的,但是返回这个过程可能是异步也可能是同步的。

同步是指:当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续执行下去。 异步是指:当程序1调用程序2时,程序1径自继续自己的下一个动作,不受程序2的的影响
同步是阻塞的,异步不阻塞进程

3.响应状态:
中断:对于中断来说,打断了当前APP的正常执行,但是APP并没有感觉到有中断产生,
因为OS透明完成(悄悄完成),所以并没有影响到APP,所以APP持续执行。
异常:后果严重时,会杀死APP,或者重新执行产生异常的指令(产生异常的指令在OS的支持下再次执行也有可能正常执行)。
系统调用:等待服务完成后继续执行,不会重复执行系统调用的指令。

中断和异常的处理

在这里插入图片描述
首先,产生一个中断或者异常,需要知道具体这个中断或异常是哪一个特定的服务历程来服务。所以需要设置中断向量表,通过标记可以清楚的知道异常/中断所处的位置。不同的外设存在一个特定的编号,也就拥有一个特定的地址,这个地址就是针对某一个特定服务历程的。
当OS收到这个中断,根据中断表,查到对应的中断服务历程的起始地址,直接跳转到这个位置来执行就可以了。为了能够让整个系统正常工作,还需要完成一些更多的事情

中断(Interrupt)的处理过程

当中断产生后,打断了当前的正常执行来处理更加紧急的中断事件,那么打断一个程序的执行,我们需要在硬件和软件方面做出一定的保存与恢复机制。才能保证完成中断处理以后整个系统仍然能够正常进行。
在这里插入图片描述
硬件:外设是一个硬件,当他需要OS给予相应的支持之后,那需要产生一个标记,让CPU知道中断的产生。CPU看到这个中断标记后,可以产生一个具体的中断号(ID),然后把中断号发给OS,从而让OS根据中断号找到对应的处理历程。
在这里插入图片描述
软件:软件方面就是操作系统本身。
首先要保存被打断的执行现场(保持当前的处理状态,这个程序执行到什么地方了,执行的寄存器的内容等,都需要保存起来,这样才能令中断处理完成后的程序继续衔接打断之前的工作内容)
其次,根据CPU给的中断号查到对应的中断处理历程的地址,然后跳过去执行,这个过程会根据外设产生的中断的具体情况来完成响应操作。Ex)这个外设是网卡,来一个数据包,则这个终端历程需要把数据包取出来做出相应的处理。
处理完之后,应该让被打断的程序继续执行,这就需要恢复上述提到的保存的数据。
中断的整个执行过程是对应用程序透明的,应用程序感受不到中断的产生。

异常(Exception)的处理过程

在这里插入图片描述
异常与中断不同,异常也是因为某个特定的指令触发了异常事件,比如除0操作。他也会有一个异常的ID,然后OS会根据这个ID保存现场(当前执行进度、这条指令地址、当前寄存器的内容等)。保存之后操作系统会根据ID进行相应处理,分为两种:
退出执行(杀死)或者重新执行[OS认为这个程序的异常原因是因为OS的服务不到位,OS会进行一定的弥补工作,然后根据刚才异常产生的现场进行恢复,让应用程序重新执行。由于OS已经将把他产生异常的原因补完,所以就不会再次产生异常,这个过程和中断相同,对应用程序是透明的。APP感受不到某条特殊指令会产生异常。

系统调用System Call

标准C库
来源于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在安全可靠的环境下运行。

发布了9 篇原创文章 · 获赞 3 · 访问量 276

猜你喜欢

转载自blog.csdn.net/Chahot/article/details/105345463
今日推荐