操作系统的基本概念
操作系统的定义,功能,目标
在学习操作系统之前,我们需要了解什么是操作系统,首先,我们来研究一下一个电脑是怎么诞生得的,首先,厂家会将很多硬件组装起来,此时这种只有硬件的东西只能叫裸机,在裸机的基础上,厂家一般还会给我们安装一个操作系统,比如windows,当我们拿到电脑时,我们就可以在操作系统上安装一些应用程序,我们就可以来使用这些应用程序,当然,我们也可以直接去操作操作系统,至此,我们可以得出计算机系统的层次结构:
操作系统(Operating System, OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调
度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本
的系统软件。
从以上定义可以看出以下几点:
- 操作系统是系统资源(软件资源+硬件资源)的管理者
- 操作系统向上提供方便易用的服务
- 操作系统是最接近硬件的软件
我们对着三点依次分析
操作系统是系统资源的管理者,我们以QQ视频聊天为例,我们要用QQ视频聊天之前,我们先得去文件目录找到QQ这个应用程序,然后运行QQ.exe,QQ程序运行之后,要和朋友视频聊天,就要将摄像头分配给QQ程序用,由此可以得出操作系统作为系统资源的管理者要提供的功能,首先,我们是通过文件目录找到QQ的,所以操作系统需要提供文件管理功能,在运行QQ的时候,需要把磁盘内的文件写入到内存中,所以操作系统还要提供存储器管理的功能,即内存管理,QQ程序写入内存后,需要将这个程序占用处理机运行,所以操作系统还要提供处理器管理的功能,最后,聊天需要使用摄像头这种设备,所以操作系统还要提供设别管理的功能。
本节课的章节就是以这四个功能展开的,所以在这里不展开
操作系统需要向上层提供方便易用的服务,从上面的计算机系统层次结构图可以看到,最底层是硬件,这些硬件对外暴露的接口是二进制指令,换句话说它只能看得懂二进制的指令,如果我们要直接和裸机打交道,就只有通过二进制指令,这样的接口是非常不友好的,在硬件上安装了操作系统,安装了操作系统后,就由操作系统来负责和硬件打交道,我们只需要和操作系统打交道即可,至于怎么利用硬件实现,就是操作系统要去关心的事,这就是一个很典型的封装思想,将不友好的硬件功能封装成一个一个方便的服务,我们只需要调用这些服务即可,不用关心具体实现细节。操作系统在封装了底层硬件后,提供了哪些服务呢,下面有几个例子:
GUI(图形化用户接口),也就是图形化界面,在没有GUI的操作系统中,我们只能通过指令去删除文件,但有了GUI后,我们直接拖拽文件就可以实现文件的删除操作,在我们拖拽之后,操作系统在背后为我们干了很多工作。
命令接口,也就是在没有GUI之前,操作系统会提供的接口,这些接口都是以指令的方式存在的,命令接口分为联机命令接口和脱机命令接口,联机命令接口就是交互式的命令接口,例如windows的cmd就是一个联机命令接口,我们可以使用time命令获取当前时间,接下来会等待用户写入一个时间来更改时间,这就是一个交互性的命令,也就是我们给他一个命令,他就会做一个事,我们再说一句,他再做一件事,这就像一个对话一样,说一句做一句,这就属于交互式命令接口,除此之外还有脱机命令接口,脱机命令接口也就是批处理命令接口,脱机命令接口就是我们事先写好一堆命令,保存在一个文件里,然后由操作系统自己去读取,自己去执行,也就是根据文件里的很多命令一条一条往后执行,这种批处理命令接口就是我们一次性给他一堆请求,操作系统一次性执行一批请求,也就是说一堆做一堆。
程序接口,操作系统还会提供一些程序接口,和上面的两个接口不同,程序接口不是给普通用户用的,程序接口是专门给程序员使用的,我们作为程序员可以通过系统调用的方式来使用程序接口,普通用户无法直接使用程序接口,举个例子,例如输出,当我们要输出一个helloworld时,我们虽然只是简单在c语言中调用了printf函数,但这个函数在内部肯定调用到了一些和显示相关的系统调用,换句话说,这个过程是我们普通程序员是通过printf间接调用了操作系统提供的接口,参考下图:
系统调用类似于函数调用,但这些函数实际上是操作系统内部对外提供的,系统调用是应用程序请求系统服务的唯一方式。
系统调用也称为广义指令
命令接口和程序接口有时候会统称为用户接口,所以狭义的用户接口并不包含GUI
操作系统作为最接近硬件的一层软件,还需要实现对硬件的扩展,CPU,内存,磁盘,这些都是只能干一件事的东西,操作系统可以将这些功能单一的硬件合理组织起来,相互协调配合,实现更加复杂的功能,这就是对硬件的一种扩展,即操作系统把裸机改造成功能更强大,使用更方便的机器,通常把覆盖了软件的机器称为扩充机器,也称为虚拟机。
操作系统的四个特征
操作系统包含并发,共享,虚拟,异步四个基本特征,其中并发和共享是两个最基本的特征,二者互为存在条件,接下来依次介绍。
并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的,但微观上是交替发生的。
除了并发外还有一个并行的概念,这两个极易混淆,并行是指两个或多个事件在同一时刻同时发生,举个例子来解释并发和并行,假设我的CPU有两个核心,分别是1核心和2核心,现在我要执行两个任务,分别是A任务和B任务,我把A任务交给1核心,把B任务交给2核心,此时两个核心同时在执行我的两个任务,这个时候这两个任务就是并行执行的,那么什么是并发呢,假设我两个任务A和B的执行时间都是1分钟,我先让A任务去1核心执行半分钟,然后让他先停下来,让B任务去1核心执行半分钟,再停下来让A任务去1核心执行半分钟,最后让B任务去1核心执行半分钟,这个时候我只用到了1核心,在宏观上看,我用2分钟的时间内执行完了A任务和B任务,但在微观上看,A和B两个任务是交替执行的,不是同时执行的,这个时候A任务和B任务就是并行执行的。
从这里就能看出,如果多个任务在同一时刻是同时发生的,就是并行,如果多个任务宏观上同时发生,但微观上交替发生,就是并发。同时,从以上例子还可以得出一个结论,由于单核CPU同一时刻只能运行一个任务,所以单核CPU是不可能实现真正意义的并行的,所以单核CPU多个程序只能并发运行,多核CPU多个程序可以并行运行。
CPU的核心数就决定了同一时刻可以并行运行的程序数量
有了并发和并行的概念之后,我们来了解一下什么是操作系统的并发性,操作系统的并发性是指在计算机系统中同时运行着多个程序,这些程序宏观同时运行,微观交替运行,前面提到过,并发性是操作系统最基本的基本特征之一,为什么呢,因为操作系统的出现就是为了支持多道程序技术的,而我们也知道,哪怕是多核CPU,他能并行运行的程序数量都是有限的,为了同时运行多个程序,就必须依靠并发性,所以也就能理解并发性为什么是操作系统的一个最重要的基本特性了。
共享即资源共享,是指系统中的资源可供内存中多个并发执行的进程共同使用。
资源共享又可以分为互斥共享和同时共享,互斥共享是指在一个时间段内这个资源只允许一个进程使用,同时共享是指在一个时间段内允许多个进程同时对他进行访问,这里的同时往往是并发的,宏观上同时,但微观上可能是交替的,例如摄像头这种资源,他就是一个互斥共享,在同一时间内只能分配给同一个进程,比如QQ占用了摄像头用来视频聊天,微信就无法再使用摄像头了,所以摄像头就是一个互斥共享资源,而在发送文件上,我们可以将同一个文件分别通过QQ和微信发送给别人,此时QQ和微信的进度条都是同时进行的,看起来QQ和微信都是同时在读取硬盘上的文件,但在微观上来看,两个进程是交替访问硬盘的。
那么为什么我们说并发性和共享性互为存在条件呢,如果失去了并发性,也就是同一时刻只能有同一个程序正在也运行,那么同一时刻也就只有一个进程来访问硬盘资源,共享性也就失去了存在意义,而换个角度,对于两个并发发送文件的进程而言,如果失去了共享性,意味着两个进程无法同时访问硬盘,这两个进程也就无法同时发送文件,也就没法并发执行。
虚拟是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。
可以看出,虚拟性就是用户感受到的和实际不同,例如我的电脑明明有16G的运行内存,但我运行的程序对内存的总需求实际上远大于16G,所以在我看来我这个16G的电脑的实际感受到的内存其实是大于16G的,同时对于CPU来说也一样,假设我的CPU只有四个核心,但我依旧可以同时运行6个应用程序,这个时候在我的感受来看,这6个应用程序在同时运行,好像是6个CPU在同时为我服务,以上两个例子分别采用空分复用和时分复用两个技术来实现,在后面会详细介绍。
异步是指,在多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。
举个异步的例子,比如两个进程A在运行过程中要用到摄像头,但我并不知道摄像头的状态,如果A要用摄像头的时候,摄像头已经被其他进程占用了,此时A就只有等着其他进程用完摄像头后再接着用,所以A在请求摄像头的时候有可能摄像头空闲直接拿来用,也有可能需要等待其他进程释放,从这就能看出进程的执行由于资源有限,所以走走停停,我们也无法预估,这就是异步性。
很显然,如果失去并发性,那么系统只能串行执行各个程序,当你请求摄像头的时候也就不可能有人和你抢摄像头,所以只有系统拥有并发性,才可能导致异步性。
操作系统的发展历程
本节主要介绍操作系统的发展历程和分类,主要关注各个类型的操作系统分别是为了解决什么问题,也要关注其优缺点
首先第一个阶段是手工操作阶段,这个阶段准确来说操作系统还没有诞生,程序员写代码是要写在纸带上面的,纸带上会打很多小孔,有空代表1,没空代表0,如下图所示
程序员用纸带打孔来编程,把纸带放到计算机上,计算机读取纸带的内容,根据纸带的内容来执行操作,操作完后把返回结果输出到纸袋机上,再由程序员取走结果,这种方式有很明显的弊端,首先就是程序员用手工的方式对纸带打孔编程本身就是效率很低的操作,其次计算机还得读取纸带,这也是一个很慢的操作,但计算机本身是一个很迅速的操作,所以一次计算的大部分时间计算机都是闲置的,都花在编程和读取纸带上了,这也就导致了资源利用率相当低。
正应如此,人们发明了单道批处理系统,引入了脱机技术来完成输入输出,各个程序员把自己写好的程序放到纸袋机上,通过一个叫外围机的设备将这些纸袋机里的程序写入到磁带,之后计算机可以直接从磁带中读写纸带数据(磁带读写速度远高于纸带),此时磁带里面会有很多作业,为了控制计算机自动从磁带里输入输出,计算机内部还会运行一个监督程序,负责控制作业的输入输出,将一个一个的任务依次输入输出,如下图所示。
这种方式的优点就是缓解了一定程度的人机速度矛盾,资源利用率有所提升。同时缺点也很明显,内存中仅能有一道程序运行,只有该程序运行结束之后才能调入下一道程序。CPU有大量的时间是在空闲等待I/O完成。资源利用率依然很低。
后来人们又发明了多道批处理系统,在这个阶段才算是有真正意义上的操作系统,也就支持多道程序并发运行。
在引入多道批处理系统之后,多道程序可以并发执行,共享计算机资源。资源利用率大幅提升,CPU和其他资源更能保持“忙碌”状态,系统吞吐量增大。同时这种方式也有缺点,多道批处理并不提供人机交互的功能,用户响应时间长,用户在提交任务之后,用户只有干等着,不能调试自己的程序,也不能在程序运行的过程中输入参数。
为了实现人机交互的功能,又发明了分时操作系统,在分时操作系统重,计算机会轮流为各个用户/作业服务,用户则可以通过终端设备进行人机交互,例如一个计算机要服务4个用户,他每隔50毫秒切换一次用户,也就是说我作为一个用户每隔150毫秒就可以得到一次服务,在我看来就像是一直在为我一个人服务一样。
综上所述,分时操作系统的优点就是用户请求可以被即时响应,解决了人机交互问题。允许多个用户同时使用一台计算机,并且用户对计算机的操作相互独立,感受不到别人的存在。分时操作系统也存在明显的缺点,分时操作系统不能优先处理一些紧急任务。操作系统对各个用户/作业都是完全公平的,循环地为每个用户/作业服务一个时间片,不区分任务的紧急性。
为了让计算机优先处理紧急任务,人们又提出了实时操作系统,实时操作系统可以优先响应一些紧急任务,某些紧急任务不需时间片排队。这种操作系统要求计算机在收到外部信号之后及时进行处理,并且在严格的时间限制内处理完成。实时操作系统主要的特点是及时性和可靠性。
而实时操作系统分为硬实时和软实时,对于一些例如导弹控制系统,自动驾驶系统,这类系统必须绝对严格地在规定时间内完成处理,如果处理不及时会导致严重的问题,而软实时操作系统能够接受偶尔的违反时间规定,换句话说,软实时操作系统哪怕有时候不及时,也不会导致特别严重的后果。
除了以上操作系统之外,还有网络操作系统,分布式操作系统,个人计算机操作系统,属于了解内容,这类操作系统在考试中不会涉及。
操作系统的运行环境
操作系统的运行机制
所谓运行机制,就是在研究操作系统是如何运行的问题
这里先插入一个知识点,我们平时编写的程序,例如C语言代码,他是需要被编译器编译成二进制的机器指令才可以去CPU执行的,换句话说,CPU也只认识二进制的机器指令,机器指令和我们的代码也有很大区别,例如我要创建一个int类型的变量a,虽然我这里只写了一行代码,但翻译成机器指令后其实做了很多操作,例如分配空间,在变量表里添加变量等等,所以这样一条代码其实是被翻译成了很多的机器指令,我们后面说的指令,就是指一条一条的机器指令,也就是CPU能处理和识别的基本命令。
要区分这里的指令和其他地方指令的区别,在cmd窗口我们输入的指令那其实是操作系统给我们的交互式命令接口,并不是我们这里说的指令
我们把系统中所有的程序分成两类,一类是内核程序,一类是应用程序,应用程序是运行在OS上的,比如QQ,微信这些,而内核程序是运行在操作系统内部的,可以看做是操作系统的底层程序,我们常说的操作系统内核就是由很多内核程序组成的,操作系统内核也简称为内核,内核是直接和硬件打交道的程序,是最接近硬件的部分,并且也是操作系统最核心的部分,一个操作系统甚至只需要内核就足够了。
内核实现了操作系统最核心的功能,我们说过,操作系统最重要的就是对系统资源的管理,而对系统资源的管理就是在内核完成的,操作系统中不那么重要的部分则被扔在了内核之外,比如GUI就不是内核程序。
现在搞明白了内核程序和应用程序,那为什么内核程序一定要运行在内核上,而应用程序却不用运行在内核上呢,主要还是处于安全性考虑。
通过前面的学习我们知道程序是一条一条指令构成的,内核程序和应用程序也一样,一个CPU可以支持很多指令,该CPU能够支持的所有指令和集合就是这个CPU的指令集,而在指令集中,我们可以把指令分为两种,一种是特权指令,一种是非特权指令,特权指令是一些比较危险的指令,比如内存清零,这些特权指令可能会对系统产生较大的影响,而非特权指令则是像加减乘除这种不会对系统造成多大影响的指令,而内核程序之所以要运行在内核,就是因为其包含了特权指令,可能会对系统产生重大影响,应用程序则只被允许使用非特权指令。
接下来问题又来了,CPU在出厂的时候,其指令集就被划分成了非特权指令和特权指令,所以其实CPU拿到指令之后是可以区分开特权和非特权指令的,而我们说了,应用程序只允许执行非特权指令,内核程序则可以执行非特权指令,那按理说,如果一个应用程序非法执行了一个特权指令,那就应该终止这个应用程序,但是CPU拿到一个特权指令之后怎么区分他是内核程序执行的还是应用程序执行的呢?我们又把CPU划分为两种状态,分别是内核态和用户态,内核程序只允许在内核态运行,而用户态则用来运行应用程序,这样这个问题就解决了,当CPU处于内核态就允许运行特权指令,而处于用户态则不允许运行特权指令。
在CPU中,会有一个程序状态寄存器,简称PSW,这里可以参考微机原理相关知识,在PSW中,会有一个二进制位用来区分当前的状态,CPU根据这个状态字就可以区分当前处于的状态,举个例子,当CPU要执行一个特权指令的时候,会先检查当前处于哪种状态,如果处于内核态,则允许执行,如果处于用户态,则不能执行该特权指令。
内核态=核心态=管态
用户态=目态
那接下来的问题就是,我们只知道CPU拿到特权指令之后可以根据PSW判断是否执行,那这个PSW的值是如何变化的呢,或者说内核态和用户态在什么时候会发生改变呢,由于这个PSW是CPU内部的寄存器,操作这个PSW本身就是很危险的操作,所以改变PSW的指令必须是一个特权指令,如果要将内核态切换到用户态这个很好理解,因为CPU本身处于内核态,当我内核程序执行完之后,我可以直接用特权指令让CPU直接变为用户态,但CPU如果处于用户态则只能通过中断来让CPU变为内核态,例如当CPU处于用户态却要执行一个应用程序,此时就会有一个中断,中断后CPU会强行切换回内核态,并进行中断处理子程序,来对该中断进行处理。除了这种非法指令可以让CPU变为内核态,还有很多方法也可以,但是不管哪种方法,本质上都是通过中断来让CPU变态,关于CPU中断的具体知识可以参考微机原理相关课程。
本节为选择题重点考点,这里总结一下,CPU在出厂前根据指令的危险性将指令分为特权指令和非特权指令,当CPU拿到一个指令后,可以区分出其是特权指令还是非特权指令,但特权指令的执行可能会带来一些严重后果,所以要对这些指令加以限制,我们只允许内核程序执行特权指令,应用程序不允许执行特权指令,CPU内部的PSW会用一位来存储当前运行的是用户程序还是内核程序,当CPU在运行内核程序时,我们称CPU处于内核态,而运行用户程序时则称为用户态,CPU拿到一个特权指令的时候会检查PSW,如果运行在内核态,则允许执行,如果运行在用户态,则不允许执行,当内核程序运行完之后,会控制CPU的PSW,让其变为用户态,以执行用户程序,而当处于用户态时,也可以使用中断来让CPU强行变为内核态。
中断和异常
首先来看中断的作用,上一节已经说的非常清楚了,内核态转换为用户态只需要执行一条特权指令,但用户态想要再变为用户态的唯一途径就是中断,一个应用程序一旦发生中断,就会让CPU立刻停止当前的应用程序,转变为内核态去执行相对应的内核程序。
其实这里可以思考一下,如果没有中断,一个应用程序一旦运行,就会一直运行下去,也就是说他会一直占用这个处理机,那也就没有并发这么一说了,我们之所以能让CPU每隔多少毫秒切换一下应用程序也是基于中断技术实现的。
从中断的来源来说,中断可以分为外中断和内中断,内中断是用户程序自己的内部中断,比如用户程序执行了一条特权指令就会导致内中断,或者当应用程序真的需要一些特权指令为他服务,也可以执行一条陷入指令来主动创造一个中断信号,前面提到的系统调用就是用这个陷入指令来完成的,外中断则是CPU外部的中断,例如时钟中断,时钟中断就是一个时钟信号发射器来每隔一段时间给CPU发送一个中断信号,CPU收到这个信号后就会转换为内核态对这个信号进行处理,处理的过程就是把CPU的使用权交给另一个应用程序,以此往复。
陷入指令并不是特权指令
内部中断也称为异常,狭义上的中断只包含外中断
内中断可以划分为三种,陷入,故障,终止,陷入也就是系统调用的实现原理,是通过陷入指令来实现的,陷入指令是程序故意执行的, 当程序想要请求OS服务的时候就可以执行一个陷入指令,而故障则是一些由错误条件引起,并且可能被内核程序修复的问题,内核程序修复后会把使用权重新交给发生故障的应用程序,而终止则是一种执行错误引起的,当终止异常之后,内核程序一般会直接终止发生问题的应用程序。
接下来介绍一下中断机制的背后原理,CPU会根据中断信号的不同,找到对应的中断处理程序,CPU在内存维护一个中断向量表,而发生中断的时候必然会产生一个中断号,根据这个中断号,就可以去中断向量表中查询到处理该中断的中断处理程序的位置。
系统调用
首先来看什么是系统调用,前面提到过,操作系统作为硬件和用户的接口,需要向上提供一些简单易用的服务,向用户提供的就是命令接口,而对程序员提供的就是系统调用,程序员编写程序的时候可以通过系统调用来获取操作系统内核服务。
那有的程序员就要问了,我写这么多程序也没见过什么系统调用呀,因为现在的高级语言给我们写好了很多库函数,这些库函数内部就会用到很多系统调用,我们是通过调用库函数间接实现的系统调用,如下图所示
介于此,我们普通程序员虽然也可以使用系统调用,但一般我们都是调用库函数,因为库函数会更加方便,这里要注意的是并不是所有的库函数都涉及到系统调用,只有部分库函数会涉及到系统调用,如果能听懂这里,那也自然能分清楚操作系统和库函数的区别。
系统调用是非常有必要的,因为系统中有很多资源是必须要互斥访问的,比如摄像头,打印机,而对这些需要互斥共享资源的统一管理是要放在内核中进行的,应用程序只能向内核发出请求,如果有多个程序请求,则会有内核程序协调处理各个请求。
系统调用按功能分可以分为以下几种:
- 设备管理:完成设备的请求/释放/启动等功能
- 文件管理:完成文件的读/写/创建/删除等功能
- 进程控制:完成进程的创建/撤销/阻塞唤醒等功能
- 进程通信:完成进程之间的消息传递/信号传递等功能
- 内存管理:完成内存的分配/回收等功能
其实这些都有一个共性,那就是凡是涉及到共享资源的操作都需要通过系统调用实现,比如文件,内存,设备,包括进程通信时也需要使用到内存,进程控制时CPU更是一个共享资源,应用程序在使用这些共享资源的时候都必须用系统调用去请求操作系统内核来帮助处理,这样可以保证系统稳定性和安全性,防止用户的非法操作。
在执行陷入指令前,我们其实会先给计算机内的寄存器传入一些参数,当CPU执行了陷入指令之后会引发内中断,CPU会自动切换为内核态,并根据这个内中断的中断号可以识别出是由于trap指令引起的,所以接下来就会去执行系统调用的入口程序,也就是中断处理程序,系统调用可以根据我们在陷入指令执行之前向寄存器传递的参数来判断这个应用程序需要什么服务,于是入口系统程序员就会去调用相关的服务来进行处理。
陷入指令可以主动引发内中断,从而让CPU进入核心态,其本身并不属于特权指令
我们可以通过陷入指令发出一个系统调用,这一步是在用户态进行的,而发出系统调用之后会立马转变为内核态,所以对系统调用的处理是在核心态进行的
操作系统结构
操作系统的体系结构就是操作系统内核应该如何设计的问题,对于本节的内容主要以了解为主
在操作系统内部,我们可以划分为内核功能和非内核功能,操作系统最核心的功能就放在内核中,比如时钟管理,中断处理以及原语操作,当然还有一些其他功能,这里提到的时钟管理和中断处理都已经很熟悉了,时钟管理就是利用中断来计时,那么这里提到的原语操作又是什么呢,原语也就是一个比较特别的程序,这个程序具有原子性,所谓原子性,可以理解为一气呵成,不可被中断,内核的划分如下图所示
上图中,进程管理,存储器管理,设备管理等功能更多的是对数据结构的操作,并不直接涉及到硬件,所以有一些操作系统并不会将其放在内核当中,但对于下面的这三种功能不论是任何操作系统都需要将其放在内核中
如果把进程管理,存储器管理,设备管理这类操作都放在内核中的话,这样的操作系统我们称之为大内核操作系统,但如果内核中只保留时钟管理,中断处理,原语,则称之为微内核操作系统,如下图所示
由于微内核操作系统把进程管理,存储器管理,设备管理这些功能放在了用户态,这样做可能会带来一些性能上的问题,因为进程管理,存储器管理,设别管理这些功能虽然放在了用户态,但也都需要内核的支持,当这些模块对应用程序请求进行处理的时候,在需要得到内核支持的时候就会临时切换到内核态,内核处理完成后又会回来继续处理,所以会导致频繁地切换状态,而对于大内核OS而言,这些操作均是在内核态运行的,不存在这种频繁的状态切换,如下图所示
状态切换并不只是更改PSW这么简单,还涉及到保护现场,加载新环境等操作,所以频繁切换模式是会降低性能的
接下来对比一下两种内核,大内核的优势很明显,就是性能高,缺点则是内核代码量庞大,结构混乱,不好维护,而微内核虽然要频繁进行状态切换,性能不如大内核,但其内核功能少,结构清晰,方便维护,可靠性也更高。
Linux,UNIX都属于大内核OS,而windowsNT则是微内核操作系统
接下来再介绍几个不太重要的操作系统结构,以了解为主
操作系统结构 | |||
---|---|---|---|
特性、思想 | 优点 | 缺点 | |
分层结构 | 内核分多层,每层可单向调用更低一层提供的接 ☐ | 1.便于调试和验证,自底向上逐层调试验证 | 1.仅可调用相邻低层,难以合理定义各层的边界 |
2.易扩充和易维护,各层之间调用接口清晰固定 | 2.效率低,不可跨层调用,系统调用执行时间长 | ||
模块化 | 将内核划分为多个模块,各模块之间相互协作。 内核=主模块+可加载内核模块 主模块:只负责核心功能,如进程调度、内存管 理 可加载内核模块:可以动态加载新模块到内核, 而无需重新编译整个内核 | 1.模块间逻辑清晰易于维护,确定模块间接口后 即可多模块同时开发 | 1.模块间的接口定义未必合理、实用 |
2.支持动态加载新的内核模块(如:安装设备驱 ☐动程序、安装新的文件系统模块到内核),增强 OS适应性 | |||
2.模块间相互依赖,更难调试和验证 | |||
3.任何模块都可以直接调用其他模块,无需采用 0 消息传递进行通信,效率高 | |||
宏内核(大内核) | 所有的系统功能都放在内核里(大内核结构的OS 通常也采用了"模块化"的设计思想) | 1.性能高,内核内部各种功能都可以直接相互调 。 用 | 1.内核庞大功能复杂,难以维护 |
2.大内核中某个功能模块出错,就可能导致整个 。 系统崩溃 | |||
微内核 | 只把中断、原语、进程通信等最核心的功能放入 内核。进程管理、文件管理、设备管理等功能以 用户进程的形式运行在用户态 | 01.内核小功能少、易于维护,内核可靠性高 | 1.性能低,需要频繁的切换用户态/核心态。用 0 户态下的各功能模块不可以直接相互调用,只能 通过内核的"消息传递"来间接通信 |
2.内核外的某个功能模块出错不会导致整个系统 0 崩溃 | 2、用户态下的各功能模块不可以直接相互调用, 0 只能通过内核的"消息传递"来间接通信 | ||
外核(exokernel) | 内核负责进程调度、进程通信等功能,外核负责 0 为用户进程分配未经抽象的硬件资源,且由外核 负责保证资源使用安全 | 1.外核可直接给用户进程分配"不虚拟、不抽象" 0 的硬件资源,使用户进程可以更灵活的使用硬件 资源 | 1.降低了系统的一致性 |
2.减少了虚拟硬件资源的"映射层",提升效率 | 2.使系统变得更复杂 |
上图中加粗部分为重点内容,本节课主要了解分层结构,模块化以及外核
首先是分层结构的操作系统,操作系统的内核会被分成多个层次,每一层只能单向调用更低层次(相邻层次)所提供的接口,如下图所示
这种分层结构的优势在于调试和验证非常方便,只需要逐层向上逐层调试验证即可,使得软件测试的过程清晰明朗,其次也很易于扩充和维护,因为层次之间的接口是固定的,我可以在两层之间新加一层,我只需要保证新的这一层只使用下面这一层的接口,并且对上提供同样的接口即可,而这种分层结构自然也是有很多缺点的,例如我们很难定义层次之间的边界,比如A功能需要使用到B功能,但B功能中可能又要用到A提供的部分功能,所以这种严格区分层次的结构有时候就会显得很死板,并且由于划分了很多层次,我作为一个用户,我的请求需要跨越多个层次才能到达硬件层面,这就使得系统效率低,系统调用执行时间长。
接下来是模块化的操作系统,这种模块化的操作系统会将内核划分为多个模块,各个模块之间相互协作,如下图所示
将系统划分为多个模块,每个模块又划分多个子模块,整个内核由主模块和可加载模块组成,主模块为核心模块,也就是必不可少的模块,可加载模型则为可选模块,可以在运行过程中动态加载到内核。模块化的操作系统模块之间逻辑清晰,易于维护,确定好模块接口之后就可以多模块同时进行开发,同时这种模块化的操作系统都支持动态加载新模块,使得OS适应性增强。任何模块之间都可以直接调用其他模块,这样使得功能的调用效率很高,缺点则是模块之间的接口定义未必是合理的,因为接口是一开始确定好的,难免会有缺陷,可能在开发过程中就会暴露出来,而当暴露出问题的时候或许已经为时已晚了,另外,模块之间是相互依赖的,这是不利于调试的。
宏内核和微内核在前面已经涉及过,宏内核也就是前面提到的大内核,宏内核就是把功能模块都放在了内核内,宏内核实际上也采用了模块化的思想,从上图可以看到,宏内核就是各个功能模块在内部调来调去,也就是模块化的思想,相比而言,微内核调用就没有那么方便,微内核系统的系统调用只能通过消息传递的方式进行,比如进程管理想要使用存储管理的功能,就要向微内核发送一个消息,由微内核将消息转发到存储管理,同时,存储管理想要返回调用结果也只能通过微内核的消息传递机制,效率就会更低。除此之外,由于微内核将一些模块放在了内核态外部,当这些模块出现错误的时候并不会导致整个系统的崩溃,而对于大内核来说,内核中存在大量的模块,只要由一个模块出错,由于模块间依耐性很强,很有可能导致整个系统的崩溃。
最后一种操作系统结构为外核,这种操作系统结构很少见,这种操作系统由内核和外核两个部分组成,内核部分只负责进程调度,进程通信等和硬件资源无关的功能,外核则负责为用户进程分配未经抽象的硬件资源,同时保证这些硬件资源的使用安全
什么是未经抽象的硬件资源?
普通操作系统给进程分配的内存空间实际上是进过抽象的,虚拟化的,这一点在后面的学习中会涉及到,外核可以为用户进程分配未经抽象的硬件资源,在有些时候这种未经抽象的硬件资源可以提升效能
外核这种操作系统由于给用户分配的是不抽象的资源,所以用户进程可以更加灵活地使用这些资源,同时由于分配的是真实的资源,也减少了映射层,提升了效率,但有外核的操作系统中,降低了系统的一致性,因为有的进程可能需要虚拟的空间,有的进程可能需要真实的物理空间,这样就需要考虑各种各样的情况,就降低了一致性,让系统更复杂。
本节内容以了解为主,重点在前面给出的那张大表中
操作系统引导
首先要来搞明白什么是操作系统引导,我们知道任何程序都需要加载到内存中并运行起来,操作系统也是程序,当开机的时候也需要加载到内存中并让其运行起来,开机的时候让操作系统运行起来的过程就是引导。
首先我们要了解一下磁盘内部有哪些数据,一个安装了操作系统的磁盘有可能是下面这个样子
后面的C,D,E,F应该很熟悉,就是我们的磁盘分区,我们的操作系统一般是安装在C盘的,除此之外,在磁盘开头的位置,会留出部分区域用于存储主引导记录,主要包含了磁盘引导程序和分区表,磁盘引导程序放在后面说,其中分区表其实就是说明了C,D,E,F这些分区的信息,包括了地址范围,占用空间等信息
由于C盘中安装了OS,我们对C盘进一步细分,如下图所示
在这个案例中,由于我们要通过C盘来启动操作系统,所以我们也可以称C盘为活动分区
在C盘中,有两个很特别的部分, 第一部分用来存储引导记录PBR(PBR放在活动分区地址的最前面),其次就是根目录,根目录顾名思义就是C盘的根目录嘛,除此之外的部分目前不做解释
到现在我们应该可以看到一个安装了操作系统的磁盘长什么样,并且还可以看到C盘内部大概有哪些东西,接下来来看C盘中的操作系统是如何启动的
在此之前,我们需要知道,内存分为ROM和RAM,其中RAM是掉电丢失,而ROM则是掉电不丢失,ROM集成在主板上,ROM里存储的是BIOS,BIOS里有一个程序叫做ROM引导程序(自举程序),如上图所示。
当CPU通上电开机的时候,就回去主存中的ROM部分去找这个ROM引导程序,执行这个程序,这个ROM引导程序会让CPU把磁盘中的主引导记录读入内存,忘记了的读者可以看一下前面的图,此时主引导程序中的磁盘引导程序和分区表都被我们读入到程序内了,接下来CPU就可以去执行内存中的磁盘引导程序了,磁盘引导程序则会根据分区表判断C盘(活动分区)所处的位置,找到C盘(活动分区)之后,由于引导记录PBR是位于活动分区首部的,所以就会从C盘(活动分区)起始位置开始读入PBR,这个PBR本质上也是一个程序,这个程序被读入内存后,CPU又回去执行引导记录这个程序,这个程序的是用来找到启动管理器的,启动管理器通常存放在根目录下面的某个位置(linux系统一般是存放在/boot目录下,windows操作系统一般放在windows/boot/下),接下来就会从根目录下找到这个启动管理器,将其读入内存并运行,这个启动管理器就会完成操作系统初始化的一系列工作,这就是操作系统的引导过程。
对于上面一长段话,我们可以做如下总结,将过程分为四步,参照下图所示:
① CPU从一个特定主存地址开始,取指令,执行ROM中的引导程序(先进行硬件自检,再开机)
② 将磁盘的第一块一一主引导记录读入内存,执行磁盘引导程序,扫描分区表
③ 从活动分区(又称主分区,一般指操作系统的分区)读入分区引导记录,执行其中的程序|
④ 从根目录下找到完整的操作系统初始化程序(即启动管理器)并执行,完成“开机”的一系列动作
虚拟机
首先来看传统的计算机,传统计算机在一个物理机器上只允许运行一个操作系统,在操作系统上又可以运行一些进程,但这种模式其实是存在弊端的,商业级计算机的硬件资源是很强的,如果只能运行一个系统,那多个进程就得同时在一个操作系统上运行,这样是会存在一些隐患的,也存在一些其他的限制,因此就有人发明了虚拟机
虚拟机使用虚拟化的技术,把一个物理机器虚拟化为多个虚拟机器,每一个虚拟机器都允许独立运行一个操作系统,虚拟化的工作通过虚拟机管理程序来实现。
虚拟机管理程序分为两类,第一类是直接运行在硬件之上,由虚拟机管理程序直接管理硬件资源,并将硬件资源划分为多个部分,分别给多个虚拟机来使用,每一个虚拟机都可以安装自己的操作系统,并互不干扰,以CPU的分配为例,就可以将CPU以时间片的方式进行分配,在上层系统看来,自己拿到的就是一个独立的CPU,但实际上只是分配了一些时间片给他,在这类虚拟机管理程序里,只有虚拟机管理程序本身运行在内核态,只允许虚拟机管理程序执行特权指令,上层的OS均运行在用户态,当上层的操作系统执行特权指令时会被底层的虚拟机管理程序截获,并模拟该特权指令的功能。
第二类虚拟机管理程序不会直接运行在硬件上,是运行在一个宿主操作系统上的,例如我这个windows系统的机器可以安装一些安卓模拟机一样,我可以在我的操作系统上安装一些虚拟机管理程序,安装了这类虚拟机管理软件之后,就可以在我原有的系统上再启动一个系统,我也可以在我的电脑上同时启动多个虚拟操作系统,这类虚拟机管理程序要给各个虚拟机分配资源就只有请求操作系统给自己分配资源,再由虚拟机管理系统对资源进行再分配,硬件资源的管理者还是宿主操作系统。
由于这两类的思想不同,就会带来很多差异,这里进行一个总结
识别结果
两类虚拟机管理程序 (VMM)的对比 | ||
---|---|---|
第一类VMM | 第二类VMM | |
对物理资源的控制权 | 直接运行在硬件之上,能直接控制和分配物理资 源 | 运行在Host OS之上,依赖于Host OS为其分配物 理资源 |
资源分配方式 | 在安装Guest OS时,VMM要在原本的硬盘上自 行分配存储空间,类似于"外核"的分配方式,分 配未经抽象的物理硬件 | GuestOS拥有自己的虚拟磁盘,该盘实际上是 Host OS 文件系统中的一个大文件。GuestOS分 配到的内存是虚拟内存 |
性能 | 性能更好 | 性能更差,需要HostOS作为"中介" |
可支持的虚拟机数量 | 更多,不需要和Host OS竞争资源,相同的硬件 资源可以支持更多的虚拟机 | 更少,Host OS 本身需要使用物理资源,Host OS上运行的其他进程也需要物理资源 |
虚拟机的可迁移性 | 更差 | 更好,只需导出虚拟机镜像文件即可迁移到另一 台HostOS 上,商业化应用更广泛 |
运行模式 | 第一类VMM运行在最高特权级 (Ring 0),可以 执行最高特权的指令。 | 第二类VMM部分运行在用户态、部分运行在内核 态。GuestOS发出的系统调用会被VMM截获, 并转化为VMM对 HostOS 的系统调用 |
支持虚拟化的CPU通常将特权指令内部又分为多个层级