【第一章】 操作系统的概述

devtools/2025/2/8 9:08:23/

目录

零、前言 

0.1 考纲内容

0.2 考情统计 

 0.3 考点解读

 0.4 复习建议

一、操作系统的基本概念

1.1 操作系统的概念

1.1.1 电脑的诞生过程

1.1.2 操作系统的定义

1.2 操作系统的功能

1.2.1 QQ聊天引入 

1.2.2 处理器管理的功能

1.2.3 存储器管理的功能

1.2.4 文件管理的功能

1.2.5 设备管理的功能

1.2.6 GUI

1.2.7 联机命令接口

1.2.8 脱机命令接口

1.2.9 程序接口

1.2.10 相关问题总结 

1.3 操作系统的目标

1.4 操作系统的特征

​1.4.1 并发

1.4.2 共享

1.4.3 虚拟

1.4.4 异步

1.5 总结

二、操作系统的发展历程

2.1 操作系统的分类和发展

2.1.1 手工操作阶段

2.1.2 单道批处理系统

2.1.3 多道批处理操作系统

2.1.4 分时操作系统

2.1.5 实时操作系统

2.1.6 其他操作系统

2.2 总结

三、操作系统的运行环境

3.1 操作系统的运行机制

3.1.1 程序在计算机硬件上底层运行机制

3.1.2 内核程序vs应用程序

3.1.3 特权指令vs非特权指令

3.1.4 内核态vs用户态

3.1.5 时钟管理

3.1.6 原语

3.1.7 总结

3.2 中断和异常

3.2.1 中断的作用

3.2.2 中断的类型

3.2.1 内中断案例一

3.2.2 内中断案例二

3.2.3 内中断案例三

3.2.4 外中断案例一

3.2.5 外中断案例二

3.2.6 外中断案例三

3.2.8 中断机制总结

3.2.3 中断的分类

3.2.4 中断机制背后的基本原理

3.2.5 中断与异常的处理过程

3.2.6 总结

3.3 系统调用

3.3.1 什么是系统调用?

3.3.2 小例子:为什么系统调用是必须的?

3.3.3 什么功能要用到系统调用

3.3.4 系统调用的过程

3.3.5 总结

3.4 中国古代的操作系统

3.4.1 操作系统的运行机制

3.4.2 特权指令和非特权指令

3.4.3 内中断

3.4.4 外中断

3.4.5 系统调用

四、操作系统的体系结构

4.1 层次结构划分

4.1.1 划分层次

4.1.2 微内核结构与大内核结构的区别

4.1.3 大内核和小内核的变态区别

4.1.4 总结

4.2 分层结构的操作系统结构

4.2.1 特性、思想

4.2.2 两个优点

4.2.3 两个缺点

4.3 模块化的操作系统结构

4.3.1 特性、思想

4.3.2 三个优点

​编辑4.3.3 两个缺点

4.4 大内核和微内核

4.5 外核的操作系统结构

4.5.1 特性、思想

4.5.2 两个优点

4.5.3 两个缺点

五、操作系统的引导

5.1 操作系统的安装

5.2 启动操作系统

5.3 操作系统初始化程序

六、虚拟机

6.1 总括

6.2 第一类的虚拟机管理程序

6.3 第二类的虚拟机管理程序

6.4 两类虚拟机管理程序的对比

6.4.1 对物理资源的控制权

6.4.2 资源分配方式

6.4.3 性能 

6.4.4 可支持的虚拟机数量

6.4.5 虚拟机的可迁移性

6.4.6 运行模式


零、前言 

0.1 考纲内容

0.2 考情统计 

 0.3 考点解读

 0.4 复习建议

一、操作系统的基本概念

1.1 操作系统的概念

1.1.1 电脑的诞生过程

现在所使用的电脑是怎么样,一步一步变成,现在看到的这个样子?

第一步:厂家会把CPU、主板等硬件组装成一台裸机。

裸机:只有硬件的机器,它不包含操作系统。

第二步:在这个机器出售前,一般厂家还会在这个裸机的基础之上,为我们安装一个操作系统,例如:Windows操作系统(常用)。

第三步:当我们买到这个电脑之后,就可以在这个操作系统之上,安装一些我们想要使用的应用程序,例如QQ。

第四步:之后,我们作为用户就可以来使用这些应用程序了。

总结:根据一台电脑的诞生过程,就可以看出这个计算机系统的一个层次结构。

  • 最底层的是纯硬件,又称之为裸机。
  • 在裸机上面会安装一层操作系统。
  • 基于操作系统,我们又可以安装一系列的应用软件。
  • 我们用户是处于最上面一层。

注意:

  • 用户和操作系统的边界是有一些相连的部分。
  • 应用程序和操作系统也会有一些相连的部分。
  • 为什么会相连,后面进行解释!

1.1.2 操作系统的定义

系统资源包含:硬件资源、软件资源。

进程:正在运行的软件,正在运行的程序,比如说大家熟悉的QQ、Chrome等等。

1.2 操作系统的功能

1.2.1 QQ聊天引入 

使用QQ和朋友视频聊天的例子,帮助大家来理解他作为系统资源的管理者要做一些什么事情。

操作系统要提供四个功能解析:

(1)除了.exe文件之外,系统当中还有各式各样的其他的文件,但是这些文件的组织和管理,其实是操作系统在帮我们完成的。——体现了操作系统的文件管理功能。

(2)双击打开.exe文件之前,背后操作系统帮我们做了一个很重要的事情,就是把这个程序的数据,从我们的磁盘放入了内存当中,并且这些数据要放到内存中的什么位置,这些其实都是操作系统在帮我们完成的。——体现操作系统的存储器管理(主存管理、内存管理)功能。

(3)当QQ程序运行的过程当中,其实是需要被处理机(CPU)进行处理的,什么时候给QQ程序分配处理机资源?这个事情是操作系统在背后给我们完成的。——体现处理机管理功能。

(4)在视频聊天的过程当中,肯定需要把摄像头这个设备分配给QQ程序使用。因此,像摄像头这一类设备资源也是要有操作系统替我们管理,由操作系统来决定,应该要把这些设备分配给哪个程序使用。——体现设备管理功能。

总结:操作系统在后期,我们要重点学习的就是它作为系统资源的管理者,要如何设计和实现这些功能。

操作系统要向上层提供方便易用的服务解析:

(1)计算机的最底层其实是裸机,也就是纯硬件。

最底层的这些硬件,其实他只能听得懂二进制指令,也就是机器语言。

也就是说,如果我们要直接和这些硬件打交道的话,那我们只能通过这种二进制的语言和这些硬件进行交互。最底层的这些硬件,其实是一些丑陋的不友好的交互接口,正常的人类很难和这些纯硬件直接来打交道。

(2)在硬件之上,安装的操作系统会向他的上层提供一种更美丽、更友好的交互接口。

安装操作系统后,我们的用户和应用程序不需要直接和硬件打交道,我们只需要告诉操作系统,我们想要得到的服务就可以。操作系统会把用户的这些命令需求翻译成二进制来告诉硬件,然后由硬件来执行我们想要的这些操作。

总结:用户只需要用更简单的方式对操作系统发出命令就可以,剩下的操作系统会替我们来完成。

 其实这种封装思想在我们日常生活中也经常被使用。

例如:汽车如果把它拆解了的话,会发现它里面有很多很多很复杂的一些构件。

汽车设计师们会把这些底层复杂的这些构件把它隐藏起来,把它封装成一个我们简单易用的接口,对用户暴露出的就只有方向盘、刹车、油门,这些用户能够理解的简单易用的接口。

但是用户不需要再关心,底层到底是怎么实现转向、加速、减速这些功能的。

操作系统做的事情本质上和这种封装没有任何的区别。

1.2.2 处理器管理的功能

1.2.3 存储器管理的功能

1.2.4 文件管理的功能

1.2.5 设备管理的功能

1.2.6 GUI

操作系统屏蔽了底层硬件细节之后,他对上层提供了哪些方便易用的服务呢?

1.2.7 联机命令接口

操作系统会告诉我们当前的时间。

接下来操作系统又会让用户输入一个新的时间,也就是说想把系统时间调到什么时候。 

time命令就是一个很典型的交互式的命令,也就是用户说一句,然后系统会跟着做一句。

接下来用户需要再说一句,然后系统才会跟着再做一句。

整个过程我们用户和操作系统是在不断的交互的。除了time命令之外,还有很多别的用于和操作系统交互的这种命令。由这些命令组成交互式的命令接口(联机命令接口)。

1.2.8 脱机命令接口

别看它复杂,本质上和我们刚才所看到的time命令没有任何区别。

只不过是把这一系列的命令,把它罗列在了这样的一个清单里,当我们执行这个.bat文件的时候,操作系统,其实就是根据这个文件当中的这些命令一条一条的往后执行。

也就是说,我们使用这种脱机命令接口(批处理命令接口)和操作系统进行交互的话,那么我们其实是一次提出一堆的请求,然后操作系统会根据我们的指示,一条一条的执行这一堆的请求,所以这也是,为什么这种命令接口称作为批处理命令接口的原因。

1.2.9 程序接口

上面提到的三种接口:GUI、联机命令接口、脱机命令接口,都是可以让普通用户直接使用的。

操作系统还有一种对外提供的接口叫程序接口,它是给程序员使用的。

(1)我们可以在我们写的程序当中,通过系统调用的方式来使用程序接口。

(2)操作系统在收到这个系统调用相关的请求之后,才会替我们去操作这个硬件,也就是显示器,并且在这个显示器上面显示出hello world。

(3)很多操作系统当中,都提供了上百种的系统调用,由这些系统调用组成了所谓的程序接口。

(4)应用程序请求操作系统服务的唯一方式,就是通过系统调用的方式。

1.2.10 相关问题总结 

(1)这个部分我们讲了很多内容,操作系统向上层提供了很多方便易用的服务。

有的接口是直接给用户使用的,比如说GUI和命令接口。

有的接口是给软件或者说给程序员使用的,比如说程序接口。

(2)为什么用户和操作系统中间有一段相接的部分了?

用户通过这些方式,可以直接的来和操作系统进行交互。

应用程序需要,通过系统调用的方式,也就是通过程序接口来和操作系统进行交互。

(3)联机命令接口和脱机命令接口的区别,有可能在选择题当中进行考察。

联机命令接口(交互式命令接口):用户说一句,系统做一句。

脱机命令接口(批处理命令接口):用户说一堆,然后系统跟着做一堆。

(4)有的教材当中,会把命令接口和程序接口统称为用户接口。

狭义的用户接口不包含GUI。

1.3 操作系统的目标

操作系统,作为最接近硬件的这个层次,所需要做的事情,就是要实现操作系统对硬件机器的拓展,使这些单纯的硬件功能更强,使用更方便。

为什么说操作系统可以实现对硬件机器的拓展?

1.4 操作系统的特征

1.4.1 并发

为什么并发性对于操作系统来说是一个很重要的基本特性?

操作系统就是伴随着多道程序技术出现的,下个小节操作系统的发展历史介绍。

总之,操作系统的出现,就是为了支持多道程序并发运行、同时运行而诞生的。

考点:

  • 一个单核CPU的系统:各个程序肯定是只能并发地执行。
  • 多核CPU:多个程序可以并行的执行。

例如:在这个CPU上,可以同时并行的运行着微信、QQ、word、Chrome四个程序。

  • 如果此时,有第五个程序也想同时运行,例如,想同时在听歌的话,那么就必须剥夺其中的某一个程序对CPU的使用权。
  • 例如:把word这个程序给换下CPU,然后让QQ音乐这个程序上CPU运行,然后,Word和QQ音乐这两个程序交替着来使用CPU。

总之,对于四核CPU来说,同一时刻只能同时执行4个程序。

  • 如果要有超过4个以上的程序,同时运行的话,那么并发性依然是必不可少的。
  • 早期的操作系统、现代的操作系统,并发性都是操作系统一个很重要的最基本的特性。

1.4.2 共享

例如:你用QQ和你的家人正在进行视频聊天,那么这个时候,你还想用电脑的微信和你的小伙伴进行视频聊天,你就会发现,这个摄像头正在已经被分配给其他的进程使用。

注意:有的时候这个同时共享,也有可能是微观上也确实在同时的共享这个资源。比如说你在玩游戏的时候,一边听歌,你会发现你的扬声器这个声音输出设备,一边在播放游戏的音效,同时还在播放音乐,所以在这种情况下,扬声器这个声音输出设备是真的,在微观上也是同时正在被共享,你的音乐播放器和游戏这两个进程同时使用的。


互斥共享方式:在一个时间段内,摄像头这个这个资源,它只能被分配给其中的一个进程使用。

同时共享方式:它们是交替的访问硬盘的,QQ先读出了文件A的某一个部分,然后微信又读出文件B的某一部分,接下来又交替着让QQ读文件A的下一部分,然后以此交替的访问这个硬盘。

所以并发和共享我们才说他们是互为存在条件的。

  • 如果失去了并发性,那么共享性就失去了存在的意义。
  • 如果失去了共享性,那么并发性就不可能被实现。

1.4.3 虚拟

除了这个游戏之外,其实我在电脑当中还有各种各样的别的一些应用软件,程序。

那么我的电脑其实只有4GB的内存,其实在我的电脑上,我可以同时打开GTA、QQ, 还可以用微信、迅雷、同时使用网易云音乐听音乐,所以看起来这么多的应用软件,他们总共需要的内存量,他们是其实是远大于4GB的,为什么他们又可以在我的电脑上同时运行呢? 

  • 实际上这个电脑只有4GB的内存,也就物理上的实体是4GB的内存,但是在用户看来,它变成了逻辑上的对应物,看起来是远远大于4GB的,所以这就是虚拟特性,虚拟技术的一个例子。
  • 虚拟存储器技术,它其实就是虚拟技术当中的空分复用技术。
  • 第三章会重点讲解。

既然这个电脑只有一个单核的CPU,这个计算机中为什么还可以同时执行这么多的程序呢?

  • 实际上只有一个单核CPU,这是物理上的实体。
  • 但是在用户看来,这个计算机似乎变成了6个CPU在同时为自己服务,这就是所谓的逻辑上的实体。
  • 虚拟处理器技术,就是典型的时分复用技术。
  • 在微观上看,一整个大的时间段被分割为了,一个一个微小的时间片、时间段,然后在各个时间段内,处理机是交替的为各个进程服务的,所以这就是时分的意思,把一个大的时间段分为了各个很小的时间片,所以这是时分复用技术。

 如果这个系统当中,这个操作系统失去了并发性,那么就意味着一个时间段内,只需要有一道程序在执行。

  • 分析可得,如果没有并发性,时分复用技术和空分复用技术就没有存在的意义。
  • 也就是说,虚拟性也就没必要实现了。
  • 没有并发性,就谈不上虚拟性,虚拟性就没有存在的意义。

1.4.4 异步

假设老渣现在要和两个女孩并发的约会。

  • 一号女孩会让老渣干这样的两件事,首先是陪他吃饭,第二是把心给他。
  • 二号女孩儿会让老炸先把心给他,然后再让老渣陪她吃饭。

执行一号的这两条指令的过程,其实就相当于是在执行第一道程序。

执行二号女孩这两条指令的过程,其实就相当于他是在执行第二道程序,而这两道程序需要并发的执行。

另外,老渣他只有一颗真心,所以老渣的心,相当于这儿提到的有限的系统资源。

由于这两道程序在系统中是并发的运行的,因此这两道程序,有可能会争抢着使用这个资源,所以老渣在执行这两道程序的时候,有可能是这么约的:

  • 第一种情况:刚开始他是和1号约,也就是执行了1号的第一条指令,陪他吃饭。接下来的这个时间段,也是和1号约,执行了他的第二个指令,根据1号的这个要求,老渣会把心给一号。也就是说,这个系统资源分配给了1号。而接下来的这个时间段是要和2号约,2号要求老渣把心给他,但是由于老渣的心,这个有限的系统资源已经给了1号,并且1号没有把这个系统资源归还给老渣,所以2号要求老渣把心给他,这个请求就不能够被满足。所以2号程序运行到这个地方的时候肯定需要被阻塞,需要先停一段时间,等他获得了他想要的这个资源之后,2号程序才可以接着往下执行。
  • 第二种情况:老渣刚开始有可能是和1号约,然后陪1号吃饭。接下来的这个时间段是和2号约,根据2号的指令,他把心给了2号。再接下来,他又和1号约,也就是说,此时他要执行1号的第二条指令,1号让老渣把心给他,但是由于此时这个有限的系统资源是被2号所占有的,没有归还给系统,因此1号的这个把心给他的请求不能够被满足,所以这个第一道程序的执行在这个位置就会被阻塞,然后等待这个系统资源的分配。

有时可能是2号需要停下来等待,有时可能是1号需要停下来等待,在实际的操作系统当中,各个并发运行的程序之间,也会出现这种争抢着使用有限的系统资源的情况,比如说,两个程序争抢着使用摄像头资源、打印机资源等等。

所以,这是多道程序环境下,多个程序并发执行所带来的一个特性,也就是所谓的异步性。

如果失去了并发性的话,就意味着系统只能串行的执行各个程序,也就是说,第一道程序执行完了,然后1号把老渣的心归还给老渣之后,他才会接着执行第二道程序。在这种情况下,各个程序的执行肯定是会一贯到底的,并不会走走停停。所以,只有系统拥有并发性,才有可能会导致我们之前所说的这个异步性。

1.5 总结

没有并发,就谈不上虚拟和异步,并发和共享本身又是互为存在条件的,得到:

没有并发和共享,就谈不上虚拟和异步,虚拟和异步就没有存在的意义,这就是为什么说它们是最基本的两个特征。

简单题:

二、操作系统的发展历程

2.1 操作系统的分类和发展

2.1.1 手工操作阶段

在这个阶段,操作系统还并没有诞生。

程序员写程序是要写在纸带上的,可以看到这个纸带上有一些小孔:

  • 有孔的地方代表的是二进制的1。
  • 没孔的地方代表的是二进制的0。

程序员通过在纸带上打孔,这样的方式,写好了自己的程序,然后把自己的这个程序,放到纸带机上,然后计算机从纸带机当中,读取我们要运行的程序,等程序运行结束了之后,又把程序运行的结果再给输出到纸带机上,之后再由程序员从纸带机,取走自己程序运行的结果。

但是,用手工操作的这种方式存在一个很明显的问题,就是程序员用手工的方式,把纸带装到纸带机,或者从纸带机取走自己的这个程序运行结果的过程,其实是超级慢的。

第一个程序员:要提交一个自己的作业,提交一个自己的程序让它运行。

  • 程序员需要花很长的时间,把自己的程序放到纸带机上,之后计算机需要把纸带机上的这个程序数据,给读到计算机里,这个读取的过程也是一个很慢速的过程。
  • 但是计算机的处理程序的速度,是非常快的。所以,其实计算机在计算的时候,只需要花很短的时间,就可以把这个程序运行结束了。
  • 当程序运行完了之后,他又同样需要花比较长的时间,把结果输出到纸带机上。
  • 之后,程序员再把纸带机上的结果,再给用手工的方式取走。

第二个程序员:等第一个程序员,把自己的运行结果取走后,第二个程序员才能接着使用计算机。

  • 同样的,他首先需要把自己的程序放到纸带机上,需要花很长的过程。
  • 接下来,又从纸带机读到计算机内,这也需要花不少的时间。
  • 但是,同样的计算机处理,只需要花很短的时间。红色代表,计算机处理的时间。

可以看到,在手工操作阶段存在的主要的缺点就是用户独占全机。

  • 在第一个程序员在自己的程序,还没有完全运行结束之前,是不允许第二个程序员和他同时使用计算机的,当他使用计算机的时候就是独占着使用。

第二个很明显的缺点,由于手工操作是一个很慢的过程,但是计算机计算是一个很快的过程。

因此,这样的人机速度矛盾会导致资源的利用率极低,特别是计算机CPU的利用率极低。

  • 我们可以看到在这么长的一段时间内(灰色的线条),其实CPU或者说计算机真正在忙碌,在工作的时间,只有这样红色的一小段和这样红色的一小段,有很大的比例,这个计算机是处于空闲的状态的。
  • 在这个阶段,计算机的造价十分昂贵,让计算机这种昂贵的资源有大量时间,处于空闲状态,显然是一种很不经济、很低效的一种方式。

 因此,为了解决这个问题,人们发明了单道批处理系统。

2.1.2 单道批处理系统

在这个阶段,人们引入了脱机输入/输出技术。

各个程序员,可以把自己的程序都依次放到纸带机上,然后会有一个叫做外围机(外围控制机)的一个专门的机器,控制着把这些纸带机里的程序或者数据先把它放到磁带上。

之后,计算机可以直接从这个磁带里,读取这些程序的数据。

此时,计算机当中会运行着一个叫做监督程序的程序,由这个监督程序控制着自动的从这些磁带当中输入、输出作业的数据。

而从磁带中输入、输出作业其实速度是要更快的。

所以,引入了这种脱机输入输出技术之后,读取一个作业所需要花的时间比例就小了很多,这个监督程序会控制着计算机。

  • 自动的从磁带当中读入第一个作业,这个读入的过程要比从纸带机读入要快很多。
  • 接下来会进行程序的运算。
  • 接下来,进行输出,输出到这个磁带上。
  • 等第一个作业完成之后,就可以开始紧接着读入第二个作业,然后计算再输出,以此类推。

可以看到,在引入了脱机输入、输出技术之后,这个作业的输入输出过程,所需要占的时间比例,其实是降低了很多的。

这样的话,就可以让CPU有更高比例的时间,处于计算,处于忙碌的状态,这就使CPU的资源利用率有一定的提升,缓解了一定程度上的人机速度矛盾。

但是,这种方式很显然它的资源利用率依然是不够高的,内存中同一时刻只能有一道程序运行,只有这个程序运行结束,并且输出完成之后。才可以读入第二个程序,也就是说各个程序之间他们是串行的执行的。

虽然,比起上一个阶段CPU利用率有所提升,但是这种方式依然会使CPU有大量的时间,是在空闲等待的状态,资源利用率依然是很低的。

因此,为了解决上面的问题,人们就发明了多道批处理系统,在这个阶段操作系统就正式诞生了。

2.1.3 多道批处理操作系统

有了操作系统之后,就可以支持多道程序并发运行了。

在多道批处理系统当中,每一次就可以往内存当中,读入多道程序,然后让这些程序并发的运行。

用甘特图的方式来看一下和上一个阶段的区别。

  • 首先,第一个作业的数据,会从磁带输入到计算机当中,等输入结束了之后,就可以开始对这个程序的计算工作。
  • 但是,当CPU在计算第一个作业的时候,其实输入设备是处于空闲的状态的。因此,CPU计算第一个作业时,可以让输入设备,同时把第二个作业也读入内存。
  • 紧接着,第一个作业计算完成,就可以使用输出设备,把这个结果输出到磁带上。
  • 第一个作业在输出的时候,其实只是输出设备在忙碌,在这个时间段内CPU已经空闲了。所以在这个时间段,其实可以让CPU,为第二个作业服务,开始计算第二个作业。
  • 同样的,这个时候其实输入设备又空闲了,在这个时候又可以开始,同时读入第三个作业。
  • 以此类推。

在引入了多道程序技术之后,多道程序可以并发的执行,共享计算机当中的资源。

  • 计算机资源包括:输入设备、输出设备、CPU等等。
  • 所以,资源利用率就得到了大幅的提升。
  • 上图中表明,引入了多道程序技术之后,CPU是会有大量的时间处于忙碌的状态的,也就是说CPU的利用率得到了更大的提升。

多道批处理系统,其实它并不提供人机交互的功能,当一个用户把自己的作业,提交了之后,接下来用户,就只能干等着,计算机把自己的作业处理完成。

  • 也就是说,在多道批处理系统当中,用户是无法调试自己的程序的。
  • 也不可能在程序运行的过程当中输入一些参数,这些事情都是无法实现的。

所以,为了实现人机交互的功能,人们又发明了分时操作系统。

2.1.4 分时操作系统

在分时操作系统当中:

  • 计算机会以时间片为单位,轮流的为各个用户or各个作业服务。
  • 用户可以通过像键盘、鼠标等等这一类的终端和计算机进行交互。

例如:这个计算机先给这个用户服务50ms、再给这个用户50ms、这个用户50ms。

总之,在分时操作系统当中,各个用户可以轮流的得到服务。

所以,分时操作系统的主要优点就是用户的请求可以被及时的响应。

老渣约会的例子,其实它就是一个分时操作系统,老渣以时间片为单位,轮流的为各个用户来服务。

这样的话,各个用户的请求,就可以被及时的响应,因为对于他们俩来说,至少每隔一个小时肯定能够被老渣服务一次,所以他们的请求,最多在1个小时之内肯定可以被老渣响应。

所以,这种分时操作系统是人机交互的问题得到了解决,并且允许多个用户,同时使用一台计算机,他们对这个计算机的操作都是相互独立的,他们还感受不到别人的存在。

在用户看来,似乎就像是自己独占全机一样。

分时操作系统也存在一个明显的缺点,就是他不能优先处理一些紧急的任务。

这种操作系统,对各个用户都是完全公平的,所以,为了能够让计算机优先的处理一些紧急任务,人们又提出了实时操作系统。

2.1.5 实时操作系统

实时操作系统能根据任务的优先级,来优先的响应一些紧急任务。

这种操作系统,要求计算机在接收到一些紧急的信号之后,需要进行及时的处理,并且要在严格的时间时限内,来处理完成,并且需要保证这个处理的结果是正确的。

因此,这种操作系统的主要特点是及时性和可靠性。

实时操作系统又分为硬实时和软实时操作系统。

  • 比如说像导弹控制系统,自动驾驶系统这类的系统,必须绝对严格的在规定时间内,完成相应的处理,否则会导致严重的问题。
  • 比如说导弹射偏了,或者自动驾驶的汽车直接开到了马路下面等等。

软实时操作系统,比如说我们熟悉的12306火车订票系统,这个系统要求,网站上可以需要实时的显示我们剩余的票到底还有多少,但是即使偶尔更新不是很及时,其实也并不会产生特别严重的后果,所以这种是软实时系统。

2.1.6 其他操作系统

这些操作系统,只需了解,考试当中一般会考察的是之前提到的那几种操作系统。 

2.2 总结

各种类型操作系统的一个特点、优点和缺点。

重点:各类型的操作系统他们主要想解决的是什么问题,是用什么样的方式来解决的。

三、操作系统的运行环境

3.1 操作系统的运行机制

3.1.1 程序在计算机硬件上底层运行机制

C语言代码,写的那些程序,都会经过编译器进行一个编译的工作,把这个C语言编译为,计算机能够听得懂的二进制代码,也就是机器指令。

平时用C语言,这种高级语言写的一条代码,经过编译翻译之后,可能对应很多很多条机器指令。

二进制代码,才是他计算机CPU能够看得懂的一种语言。

其实,这一段程序运行的过程,其实就是CPU把这些机器指令一条一条的执行的一个过程。

这些机器指令在背后实现的,其实就是我们用代码表示的这一小段的逻辑,只不过这种二进制的机器指令是CPU能看懂,但是我们看不懂的。

机器指令,其实就是让处理器(CPU)能够识别和执行的最基本的一个命令。

例如:进行一个加法操作、进行一个赋值的操作等等。

这种小黑框里输入的这种什么ls、cd等等,这样的命令称作为指令。其实,这个小黑框里所用的这个命令,是我们之前提到过的交互式命令接口,和我们这个小节所讲的指令是不一样的。

我们这里指的是二进制的机器指令,要对接下来所提到的指令进行一个区分。

总之,平时用高级语言编写的这种程序,最后执行时,肯定是需要变成CPU能够读得懂的,用二进制的机器指令表现的这种形式。

这就是程序运行的一个基本原理。

3.1.2 内核程序vs应用程序

《操作系统》中,注意区分两种类型的程序:

  • 一种叫内核程序,
  • 一种叫应用程序。

应用程序是跑在操作系统之上的,平时很熟悉的程序。例如:QQ、微信等。

  • 普通程序员写的程序是应用程序,是跑在操作系统之上的。
  • 像微软、苹果,这帮人是负责开发操作系统,这帮人写的程序,就是操作系统的内核程序,这帮人编写了很多的内核程序,这些内核程序最终组成了操作系统的内核(kernel),这个内核就是操作系统最核心的部分,也是最接近硬件的部分。

操作系统最重要的一个角色——作为系统资源的管理者。

操作系统对系统资源的管理工作,其实就是在内核部分来实现的。

甚至可以说一个操作系统,只要有内核就可以了。

例如:容器技术中的Docker,在Docker容器里只需有Linux内核,就可实现Linux的所有功能。

用户平时使用的操作系统,其实包含的不止是内核的功能。例如:图形化的用户界面,就不是放在内核当中实现的。

即使没有图形化的用户界面,我们依然可以用命令行,也就是之前所提到的小黑框的那种方式来使用操作系统。

操作系统的内核当中所包含的,只是操作系统当中最重要、最核心、最必不可少的那些功能。

3.1.3 特权指令vs非特权指令

操作系统内核是系统资源的管理者,作为管理者的一个角色,有时就有可能会让CPU执行一些比较特殊的指令。

例如:内存清零的指令,这种指令会对其他程序的运行造成很严重的影响。

像这样的特权指令运行之后,是有可能影响到其他程序的正常运行的。

这样的特权指令就应该只允许我们系统的管理者,也就是操作系统内核来使用。

  • 假如这一段程序0101010000111010101是内核程序的话,这些指令当中可以出现特权指令。
  • 假如说这段程序0101010000111010101是普通的应用程序的话,其中就不应该包含特权指令,只能使用非特权的指令。
  • 例如:让CPU做加减乘除运算的一系列的指令,就是非特权的指令。

CPU在设计和生产时,就划分:

  • 哪些指令:属于特权指令,
  • 哪些指令:属于非特权指令。

CPU在拿到一条指令时,CPU是可以区分出它到底是特权指令,还是非特权指令的。

问题就出现了,CPU在执行程序时,CPU只会负责把这些指令一条一条的执行,虽然他能够分辨出哪些指令是特权指令,哪些指令是非特权指令。

但是,CPU又怎么分辨出此时正在执行的这个指令,它到底是一个应用程序的指令,还是一个内核程序的指令?

3.1.4 内核态vs用户态

为了让CPU能够区分此时正在运行的这些指令,是属于应用程序还是内核程序。

CPU会被划分成两种状态:

  • 内核态:当CPU处于内核态时,说明此时CPU正在运行的是内核程序,此时CPU是可以执行特权指令的。
  • 用户态:如果CPU处于用户态,说明此时CPU正在运行的是应用程序,此时CPU就只能执行非特权指令。

怎么区分CPU到底处于哪种状态呢?

在CPU当中会有一个寄存器,也就是一个存数据的地方,叫做程序状态字寄存器,英文缩写叫PSW,然后这个寄存器中有一个二进制位。

  • 二进制为1时,表示CPU此时是处于内核态。
  • 二进制为0时,表示CPU此时处于用户态。
  • 当然也有一些CPU有可能是0表示内核态,1表示用户态。
  • 这个无关紧要,我们只需要知道,用二进制的方式,就可以实现对CPU状态的一个标记。

CPU要怎么实现这两种状态之间的切换?

直接用一个例子,让大家看一下CPU状态切换的一个过程。

(1)首先,当我们开机时,需要加载操作系统。

操作系统就需进行一些初始化工作。

系统初始化的工作,其实就是由操作系统当中的某一些内核程序来完成的。

开机过程中,需要执行内核程序。

CPU需要处于内核态,需执行系统初始化相关的,这一系列的内核程序的这些指令。

(2)开机完成后,用户就可以启动某一些应用程序。

这个应用程序要正常运行的话,肯定需要让CPU执行他的这一系列的指令。

但是,我们刚才不是说CPU此时正在执行的是内核程序,如果说要让它运行这个应用程序的话,怎么实现这个事情呢?

如果操作系统的内核想让这个应用程序,开始运行的话,这个内核程序就需要执行一条特权指令,这个特权指令会把PSW的标志位,从内核态转变为用户态,这样的话就完成了CPU状态的一个切换。

(3)接下来,操作系统内核就会让出CPU的使用权,让这个应用程序上CPU运行。

此时,CPU已经被切换为用户态了。

接下来,应用程序会在用户态下运行,CPU会执行这个应用程序的一条一条的指令。

(4)假如此时有一个猥琐的黑客,在应用程序中植入了一条特权指令的话,会发生什么事呢?

首先,CPU在读入这条指令时,CPU就已经能够判断这条指令是一条特权指令。

但是,CPU又检查了自己的PSW寄存器,发现自己此时处于用户态的。这样的话,CPU就能够知道,我此时正在运行的其实是应用程序,而不是内核程序。

一个应用程序竟然他妈想要用一个特权指令,这个事情坚决不能让他干。

所以,这样的一个非法事件,会引起一个中断信号。

(5)当CPU检测到这个中断信号之后,就会立即强行变态,强行变成核心态,并且CPU会拒绝执行这一条特权指令。

接下来,CPU会暂停执行这个应用程序后面的这一系列的指令代码,转而会执行一个处理中断信号的内核程序。

接下来CPU就在内核态下,来执行这个内核程序中相应的这一系列的指令。

其实,刚才发生了这个中断信号之后,让操作系统内核又重新夺回了CPU的控制权。

(6)接下来,操作系统的内核程序会对这个中断进行相应的处理,等处理完了之后,他才会把CPU的使用权再重新还给应用程序。

从这个故事当中,我们就可以很形象的看到,CPU从内核态切换回用户态,是执行了一条修改PSW标志位的一个特权指令来完成的。

执行了这个特权指令之后,意味着操作系统内核要主动的让出CPU的使用权了。

当CPU切换为用户态之后,就可以在CPU上运行用户程序。

CPU的状态,从用户态又切换回内核态是由中断引发的,由CPU硬件自动的完成这个变态的过程,并且CPU变回内核态之后,会停止运行当前正在运行的应用程序,转而运行一个内核程序。其实触发了一个中断信号,意味着操作系统会强行重新夺回CPU的使用权。

具体有哪些中断,下一个小节当中进行更进一步的介绍。

3.1.5 时钟管理

时钟是操作系统中是一个比较重要的硬件设备。

时钟干什么用呢?

(1)计时。

操作系统需要通过时钟管理,向用户提供这个标准系统时间。

不光是人需要获取时间,应用程序也需要。

比如说我有一个程序,想要获取服务器上的这个响应,但是服务器一直没有给我数据,我怎么办呢?

我做了一个定时,轮流的向服务器去进行请求,轮询的一个动作,轮流向用户去请求,每隔一段时间去请求一次,那这个过程就需要时间,计时。

(2)时钟中断。

进程切换进程、线程的切换。

分时操作系统中可以使用,时间片轮转调度机制。

例如:给四个小姐姐,每人一个很短的时间片,让他们去执行时间片。执行完成之后,CPU就切换到另外一个时间片,或者说另外一个应用程序上去执行。这些都是基于时钟中断来实现的。

时钟中断:时钟会产生一个中断。

3.1.6 原语

3.1.7 总结

CPU运行程序的过程,其实就是在执行一条一条机器指令的过程。

系统中存在两种程序:

  • 内核程序。
  • 应用程序。

内核程序是整个系统的管理者,需要使用到一些特权指令,并且特权指令只有内核程序可以使用。

  • 当CPU的状态是内核态时:说明此时正在跑的这些指令,是属于内核程序的。
  • 当CPU处于用户态时:说明此时正在跑的是应用程序的指令。

由一系列的内核程序就组成了操作系统的内核,它是整个系统最核心、最接近硬件的部分。

CPU是如何变态的?

  • 内核态到用户态的转变,是用一条特权指令实现的。
  • 用户态到内核态的转变,是由中断引起的,然后由CPU硬件自动完成。

考察一个选择题,分值占的也不高。

3.2 中断和异常

3.2.1 中断的作用

内核程序:整个系统的管理者,最为一个管理者的角色。

计算机在刚启动时,其实在上边跑的肯定是内核程序,只不过在时机合适时,内核程序会把CPU的使用权主动的让给应用程序。

这个部分是第二章进程管理当中我们会具体学习的内容,这儿暂且先不展开。

总之,一个应用程序在上CPU运行之后,它就会一直运行下去,除非发生了中断,而一旦发生中断,就会让CPU立即停止此时正在执行的应用程序,转而执行相应的内核程序。所以,中断是让这个管理者,也就是操作系统内核重新夺回CPU使用权的一个唯一的途径。

中断会使CPU由用户态变为内核态。

可以思考一下,假如说没有中断机制,一旦一个应用程序上CPU运行,这个应用程序就可一直运行下去,如果一个CPU一直运行同一个应用程序的话,怎么可能实现多道程序并发这个事情呢?

  • 没有中断技术,就没办法实现多道程序并发。
  • 甚至可以说,没有中断技术,就没有操作系统。

总之,操作系统内核是一个管理者,当他想要把CPU使用权让给应用程序时,他会自愿的用一个特权指令,完成这个事情。

但是,当他想要把CPU的使用权重新给夺回来时,也可以通过中断的这种方式来实现。

3.2.2 中断的类型

3.2.1 内中断案例一

一个应用程序它运行在用户态,CPU会依次处理它的这些指令。

假如说有一个猥琐的黑客,在这个应用程序当中尝试植入一条特权指令。

当CPU拿到这条特权指令时,他发现此时正在处于用户态,此时在运行的是应用程序。

于是这个非法的事件,会触发一个中断信号,CPU会拒绝执行这一条特权指令。

接下来,CPU会自动的转变为内核态,并且会开始执行处理这个中断信号相关的内核程序。

于是在发生了这样非法的事情之后,这个系统的管理者又重新夺回了CPU的控制权。

这就是一个很典型的内中断的例子。

CPU在执行当前的一条指令时,由这条指令引发了一个中断。

3.2.2 内中断案例二

有时,即使CPU执行的是非特权指令,也有可能会引发内中断。

例如:CPU在执行一个除法指令的时候,发现除数是零。

在这种情况下,肯定也不能让这个程序继续执行下去,所以这种情况也会引发一个内中断。

结论:如果当前执行的这个指令本身是非法的,或说指令的一些参数是非法的,这种情况下就会产生一个内中断的信号。

3.2.3 内中断案例三

一个应用程序运行在用户态,但有时应用程序想请求操作系统内核,为他提供一些服务的。

在这种情况下,应用程序就会执行一条特殊的指令,叫做陷入指令。

这个指令会引发一个内部中断信号。

接下来,CPU又会转向这个中断信号相应的处理程序,进行这个中断信号的处理。

当一个应用程序执行了陷入指令时,意味着这个应用程序主动的把CPU使用权还给操作系统内核,想让操作系统内核为他提供某些服务。

  • 系统调用,其实就是用这个陷入指令来完成的。

需要强调的一点是,陷入指令它是一个比较特殊的指令,但是它并不是特权指令。因为我们可以想一下,这个应用程序是运行在用户态的,在用户态下可以执行这个陷入指令的话,这个陷入指令肯定也不是特权指令了。

总之,刚才提到的这个中断信号,其实也和当前执行的这条指令是有关系的,这个中断信号是来自于CPU的内部。

3.2.4 外中断案例一

这种中断信号,是由计算机的硬件——时钟部件,来发出的这个时间。

这个时钟部件会每隔一段时间给CPU发一个中断信号。

通过这个时钟中断信号,就可以实现多道程序并发运行了。

假设,此时系统当中想要并发的运行两个应用程序:

应用程序1:运行在用户态,然后CPU会执行他的这些指令。

当他执行了两条应用程序1的指令的时候,这个时钟部件发现此时已过了50ms,时钟部件会给CPU发送一个中断信号。

  • 注意,这个中断信号和当前执行的指令是没有关系的,它来自于CPU的外部。
  • 然后,根据我们之前学习到的知识,我们知道当CPU检测到一个中断信号时,会先暂停此时正在运行的应用程序,然后转而执行一个相应的内核程序,来处理这个中断信号。

接下来CPU会对这个时钟中断信号进行处理,并且转为内核态。

  • 在内核态下,CPU开始执行这个内核程序,来处理刚才收到的中断信号。
  • 然后,这个内核程序执行的过程当中发现,应用程序1,刚才已经用了50ms的这个时间了。
  • 所以接下来为了公平起见,我想让第二个应用程序上CPU运行。接下来这个内核程序会把CPU的使用权,给第2个应用程序。

接下来,又切换回用户态,然后第二个应用程序开始上CPU运行,而当第二个应用程序执行了一系列的指令之后,又过了50ms,此时这个时钟部件又会给CPU,发送一个来自于外部的中断信号,同样的,CPU还是会回去执行,和这个中断信号相关的那个处理程序。

接下来,他又会开始执行这个内核程序。

接下来这个操作系统内核发现程序2已经用了50毫秒了。

接下来,公平起见,又想让程序1上去接着运行,于是它会把CPU的使用权让给应用程序1。

然后应用程序1,就可以接着往下执行他之后的那一系列的指令了。

很清晰的看到,两个应用程序是如何在中断机制支持下,实现并发运行,一直不断的相互切换。

可以看到,中断在现代的这个计算机当中到底有多大的作用。

3.2.5 外中断案例二

除了这个时钟部件,发出的中断信号之外,有时还会有来自于I/O设备,也就是输入/输出设备发来的这种中断信号。

某一个应用程序,可能会请求打印机的打印服务,打印机在打印输出完成了之后,会向CPU发送中断信号,用来通知CPU我的任务已经完成了。

接下来,CPU会用这个中断信号,相对应的内核程序来对这个中断信号进行处理。

总之,我们这儿提到的这两种中断,中断的信号都来自于CPU的外部,和当前执行的指令是没有关系的,CPU在每一个指令执行结束时,都会例行检查一下是否有外部中断的信号,需要我来处理。

中断这种机制,具有一个强大的作用。

3.2.6 外中断案例三

 举例:火车就是一个CPU。

右边路它就是一个应用程序,这时候,突然间来了一个劫盗(I/O设备)。

劫盗要做文件,我们要等待用户的录入,我们要这个进行一些系统功能的调用等,都有可能。那这个时候呢,我们就会发现,我们只能等着了,就比较尴尬,我们不会白白浪费CPU的执行效率的。

我们就直接把它切换到另外一个应用程序就可以了。

总结:外部设备产生一个中断,CPU会做一系列的处理,切换到另外一个应用程序上。

3.2.8 中断机制总结

CPU在执行到一个应用程序时,突然间不执行了,然后切换到另外一个应用程序上去,这个过程,就需要一个中断,也就说它是在第一个程序上中断了。

为什么需要时钟中断的这个机制呢?时钟中断的机制,它的目的是什么呢?

提高多道程序环境下,CPU的利用率。

为什么中断机制可以提高CPU利用率呢?

CPU在一个应用程序要执行时,突然间遇到了I/O,需要I/O设备,需要I/O操作:

  • I/O操作比较低效的。CPU不是就只能等。
  • 此时,直接让CPU不要在这里进行操作了,中断一下,完了之后直接切换到另外一个应用程序上,执行另外一个程序。
  • 等第一个程序的IO设备操作完成后,再回来就好了。
  • 提高了这个程序的CPU的利用率,这个过程是基于中断来实现的。

结论:中断机制可以提高多道程序CPU利用率。

并发的情况下,提高CPU的利用率。

外部设备:

  • I\O设备、输入输出、文件操作等。
  • 外部设备都是,有可能导致中断的。

中断信号来源:

  • 外中断的中断信号来源:外部设备。
  • 内中断的中断信号来源:当前指令。

当前指令:

  • 现在正在执行的应用程序。
  • 这个程序就是按指定一个一个指令的去执行的。

内中断和外中断:

  • 当前正在运行的程序,内部发生了一些问题,出现了一些中断,这个叫内中断。
  • 外部中断,其实是什么是别的设备告诉CPU,你要中断,它其实是被迫中断。

内中断:

  • CPU正在执行过程中,当前执行的程序告诉CPU,需要中断。
  • 其实就是说,CPU内部自己商量着来,其实是由当前程序来决定的。
  • CPU正在执行的指令内部,所产生的这个事件。

中断和异常,提到中断的话,通常指的是外中断。

一提到异常的话,通常指的是内中断。

  • 外中断:被迫的。
  • 内中断:自愿的。

结论:所有的中断都会有对应的中断处理程序。

3.2.3 中断的分类

要分辨一个中断是内中断还是外中断,只需要判断,这个中断信号的产生和当前执行的指令是否有关就可以了。

使用的是内中断和外中断这两个术语,因为内外这两个形容词,可以让大家很形象的理解到他们俩之间的一个本质的区别。

  • 不过其实在很多教材,还有很多学校的考试当中,内中断一般会称为异常,外中断一般就直接称为中断,所以考试当中遇到的中断一般来说是这个狭义的中断,它特指外中断。
  • 我们这个小节当中之前一直在说的中断,其实指的是这种广义的中断。
  • 所以大家要理解它们的区别,可以用内外这两种方式来理解。但是在做题的过程当中,更需要注意的是中断和异常这两个术语。接下来,会经常使用到异常这个术语,大家需要知道它其实指的就是内中断。

第一种异常:陷入(陷阱trap)

其实就是刚才我们所提到的,由陷入指令引发的那种异常,其实这种异常是程序故意引发的,当一个程序想要请求操作系统的服务时,他就会故意的执行这一条指令。

其实这也是系统调用实现的一个原理。

产生中断不一定是坏事,也有可能是正常的情况。举例:这是一个小程序。

它执行了很多很多的指令,执行到指令3的时候,这个时候指令3,要调用一个系统服务,也就是读文件,也可以去进行其他的操作。CPU就说,我知道了。这时候就会产生一个陷入指令,产生陷入指令的过程其实就会产生一个中断。CPU当执行到指令3的时候发现当前的应用程序,需要去访问系统的服务,需要去调用系统内核的程序。CPU就立刻从当前程序中断,就不再执行当前程序。这就是会有一个中断,那中断之后呢会CPU会产生一个陷入指令。

陷入指令是一个指令,他要干一些事情的。

每一个陷入指令,它都会执行不同的操作。

既然是读文件的,陷入指令最终处理完成之后,就会去读文件。

CPU读到这儿的时候,你要去读文件,直接中断咔中断了,完了之后产生一个陷入指令,陷入指令是由CPU产生的,并不是由应用程序产生的,是由我们应用程序引发的,但不是应用程序产生的。

CPU来决定先中断完了之后,产生这个指令之后,执行完成之后,接下来我们CPU就开始去读文件了,因为请求的就是要读文件,CPU从用户态切换到了什么内核态,切换到内核态执行读文件的操作,读文件的一系列操作,其实执行特权指令。

陷入指令也有一种说法,叫自陷指令、访管指令。

由应用程序主动引发这种情况下,一般都是什么,都是我们系统调用,就是应用程序想要访问操作系统内核的程序。进行系统调用的过程会产生一个陷入指令,陷入指令就会伴随着中断,中断之后陷入指令执行,陷入指令执行之后会切换到内核态,去执行内核态的这个内核程序。

第二种异常:故障

故障是一种由错误条件引起的,并且可能被内核程序修复的问题,内核程序修复故障之后,会把CPU的使用权还给应用程序,让它继续执行下去。

例如:第三章当中会学习到的缺页故障。

游戏文件其实特别大,内存肯定装不下。游戏在运行时,只加载核心程序进入到内存去执行,后面在执行的过程中,发现如果有些程序在内存中不存在,这时就去磁盘上重新把其他程序文件,给它加载进来即可。

加载一些其他的这个程序文件,产生一个内存缺页的故障。读到这儿发现没有文件,直接给你一个故障,这个故障其实就是我们这里所谓的错误条件。

CPU产生一个故障中断,现在没有这个数据文件,我去这个磁盘上给你去读。

无论什么样的中断,它都会有一个对应的处理程序。

这个处理程序会把对应的文件加载到内存中之后,接下来开始继续执行后面的指令。

故障中断执行完成之后,处理完成之后,他继续执行当前程序的后续指令,不会是直接去执行特权指令,但是后续指令要干什么事情要不要去执行特权指令,要不要进行系统调用,后话。

第三种异常:终止

终止是由致命的错误引起的,这种错误一般是内核程序无法修复的。

  • 一般来说,当发生这种类型的异常之后,内核程序就不会再把CPU的使用权还给引发终止的那个应用程序。
  • 一般来说会把应用程序直接给干掉,让他终止掉。

例如:

  • (1)整数除以零,这种错误其实就是程序bug,也不是操作系统能够修复的。
  • (2)程序本身的问题或非法的使用特权指令,这样的程序也应该直接把它干掉,不应该再把CPU的使用权再还给他。

致命错误:算术溢出、地址越界、非法操作码、非法访问特权指令等。 

3.2.4 中断机制背后的基本原理

刚才我们举了各种各样的中断的例子:

  • 整数除以零。
  • 时钟部件引起的。
  • 应用程序自己引发的。

这么多的中断的类型,不可能用同一个程序来处理。

根据中断的信号的不同,来找到和这个中断信号相对应的中断处理程序。

具体的做法:CPU在检测到中断信号之后,会根据这个中断信号的类型来查询中断向量表,然后通过这个中断向量表,找到各种类型的中断所对应的中断处理程序。

之前讲了那么多例子,大家应该也能够想到中断处理程序,其实就是一种内核程序,肯定是需要运行在内核态的。

这就是中断机制的一个基本的实现原理,具体的硬件上应该怎么实现,这是《计组》。

3.2.5 中断与异常的处理过程

 (1)中断响应过程

 (2)中断处理过程

3.2.6 总结

中断是让CPU从用户态变内核态的一种唯一的方式,它是让操作系统强行的夺回CPU控制权的一种方式。

在引入中断机制后,才能让操作系统正常工作,才能实现多道程序并发运行,这个美好的事情。

中断机制的基本实现原理分为这样的两个步骤:CPU首先会检查中断信号。

  • 对内中断信号的检查:CPU在执行指令时就会进行检查,它会检查是否有异常的发生。
  • 对外中断信号的检查:在每个指令周期的末尾,也就是CPU每执行完一条指令之后,他都会例行的来检查是否有外部中断信号。

当CPU检测到一个中断信号之后,又会根据这个中断信号来查询中断向量表,然后找到这个类型的中断信号所对应的中断处理程序来进行处理。

3.3 系统调用

3.3.1 什么是系统调用?

开篇就有介绍过,操作系统作为用户和计算机之间的接口,它需要向上提供一些简单的应用的服务,给用户使用的就是命令接口GUI,程序接口由一些系统调用组成。

系统调用,就是应用程序、程序员来请求操作系统内核服务的一个途径,它和我们平时编程的时候使用的函数调用,其实是很类似的。

但是,系统调用又和普通的库函数的调用又有一定的区别。

  • 写程序时,是可以用汇编语言的方式,来直接请求这个系统调用服务的。
  • 现在更多使用高级语言编程,一般会直接使用高级语言提供的库函数。

但是这些高级语言的库函数在底层,其实也会用到操作系统提供的系统调用功能,来请求操作系统的服务,所以系统调用应该是比高级语言的库函数,更为底层的一个接口。

裸机之上是操作系统,操作系统向上层提供的接口是系统调用,使上面的这些库函数、应用程序能够通过系统调用的方式,来请求操作系统的内核的服务。

操作系统之上,各种各样这样的高级编程语言,会用库函数的方式来封装这些系统调用,然后向更上层的这些应用程序的程序员,来暴露一些更好用的编程接口。

并不是所有的库函数在底层,都会使用系统调用。

很多库函数,比如说像C语言里的math.h,里边就会提供很多,取绝对值之类的数学运算相关的库函数,这些库函数的功能不需要特权指令也可以完成,所以像这一类的库函数,它在底层也并不需要使用系统调用。

有的库函数,比如说像创建一个新文件,这个事情必须请求操作系统内核的服务,才可以完成,所以对于创建新文件这样的库函数来说,它在底层就肯定需要使用,操作系统提供的系统调用服务。

3.3.2 小例子:为什么系统调用是必须的?

当你的论文打印到一半时,另一同学用word也按下了打印按钮,请求打印他的论文。

WPS和Word在这个电脑里,这个系统当中,其实是两个正在并发运行的进程。

所以如果这两个进程能够随意的、并发的、来共享使用打印机资源的话,就会发生这样的情况:

由于两个进程,交替的并发的向打印机提出打印的请求命令,那么打印机设备就会交替的收到这两个进程发来的这个打印请求,这样的话,打印机就会把这两篇论文的内容给混杂在一起打印,先打印一行这个,再打印一行这个,不是我们想要得到的结果。

所以由于系统当中,有各种各样的并发的进程,而这些并发的进程,又需要共享的使用,类似于打印机设备这样的共享资源,但是这样的共享资源其实是需要各个进程互斥的共享的,那怎么实现对共享资源的互斥访问呢?

最好的方式就是,让操作系统内核,也就是这个系统的管理者,来对共享资源进行统一的管理,然后上层的那些应用程序,只能通过系统调用的方式,来请求操作系统,给他分配这种资源,之后这个进程才可以对这种共享资源进行使用和访问。

而各个进程的请求会由操作系统内核,来协调处理,保证他们并发运行的时候不会发生这种奇奇怪怪的事情。

从这个案例中,我们发现:系统调用的功能是必须存在的。

3.3.3 什么功能要用到系统调用

到底哪些功能需要用系统调用的方式,对外提供呢?

共同特点:凡是和共享资源有关的操作。

  • 例如:对内存的分配与回收。
  • 共享资源:内存、I/O设备、文件等。

总之,只要是对共享资源的访问,肯定是需要通过系统调用来进行,因为这些共享资源是有限的,所以操作系统会对这些共享资源统一的管理和分配。

因此,应用程序在使用这些资源时,必须通过系统调用的方式,请求操作系统内核,来帮他进行接下来的处理。

这样的话,我们就可以保证系统的稳定性和安全性,防止非法操作。

3.3.4 系统调用的过程

假设一个应用程序,想要进行系统调用,它在背后,需要做一些什么事情呢?

一个应用程序,运行在用户态,应用程序的各个指令,会被CPU依次执行。

当应用程序想要发出系统调用时,需用传参数指令,给CPU的寄存器当中,传递一些必要参数。

例如:在某一个寄存器当中放入了一个参数1。这个参数1指明了,我此次要进行哪种类型的系统调用,比如说像Linux里边的fork系统调用。

传递参数的指令,可能会有多条,主要要看我们的这个系统调用需要传递几个参数,操作系统会根据应用程序提供的这些参数,来判断他想要的到底是哪种类型的服务。

当这些参数都放到了寄存器当中之后,应用程序就会执行一条特殊的指令叫做陷入指令,这个陷入指令的执行会引发一个内中断。

CPU在检测到这个内部中断信号之后,CPU发现这个内部中断信号是由trap指令引起的,于是这个CPU接下来就会暂停运行这个应用程序,转而去执行处理陷入指令的那个处理程序。

这个程序就是系统调用入口程序,显然接下来要执行的这个程序,肯定是属于内核程序,因此它需要在内核态下运行,也可以说这个程序其实也是某一种中断处理程序,只不过它处理的是由陷入指令引发的那个内中断。

接下来,这个系统调用入口程序,会检查寄存器里的这些参数,通过第一个参数,它会知道此时这个应用程序,它想要的是这种fork类型的系统调用服务。

接下来,这个入口程序,就会调用与特定的系统调用类型所对应的处理程序,然后让这个程序上CPU运行。

这个系统调用处理程序在执行时,就可根据应用程序传递的其他的参数,来看一下它所需要的,具体是哪些服务。

当这个系统调用被处理完后,CPU又会转回用户态,然后接着执行之前的这个应用程序。

库函数系统调用:

普通的程序员,可以用高级的编程语言来写我们自己的代码,然后在我们的代码中,可以调用这个高级编程语言提供的一些库函数。

但是,有的库函数内部,其实是使用到了操作系统提供的系统调用的。

具体来说,一个系统调用的过程,是需要先传递系统调用所需要用的参数,接下来要执行一条很特殊的指令,叫做陷入指令。

执行了陷入指令之后,就意味着应用程序把CPU的控制权,主动的交还给了操作系统的内核,用这样的方式,来请求操作系统内核的服务。

所以,陷入指令执行之后,就会产生一个内中断,然后CPU会转向执行一个处理系统调用的内核程序,而这个内核程序显然是需要运行在核心态。

当这个系统调用处理完了之后,它又会返回原先的这个应用程序,让应用程序接着往下执行。

考点:

  • 第一,陷入指令,它其实是在用户态下执行的,它是一个很特殊的指令,但是他并不是特权指令,它是一个非特权指令。在执行了这个指令之后,会主动的引发一个内中断,让CPU进入核心态运行内核程序。
  • 第二,发出系统调用请求,这个动作是在用户态下进行的,但是对系统调用的相应处理是在核心态下完成的。

3.3.5 总结

在系统当中,有各种各样并发的进程,这些进程会争抢着使用,各种各样的系统资源,这些资源应该由系操作系统来统一的掌管。

所以,只要是对共享资源的访问操作,就肯定需要用系统调用的方式来实现。

如果一个应用程序执行了陷入指令,就意味着这个应用程序主动的把CPU的控制权还给了操作系统,然后用这种方式来请求操作系统的服务。

3.4 中国古代的操作系统

3.4.1 操作系统的运行机制

遥远的古代中国,操作系统的机制、设计思想就已经被设计发明了。

小李子:每天的工作就是执行各种各样的指令,忙的就像一个CPU一样。

皇帝:国家系统的管理者=操作系统的内核。

普通的大臣:也会对小李子指指点点,只不过他们只是普通的应用程序。

小李子平时的工作分为两种状态:

  • 当他为大臣服务的时候,处于用户态。
  • 当他为皇帝服务的时候,处于内核态。

3.4.2 特权指令和非特权指令

当小李子处于用户态时,此时它正在为普通的大臣服务,而这些大臣会给小李子发送一些指令,比如说让他端茶、送水。

有的时候我还会尝试发送一些特权指令,比如说让他拟道圣旨。但是小李子是一个极其正直的人,当他在用户台收到一个特权指令的时候,他会拒绝执行这个特权指令。

如果它处于内核态的话,说明他此时的服务对象是这个皇帝。皇帝会给小李子发送各种各样的指令,让小李子去执行。

当然,其中有非特权指令,也有特权指令,但是由于此时处于内核态,所以他既会执行特权指令,也会执行非特权指令。

3.4.3 内中断

如果小李子此时处于用户态,但是他又收到了一个特权指令,那么首先他会拒绝执行这个特权指令,并且这个非法的行为会在小李子的心中引发一个中断信号。

由于这个中断信号的引发和当前要执行的指令是有关系,所以这个中段被称为内中断,或叫异常。

当小李子检测到这个中断信号的时候,它就会停止为当前的应用程序服务,立即转为内核态。

然后,内核程序会跟中断信号的类型来决定这个中断,应该怎么处理。

需要注意的是,中断信号有很多种,不同的中断信号有不同的处理方式,比如说外中断的处理方式就不一样了。 

3.4.4 外中断

如果说此时小李子处于用户态,那么它会为这个应用程序服务。

此时,如果说小李子听说皇帝要传小李子觐见,那么这个外中断信号,就会让他暂停为当前的应用程序服务,从用户态转为内核态。

接下来,内核程序会告诉他,你应该怎么处理这个外部中断信号。

3.4.5 系统调用

普通的大臣不能让小李子执行特权指令,但有时,又必须要有特权指令才可以完成。

比如说此时这个国家正在打仗,这个大臣可能会有这样的需求,他想给边疆的将士们发配粮食,发配的粮食是大于1000石的,最后他会告诉小李子说,劳烦小李子禀报皇上。

  • 第一条指令:其实就是指明了系统调用的类型。
  • 第二条指令:指明系统调用的参数。
  • 第三条指令:其实就是陷入指令。

那么小李子会把这些系统调用的参数给记录在自己的脑子里,也就是记录在这个寄存器当中。

当小李子执行到第三条指令,也就是陷入指令的时候,这条指令会引发一个异常,或者说内中段。

接下来小李子,用户态转为内核态。

紧接着,内核程序会检查寄存器里的参数,用来确定系统调用的类型。

最后,内核程序会根据系统调用的类型和参数来做出相应的处理。

四、操作系统的体系结构

操作系统的内核,应该怎么设计?

考试中常考两种体系结构:

  • 大内核
  • 微内核

 红色星号:全新内容。

黄色星号:涉及到大内核和微内核的老内容。

4.1 层次结构划分

4.1.1 划分层次

操作系统内部,还可再进一步划分:

  • 一部分:内核的功能。
  • 另一部分:非内核的功能。

操作系统最核心的那些功能,需要放在操作系统的内核当中,例如:

  • 时钟管理:时钟中断来实现了,计算机记时的功能,想要实现程序并发,就必然离不开时钟管理,这个很重要的内核功能。
  • 中断处理。
  • 原语。所谓的原子性,就是说这种程序,要么就一气呵成的全部运行完成,要么就是不运行。它的执行过程是不可被中断的,即在执行原语的这一小段程序的过程中,即使有外部中断信号过来,CPU也会继续把原语执行完成,才去处理那个外部中断信号。
  • 进程管理。
  • 存储器管理。
  • 设备管理。
  • 其他。
  • 这些功能,都是要放在操作系统内核当中的。

最下面这一层:时钟管理、中断处理、原语,是和硬件结合最为紧密的,所以必须放在操作系统的内核当中。

  • Ubuntu、CentOS等都是Linux操作系统,这些系统的内核使用的就是Linux的内核。
  • 内核由一系列内核程序组成,内核程序必须运行在内核态。

例如:进程管理、存储器管理、设备管理,对于这些功能的管理,更多的是对数据结构的一个操作,而不会直接涉及到硬件。

所以,有的操作系统,并不把这些管理功能(进程管理、存储器管理、设备管理)放在内核当中,而只在内核中保留,与硬件接触最紧密的部分。

因此,这就引出了两种截然不同的内核的设计方法:

  • 把所有的这些功能,都包含在操作系统内核当中的这种结构,就叫做大内核。
  • 内核当中只保留与硬件关系最紧密的这些部分,这种内核就叫做微内核。

4.1.2 微内核结构与大内核结构的区别

采用微内核结构:

  • 属于内核的这些功能是需要运行在内核态的。
  • 不属于内核的,上面的这些功能,需运行在用户态。
  • 这会对我们系统的性能,造成一定的影响。

假设现在有两种体系结构的系统,第一种采用的是大内核的体系结构,由于进程管理,存储管理等,这些这些功能都是被划分在内核当中,所以这些功能的处理都需要运行在内核态。只有应用程序是运行在用户态的。

对于采用微内核结构的操作系统来说,只有和硬件联系最紧密的这些功能,被划分在内核当中:

  • 只有这些功能是需要在内核态下,才可执行。
  • 其他的这些功能模块在用户态下,就可运行。

4.1.3 大内核和小内核的变态区别

假设现在这个应用程序,想要请求操作系统的服务,并且这个服务的背后需要同时涉及到进程管理、存储管理、设备管理这几个功能。

如果采用的是大内核的体系结构的话:

  • 应用程序向操作系统提出服务的请求。
  • 此时CPU会从用户态切换为核心态,然后开始运行这一系列的内核程序。

如果采用的是微内核的体系结构的话:

  • 应用程序向操作系统提出服务的请求,接下来操作系统的这几个模块都需要为应用程序服务。
  • 进程管理模块在处理应用程序的请求时,同样也需得到内核的支持,所以该模块对内核的访问就涉及到CPU从用户态转到内核态,服务完成后,又会从内核态再转回用户态。
  • 同样的,存储管理和设备管理,这两个模块他们也在完成相应的工作时,同样也需得到内核的支持。
  • 因此,每一个模块,都需要请求内核的服务,每一次请求内核的服务都会涉及到一个CPU状态转换的过程。

总结:

  • 采用的是大内核的体系结构的话,那么应用程序的这个请求只需要两次变态即可。
  • 采用的是微内核的体系结构的话,整个过程的处理,就需要有六次变态。

CPU的状态转换,这个过程其实是有成本的,需要消耗不少的时间,因此频繁的切换CPU的状态,是会降低系统性能的。


强调:

  • 考试时:不要使用变态这个词,这里只是方便描述,使用的一种描述方式。
  • 答题时:CPU状态的转换。

4.1.4 总结

大内核:

  • 优点:性能高。因为应用程序在请求内核服务时,这个变态的过程会比较少。
  • 缺点:由于把很多功能都放在内核里,内核代码就会变得比较庞大,结构混乱,难以维护。

微内核的缺点:需要频繁的在核心态和用户态之间切换,性能会更低一些。

今年大纲又增加了分层结构、模块化的结构,以及外核,这种比较特别的操作系统结构。

考察:各种操作系统结构特性,以及每一种结构它具有什么优点或者缺点。

4.2 分层结构的操作系统结构

分层结构的操作系统,图示如下:

最底层是硬件,最上层是用户接口。

4.2.1 特性、思想

操作系统的内核会被分为多层,就像一个洋葱一样。

每一层它可以单向的调用,更低一层提供的接口。

注意:每一层,它只能调用相邻的那一层所提供的接口。

下图分为3个层次:1层、2层、3层。

  • 第1层:最接近硬件,只有第1层可以直接操作硬件。
  • 第2层:只能使用第1层,提供的一些功能接口。
  • 第3层:只能使用第2层,提供的功能接口。
  • 每一层只能去调用更低的相邻那一层,提供的功能,而不能跨层的调用。

这种分层的设计思想,造成了这种结构的一些优点和缺点。

4.2.2 两个优点

这种分层结构,很便于调试和验证,只需要自底向上的逐层调试和验证即可。

每一层只会使用到相邻的低层,所提供的功能。因此,这种分层结构的操作系统的内核开发完后,得调试、验证它的功能是否正确,调试验证就可以这么做:

  • 首先,第1层只会使用到底层的硬件,硬件厂商在出厂该硬件时,肯定已保证硬件特性没问题。所以,第1层只需要管第1层中的一些代码功能,去验证、调试一下第1层,都OK,那么第1层的调试就结束了。
  • 接下来,再对第2层功能进行调试和验证。由于第2层只会使用到,第1层提供的一些功能,刚才已经调试过第1层的这些功能和代码,确保是没有问题的。因此,第2层的调试和验证就可基于正确的第1层来进行。
  • 一层一层的往外调试,先保证更低层的功能特性是调试过的,被验证是OK的,再来调试更高一层。

这样的话,操作系统分层开发完后,一层一层的来分层调试,变得很方便,就可使得软件测试的这个过程变得很清晰明朗。

这种分层结构的操作系统内核,在设计时,肯定得先设计好,层与层之间的调用接口是什么样的?这个函数名叫什么?

  • 调用的参数需要提供哪些?
  • 各个参数的含义是什么?
  • 每一个调用又会给你返回什么东西?

这些层与层之间的调用接口,在最初设计时,已规定好,所以这些层间的调用接口固定不变。

因此,如果要在原有的两层之间,再新加一层,也是OK的,只需保证:

  • 新加的这一层,向更上层提供同样的接口。
  • 新的这一层,对于更下层的这个接口的使用。
  • 以前这个更低层已经实现了,所以,要在两层之间再扩充新的一层是很简单的,只需要保证原来定义的这个接口不变即可。

由于,这种层间的接口固定,也比较方便维护。 

4.2.3 两个缺点

分层结构的操作系统,只能调用相邻的低层,但有时又难以合理的定义各层之间的边界。

A功能有可能会调用B功能,B功能有时难免也需使用到A提供的功能。

例如:进程管理有时需要使用到,内存管理相关的一些功能。

例如:内存管理相关的功能,有时又需使用到进程管理提供的一些东西。

所以,这种很严格的,只能由高层调用低层的这种规定,就很不灵活。

  • 有时很难把操作系统内核的这些功能,把它严格的这种一层一层的给它分出来。
  • 同时,还要保证只能高层调用低层,这个很难。

效率低,由于不可以跨层调用,所以系统调用执行的时间会很长。

例如:一个用户进行系统调用,这个系统调用只需使用到最底层提供的这个功能,但最上面这一层,无法直接调用最底层提供的这个功能接口,只能一层一层的往下传递。

类似于A、D通话:

  • A和D要说一句话,但是分层结构就规定A不能直接和D讲话。
  • A只能先告诉B,B再告诉C,C再告诉D。

同时,如果D要给A回话:

  • D只能先给C回话,C再给B回话,然后B再给A回话。
  • A和D之间不可以直接的进行相互的调用,以及返回结果。

这就会导致,系统调用的执行时间变长,效率变低。

4.3 模块化的操作系统结构

4.3.1 特性、思想

模块化,是一种很经典的程序设计思想。

把操作系统的内核划分为多个模块,然后各模块之间相互协作着工作。

例如:把操作系统内核分为多个模块,每一个模块又可以进一步划分为子模块。

  • 进程管理
  • 存储器管理
  • 文件管理
  • 设备驱动程序等

整个内核有主模块和可加载内核模块来组成。

主模块:

  • 必不可少的、最核心、最重要的那些模块,没有这些模块,操作系统就无法运行。
  • 例如:进程管理、内存管理,这些重要的模块,共同组成主模块。

可加载内核模块:可动态加载到操作系统内核当中的。

例如:设备的驱动程序,就属于可加载的内核模块。

  • 是否拥有这个驱动程序,并不会影响到系统能否正常运行,只会影响到系统的一个可拓展性。
  • 如果加载了这个驱动程序的模块,系统就可支持新的设备。
  • 可加载内核模块,算是一个锦上添花的东西。

4.3.2 三个优点

模块之间逻辑清晰、易于维护,我们是按逻辑功能进行模块的划分的:

  • 有的模块专门管进程。
  • 有的模块专门管内存。

只需确定每个模块之间的接口,就可多模块同时进行开发。

例如,进程管理这个模块,需对外暴露3个功能接口。

  • 只要规定好这3个功能接口的函数的名字是什么?参数列表是什么?返回值是什么?
  • 即便该模块现还未完成开发,其他模块也可同时并进。
  • 如果我要使用到你这个模块提供的这些功能接口,我按照提前约定好的这些模块之间的接口来开发,就OK。

只要确定了各个功能模块之间的接口后,就可多个各模块同时并行的开发。

这种模块化的操作系统内核,通常都可支持动态的加载一个新的内核模块。

  • 如果想让你的操作系统支持一个新的设备,把这个设备驱动程序作为一个新的内核模块,加载到操作系统当中,就OK。
  • 如果要让操作系统支持一个新的文件系统,把新文件系统的这些代码、数据,给它作为一个新的内核模块,加载到操作系统内核里边。

这种特性,可使得操作系统的适应性增强,加载一个新的模块,不需要重新编译整个内核。

各个模块之间可直接相互调用,效率高。

  • 各个模块之间,我要使用你的功能,就直接调你的函数即可。
  • 类似平时写代码,直接调一个函数一样。
  • 这种方式,可以使得功能的调用效率非常高。

相比之下,在微内核结构的操作系统当中,就需使用到消息传递的这种方式,来调用其他模块提供的一些功能。


4.3.3 两个缺点

例如:进程管理模块,本来只提供3个功能,但开发过程中,可能会发现,其他模块好像还需使用到一个新的功能。

各个模块间,可能相互调用会比较多。因此,开发各个模块时,该接口定义,可能比较难。

我这个模块之间,你调我,我调你,我们相互调。

如果调试时发现,我这个模块好像功能出问题:

  • 到底是我模块本身的问题。
  • 还是我调用的这个东西它的问题。
  • 还是说我调了他,然后他又掉了我之后,才产生的问题。
  • 就会导致,调试和验证会比较困难。

相比之下:

  • 分层结构也是一种模块化的设计思想,每一层就是一个模块。
  • 分层结构模块之间有一个很清晰的单向依赖的关系,高层可调用更低一层。
  • 所以分层结构在调试和验证时,显然就会更容易一些。

4.4 大内核和微内核

在内核中:

  • 有进程管理、存储管理、设备管理。
  • 以及和硬件关系最紧密的中断处理、原语、时钟管理等。

所有的这些功能,都放在内核中,整个内核里边的各种功能间,也可直接相互调用,和普通的函数调用一样。

微内核:

  • 微内核只会把最基本的,与硬件最紧密的一些功能,放在内核中。
  • 大多数的功能,会被放到微内核之外。

该情况下,功能和功能之间的调用,就没有那么方便。

进程管理想调用存储管理相关的功能,只能通过消息传递的方式来进行。

  • 进程管理的模块向微内核发送一个消息,这个消息里边就是指明了,我想要调用谁?我给它的这个调用参数是什么?
  • 然后,由微内核的进程间通信相关的这个功能,把他的这个消息传递给存储管理这个模块。
  • 然后,这个模块接收到进程管理的这个消息后,他才会处理这个调用的请求。
  • 同时,如果要返回这个调用的结果,同样的也需要通过消息传递的方式,让微内核来协助各个模块之间的这种调用及返回。

显然,直接进行函数调用,肯定要比通过消息传递来通信、来调用,要快得多。

大内核通常性能会更好,而微内核的性能会更差。

 微内核由于内核更小,功能更少,所以对于内核的这个维护,就变得更简单。

同时,内核越简单,可靠性肯定也越高。

由于各个功能模块,都被放到了内核外,所以某一个功能模块出错,并不会导致整个系统的崩溃。

对于大内核:

  • 只要某一个功能模块出错。由于这些模块之间相互依赖很强。
  • 所以,内核中任何一个功能模块出错,就有可能导致整个系统内核崩溃。

4.5 外核的操作系统结构

4.5.1 特性、思想

这种结构的操作系统非常少见。

应用程序可以通过系统提供的库函数,去调用普通内核的一些功能。

应用程序也可以通过库,直接去调用外核所提供的一些功能。

这种操作系统,由内核、外核两个部分来组成。

外核负责为用户进程分配未经抽象的硬件资源,例如:

  • 磁盘的存储空间。
  • 内存的存储空间。

区别:

  • 硬件资源都是由外核来负责管理的。
  • 内核管理的都是与硬件资源分配、回收无关的工作,例如:内核只管进程调度、进程通信等。

给用户进程分配未经抽象的硬件资源,对这句话的理解:

(1)在普通的操作系统中,用户进程想申请使用一片内存空间,给他分配的内存空间,是经过抽象的、经过虚拟化的。

  • 用户进程看到的视角,似乎是自己拥有一整片、连续的内存空间,但事实上,这只是虚拟的地址空间。
  • 操作系统内核会把这些虚拟页面,映射到实际的物理页框中,这些物理页框在内存当中通常是离散的。
  • 所以,普通操作系统给进程分配的内存空间,其实是经过抽象的,经过虚拟化的。

(2)给进程分配的文件存储空间、外存空间,也是经过抽象的。

  • 对于进程来说,他觉得自己的文件,好像是连续的一个地址空间。
  • 事实上,这个文件的各个块在磁盘中,很有可能是被离散的存放的。
  • 所以,普通的操作系统,给用户进程分配的这个磁盘空间,也是经过抽象的。

外核这种操作系统,可以给用户进程,直接分配未经抽象的这种硬件资源,这样的资源管理策略有时会很有作用。

例如:一个用户进程,他很有可能知道,自己的这一片存储空间,是经常需要随机访问的。一会儿访问A这个地方、一会儿访问B这个地方。

  • 如果给这个用户进程,分配的磁盘空间,在外存空间是离散的,这儿一块、那儿一块。
  • 用户进程,在随机访问他这些文件块时,就意味着这个磁头需要来回横跳,会导致用户进程对自己的文件随机访问的这种性能、效能变低。
  • 因为用户进程的这个文件,在外存当中到底被零散的分布到哪些位置,是完全由操作系统来决定的,用户进程自己并不能控制。

如果采用外核的这种策略,外核可直接给用户进程,分配未经抽象的硬件资源。

也就是说,如果这个用户进程他知道,自己的这个文件,需要频繁的被随机访问,就可向外核申请,给我分配一整片连续的这个磁盘块。

  • 例如:从0号块到1024号块,全部都是我的。
  • 用户进程,他的这些文件数据,直接存放到连续的几个磁盘块中,之后想要随机访问自己这个文件里边的任何一块,磁头移动的这些距离就会变少,性能就会提升。

外核能够给用户进程,直接分配一个未经抽象的系统资源,在有的情况下是非常有用的。

除了外存分配,内存分配也是一样:

这是一个物理内存,在有外核的操作系统当中,一个应用程序完全可以向外核申请,给自己分配连续的一整片物理内存空间,从a到b这些地址,都是我的地盘,外核给用户进程分配的这个内存区域也是未经抽象的,用户进程看到就是一片实实在在的物理地址空间,而不是虚拟的、经过抽象的地址空间。

外核除了分配回收这些未经抽象的硬件资源外,还需负责保证这些硬件资源的使用安全。

  • 比如a到b这个空间已经分配给了P1这个进程。
  • 另一个进程P2想要访问这片区域,外核就需及时的发现并制止。
  • 同时,原本的这个进程P1,如要访问a和b之外的其他空间,这个行为也应该被外核发现并制止。

所以,外核分配了这些硬件资源后,还需要保证这些硬件资源的使用安全。 

4.5.2 两个优点

由于给用户进程分配的是不虚拟、不抽象的这种硬件资源,因此,用户进程在使用这些硬件资源的时候,就可以更灵活。

同时,这种硬件资源的分配方法,也减少了虚拟硬件资源的映射层,也提升了效率。

  • 普通的操作系统,给用户进程分配的,都是虚拟的硬件资源,例如:虚拟内存。
  • 一个用户进程,他在访问自己的地址空间时,只能提供虚拟地址,而操作系统需要去查页表,甚至查二级三级列表,通过多次的访存,才能够把虚拟地址转换成实际的物理地址。
  • 把这个虚拟地址映射到实际的物理地址,这个映射的过程就需要一些时间的代价,就会降低系统的整体效率。

对于外核这种结构来说:

  • 我给进程分配的a到b这片区域,只要进程访问的这个物理空间是在a到b内,直接访问就行。
  • 操作系统不需要帮你进行地址映射,这就可以使得效率提升。

4.5.3 两个缺点

缺点一:降低了系统的一致性。

有外核的这种操作系统中:

  • 有的进程(例如:P1)申请的可能是一个虚拟的地址空间,然后系统还要帮我映射。
  • 有的进程(例如:P2)申请的可能就是一个物理的地址空间,操作系统不需要帮他映射。

所以,给各个进程分配内存、外存分配的这种策略以及后续的一些管理,需要考虑到各种各样的情况,所以就降低了系统的一致性,会使得系统变得更加复杂。

五、操作系统的引导

5.1 操作系统的安装

开机时,如何让操作系统在电脑上运行起来?这就是操作系统引导做的事情。

在磁盘中,有哪些和操作系统引导相关的一些数据。

买来的磁盘内部,刚开始是空的。

作为计算机专业的同学,都应有能力把一个新磁盘装到电脑主板上,然后,就可在该磁盘中安装操作系统。

如果安装完操作系统,磁盘中就有可能是这个样子。

操作系统是安装在C盘里的。

除了你能看得见的这些磁盘分区之外,在磁盘的开头的位置会留出一片区域,这片区域用于存储主引导记录(MBR),里边主要包含了两个重要的东西:

  • 磁盘引导程序。
  • 分区表。

分区表:

  • 其实就是一个数据结构。
  • 这个数据结构里边说明了在你的这个磁盘当中,每一个盘,每一个分别占多大空间?以及每个分区的地址范围?

C盘安装操作系统,并且会使用这个C盘来启动操作系统。在这种情况下,我们就可以把C盘称为你的这个磁盘的活动分区。

再把C盘内部进一步细分,你会看到有两个比较特别的部分。

  • 有一片区域用于存储引导记录,叫PBR。
  • 根目录相关的数据。这个根目录就是你双击打开C盘之后看见的那些东西,根目录里边有可能会包含一些文件夹、文件,那除了这两个部分之外。

一个安装操作系统的磁盘内部大概长什么样,分为哪些部分,以及C盘的内部可以分为哪些内容。

5.2 启动操作系统

C盘里边安装的操作系统是怎么一步一步启动的?操作系统要启动,操作系统的数据肯定需要被放到主存里边。

计算机的主存由RAM和ROM两个部分组成。

平时说手机内存是多少,电脑内存是多少,通常说的是RAM。

除了RAM之外,还有ROM芯片被集成在你的电脑主板上:

  • 这个存储芯片里存储BIOS,即基本输入输出系统(Basic Input\Output System)。
  • 基本输入输出系统,就是由一系列的程序组成的。
  • 其中最重要的一个程序是就是ROM引导程序,又可以称为自举程序。

关机断电的区别:

  • RAM芯片里面的数据:只要一关机,一断电,里面的数据就被清空了。
  • ROM芯片里面的数据:不会因断电而丢失的。

开机时:

  • CPU一旦通上电,CPU就可去主存中固定位置,找到这个ROM引导的程序,即自举程序。
  • 然后执行这个程序里边的指令,一条一条往后执行。

执行ROM引导程序的作用:

  • ROM会指示着CPU去把磁盘的主引导记录,把它给读入内存。
  • 主引导记录里边有分区表、磁盘引导程序的东西,这两个东西现在都被读到主存中。

接下来CPU就可去执行主存中的磁盘引导程序:

  • 磁盘引导程序会根据分区表,去判断C盘所处的位置。
  • 接下来,就可读入C盘的第一部分的数据,即读入PBR引导记录,这个引导记录本质上也是一个程序。

接下来,CPU就可去执行这个引导记录里边的程序:

  • 执行这个程序的主要作用:会负责找到启动管理器。
  • 启动管理器它又是另一个程序,这个程序通常存放在根目录下面的某个位置。

接下来,从根目录里面找到这个启动管理程序,然后CPU再执行这个启动管理程序。

接下来,这个启动管理程序就会完成操作系统初始化的一系列的工作。

操作系统引导的过程,大致上可把它分为这样的四个主要步骤:

第一步:

  • CPU首先从一个特定的主存地址开始,去取指令。
  • 因为这个ROM引导程序,是存储在某一个特定的位置,固定不变的。从这个位置开始,一条一条往后执行这些指令,这些指令就是ROM引导程序。
  • 通常这个ROM引导程序,会先进行硬件的自检。检查有没有插磁盘?有没有插内存条等?即进行硬件的自检。
  • 所有硬件都没问题后,才会进行下一步。

第二步:

  • 会把磁盘当中的第一块,即主引导记录给读入内存。
  • 同时执行这个磁盘引导程序。
  • 扫描分区表,找到这个活动分区,也就是安装了操作系统的分区。

第三步:

  • 只要找到了活动分区的起始地址,接下来就可以去读入这个活动分区的第一个部分的内容,也就是引导记录。
  • 把这个引导记录读入内存之后,就可以执行这个引导记录里边的程序。
  • 这个程序完成的事情,会从根目录下面,去找到完整的操作系统初始化程序,即启动管理器。

第四步:

  • 最后再执行,操作系统初始化的这个程序。
  • 就可以完成一系列开机的动作。

这就是操作系统引导也就是开机的过程。

5.3 操作系统初始化程序

如果使用Windows操作系统的话:

Boot文件夹里的东西:操作系统初始化程序相关内容。

上面第四步,本质上:

  • 要从根目录C盘出发,然后找到操作系统初始化程序。
  • 接下来,在运行这一系列的程序,从而完成开机的那个动作。

开机时,电脑在转圈圈,正在启动,该画面在背后执行的就是这些个东西。

六、虚拟机

6.1 总括

传统计算机:

  • 在一个物理机器上只能运行一个操作系统。
  • 操作系统之上,又可以运行各种各样的用户进程,看起来似乎是很合理的。

某些商业化环境中,会导致硬件资源利用率不充分的问题:

  • 例如:拥有一个非常强大的一个物理机,性能很好,但这台机器只能运行一个操作系统。
  • 该机器,如果想租给其他人用,其他人要运行东西,只能在同一个操作系统之上运行。

例如:王者荣耀游戏服务器、吃鸡游戏服务器,二者必须放在同一个操作系统上来运行。

  • 两个进程在同一个操作系统上,有可能会有一些安全隐患。
  • 可能会相互影响,也会相互的争夺操作系统管理的这些资源。
  • 在商业环境当中,两个应用同时在一个操作系统上运行,这可能会造成一些隐患。

解决方法一:

  • 把其中一个应用,迁移到另一台物理机器上。
  • 这台物理机器上,有一个独立的操作系统。
  • 两个应用就不可能相互影响。

缺点:

  • 导致物理机器硬件资源的极大浪费。
  • 很多商业级的这种物理机器,硬件性能是很强的。
  • 同一个硬件性能本来可同时支持两个进程使用,绰绰有余。
  • 为了安全起见,不得不浪费,把其中一个进程迁移到另一台机器上去运行。

所以,传统的这种结构,一个机器上只能安装一个操作系统,会带来很多应用上的限制。

因此,就有人发明了虚拟机。

要把一台物理机器,虚拟化为多台虚拟机器,需使用到,虚拟机管理程序,缩写VMM。

接下来,我们会使用虚拟机管理程序,这个术语。

虚拟机管理程序,分为两类。

6.2 第一类的虚拟机管理程序

第一类的虚拟机管理程序:直接运行在硬件之上。

由虚拟机管理程序,会把一个物理机器虚拟化为多台虚拟机器,把一个总的硬件资源划分为多个部分,分别给各个虚拟机来使用。

每一台虚拟机器上面,可安装各自的操作系统,例如:

  • 第一台虚拟机上面:安装windows。
  • 第二台上面:安装Linux。
  • 第三台上面:又安装windows。

在每个操作系统之上,又可以运行各自的用户进程。

如何把一个机器上的硬件资源,分配给各个虚拟机器?

一个单核CPU,也可模拟多台虚拟机器。

  • 只需把这个CPU的时间片,进行划分,每个虚拟机器分配若干个时间片。
  • 在上层的操作系统看来,似乎给自己分配的就是一个独立的CPU。
  • 事实上,只是把这个物理CPU的某一些时间片,分配给他了而已。

磁盘和内存的分配:

  • 只需把磁盘空间划分出来,分配给各个虚拟机器。
  • 内存空间划分出来,分配给各个虚拟机器。

每个虚拟机器,都可以拥有各自独立的硬件资源。

类似于传统计算机中,操作系统把硬件资源进行划分:

  • CPU:按时间划分为时间片。
  • 内存、磁盘等:划分为不同空间,分配给各个进程。
  • 这样每个进程,也可拥有自己独立的资源。

第一类虚拟机管理程序:

  • 类似传统的操作系统一样,会负责直接管理这些硬件资源。
  • 并且分配这些硬件资源,这只是一层更深的套娃而已。

只有虚拟机管理程序,是运行在内核态的,只有它可使用特权最高的那些指令。

上层的操作系统和应用程序,实际上是运行在用户态的。

导致的问题:

  • 上层操作系统不知道自己运行在用户态,以为自己运行在内核态。
  • 所以,上层的操作系统,依然会使用一些特权指令。

只有虚拟机管理程序运行在内核态,上层的操作系统不能让他使用特权指令,怎么办呢?

  • 当上层的操作系统想要使用特权指令时,他的这个行为动作会被虚拟机管理程序给截获。
  • 然后,这个虚拟机管理程序会负责把上层的操作系统,想要执行的这个特权指令进行一些等价的转换。
  • 要给上层模拟出,好像这个指令执行成功了的感觉。

上层操作系统:

  • 运行在虚拟内核空间的,并不是真正的内核空间,实际上是用户空间。
  • 只不过他自己以为,自己运行在内核空间而已。

第一类虚拟机管理程序,直接运行在硬件之上,直接会管理硬件,分配硬件资源。

6.3 第二类的虚拟机管理程序

这一类的虚拟机管理程序,不是直接运行在硬件上的,而是运行在一个宿主操作系统上,即Host OS。

例如:在Mac电脑上,安装MacOS操作系统,还可在此电脑上安装第二类的虚拟机管理程序。

我的电脑上安装Virtual软件后,就可去下载另一个操作系统的镜像文件。

例如:在Virtual box上安装一个Linux操作系统。

  • 并且可指定要给上层安装的这个操作系统,分配多大的内存?
  • 也可给他指定,要给他分配多大的磁盘空间?

接下来,就可点击启动,然后在Mac电脑上套娃启动一个Linux操作系统。

我的电脑本身是MacOS操作系统,这就是宿主操作系统。

  • 在我的操作系统之上,安装了某一个第二类的虚拟机管理程序Virtual box,在这个虚拟机管理程序之上,我就可以安装一个客户操作系统,比如我安装的就是一个Linux,如果我愿意的话我也可以安装一个windows的操作系统。
  • 如果我再安装一个windows的这个客户操作系统的话,我可在我的电脑上同时启动两个客户操作系统。
  • 当然也可以只启动一个,只要我的硬件资源足够,我就可以在我的这个宿主操作系统之上,安装并且同时启动多个客户操作系统。

在我的操作系统之上,除了客户操作系统之外,还会同时运行着很多自己安装的一些进程。

例如:我现在正在给大家录课。

  • 我的电脑上还在运行ppt相关的进程。
  • 同时,也可以启动客户操作系统,让客户操作系统也正常的工作。
  • 客户操作系统启动了之后,就跟正常的操作系统一样,也可在上面安装软件,也可以让这些软件正常的运行,所以有时我录课,如果想要给大家演示windows操作系统的一些情况,我就会启动一个windows操作系统的虚拟机器,然后在上面安装一些windows的应用程序,给大家做这样的演示。

第二类的虚拟机管理程序:

  • 并不是直接运行在硬件之上,而是运行在宿主操作系统上面。
  • 这个虚拟机管理程序,想给各个虚拟机器分配硬件资源,那么,他只能请求操作系统给他分配,然后再由这个虚拟机管理程序进行再分配。
  • 硬件资源的管理者,依然是宿主操作系统。

第一类虚拟机管理程序和第二类虚拟机管理程序,思想各不相同。

由于他们的实现方式不同,因此就造成了一些特性上的差异。

6.4 两类虚拟机管理程序的对比

6.4.1 对物理资源的控制权

第一类虚拟机管理程序:直接运行在硬件之上的,可以直接控制和分配这些物理硬件资源。

第二类虚拟机管理程序:运行在宿主操作系统之上的,依赖于宿主操作系统,为其分配物理资源,

他要使用硬件资源,只能向宿主操作系统申请,并且被分配。

6.4.2 资源分配方式

两类虚拟机管理程序,在给上层的这个客户操作系统分配资源的时候,也会出现一些差异。

第一类虚拟机管理程序:在安装客户操作系统时,会直接在原本硬盘上,给它划分一些存储空间,直接分配给他。例如:

  • 把0号块~1024号块,全部分配给第一台虚拟机——VM1。
  • 从1025号块~2048号块,全部给他分配到第二台虚拟机——VM2。

可直接把一些未经抽象的物理资源,分配给上层的虚拟机器,类似于外核的分配方式。

相比之下,第二类的虚拟机器,向宿主操作系统申请物理资源的时候,宿主操作系统给他分配的这个资源是经过抽象的。

例如:第二类虚拟机管理程序想要给第一台虚拟机,分配10GB的磁盘存储空间,虚拟机管理程序会向操作系统申请,创建一个大小为10GB的大文件。

  • 该大文件具体存放在磁盘当中的哪些位置,是由宿主操作系统来决定的。
  • 虚拟机管理程序在获得了这10GB的大文件后,又会把这10GB的空间给分配给第一台虚拟机器,让第一台虚拟机器以为自己好像拥有10GB的磁盘存储空间一样,但事实上这只是一个虚拟磁盘。
  • 这10GB的空间,在磁盘当中未必是连续的,背后对应的是宿主操作系统、文件系统当中的一个大文件而已。

这是对于外存的分配,那对于内存的分配也一样。

例如:第二类的虚拟机管理程序向操作系统请求,给他分配一片内存空间。

  • 宿主操作系统给他分配了4GB的内存空间,但这4GB事实上是虚拟内存,各个页面也许是离散存储的。
  • 这个虚拟机管理程序,获得了这4GB的虚拟内存后,又可以把这4GB的虚拟内存拆分,分给不同的这个虚拟机器,比如第一台虚拟机器分2GB,第二台虚拟机器又分2GB,它就是这么干的。
  • 就是多增加了一层套娃,它本身得到的也是一个虚拟的硬件资源,然后他又把这个虚拟的硬件资源再虚拟、再分配给各个虚拟机器。

6.4.3 性能 

显然,这会导致第一类虚拟机管理程序性能更好。

  • 因为每进行一次硬件的虚拟化,就意味着上层的这些用户操作系统,在使用这些硬件资源时,他的这个地址,需要先映射为虚拟机管理程序,获得的这个虚拟地址空间。
  • 然后,宿主操作系统又需要把这个4GB的虚拟地址空间,再给映射到实际的物理地址空间,经过了多层的虚拟,意味着在使用这些资源的时候,又需要经过多层的映射,才可以对应到最终的一个物理地址上,就会导致第二类的虚拟机管理程序它的性能更差,总是需要通过这个宿主操作系统作为中介。

6.4.4 可支持的虚拟机数量

在一台机器,它的硬件资源确定固定不变的情况下,第一类的虚拟机管理程序往往可以支持更多台的虚拟机,原因如下:

(1)第一类的这个虚拟机管理程序,它拥有所有的硬件资源。

(2)第二类的虚拟机管理程序,他自己的资源需要跟宿主操作系统申请,宿主操作系统本身也需要一些硬件资源,同时它上面的这些进程也需要使用一些硬件资源。

所以,在硬件资源相同的情况下,第一类的这个虚拟机管理程序可以支持更多台的虚拟机。

6.4.5 虚拟机的可迁移性

第一类的虚拟机管理程序:要迁移虚拟机。

例如:要把这台虚拟机器,迁移到另一个虚拟机管理程序之上,这个迁移的动作是比较麻烦的。

而第二类的虚拟机管理程序,要迁移一台虚拟机器就非常方便。

例如:我自己的这个虚拟机管理程序上,我要迁移这个Linux操作系统,我要把我的这个操作系统给你用,我只需要把这个操作系统导出,导出为ISO镜像文件。你直接把我这个镜像文件拷过去,在你自己的Virtual box上面加载、安装,你就可以使用到跟我这边完全一样的操作系统了。

包括我在我的这个客户操作系统上,安装的一些程序、数据等,都可以一起打包给你,然后你可以很方便的迁移到你自己的Virtual box上。

正是由于它的可迁移性好,所以,第二类的这个虚拟机管理程序,它的商业化应用目前来看是更广泛一些的,使用起来会很灵活。

6.4.6 运行模式

第一类的虚拟机管理程序,它运行在最高的特权级别,可以使用最高特权的指令。

以前,当我们介绍指令时,只是把指令分为了非特权指令和特权指令,这样的两个特权级别。

但在近几年的CPU当中,指定的特权集可能会被进行更多层次的划分,例如,划分为:

  • 0级:Ring 0
  • 1级:Ring 1
  • 2级:Ring 2
  • 3级:Ring 3

Ring 0→Ring3:越往上,特权级越低。

Ring 3→Ring0:越往下,特权级别就越高。

所以,第一类的虚拟机管理程序运行在0环(Ring 0),指的就是它运行在最高的这个特权级别,

它可以使用最高特权的一些指令。

把指令划分为更多的特权级别是有好处的,例如:

  • 如果指令只分为非特权指令、特权指令,任何一条特权指令的这个使用,都需要虚拟级管理程序把它截获,并且判断这条特权指令的使用是否合法,如果合法的话,还需要由他来模拟出特权指令执行的一个效果。
  • 但是如果对指令的特权集,进行更精细化的分类,让第一类的虚拟程序运行在最高特权级(Ring 0),把这个最高的特权级别称为0环。
  • 然后让这个客户操作系统的内核,运行在一环,次高特权级。
  • 接下来让用户进程运行在二环,就是最低的特权级。

进行了这样的划分,就可以保证当上层的客户操作系统,它使用二环、一环的这些指令时,我这个虚拟机管理程序不用管,直接让你执行了。

除非还的指令,我才帮你检查这些0环的指令,应不应该让你执行,会不会导致不安全等等。

这就相当于把原本的特权指令进一步的进行了细分:

  • 把特权指令当中的一些敏感指令放在0环。
  • 然后把一些不敏感的指令放在一环。 

这样的话,虚拟机管理程序不用检查每一条特权指令执行,只需检查某一些敏感指令的执行即可。

在第一类的虚拟机管理程序当中,只有虚拟机管理程序是运行在最高特权级的。

上层的操作系统,运行在次高优先级。

第二类虚拟机管理程序:

  • 一部分运行在用户态。
  • 一部分运行在内核态。

在内核态的这个部分,是以虚拟机驱动程序的方式加载到操作系统内核当中的。

当上层的这个应用程序发出系统调用,比如说要write系统调用的时候,上层的这个操作系统,不可直接去读写磁盘。

所以,用户进程的系统调用,会被虚拟机管理程序截获,然后由虚拟机管理程序进行一些处理,代替它,向底层的这个宿主操作系统发出write系统调用,来请求底层宿主操作系统的服务。

无论怎么看,第二类的虚拟机管理程序,它的性能肯定都会更差一些。

这个宿主操作系统,它总是要作为一个工具人,作为一个中介来被使用。


http://www.ppmy.cn/devtools/157059.html

相关文章

Linux——基础命令2

1、用户 Linux是一个多用户多任务操作系统,任何一个要使用系统资源的用户,都必须首先向系统管理员申请一个账号,然后以这个账号的身份进入系统。 Linux系统支持多个用户在同一时间内登陆,不同用户可以执行不同的任务&#xff0c…

群晖NAS如何通过WebDAV和内网穿透实现Joplin笔记远程同步

文章目录 前言1. 检查群晖Webdav 服务2. 本地局域网IP同步测试3. 群晖安装Cpolar工具4. 创建Webdav公网地址5. Joplin连接WebDav6. 固定Webdav公网地址7. 公网环境连接测试 前言 在数字化浪潮的推动下,笔记应用已成为我们记录生活、整理思绪的重要工具。Joplin&…

(苍穹外卖)项目结构

苍穹外卖项目结构 后端工程基于 maven 进行项目构建,并且进行分模块开发。 1). 用 IDEA 打开初始工程,了解项目的整体结构: 对工程的每个模块作用说明: 序号名称说明1sky-take-outmaven父工程,统一管理依赖版本&…

​PDFsam Basic是一款 免费开源的PDF分割合并工具

PDFsam Basic 是一款功能强大的 PDF 工具,专为满足用户对 PDF 文件的各种操作需求而设计。它能够高效地拆分、合并、提取页面、混合以及旋转 PDF 文件,为用户提供灵活的文档处理解决方案。 合并 PDF 文件 PDF 合并是 PDFsam Basic 最受欢迎的功能之一。…

C++中的based for 循环

文章目录 范围基 for 循环(Range-based for Loop)语法格式例子1. 遍历数组2. 遍历 std::vector3. 使用引用避免拷贝4. 使用常量引用 特殊用法5. 遍历 std::map 或 std::unordered_map 总结 在 C 中,based for 循环并不是一种标准的语法&#…

Jmeter接口自动化测试

之前我们的用例数据都是配置在HTTP请求中,每次需要增加,修改用例都需要打开JMeter重新编辑,当用例越来越多的时候,用例维护起来就越来越麻烦,有没有好的方法来解决这种情况呢?我们可以将用例的数据存放在cs…

某团面试题①—kudu读写流程

kudu 读写流程 前言 为什么会有kudu?先贴一个经典的图。 kudu诞生之前大数据的主要2种方式存储 静态数据 以hdfs引擎作为存储引擎,适用于高吞吐量的离线大数据分析场景,缺点是实现随机读写性能差,更新数据难 动态数据 以Hbase…

利用真值表法求取主析取范式及主合取范式

利用真值表法求取主析取范式及主合取范式 程序结构 ├─ code │ ├─logical_operation │ │ ├─__init__.py │ │ ├─Algorithm.py │ │ └─operator.py │ └─main.pycode:主程序文件夹 logical_operation:定义与逻辑运算有关的符号…