概念(定义)
- 什么是操作系统
功能和目标
- 操作系统要做些什么?
一台电脑的诞生
Step1:厂家组装一台裸机
裸机(纯硬件):包含CPU、内存、硬盘、主板等
Step2:出售前安装操作系统
Step3:用户安装应用程序(如:QQ)
Step4:使用QQ聊天
最底层的是裸机、纯硬件,在裸机上面安装一层操作系统,在操作系统之上我们可以安装一系列的应用软件,用户是处于最上面一层的。
这张图中,应用程序和操作系统相连,是因为应用程序能够通过系统调用和操作系统进行交互。用户和操作系统相连,是因为用户可以通过GUI、命令接口和操作系统进行交互。(下文有具体讲解)
至此,给出操作系统的定义:(来自王道资料书)
操作系统(Operating System,OS)是指控制和管理整个计算机系统的硬件和软件资源,并合理地组织调度计算机的工作和资源的分配;以提供给用户和其他软件方便的接口和环境;它是计算机系统中最基本的系统软件。
这段话想表达的是:
①操作系统是系统资源的管理者。
直观的例子,如,我们打开Windows操作系统的“任务管理器”,能够看到
②向上层提供方便易用的服务。
③是最接近硬件的一层软件。
- 提供的功能
- 处理机管理
- 存储器管理
- 文件管理
- 设备管理
- 目标
- 安全、高效
用QQ和朋友视频聊天的过程
Step1:在各个文件夹中找到QQ安装的位置(如D:/Tencent/QQ/Bin)
通过一层一层的文件夹(目录),找到了QQ的启动程序QQ.exe,但是除了这个可执行文件外,目录下还有很多其他文件,而对于这些文件的组织和管理,是操作系统来帮我们完成的。
这就涉及到了操作系统作为系统资源的管理者,所要提供的文件管理的功能。
Step2:双击打开QQ.exe
执行一个程序前需要将该程序放到内存中,才能被CPU处理。
我们在双击完QQ.exe后,操作系统帮我们做了一个很重要的事情,把我们的数据从磁盘放到了内存中,具体放在什么位置,都是由操作系统帮我们完成的。
这一步就涉及到操作系统的另一个功能:存储器管理(或者叫主存管理、内存管理)
Step3:QQ程序正常运行
在QQ程序运行的过程中,是需要经过CPU(处理机)进行处理的,而什么时候给这个进程分配处理机资源,这个事情是操作系统在背后为我们完成的。
这就涉及到操作系统的又一个功能:处理机管理。
Step4:开始和朋友视频聊天
在视频聊天的过程中,肯定要把摄像头这一设备分配给QQ使用,因此,像摄像头这一类设备资源,也需要操作系统来帮助我们管理,由操作系统来决定把哪一设备分配给哪个进程使用。
这就又涉及到设备管理。
- 直接给用户使用的
- GUI(图形用户界面)
- 命令接口(用户可直接使用)
- 联机命令接口(说一句做一句)
- 脱机命令接口(说一堆做一堆)
- 给软件/程序员使用的
- 程序接口(即“系统调用”,用户通过程序间接使用)
【注】在有的教材中,命令接口与程序接口被统称为“用户接口”,即狭义的用户接口不包括GUI。
硬件只听得懂二进制指令,如:01010111101110111010101。
硬件对外暴露了“丑陋”、不友好的交互接口。
通过刚才的分析可知,计算机的最底层是裸机(纯硬件),最底层的这些硬件,它只听得懂二进制指令,即机器语言。也就是说,如果我们直接和这些硬件打交道的话,那么我们只能用二进制的语言和这些硬件进行交互。所以我们说,最底层的纯硬件,它对外暴露出的,是不友好的、“丑陋的”交互接口,正常的人类很难和这些纯硬件直接来打交道。
在硬件之上安装了操作系统,操作系统对外暴露了“美丽”、友好的交互接口。
但是在硬件之上,安装的操作系统,会向它的上层提供一种更美丽、更友好的交互接口。在安装了操作系统之后,用户/应用程序不需要直接和硬件打交道,而只需要告诉操作系统想要得到的服务就可以。操作系统会把这些命令/需求翻译成二进制,来告诉硬件,然后由硬件执行操作。
封装思想:操作系统把一些丑陋的硬件功能封装成简单易用的服务,使用户能更方便地使用计算机。用户无需关心底层硬件的原理,只需要对操作系统发出命令即可。
这其实是一种典型的封装思想。这种封装思想在生活当中也很普遍,比如一辆汽车,如果我们把它拆解了的话,里面有很多很复杂的构件,但是汽车的设计师们会把这些底层复杂的构造隐藏起来,对外只暴露用户能够理解的、简单易用的接口(方向盘、刹车、油门……),但是用户不需要关注它底层是怎么实现这些功能的(转向、加速、减速……)。而操作系统对底层硬件的封装,其实和汽车的这种封装本质上没有区别。
刚刚说操作系统对底层进行封装,向上层提供方便易用的服务。那么提供了哪些服务呢?
用户可以使用形象的图形界面进行操作,而不再需要记忆复杂的命令、参数。
例如,在Windows操作系统中,删除一个文件只需要把文件“拖拽”到回收站即可。但这一简单的“拖拽”动作背后,操作系统其实做了很多事情。
很多现代操作系统都提供GUI,而一些早期的操作系统是并没有这些图形化界面的。早期的操作系统都是让用户通过命令接口的方式和操作系统进行交互,也就是用命令行的方式和操作系统进行交互。
命令接口又分为两种:联机命令接口、脱机命令接口。
联机命令接口实例(Windows系统)
联机命令接口 = 交互式命令接口。
特点:用户说一句,系统跟着做一句。
Step1:win键+R
Step2:输入cmd,按回车,打开命令解释器
Step3:尝试使用time命令
time命令的功能是显示当前时间,并允许用户输入一个新时间来修改时间,是一个典型的交互式命令,整个过程,用户是在和操作系统不断进行交互的。
脱机命令接口实例(Windows系统)
脱机命令接口 = 批处理命令接口
特点:用户说一堆,系统跟着做一堆。
使用Windows系统的搜索功能,搜索C盘中的*.bat
文件,用记事本任意打开一个。
虽然其内容看起来很复杂,但本质上和刚才我们使用的time命令没有区别。它只不过是把一系列的命令都罗列成了一个清单,当我们执行这个.bat
文件的时候,操作系统就会根据这个文件当中的命令,一条一条地执行。我们使用这种脱机命令接口(批处理命令接口)和操作系统进行交互的话,我们其实是一次提出一堆的请求,操作系统根据我们的指示,一条一条地执行这一堆的请求。这也是这种命令接口被称为“批处理”命令接口的原因。
到此我们讲了三种接口:GUI、联机命令接口、脱机命令接口,都是可以让普通用户直接使用的。操作系统还有一种对外提供的接口,叫做程序接口,这种接口是给我们程序员使用的。
程序接口:可以在程序中进行系统调用来使用程序接口。普通用户不能直接使用程序接口,只能通过程序代码间接使用。
如:写C语言“Hello world”程序时,虽然我们只利用简单的printf函数就实现了,但是在其底层一定使用到了操作系统提供的显示相关的“系统调用”。
程序员在写C语言时,调用C语言库函数,而库函数的底层使用到了一系列系统调用,操作系统在接收到了系统调用后,就会让底层硬件执行相关功能。系统调用类似于函数调用,是应用程序请求操作系统服务的唯一方式。
在有的教材中,系统调用,又会被称为广义指令。
需要实现对硬件机器的扩展。
没有任何软件支持的计算机称为裸机。在裸机上安装的操作系统,可以提供资源管理功能和方便用户的服务功能,将裸机改造成功能更强、使用更方便的机器。
通常把覆盖了软件的机器称为扩充机器,又称之为虚拟机。
这些概念无关紧要,更重要的是要理解:为什么操作系统能够实现对硬件机器的扩展?
类比汽车:
发动机——只会转;轮胎——只会滚。
在原始的硬件机器上覆盖一层传动系统——让发动机带着轮子转——使原始的硬件机器得到拓展。
操作系统对硬件机器的拓展:将CPU、内存、磁盘、显示器、键盘等硬件合理地组织起来,让各种硬件能够相互协调配合,实现更多更复杂的功能。
操作系统的概念、功能和目标
操作系统的四个特征
- 并发
- 共享
- 并发和共享是两个最基本的特征,二者互为存在条件(原因见下文)
- 虚拟
- 异步
并发:指两个或多个事件在同一时间间隔内发生。这些事件宏观上是同时发生的,但微观上是交替发生的。
并行:指两个或多个事件在同一时刻同时发生。
个人理解:并行是“真的”同时发生,并发是“假的”同时发生。
操作系统的并发性是指计算机系统中“同时”运行着多个程序,这些程序宏观上看是同时运行着的,而微观上看是交替运行的。
为什么说并发性对于操作系统是一个很重要的基本特性?
操作系统就是伴随着“多道程序技术”而出现的。因此,操作系统和程序并发是一起诞生的。
单核CPU同一时刻只能执行一个程序,各个程序只能并发地执行。
多核CPU同一时刻可以同时执行多个程序,多个程序可以并行地执行。
比如Intel的第八代i3处理器就是4核CPU,意味着可以并行地执行4个程序。
但是,如果已经运行了4个程序,还想再运行第5个程序时,第5个程序就要剥夺原有程序占用的CPU资源,轮流地使用CPU,就仍需要并发性。
因此,即使是对于4核CPU来说,只要有4个以上的程序需要“同时”运行,那么并发性依然是必不可少的,因此,并发性是操作系统一个最基本的特性。
共享即资源共享,是指系统中的资源可供内存中多个并发执行的进程共同使用。
资源共享可以分为两种共享方式
互斥共享方式
同时共享方式
此处的“同时”为什么要打双引号,因为此“同时”往往是宏观上的,而在微观上,这些进程可能是交替地对该资源进行访问的(即分时共享)。但是也有微观上同时访问的情况,例如扬声器同时播放游戏音效和音乐播放器中的歌曲,从微观上就是同时的。
【生活实例】
互斥共享方式:使用QQ和微信视频。同一时间段内摄像头只能分配给其中一个进程。
同时共享方式:使用QQ发送文件A,同时使用微信发送文件B。宏观上看,两边都在同时读取并发送文件,说明两个进程都在访问硬盘资源,从中读取数据。微观上看,两个进程是交替着访问硬盘的。
并发性是指计算机系统中同时存在着多个运行着的程序。
共享性是指系统中的资源可供内存中多个并发执行的进程共同使用。
通过一个例子来看并发与共享的关系:
使用QQ发送文件A,同时使用微信发送文件B。
1、两个进程正在并发执行(并发性)
2、需要共享地访问硬盘资源(共享性)
如果失去并发性,则系统中只有一个程序正在运行,则共享性失去存在的意义。
如果失去共享性,则QQ和微信不能同时访问硬盘资源,就无法实现同时发送文件,也就无法并发。
因此,并发性、共享性,互为存在条件。
虚拟是指把一个物理上的实体变为若干个逻辑上的对应物。物理实体(前者)是实际存在的,而逻辑上对应物(后者)是用户感受到的。
例如,一款游戏需要4G运行内存,QQ需要256M运行内存,网易云音乐需要256M运行内存……
我的电脑:4G运行内存
问题:这些程序同时运行需要的内存大于4G,为什么它们还可以在我的电脑上同时运行?
答:这是虚拟存储器技术。物理实体只有4GB,但在用户看来似乎远远大于4G。这也是虚拟技术中的“空分复用技术”。
例如,某单核CPU的计算机中,用户打开了若干软件:QQ、Chrome浏览器、QQ音乐、迅雷……
问题:既然一个程序需要上CPU才能正常执行,那么为什么单核CPU的电脑中能同时运行这么多个程序呢?
答:这是虚拟处理器技术。实际上只有一个单核CPU,但在用户看来似乎有多个CPU在同时为自己服务。这也是虚拟技术中的“时分复用技术”,微观上处理机在各个微小的时间段内交替着为各个进程服务。
(注:一个程序需要放入内存并给它分配CPU才能执行。)
虚拟技术
显然,如果失去了并发性,则一个时间段内系统中只需运行一道程序,那么就失去了实现虚拟性的意义了。因此,没有并发性,就谈不上虚拟性。
异步是指,在多道程序环境下,允许多个程序并发执行,但由于资源有限,进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进,这就是进程的异步性。
由于并发运行的程序会争抢着使用系统资源,而系统中的资源有限,因此进程的执行不是一贯到底的,而是走走停停,以不可预知的速度向前推进。
如果失去了并发性,即系统只能串行地运行各个程序,那么每个程序的执行会一贯到底。因此,只有系统拥有并发性,才有可能导致异步性。
同步与异步
同步:同步是指一个进程在执行某个请求的时候,如果该请求需要一段时间才能返回信息,那么这个进程会一直等待下去,直到收到返回信息才继续执行下去。
异步:异步是指进程不需要一直等待下去,而是继续执行下面的操作,不管其他进程的状态,当有信息返回的时候会通知进程进行处理,这样就可以提高执行的效率了,即异步是我们发出的一个请求,该请求会在后台自动发出并获取数据,然后对数据进行处理,在此过程中,我们可以继续做其他操作,不管它怎么发出请求,不关心它怎么处理数据。
补充理解
同步是指两个进程的运行是相关的,其中一个进程要阻塞等待另外一个进程的运行。“同”是协同,按照一定的顺序有序进行,而不是同时。即一组进程为了协调其推进速度,在某些地方需要相互等待或者唤醒,这种进程间的相互制约就被称作是进程同步。这种合作现象在操作系统和并发式编程中属于经常性事件。具有同步关系的一组并发进程称为合作进程。
异步的意思是两个进程毫无相关(不用互相等待),自己运行自己的。进程以不可预知的速度向前推进。内存中的每个进程何时执行,何时暂停,以怎样的速度向前推进,每道程序总共需要多少时间才能完成等,都是不可预知的。
OS的发展与分类
- 手工操作阶段
- 批处理阶段
- 单道批处理系统
- 多道批处理系统(操作系统开始出现)
- 分时操作系统
- 实时操作系统
- 网络操作系统
- 分布式操作系统
- 个人计算机操作系统
【注】要重点关注和理解各类操作系统主要想解决的是什么问题,各自的优缺点。
在这个阶段,操作系统还并没有诞生。程序员写程序,还是要写到纸带上的。可以看到,纸带上有小孔,有孔的地方代表1,没孔的地方代表0。程序员通过在纸带上打孔的方式写好自己的程序,然后把这个程序放到纸带机上。然后计算机从纸带机当中读取我们要运行的程序,等运行结束以后,又把程序运行的结果输出到纸带机上,之后再由程序员取走程序运行的结果。
但是,用手工操作的方式,存在一个很明显的问题。
程序员用手工的方式把纸带装到纸带机,或者从纸带机上取下带有程序运行结果的纸带的过程,是非常慢的。
此外,计算机对纸带机上的纸带进行读取的过程,也需要花费不少的时间。
但是,计算机对已经读好的程序进行处理的过程,是非常快的。
手工操作阶段存在的主要缺点就是:
用户独占全机。第一个程序员在没有使用计算机处理程序并取走结果之前,第二个程序员是无法使用计算机的,所以当一个用户使用计算机的时候,他就是独占地使用。
由于手工操作是一个很慢的过程,但是计算机的计算又是个很快的过程,这样的人机速度矛盾会导致资源的利用率极低,特别是计算机CPU的利用率极低(由上图即可观之)。在那个年代计算机的造价还是很昂贵的,使得计算机这样昂贵的资源大量的时间处于空闲状态,显然是一种很不经济、很低效的方式。
为了解决手工操作存在的问题,人们引入了单道批处理系统。
引入脱机输入/输出技术(用外围机+磁带完成),并由监督程序负责控制作业的输入、输出。
各个程序员都可以把自己的程序依次放到纸带机上,会有一个叫“外围机”这样专门的机器,控制着把这些纸带机里的程序先放到磁带上。之后,计算机可以直接从磁带里读写这些数据。而计算机对磁带的读写速度,比纸带机快很多。
此时计算机当中会有一个监督程序的程序,自动地控制着对磁带的输入、输出。
引入这种脱机输入输出技术后,我们读取一个作业所花费时间的比例就小了许多,就可以让CPU有更高比例的时间处于计算、处于忙碌的状态。在一定程度上缓解了人机速度矛盾。
但是资源利用率虽然相比于手工操作有所提高,但依然是很低的。内存中同一时刻还是只能有一道程序运行,只有当这个程序运行结束并且输出完成之后,才可以继续读入第二个程序,各个程序之间是串行地执行的。这种方式依然会使CPU有大量的时间是在空闲等待的状态,资源利用率依然的很低的。
为了解决这一问题,人们就发明了多道批处理系统。在这一阶段,操作系统就真正诞生了。
首先,第一个作业的数据会从磁带输入到计算机当中,输入结束之后,就可以开始对这个程序的计算工作;
但是,当CPU在计算第一个作业的时候,其实输入设备是处于空闲状态的,因此,CPU计算第一个作业的时候,其实可以让输入设备同时把第二个作业也读入内存;
紧接着,第一个作业计算完成,第一个作业就可以通过输出设备,把计算结果输出到磁带上;
而第一个作业在输出的时候,其实只是输出设备在忙碌,在这时,CPU已经空闲了,所以在这个时间段可以让CPU为第二个作业服务,开始计算第二个作业;
同时,在这个时候,输入设备其实又开始空闲了,因此又可以同时开始读入第三个作业……
总之,在引入多道程序技术之后,多道程序可以并发地执行,共享计算机当中的资源(输入设备、输出设备、CPU等)。CPU和其他资源更能保持“忙碌”状态,资源利用率大幅度提升,系统吞吐量增大。
不过,多道批处理系统并没有提供人机交互的功能,当一个用户把自己的作业提交了之后,接下来用户就只能干等着计算机把自己的作业处理完成。也就是说,在多道批处理系统中,用户是无法调试自己的程序的,也不可能在程序运行的过程中输入一些参数。
为了实现人机交互的功能,人们又发明了分时操作系统。
分时操作系统:计算机以时间片为单位轮流为各个用户/作业服务,各个用户可通过终端与计算机进行交互。
(比如在上图中,计算机按照顺时针的顺序,依次为每个用户服务50ms,不断轮转)
因此,在分时操作系统中,用户请求可以被即时响应,解决了人机交互问题。允许多个用户同时使用一台计算机,并且用户对计算机的操作相互独立,感受不到别人的存在。
分时操作系统也存在一些缺点,它不能优先处理一些紧急的任务。操作系统对每个用户/作业都是完全公平的,循环地为每个用户/作业服务一个时间片,不区分任务的紧急性。
所以,为了让计算机能够优先地处理一些紧急任务,人们又提出了实时操作系统。
实时操作系统,能够根据任务的优先级,来优先地响应一些紧急任务,某些紧急任务不需时间片排队。
在实时操作系统的控制下,计算机接收到一些紧急的信号之后,需要进行及时处理,并且要在严格的时限内处理完成,还要保证处理的结果是正确的。因此,这种操作系统的主要特点是及时性和可靠性。
实时操作系统又分为硬实时和软实时:
除了刚才我们说的几种操作系统之外,还有网络操作系统、分布式操作系统、个人计算机操作系统。这些操作系统简单了解一下就可以,此处不再展开。
操作系统的运行机制,也就是操作系统在计算机上是怎样运行的问题。
操作系统的运行机制
- 两种指令
- 特权指令
- 非特权指令
- 两种处理器状态
- 核心态
- 用户态
- 两种程序
- 内核程序
- 应用程序
我们平时写的C语言代码,要先经过编译器“翻译”为机器能读懂的机器指令(二进制编码)。
C语言写的简单的2行代码,编译成机器指令,就会形成多条机器指令(此处是瞎编的01编码,反正都看不懂)。
虽然我们看不懂这些二进制编码,但是,对于CPU来说,对于计算机来说,这样的二进制代码才是它能够看得懂的语言。
程序运行的过程其实就是CPU执行一条一条的机器指令的过程。
这里提到的“指令”(机器指令),就是能让CPU处理、识别的最基本命令。(比如让CPU执行一个加法操作、执行一个赋值操作等等)
很多人习惯把Linux、Windows的“小黑框”中使用的命令(比如ls、mkdir)也称为“指令”,其实这是“交互式命令接口”。注意与此处的“指令”区别开。此处的“指令”指的是二进制机器指令。
应用程序就是平时跑在操作系统之上的,我们平时很熟悉的、很喜欢用的那些程序,比如QQ、微信等。
我们普通程序员写的程序就是“应用程序”,是跑在操作系统之上的。
但是还有一批人,比如微软、苹果,开发操作系统的这帮人写的程序,就是操作系统的内核程序。这帮人写了很多内核程序,这么多内核程序,最终组合成了操作系统内核,或简称“内核(Kernel)”。
内核是操作系统最重要最核心的部分,也是最接近硬件的部分。
之前说过,操作系统最重要的一个角色,是他要作为系统资源的管理者,而操作系统对系统资源的管理工作其实就是在内核部分实现的。甚至可以说,一个操作系统只要有内核就够了。(例如:Docker→仅需Linux内核)
用户平时使用到的操作系统的功能,并不是都在内核中的,例如图形化用户界面GUI。即使没有GUI,我们依然可以通过命令行的方式来使用操作系统。所以操作系统的内核中包含的是操作系统的最重要、最核心、最必不可少的那些功能。
既然操作系统内核是系统资源的管理者,那么它作为管理者这样一个角色,有的时候就有可能让CPU执行一些比较特殊的指令,比如“内存清零指令”。这种指令会对其他程序的运行造成很严重的影响,因此,像这样的“特权指令”,就只允许“管理者”——即操作系统内核来使用。
假如CPU要执行的一段程序是内核程序,那这些指令当中,可以出现特权指令。
如果这段程序是普通的应用程序,那么,这其中就不应该包含特权指令,而只能使用非特权的指令,如:加减乘除运算指令。
CPU在设计和生产时,就已经划分了哪些指令是特权指令、哪些指令是非特权指令,因此CPU在拿到一条指令的时候,它就可以区分出这到底是特权指令还是非特权指令了。
此时有一个问题:CPU能判断出指令类型,但是它怎么区分此时正在运行的是内核程序还是应用程序?
为了让CPU能够区分此时运行的程序是内核程序还是应用程序,CPU被划分为两种状态:“内核态”和“用户态”。
处于内核态时,说明此时正在运行的是内核程序,此时可以执行特权指令。
处于用户态时,说明此时正在运行的是应用程序,此时只能执行非特权指令。
怎么看CPU当前到底处于哪种状态呢?
CPU中会有一个寄存器叫程序状态字寄存器(PSW),其中有个二进制位,1表示“内核态”,0表示“用户态”。(有的CPU也可能1和0表示状态是反过来的,这无所谓了,知道通过这个方法对CPU状态能够进行识别就可以)
别名:内核态 = 核心态 = 管态; 用户态 = 目态。
接下来,要探讨的问题是:CPU怎么实现这两个状态之间的切换?
一个例子:
①刚开机时,CPU为“内核态”,操作系统内核程序先上CPU运行。比如系统初始化的工作就是由内核当中的一些程序来完成的,所以此时也肯定处于内核态。
②开机完成后,用户就可以启动某应用程序。但是问题是,CPU还正处于内核态,在执行内核程序中的指令,怎么办?此时,如果操作系统的内核,它想让用户的应用程序开始运行的话,那么这个内核程序就需要执行一条特权指令,这个特权指令会把PSW的标志位,从内核态转变为用户态,就完成了CPU状态的切换。
③操作系统内核程序会在合适的时候主动让出CPU,让该应用程序上CPU运行,并切换至“用户态”。
④应用程序运行在“用户态”。CPU会执行该程序一条条的指令。
⑤此时,如果该程序出现意外,例如一个黑客在该应用程序中植入了一条特权指令,企图破坏系统。
⑥CPU其实在读入这条指令的时候,它就已经知道,这条指令是一条特权指令,但是CPU又根据自己的PSW寄存器,发现自己是处于“用户态”。因此,CPU目前知道这是一个应用程序,想要执行的是一条特权指令,那么这种事情是坚决不能进行的,是一个非法事件。
⑦这个非法事件会引发一个中断信号。当CPU检测到中断信号后,会立即强行变为“核心态”,同时拒绝执行这条特权指令,并停止运行当前的应用程序,转而会执行一个处理中断信号的内核程序。
⑧“中断”使操作系统再次夺回CPU的控制权。
⑨内核程序会对这一“中断”进行相应处理,等处理完后,它才会把CPU的使用权再重新还给别的应用程序。
可以看出,CPU从内核态→用户态,其实是修改了一下PSW的标志位,通过执行一条特权指令把PSW的标志位置为“用户态”,这个动作意味着操作系统将主动让出CPU使用权。之后,CPU上就可以运行应用程序。
而CPU从用户态→内核态,是由“中断”引发的,硬件自动完成变态过程,触发中断信号意味着操作系统将强行夺回CPU的使用权。
除了非法使用特权指令之外,还有很多事件会触发中断信号。但有一个共性是,但凡需要操作系统介入的地方,都会触发中断信号。
- 中断的作用
- 中断的类型
- 内中断(也称“异常”)
- 外中断
- 中断机制的基本原理
CPU上会运行两种程序,一种是操作系统内核程序,一种是应用程序。
内核是整个系统的管理者,在计算机刚启动的时候,运行的是内核程序。在合适的情况下,操作系统内核会把CPU的使用权主动让给应用程序。(具体见第二章进程管理相关内容)
“中断”是让操作系统内核夺回CPU使用权的唯一途径。“中断”会使CPU由用户态变为内核态,使操作系统重新夺回对CPU的控制权。
而一个应用程序如果运行,则它会一直运行下去,除非发生了中断。可想而知,如果没有“中断”机制,那么一旦应用程序上CPU运行,CPU就会一直运行这个应用程序。而如果CPU一直都只是在运行1个应用程序的话,又何来“并发”?
所以,没有中断技术,就没有多道程序并发。甚至可以说,没有中断技术就没有操作系统。
总之,操作系统内核是一个管理者,当它想要把CPU使用权让给应用程序的时候,它会自愿地用一个特权指令来完成这件事情。但是,当它想要把CPU的使用权重新夺回来的时候,它就要通过“中断”的方式来实现。
内核态→用户态:执行一条特权指令——修改PSW的标志位为“用户态”,这个动作意味着操作系统将主动让出CPU使用权。
用户态→内核态:由**“中断”**引发,硬件自动完成变态过程,触发中断信号意味着操作系统将强行夺回CPU的使用权。
内中断(也称异常、例外)
与当前执行的指令有关,中断信号来源于CPU内部
陷阱、陷入(trap)
由陷入指令引发,是应用程序故意引发的。
故障(fault)
由错误条件引起的,可能被内核程序修复。内核程序修复故障后会把CPU使用权还给应用程序,让它继续执行下去。如:缺页故障。
终止(abort)
由致命错误引起,内核程序无法修复该错误,因此一般不再将CPU使用权还给引发终止的应用程序,而是直接终止该应用程序。如:整数除0、非法使用特权指令。
外中断(也称“中断”)
与当前执行的指令无关,中断信号来源于CPU外部
- 时钟中断
- I/O中断请求
注:在很多地方,“内中断”一般称为异常,“外中断”一般直接称作中断。所以如果说一个狭义的中断往往就是指的外中断,而如果对“中断”进行细分,再谈内外中断。
内中断
若当前执行的指令是非法的(指令本身是非法的/指令的参数是非法的),则会引发一个中断信号。
例子1:应用程序试图在用户态下执行特权指令,指令本身是非法的,CPU在执行其程序指令的过程中,会拒绝执行并发出中断信号,转变为内核态进而执行一段处理中断信号的内核程序。
例子2:执行非特权指令时也会有问题,比如执行除法指令时发现除数为0,也会引发一个内中断,指令的参数是非法的。
例子3:有时候应用程序想要请求操作系统内核的服务,此时会执行一条特殊的指令——陷入指令,该指令会引发一个内部中断信号。(陷入指令是特殊的非特权指令,不是特权指令,毕竟它是在用户态下由应用程序发出的)
执行“陷入指令”,意味着应用程序主动地将CPU控制权还给操作系统内核。“系统调用”就是通过陷入指令完成的。
外中断
例子1:时钟中断——由时钟部件发来的中断信号。
计算机会有一个时钟部件,时钟部件每隔一个时间片(如50ms)会给CPU发送一个时钟中断信号。
如应用程序1正在CPU上执行一条条指令时,当执行时间到达50ms,就会暂停运行(只是先暂停一下,如果一会儿它又上CPU了,那么会接着刚才的继续运行),由用户态转为内核态并执行一个内核程序来处理时钟中断信号,并由内核决定接下来该让哪一个应用程序接着上CPU运行。
例子2:I/O中断——由输入/输出设备发来的中断信号。
如某程序向打印机请求打印服务,则打印机在打印完成后会向CPU发送中断信号,用来通知CPU,输入输出任务已经完成了,接下来CPU会转而执行内核程序来处理I/O中断信号。
总之,这两种中断信号,都来自CPU的外部,和当前CPU上运行的指令内容是没有关系的。CPU在每一条指令执行结束的时候,都会例行检查是否有外中断信号。
刚才我们举的例子当中,有非法指令的中断、I/O中断、除数为0中断等等,每一个中断都对应内核当中不同的中断处理程序。那么CPU是怎么知道自己该执行哪种中断程序的呢?
**不同的中断信号,需要用不同的中断处理程序来处理。**当CPU检测到中断信号后,会根据中断信号的类型去查询“中断向量表”,以此来找到相应的中断处理程序在内存中的存放位置。
显然,中断处理程序一定是内核程序,需要运行在“内核态”。
没有中断机制,就不可能实现操作系统,不可能实现程序并发。
- 什么是系统调用?
- 系统调用与库函数的区别
- 小例子:为什么系统调用是必须的?
- 什么功能要用系统调用实现?
- 系统调用的过程
操作系统作为用户和计算机硬件之间的接口,需要向上提供一些简单易用的服务。主要包括命令接口和程序接口。其中,程序接口由一组系统调用组成。
“系统调用”是操作系统提供给应用程序(程序员/编程人员)使用的接口,可以理解为一种可供应用程序调用的特殊函数,应用程序可以通过系统调用来请求获得操作系统内核的服务。
系统调用和库函数的调用有区别。平时我们写程序时,可以用汇编语言的方式来直接请求系统调用服务。但是现在的编程更多的是使用高级语言编程,我们会直接使用高级语言的库函数,但是这些高级语言的库函数,在底层也会用到操作系统提供的系统调用功能,来请求操作系统的服务。
系统调用,应该是比高级语言的库函数更为底层的接口。
普通应用程序 | 可直接进行系统调用,也可使用库函数。有的库函数涉及系统调用,有的不涉及 |
---|---|
编程语言 | 向上提供库函数。有时会将系统调用封装成库函数,以隐藏系统调用的一些细节,使程序员编程更加方便。 |
操作系统 | 向上提供系统调用,使得上层程序能请求内核的服务。 |
裸机 |
不涉及系统调用的库函数:如“取绝对值”的函数
涉及系统调用的库函数:如“创建一个新文件”的函数
生活场景:你去学校的打印店打印论文,你按下了WPS的“打印”选项,打印机开始工作。
你的论文打印到一半时,另一位同学按下了Word的“打印”按钮,开始打印他自己的论文。
思考:如果两个进程可以随意地、并发地共享打印机资源,会发生什么情况?
两个进程并发运行,打印机设备交替地收到WPS和Word两个进程发来的打印请求,结果两篇论文的内容混杂在一起了。这显然不是我们想要的结果。
由于系统当中有各种各样的并发的进程,而这些并发的进程又需要共享地使用类似于打印机设备这样的共享资源,但是这样的共享资源其实是需要各个进程互斥地共享的。
那么怎么实现对共享资源的互斥访问呢?最好的方式就是让操作系统内核来对共享资源进行统一的管理。上层的那些应用程序只能通过“系统调用”的方式,来请求操作系统,给它分配这种资源,之后,这个进程才可以对这种资源进行使用和访问。而各个进程的请求会由操作系统内核来协调处理,保证它们并发运行的时候不会发生这种混乱的事情。
解决方法:由操作系统内核对共享资源进行统一的管理,并向上提供“系统调用”,用户进程想要使用打印机这种共享资源,只能通过系统调用向操作系统内核发出请求。内核会对各个请求进行协调处理。
因此,系统调用是必须的。
王道资料按照功能划分,分为了以下五种:
系统调用(按功能分类)
设备管理
完成设备的 请求/释放/启动 等功能
文件管理
完成文件的 读/写/创建/删除 等功能
进程控制
完成进程的 创建/撤销/阻塞/唤醒 等功能
进程通信
完成进程之间的 消息传递/信号传递 等功能
内存管理
完成内存的 分配/回收 等功能
拓展:可以搜索“Linux系统调用”,了解Linux操作系统提供了哪些系统调用。
总之,只要是对共享资源的访问,那肯定是需要系统调用来进行,因为这些共享资源是有限的(如:内存、I/O设备、文件),所以操作系统会对这些共享资源进行统一的管理和分配,因此应用程序在使用这些资源的时候就必须通过系统调用的方式请求操作系统内核来帮它进行接下来的处理。
应用程序通过系统调用请求操作系统的服务。而系统中的各种共享资源都由操作系统内核统一掌管。因此凡是与共享资源有关的操作(如存储分配、I/O操作、文件管理等),都必须通过系统调用的方式向操作系统内核提出服务请求,由操作系统内核代为完成。这样可以保证系统的稳定性和安全性,防止用户进行非法操作。
一个应用程序运行在用户态,这个程序的各个指令会依次地被CPU执行。
当它想要发出系统调用的时候,它需要用传参数的指令,给CPU的寄存器当中传递一些必要的参数。比如在某寄存器当中传入了参数1
,参数1
的作用是:指明本次将要发出的系统调用是什么类型(如Linux系统中的“fork”,我指明一会儿要做的就是它)。而传递参数的指令可能会有多条(如图,还有参数2
),主要要看这次系统调用需要传递几个参数。而操作系统会根据应用程序提供的这些参数,来判断它想要的到底是哪种类型的服务。
当这些参数都放到了寄存器当中之后,应用程序就会执行一条特殊的指令,叫作陷入指令。这个陷入指令的执行,会引发一个内中断。CPU在检测到这个内部中断信号之后,它发现这个内部中断信号是由trap指令引起的,于是,CPU接下来就会暂停运行这个应用程序,转而去执行陷入指令处理程序
,即系统调用入口程序(可以理解为一种中断程序,只是由陷入程序引发的内中断)。【CPU转为内核态了】
接下来,系统调用程序会检查寄存器中的若干参数,最终知道这个程序想要调用的是哪种系统调用服务,于是,接下来,系统调用入口程序,就会调用特定的系统调用类型所对应的处理程序,然后让这个程序上CPU运行。
系统调用处理完毕后,CPU又会转为用户态,接着执行之前的应用程序。
传递系统调用参数→执行陷入指令(用户态)→执行相应内核程序处理系统调用(核心态)→返回应用程序
注意:
1、陷入指令是在用户态执行的,执行陷入指令之后立即引发一个内中断,使CPU进入核心态
2、发出系统调用请求是在用户态,而对系统调用的相应处理在核心态下进行。
- 大内核/单内核/宏内核
- 微内核
通过之前的学习,我们知道计算机系统的层次结构是这样的。
但是操作系统的内部其实还可以再进一步地划分。
一部分是内核的功能,一部分是非内核的功能。
操作系统最核心的功能,要放在内核当中。比如:时钟管理、中断处理、原语;进程管理、存储器管理、设备管理等等。这些功能都是要放在操作系统内核当中的。
时钟管理:利用时钟中断实现计时功能。
中断处理:略。
原语:原语是一种特殊的程序,具有原子性。也就是说,这段程序的运行必须一气呵成,不可被“中断”。要么就一气呵成的全部运行完成,要么就不运行。它的执行过程是不可被中断的,也就是在执行原语这一段程序的过程当中,即使有外部中断信号过来了,CPU也会继续把原语执行完成,然后才去处理那个外部中断信号。
总之,上图“内核”部分当中,最底层的那三个(时钟管理那一行),是与硬件结合最为紧密的,它们必须放在操作系统的内核当中。
Ubuntu、CentOS的开发团队,其主要工作是实现非内核功能,而内核都是用了Linux内核。
内核是操作系统最基本、最核心的部分。
实现操作系统内核功能的那些程序就是内核程序。
对于那三个最底层的功能,它们是与硬件结合最为紧密的,也必须放在操作系统内核当中。
而进程管理、存储器管理这些管理功能,不会直接涉及硬件,所以,有的操作系统并不把这些管理功能放到内核当中。而只在内核当中保留那些与硬件接触最紧密的部分。
因此,这就引出了两种截然不同的内核设计方法。
把所有的这些功能都包含在操作系统当中的,这种结构就叫大内核;
在内核当中只保留与硬件结合最紧密的这些部分,那么这种结构就叫做微内核。
注意:操作系统内核需要运行在内核态;操作系统的非内核功能运行在用户态。
需要注意的是,如果采用微内核结构的话。
属于内核的这些功能,是需要运行在内核态的。而不属于内核的,上面那些管理功能,就不属于内核,就要运行在用户态。
这会对我们系统的性能造成一定影响。
如何造成影响,我们用一个更直观的例子来体会。
现在,我们有两种体系结构的系统。
第一个系统,采用的是大内核的体系结构,那么,由于进程管理、存储管理等等这些功能都是被划分在内核当中的,所以这些功能的处理都需要运行在内核态,只有应用程序是运行在用户态的。
而如果采用微内核结构的操作系统,只有与硬件联系最紧密的这些被划分在了内核当中,只有这些功能是在内核态下才可以执行的,而其他的功能模块在用户态下就可以运行。
此时有这样一种情况。
假设,现在应用程序想要请求操作系统的服务,这个服务的背后需要涉及到进程管理、存储管理、设备管理这几个功能。
如果采用的是大内核的体系结构的话,那么应用程序向操作系统提出服务的请求,此时CPU会从用户态切换为核心态,然后开始运行这一系列的内核程序。
而如果采用的是微内核的体系结构的话,应用程序向操作系统提出服务的请求,接下来操作系统的这几个模块都需要为应用程序提供服务,而这几个模块之中,进程管理这一个模块在处理应用程序请求的时候,它同样也需要得到内核的支持(如时钟、中断等),所以在进程管理这一个模块服务的过程中,CPU还需要由用户态转为内核态,服务完成之后又会从内核态再转回用户态。同理,存储管理、设备管理这两个模块也是一样的,它们在执行相应工作的时候,同样也需要得到内核功能的支持。因此,每一个模块,都需要请求内核的服务,每一次都需要涉及到一次CPU状态转换的过程。
因此,如果采用的是大内核的体系结构,CPU只需要进行两次状态转变就可以了。
而如果采用的是微内核体系结构,整个过程的处理就需要有六次变态。
需要注意的是,CPU的状态转换,这件事情是有成本的,需要消耗不少的时间。因此,频繁地切换CPU的状态,是会降低系统性能的。
【注意】此处“变态”这个词只是我们为了方便表达,正确的说法应该是“CPU状态的转换”。
典型的 大内核/宏内核/单内核 操作系统:Linux、UNIX
典型的 微内核 操作系统:Windows NT
(了解即可,重要的是知道两种体系结构各自的优缺点)
(对于考研408而言)
这个章节主要以选择题形式考察。
总的来说,这个章节考察的深度、难度不会太大。另外,这个章节的分值占比是比较低的。
不过,对第一章的学习,有助于我们理解操作系统和普通的应用程序是怎么在电脑上有条不紊的运行的。特别是上图做了标记的3个部分,有助于理解操作系统的运行原理。
先说图中没有标记的几个小节:
第一个小节中,学习了操作系统的概念和功能。
需要回忆一下,操作系统向上层提供了哪些接口。包括它给用户提供了哪些接口;又给应用程序提供了哪些接口。
第二个小节中,学习了操作系统的发展历史。
单道批处理阶段、多道批处理阶段、分时操作系统和实时操作系统,这几个阶段,各自的优点和缺点是什么。
第三个小节中,学习了操作系统的特征。
分别是,并发、共享、异步和虚拟。操作系统的这些特征,随着时候的学习,肯定会理解的越来越深,不需要死记硬背。
最后一个小节中,学习了操作系统的两种体系结构。
分别是大内核和微内核。它们两个各自的优点和缺点分别是什么,这个也是需要回顾一下的知识。
图中做了标记的几个小节:(标了小红旗的)
在操作系统的运行机制这个小节中。
我们学到了,CPU上有可能会运行两种程序:一种是操作系统内核程序,一种是普通的应用程序。
操作系统内核程序是管理者,普通应用程序是被管理者。
所以,当CPU上正在运行操作系统内核程序的时候,我们可以说,此时CPU是在为管理者办事。而当CPU上面正在运行的是普通应用程序的时候,我们可以说此时CPU是正在为被管理者办事。
其中,只有当CPU正在为管理者办事的时候,它才会执行特权指令。
需要重点理解的是,在什么时候,CPU会从内核态转变为用户态;又在什么时候,CPU会从用户态转变为内核态?
当我们的电脑刚开机的时候,CPU上面跑的肯定是操作系统内核程序。也就是说,刚开始,CPU是在为管理者办事。但是,当时机成熟的时候,这个管理者会告诉CPU,接下来你应该去执行哪一个应用程序。
也就是说,从内核态转变为用户态这个过程,其实是操作系统的主动行为。当它希望把CPU的使用权让给应用程序的时候,CPU才会转向用户态。
什么时候CPU又需要从用户态转为内核态呢?当CPU处于用户态的时候,说明CPU此时正在为某一个被管理者办事、正在执行某一个应用程序。
在执行这个应用程序的过程当中,它有可能会接收到某一来自外部的中断信号,当它收到这个外部中断信号的时候,就会暂停执行应用程序,立即转为执行操作系统的某一内核程序。或者,在执行这个应用程序的过程当中,发生了一些特殊的情况,也就是异常,当CPU在执行应用程序的指令的过程中检测到了异常,它也会立即停止执行当前的应用程序,转而执行某一操作系统内核程序。
即,**当发生了中断或异常的时候,会促使CPU由用户态又转变回内核态。**也就是CPU会被重新召回、为管理者办事。
这种运行机制即使并没有特别高深。在中国古代已经有了类似这样的运行机制了,就像古代的皇帝、太监、大臣。
管理者与被管理者
异常的情景
系统调用的情景
中断(专指外中断)的情景
所以,其实操作系统的运行机制的设计思想离我们并不遥远(虽然看起来抽象)。要先在宏观上理解操作系统和应用程序是怎样有条不紊地上CPU运行,它们对CPU的运行权是怎样相互交接的。之后再不断地巩固、加深理解其背后的具体细节。