嚎羸的博客

因为Hexo是静态博客,部署多有不便,建议查看我的语雀文档

0%

操作系统

本笔记来自王道考研

操作系统的概念、功能和目标

操作系统的概念

其实经过了上面的历史发展的讲解,我们可能对计算机的发展有了一个大概的了解,但是现在还是要理解一下操作系统的层次结构

image-20210130090345556

所以操作系统从层次结构上来说,是这样的:

1、操作系统介于硬件和应用程序之间,负责协调硬件和软件等计算机资源

2、操作系统属于软件而不属于硬件

3、操作系统负责封装底层,为上层的应用程序和用户提供简单易用的服务

image-20210130090813638

我们看,上面就是操作系统的资源管理的一种具体表现方式:资源管理器

资源管理器中明确列举了计算机中的硬件和软件,并且给出了它们的占用情况

虽然在直观上来看是一个平面的,但是其实硬件、操作系统、软件、用户,这几个部分是分层的


操作系统的功能和目标

概述

image-20210130091206606

我们看上图,操作系统对于两方、对于上方、对于下方,其实都有自己的角色:

1、对于应用程序和硬件来说:操作系统是系统资源的管理者,包括硬件、软件、文件等

2、对于单独的软件来说:操作系统是软件要调用硬件功能的一个入口

3、对于单独的硬件来说:操作系统是对硬件的封装,需要对硬件资源作为一个调配,需要在硬件的基础上实现某些功能


作为系统资源的管理者

操作系统对于硬件和软件来说,有一个共同的角色,就是系统资源的管理者,包括管理硬件、软件等

我们知道,在一个可执行文件:比如QQ.exe,它首先需要一个单独的进程去管理,执行的时候需要放到内存中,然后由CPU去调度执行

那么我们想要运行一个程序,要经过如下过程:

1、在文件夹中找到QQ.exe

这个过程就是操作系统的==文件管理==

2、双击打开QQ.exe

这个过程就是将程序放到内存中,涉及到操作系统的==内存管理(存储器管理)==

3、QQ正常运行

这个过程中,我们的CPU去执行了对应的QQ进程

但是我们的计算机中肯定不止是QQ一个进程,所以我们需要涉及到CPU的调度,关于什么时候执行QQ进程,什么时候去执行其他进程,这都是操作系统的作用:==处理机管理==

4、正常使用

假如我们需要视频聊天,我们肯定会占用,这个时候也是由操作系统来进行硬件设施的分配的

这就涉及到了操作系统的==设备管理==

所以操作系统提供如下功能

image-20210130092352662


作为用户和硬件之间的接口

那么操作系统对于应用程序和软件来讲,其实是对硬件访问的一个接口

那么操作系统对上层提供的接口有三个:

1、命令接口

2、程序接口

3、GUI

命令接口

命令接口允许用户直接使用,其中命令接口又分为==联机命令接口==和==脱机命令接口==

  • 联机命令接口的意思是用户说一句,系统做一句

  • 脱机命令接口的意思是用户说一堆,说完之后系统做一堆

举个例子:

image-20210130092926473

上面就是联机命令接口,也就是我说一句,它做一句

所以说联机命令接口也叫做==交互式==的命令接口

脱机命令接口,比如我们现在写了一个.bat文件,然后一次执行,这就是脱机命令接口,所以我们也常说为==批处理==命令

程序接口

和命令接口不同,程序接口是我们不能够直接使用的,但是我们可以使用程序来==间接使用==

比如说,windows中的一堆.dll文件,我们不可以直接使用,但是我们可以通过程序间接调用

这个过程就是我们的==系统调用==,系统调用也被称为==广义指令==

GUI

也就是图像,图形用户界面


作为硬件的上层封装

作为最接近硬件的层次,主要实现的功能就是对硬件机器的拓展

对计算机来说,没有任何操作系统安装的计算机叫做裸机,我们现在的电脑至少都会在裸机上面安装一个操作系统,作为硬件的管理调用


操作系统的四个特征

概述

操作系统有四个特征:并发、共享、虚拟、异步

其中并发和共享是最基本的两个特征,而且它们互相为存在条件,也就是说要么同时存在,要么同时消失

并发

并发指的是多个事件在同一时间发生,这些事件==在宏观上是同时发生的,但是在微观上是交替发生的==

举个例子,我早上要吃早饭,这个东西就是宏观上的理解,但是事实上我可能有时在吃饭,有时在喝水,这就是微观上的

与之相对的是并行的概念,并行的意思就是在微观上也同时发生,比如我在同一时间即在吃饭,也在看手机

我们知道,CPU在同一个时间点只能运行一个程序,所以我们假如只有一个CPU,想要运行多个程序,只能通过CPU的快速切换来保证我们看到的是同时运行,但其实它们在快速交替执行

共享

共享,也就是资源的共享,当我们的计算机中存在多个程序运行时,可能这些程序都需要某一些或某一种资源,那么在这种情况下,计算机的资源是有限的,共享就称为了不可缺少的东西

那么共享有两种:==互斥共享、同时共享==

  • 互斥共享的意思是:一个程序抱有,其他程序就不能使用了,比如摄像头

  • 同时共享的意思是:多个程序可以同时使用这些资源,比如文件的读取

这个同时共享其实也有可能是并发的状态,而不是并行的状态

但是有些时候会出现这样一种情况:比如我在读取一个文件,这个时候要进行文件的删除,那么操作系统会提示你它正在使用,不能删除

那么这个时候我可以使用多个文件查看器来进行查看,是一种共享的策略

在这个时候我不能进行删除,那么这是一种互斥的策略

所以有些时候,是某些共享但同时互斥


虚拟

虚拟的意思是将物理上的实体变为若干个逻辑上的对应物。物理实体是真实存在的,而逻辑对应物是用户能够感觉到的

比如现在有程序A需要4G,程序B需要1G,程序C需要2G吗,……

这些程序运行起来远远大于4G,但是事实上它们是可以在电脑上同时运行的,那么这就需要我们的虚拟存储器技术,是一种==空分复用技术==

那么假如在一个单核CPU上,运行了很多软件,那么我们在宏观上看确实是并发的,这是==时分复用技术==

空分复用技术和时分复用技术在后面会讲解

异步

异步的意思就是,在多个程序中,允许多个程序并发执行,但是由于资源有限,程序执行并不是一个程序就一次执行完成,而是执行完这个程序一些可能要去执行下一个程序,然后再回来,然后在过去。。。。

一个进程的执行总是走走停停,这就是程序的异步性


操作系统的发展和分类

手工操作阶段

计算机刚刚被发明出来时,没有操作系统,程序员需要在纸带上进行打孔,然后将纸带装到纸带机上读取,然后给CPU处理,最后CPU执行完成之后交给纸带机输出到纸带上

这个阶段有一些缺点:用户独占全机,人和机器的速度不一致导致资源利用率极低

单道批处理系统

程序员可以提前将纸带打好,放到纸带机上,这时候会有一个专门的外围控制机读取到一个磁带上。这个时候CPU会一个一个从磁带中读取纸带,然后处理完成之后交给另一个用于输出的磁带,然后磁带逐个将纸带通过控制机进行输出

在这个时候,引入==脱机输入/输出==,缓解了一些人机速度的矛盾,资源的利用率有所提升

但是单道批处理系统有一些缺点:

内存中只能有一个程序来被执行,只有等到这个程序执行完成之后,下一个程序才会加载到内存中被CPU执行

在这种情况下,很有可能发生CPU做完了,I/O正在输出的情况,或者CPU正在执行,I/O设备不用但是仍然被占用的情况

资源的利用率仍然不够

多道批处理系统

在这种情况下,程序员首先将纸带放到磁带中,然后计算机会从磁带中一次==读取多道程序==放到内存中,然后CPU会并发执行,那么在并发执行的过程中自然也就需要==中断技术==的支持,那么各个程序需要并发执行,也就是交替执行

实现了这个功能,我们说在这个阶段,操作系统才算正式诞生

多道批处理系统有很大的优点:并发执行,共享计算机资源。资源的利用率大幅提升,CPU和其他资源一直会处于执行状态

但是这个时期还是有一些缺点:

用户响应时间长,没有人机交互的能力(提交之后只能等待处理完成,不能控制自己的作业执行)

分时操作系统

分时操作系统,就是计算机以==时间片==为一个单位,轮流为各个用户(作业)进行服务,并且各个用户可以通过终端对计算机进行交互

比如每隔50ms就切换到下一个用户,轮流提供服务

那么在这种情况下,多个用户可以同时使用一台计算机,并且用户对计算机的操作相互独立,感受不到别人的存在

但是这种情况下还是有缺点的:

不能够处理一些紧急的任务,只能通过下一个时间片轮转过来才能处理任务。每个任务在计算机看来都是平等的

实时操作系统

解决了分时操作系统中,紧急任务不能优先处理的问题,在这个操作系统下,紧急任务不需要时间片来进行排队

保证了操作系统紧急事情优先执行

那么实时操作系统的主要特点就是:及时性、可靠性

实时操作系统有两种:

  • 硬实时操作系统:必须严格遵守,在规定时间内必须完成任务
  • 软实时操作系统:偶尔可以违反时间规定

操作系统的运行机制和体系

操作系统的运行机制

指令

首先要介绍一下指令的概念

指令其实就是我们计算机中的CPU可以识别并且执行的最基本的命令,比如我们写程序x = x + 1,到最后会识别成机器指令,去让CPU去执行,而这些指令都是二进制的

在我们的计算机中,有很多不同的指令,但显然指令和指令是不同的,比如有一些普通的加减乘除这种运算指令,也有一些造成后果很严重的,比如内存清零指令

假如我们不给指令一些权限的划分,那么可能就会产生一个用户删除了另一个用户数据的情况发生、

所以根据这种情况,我们将指令划分为两种:

  • ==特权指令==:比如内存清零的指令,需求权限较高,不允许普通用户执行
  • ==非特权指令==:比如加减乘除,属于一般的指令

CPU的状态

我们刚才说到,指令分为两种,其中特权指令需求的权限比较高,一般用户不能执行,那么现在有了问题:处理器是如何分辨当前的用户是否可以执行对应的指令呢?

这就涉及到处理器的两种状态:

  • ==用户态(目态)==:此时CPU只能执行非特权指令
  • ==核心态(管态)==:可以执行所有指令

那么CPU的这两种状态,其实是由==程序状态寄存器(PSW)==中某一个标志位来规定的,比如0为用户态,1为核心态


两种程序

有些程序需要核心态去执行特权指令,有些程序只需要用户态去执行非特权指令

那么对应的两种程序:

  • ==内核程序==:在核心态去执行所有指令,这种程序是系统的管理者
  • ==应用程序==:在用户态去执行非特权指令

操作系统内核

操作系统的内核

我们在上面说,操作系统有一些程序,或者说功能,是需要在核心态来执行特权指令的

那么这就又牵扯到一个问题:什么样的程序需要用到特权指令呢?

我们来看一张图片:

image-20210130164152646

很明显,在这个图片当中,把操作系统分为了两块,一块是需要用到特权指令的内核,一块是不用必须存在的一些非内核功能


操作系统的内核结构

举个例子

我们的任务管理器就属于非内核功能,少了它操作系统仍然可以非常轻松地操作硬件,多了它只是锦上添花

我们的CPU切换就属于内核功能,没有了它,操作系统不可能对上层提供正常的服务

所以我们再次来看:

image-20210130164601635

解释一下:

1、时钟管理:我们的时间片,一个进程到底应该占用CPU多长时间才去切换到下一个进程,和时钟管理有关

2、中断处理:一个程序应该如何停止,交给下一个进程,和中断处理有关

3、原语:具有原子性,也就是说要么一次执行完毕,要么不执行

4、进程管理、存储器管理、设备管理就不用说了


大内核和微内核

那么我们再说一次内核:

内核是计算机上配置的底层软件,是操作系统最基本、最核心的部分,实现操作系统的就是这些内核部分

image-20210130164931803

对于操作系统来说,不同的操作系统的划分是不同的

有一些操作系统可能只包含上面的第一个框中的部分

有一些操作系统可能还包含下部分甚至更多

那么对于这种,又引出两种划分:==大内核、微内核==

对于只有时钟管理、中断处理、原语的内核,我们称为微内核

假如存在对系统资源进行管理,甚至更多的功能,我们称为大内核

image-20210130165423152

假如现在有公司A,管理层的人负责大多数事情

比如对于采购方面来讲,管理层的人直接就拿钱进行了采购,这样是效率高,但是不成规模结构,不成体系

对应到计算机来说,所有的事情都运行在核心态,这样办事效率会很高,但是对于内核代码来说会非常庞大,不便维护

假如现在有公司B,管理层和业务层结构分明

还是采购方面,管理层的人要求业务层去采购,那么业务层要去和管理层申报采购什么东西,等批下来之后去采购,再去向管理层申请经费,最后才能买回来

对应到计算机来说,就是频繁在用户态和核心态之间进行切换,这样办事效率虽然会降低,但是体系分明,结构清晰

其实对于操作系统来说,没有一定靠谱的内核,只有在不同情况下的不同选择


中断和异常

中断的诞生

在我们讲计算机的发展历史中,曾经讲过,计算机一开始是没有并发这个概念的

那个时候的计算机只能串行执行,也就是说执行完成一个之后再去执行其他的

随着科技的进步和发展,并发出现了,多个程序可以加载到内存中,然后由CPU挑选执行

但是这样多个程序的等待执行会出现一些问题:我该如何从这个程序到达另一个程序呢?这就是中断

中断保证了多道程序的并发执行,它的本质其实就是==操作系统的介入,开展管理工作==


中断的举例

中断其实不止一种,它存在多种中断方式,现在用一个例子来说明:

1、现在有三个进程A、B、C在内存中等待CPU执行

2、首先CPU进入核心态将使用权限交给操作系统

3、操作系统将CPU交给第一个进程A,CPU进入到用户态,让进程A执行对应的任务

4、一段时间过后,==CPU的计时设备发出中断信号==,提示时间片到期

5、当前任务暂停,CPU立刻进入到核心态,并且将CPU的使用权限交给操作系统

6、操作系统对中断信号进行处理,发现是时间片到期了,那么将CPU分配给进程B,CPU切换到用户态进行B的处理

7、进程B可能需要用到I/O设备,但是在用户态没有操作的权限,于是==进程B使用系统调用的方式发出内中断信号==

8、当前任务暂停,CPU立刻进入到核心态,并且将CPU的使用权限交给操作系统

9、操作系统对中断信号进行处理,发现要使用I/O设备,那么操作系统会令I/O设备工作,并暂停进程B的运行

10、在进程B占用I/O设备的同时,操作系统将CPU交给进程C,CPU切换到用户态执行进程C的任务

11、这时候可能I/O设备完成任务,那么发送==中断信号==

12、当前任务暂停,CPU立刻进入到核心态,并且将CPU的使用权限交给操作系统

13、操作系统发现I/O设备运行完毕,那么返回到进程B进行任务的继续,直到时间片分配完成

14、…..


特点

通过刚才的这个例子,我们发现有几个特点:

1、当中断发生后,CPU立刻进入核心态,并将控制权交给操作系统

2、当中断发生后,当前进程的任务暂停运行,并由操作系统对中断进行处理

3、对于不同的中断信号,操作系统有不同的处理方式

当发生了中断后,操作系统就介入了工作。由于操作系统的管理工作(比如进程切换、I/O调度)需要用到特权指令,所以CPU此时会切换到核心态,也就是说中断可以让CPU从用户态切换到核心态,让操作系统获得对计算机更高的控制权。

注意:用户态到核心态是通过中断来实现的,并且==中断是唯一的途径==

而核心态到用户态的转换,只需要一个特权指令,将程序状态字(PSW)的标记为设置为用户态即可


中断的分类

中断可以分为:==内中断、外中断==

1、内中断:也称为异常、例外、陷入,其中内中断又分为两种

  • 自愿中断:指令中断,比如需求I/O设备
  • 强迫中断:比如硬件的故障,软件的中断(比如除零)

2、外中断:是外设请求或者人工干预,外中断也可以称为中断,这个中断是狭义上的中断

内部中断和外部中断的区分,关键是看中断信号是来自CPU还是来自外部

比如请求I/O就是内部中断。而I/O执行完成发送的中断信号就是外部中断。


对外部中断的处理过程

注意,这里说的是外中断

假如现在CPU在用户态执行指令,每执行一条指令,CPU就会去检查是否有外部中断信号进行处理

假如有外部中断信号,那么

1、首先进行状态的保存(比如程序状态字PSW、程序计数器PC、各种通用寄存器)

2、然后让用户程序暂停执行

3、根据终端类型信号转入对应的中断处理程序,同时CPU进入核心态

4、处理中断

5、CPU进入到用户态,恢复到刚才保存的状态

6、继续执行


系统调用

概述

在之前,我们已经讲过了一次系统调用,当时是这么说的:

程序接口是我们不能够直接使用的,但是我们可以使用程序来==间接使用==,这个过程就是我们的==系统调用==,系统调用也被称为==广义指令==,这是因为使用了系统调用它==本质会执行一个内中断==,会让用户态进入到核心态

那么我们其实可以将系统调用看成操作系统提供给我们的一组可以被应用程序调用的特殊的函数,应用程序可以通过发出系统调用请求来获得操作系统的服务

那么为什么需要系统调用呢?也就是为什么我们不能直接使用程序接口,而是需要系统调用的方式来调用程序接口呢?

这是因为只要使用系统调用的方式,那么一定会经过操作系统,操作系统进行这些请求的资源调度

我们现在想一个情况:A和B同时访问打印机,假如我们不通过操作系统的分配,最后的结果一定是打印结果混合在一起

那么现在我们经过操作系统,就会由操作系统进行资源的调度分配,不会出现结果混合的情况出现

所以系统调用的目的就是为了==让操作系统进行统一的掌管==,来保证系统的==稳定性和安全性==


系统调用的分类

系统调用很多的东西都是在核心态下的,所以一般就将系统调用划分到操作系统内核中

那么系统调用按照功能的分类,可以分为如下几种:

1、设备管理:完成设备的请求、释放、启动等功能

2、文件管理:完成文件的读、写、创建、删除等功能

3、进程控制:完成进程的创建、撤销、阻塞、唤醒等功能

4、进程通信:完成进程之间的消息传递、信号传递等功能

5、内存管理:完成内存的分配、回收等功能

这些东西都有特点:要么和资源有关系,要么和其他进程有关系


系统调用和库函数的区别

一般来说,我们使用程序可以直接使用汇编语言的形式来直接操作系统调用,但是现在我们现在的程序开发基本都是高级语言,不能直接操作系统调用,所以为了解决,专门写了一个库函数来操作系统调用,高级语言就可以直接操作库函数来操作操作系统

image-20210201100632210

最终操作的其实还是系统调用,只不过封装了一层,通过库函数来调用系统调用

但是需要注意的是,==系统调用是使用库函数来封装的,但不是所有的库函数封装的都是系统调用==


进程

进程的概述

进程的定义

程序其实就是一个指令的序列,在早期的程序中,计算机只支持单道程序,所以这个时期,程序的信息只需要放到内存中某一个固定的位置即可,比如寻找程序代码就去这个地方,寻找数据就去那个地方

在我们计算机支持多道程序时,多个程序的代码和运算数据都要存放到内存中,那么这个时候操作系统在执行程序时,要寻找这个程序对应的数据或者代码或者哪些I/O设备,就需要精确定位内存位置

为了方便在多道程序中,操作系统的管理,完成各个进程的并发执行,我们引入了进程进程实体的概念

==进程实体(进程映像)==

  • PCB:也可以理解为目录,系统为每一个运行的程序配置了一个数据结构,被称为进程控制块(PCB),用来描述进程的各种信息(比如描述程序当中的代码存放位置),用于操作系统对其管理
  • 程序段:代码本身,指令序列
  • 数据段:程序运行时产生一些数据

PCB+程序段+数据段,三者结合就是进程实体

==进程==

一般来说,我们可以将进程实体简称为进程,所以我们在创建进程,其实是创建进程实体中的PCB。而撤销进程其实是撤销进程实体中的PCB

PCB是进程存在的唯一标志

从不同的角度,进程可以有不同的定义,比较传统典型的定义有:

1、进程是程序的一次执行过程

2、进程是一个程序和数据在处理机上顺序执行时所发生的活动

3、进程是具有独立功能的程序在程序集和上运行的过程,它是系统进行资源分配和调度的一个单位

以上三点其实都在强调进程是动态的

严格来讲,进程和进程实体是不同的,进程实体是静态的,而进程是动态的。不过只要没有严格区分二者的区别,就可以理解为进程和进程实体是一码事

进程的组成

我们刚才讲过,进程其实可以理解为进程实体,那么进程实体包含的三部分:PCB、程序段、数据段

程序段:也就是指令集

数据段:也就是在程序执行时,产生的一些数据

PCB:PCB包含的内容比较多,我们知道它是一些信息,用于操作系统的管理

1、进程描述信息:一些比较基本的信息

  • 进程标识符PID:每一个进程的标识符都独一无二
  • 用户标识符UID:进程属于哪个用户

2、进程控制和管理信息:进程是资源分配的基本单位

  • 进程当前状态
  • 进程优先级

3、资源分配清单

  • 程序段指针
  • 数据段指针
  • 键盘
  • 鼠标

4、处理机相关信息

  • 各种寄存器的值

进程的组织方式

进程的组成是一个进程内部的构成,而进程的组织是多个进程间的组织方式的问题

在一个系统中,通常有数百数千个PCB,为了能够对它们进行有效管理,应该用适当的方式将这些PCB组织起来

进程的组织方式分两种:

1、链接方式:按照进程状态将PCB分为多个队列,操作系统持有指向各个队列的指针

比如执行队列、就绪队列、阻塞队列等

但是注意,因为操作系统每一刻只能执行一个程序,所以执行队列只有一个节点

但是就绪队列和阻塞队列中可以有多个节点

image-20210214090107529

2、索引方式:根据进程状态的不同建立几张索引表,操作系统持有指向各个索引的指针

类似链接方式,只不过索引方式指向的是一张索引表而不是链表

image-20210214090158430

进程的特征

程序和进程不同,进程拥有以下特性:

1、动态性:进程是程序的一次执行过程,是动态产生、变化、消亡的

2、并发性:内存中有多个进程实体,各个进程可以并发执行

3、独立性:进程是能够==独立运行、独立获得资源、独立接受调度的基本单位==

4、异步性:各个进程按照各自独立的,不可预知的速度向前推进,操作系统要提供==进程同步机制==来解决异步问题

5、结构性:每个进程都会配备PCB、程序段、数据段


进程的状态和转换

进程的状态

1、创建态:操作系统为进程分配资源并初始化PCB

2、就绪态

3、运行态

4、阻塞态

5、终止态:操作系统回收资源,撤销PCB

进程的三种基本状态

运行态(占用CPU)

就绪态(已经有运行条件但是没有CPU空闲)

阻塞态(等待某一件事情而暂时不能运行)

进程状态的转换

image-20210214094840645


进程控制

进程控制的基本概述

可以理解为,进程控制就是进行进程各个状态之间的转换

我们之前讲过,进程结构中存在PCB,进程之间的组织关系有索引、链接方式,那么我们以链接方式来看下面的图

image-20210214095448389

我们看上图,其实就是进程之间的切换方式,首先从就绪队列开始看

1、进程之间的组织方式使用链接方式,现在就绪队列中存在多个进程需要CPU进行调度

2、假如CPU进行了调度,那么进程就从就绪状态转换为运行状态

从图片上来看,其实就是就绪队列中的一个进程放到了运行队列中,并且这个进程的PCB中的状态信息更改了一下

3、假如需要用到了某些资源,那么这个进程进入到了阻塞状态

从图片上来看,其实就是将这个进程从运行队列中放到了阻塞队列中,然后这个进程的PCB信息的状态更改了一下,并且更新了PCB中寄存器的一些信息等

4、可能这个进程得到了所需要的资源:比如打印机,那么从阻塞队列中进入到就绪队列中

从图片上来看,其实就是将进程从阻塞队列放到就绪队列,并且更改了PCB的状态信息

5、就绪队列被调度,重复上面的内容

6、可能此程序运行完成,那么从运行态到终止态

操作系统回收资源,撤销PCB


上面是进程状态切换的基本过程,那么我们想:假如在进程切换的过程中出错了怎么办?比如就绪队列切换到了运行队列,但是PCB中的状态信息更改失败,这种情况会发生吗?

其实是不会的,这里就要涉及到一个概念:原语

原语的特点是:期间不允许中断,只能一气呵成执行完毕。也就是说==要么同时成功,要么同时失败==,类似MySQL的原子性


原语其实是通过==关中断指令和开中断指令==来实现的,我们来看一下过程

1、关中断指令:关中断指令结束之后,会暂时忽略外部的中断信号,以达到原语不会被中断的效果

2、原语代码1

3、原语代码2

4、…..

5、开中断指令:此时会读取外部中断信号,也就是说被被中断

通过管中断指令和开中断指令,可以完美的将中间的代码进行一次执行,一气呵成的操作

我们可以看到,关中断和开中断指令的权限是十分大的,假如可以随便使用,那么任何一个进程都可以一直霸占系统资源,所以这两个指令我们只允许在核心态下运行,显然是一种特权指令

进程控制相关的原语

无论是什么原语,要做的东西只有三个事情:

1、更新PCB中的信息

  • 所有的进程控制原语都会更改进程状态标识
  • 有一些进程会剥夺当前进程的CPU使用权,并保存运行环境
  • 有一些进程开始运行前要恢复运行环境

2、将PCB插入到合适的队列:比如就绪队列、阻塞队列中

3、分配/回收资源:比如创建和销毁进程时

那么有下面几种原语

1、创建原语

  • 申请空白的PCB
  • 为新进程分配所需资源
  • 初始化PCB
  • 将PCB插入就绪队列

2、引起进程创建的事件

  • 用户登录:分时系统中,用户登陆成功会为其建立一个新的进程
  • 作业调度:多道批处理系统中,有新的作业放入内存中,会为其建立一个新的进程
  • 提供服务:用户向操作系统提出某些请求时,会创建一个进程处理该请求
  • 应用请求:由用户进程主动请求创建一个子进程

进程通信

什么为进程通信

进程通信就是进程之间的信息交换

进程是资源分配的基本单位,为了安全,进程之间不能直接进行信息访问,但是信息有时还是要进行交换的

为了保证安全性,操作系统提供了三种进程之间进行消息传递的方式:

1、共享存储

2、消息传递

3、管道通信

共享存储

两个进程拥有一个共享空间,两个进程都可以进行访问这个空间

但是注意,进程之间访问共享存储是互斥的,一般情况下使用操作系统的P、V操作来实现互斥

共享存储分为两种方式:

1、基于数据结构的共享

2、基于存储区的共享

基于数据结构的共享

比如空间内只能放入一个长度为10的数组

这种方式效率低下,是一种低级的通信方式

基于存储区的共享

这种共享方式的意思是:在内存中划分出一块共享存储区,数据的形式、存放位置都由进程控制。

相比之下是一种较为高级的通信方式

消息传递

管道其实指的是用于连接读写进程的一个共享文件,名为pipe

其实就是在内存中开辟一个固定大小的缓冲区

需要注意的是

1、管道只能实现==半双工通信==,要实现双向同时通信,必须要设置两个管道

半双工的意思是:可以从左向右,也可以从右向左,但是同一时间只能选择一个方向

可以想象一下水管,可以从这头到那头,也可以从那头到这头,但是同一时间只能有一种方向

这个概念在计算机网络中可以进行学习

2、各个进程互斥访问管道

3、数据以流的形式写入管道

只有当管道写满之后,写进程的系统调用将会被阻塞,等待读进程

读进程将数据全部读取之后,管道变空,进入阻塞,等待写进程

4、管道数据一旦被读取,就会被抛弃,这意味着读进程最多只有一个

管道和共享数据区有所不同,管道以文件方式、共享内存以内存方式

其实我相信Java中的阻塞队列也是借鉴的操作系统,因为阻塞队列也是这样的

管道通信

进程之间会以格式化的消息(Message)为单位,进程之间是通过操作系统提供的==发送消息/接受消息==两个==原语==进行数据交换

一个格式化的消息分为两个结构:

1、消息头

报文:各种信息,包括发送和接受的进程ID、消息类型、消息长度,….

2、消息体

消息通信又分为两种方式:

1、直接通信

每个进程都有一个消息缓冲队列,这种直接通信的方式就是直接将消息挂到接受进程的消息缓冲队列上

2、间接通信

先发送到一个公共地方(信箱),然后取走

并且这个信箱中可能有很多进程互相存储或者取出任何消息,从消息头可以得到消息的进程,所以无需考虑是否会发错


进程同步和互斥

进程同步

进程有异步性,这个指的是各个并发进程以自己独立的、不可预知的速度向前推进

那么假如现在有要求:进程A要写数据,进程B要读数据,那么就必须让进程A先写数据,进程B才能读数据

如何解决进程的异步性问题,就是进程同步要讨论的内容

进程互斥

资源在一个时间内只能由一个进程来访问,这种资源被称为临界资源,这种情况被称为进程互斥

进程在访问临界资源的时候会上锁,访问资源完成之后就解锁


线程

线程概述

什么是线程

在没有引入进程之前,各个程序是串行执行的,在引入进程之后,各个程序可以并发执行

但是多进程还是不够的,因为进城本质上来讲是程序的一次执行

显然,假如一个程序想要达到QQ的效果,一次执行肯定是不够的

有很多进程是需要同时处理很多事情的,但是传统的每一个进程只能执行一件事务,所以引入了线程增加并发度

进程包含一个或多个线程,至少包含一个线程

这个时候,线程就变为了CPU执行的最小单位

所以这个时候我们说,==进程是资源分配的基本单位,线程是CPU调度和执行的基本单位==

线程引入之后的变化

1、在传统的进程中,进程是CPU的调度和执行的基本单位,也是资源分配的基本单位

但是在引入线程之后,线程变为了CPU调度和执行的基本单位,进程还是资源分配的基本单位

2、传统进程中,只能进程之间并发执行

引入线程之后,各个进程之间可以并发,线程也可以并发,提高了并发度

3、传统的进程实现并发操作需要切换进程之间的运行环境,系统开销很大

引入线程之后,假如是同一进程内的线程切换,不需要切换进程之间的运行环境,系统开销很小

线程中的重要属性

1、线程是CPU调度的单位,多核CPU中,多个线程可以占用不同的CPU

2、每一个线程都有一个线程ID、线程控制块(TCB)

之前我们讲每一个进程都有一个PCB,这里的线程是TCB

3、线程也有就绪、阻塞、运行 状态

4、进程拥有资源,线程几乎不拥有系统资源,但是同一个进程中的不同线程可以共享进程资源

5、由于共享一个进程的内存地址空间,所以同一个进程中的线程通信几乎无需系统干预

6、同一个进程中的线程切换不会引起进程切换,只有不同进程中的线程切换才会引起进程切换

7、切换同一个进程中的线程,开销很小、切换不同进程的线程其实就是切换进程,开销较大


线程实现方式

线程的实现方式有两种:

1、用户级

2、内核级

用户级

用户级线程由应用程序通过线程库实现。所有的线程管理工作都由应用程序负责,包括线程切换。

在用户级线程中,线程切换可以在用户态下即可完成。用户级线程中线程切换可以在用户态下即可完成。无需操作系统干预。

也就是说,用户级线程是在用户的角度下可以看到的

内核级线程

必须由操作系统内核完成,在核心态下完成。用户看不见

==内核级线程才是CPU分配的单位==


处理机调度

处理机调度概述

处理机调度的概念

处理机调度的意思就是说:当任务很多但是资源有限的情况下,需要确定某种规则来决定处理的顺序

三个层次

高级调度:

按照一定的原则从队列中挑选出一个作业,并建立相应的进程(PCB),让他们获得竞争处理机的权利

中级调度:

引入虚拟存储技术(后面讲),提高内存利用率和系统吞吐量

低级调度:

也称为进程调度,是实现进程并发的一个基础,进程调度的频率很高,大概几十毫秒一次

进程调度的时机

刚才我们讲过,进程调度实际上属于低级调度,那么进城调度本质上就是按照某种算法从就绪队列中选择一个进程为其分配处理机

需要进程调度的情况:

1、当前运行的进程主动放弃处理机(比如发生阻塞或者进程正常执行完毕)

2、当前运行的进程被动放弃处理机(比如时间片用完了或者有更高优先级的任务需要处理)

不能进行进程切换的情况:

1、在处理中断的过程中

2、进程在操作系统内核程序临界区中(内核程序临界区不等于临界区)

这里要补充一个概念:临界资源

临界资源指的是一个时间段内只允许一个进程使用的资源,各个进程需要互斥访问临界资源

3、在原语操作过程中

进程调度方式

1、非剥夺调度方式,又称为非抢占方式,只允许主动放弃处理机

2、剥夺调度方式,又称为抢占方式,允许更重要的进程优先占用处理机


调度算法

调度算法的评价指标

CPU利用率

CPU利用率,指的是CPU忙碌的时间占总时间的比例,也就是忙碌的时间/总时间

系统吞吐量

单位时间完成作业的个数

周转时间

作业从提交到完成的时间

平均周转时间

所有作业的周转时间平均起来得出结论

带权周转时间

(作业完成时间-作业提交时间)/作业实际运行的时间

等待时间

用户作业等待被CPU处理的时间之和

响应时间

用户提交请求到响应的时间


几种调度算法

先来先服务(FCFS)

这个算法就像他的名字,是非抢占式算法,也就是说CPU不能其他高优先的作业强占

对长作业有利,对短作业不利

短作业优先(SJF)

这个作业要求时间最短的先执行,默认是非抢占式,可以为抢占式算法

对短作业有利,对长作业不利

有可能会导致超长作业一直不会被执行的情况

高响应比优先(HRRN)

综合考虑作业的运行时间还要考虑作业的等待时间,算法是:(等待时间+要求服务时间)/要求服务时间

利用高响应比,比前面的两种算法更好

时间片轮转(RR)

轮流为各个进程服务,让每个进程在一定时间间隔内都可以得到响应,是抢占算法,也就是这个任务没执行完成就会到其他任务

缺点就是高频率的切换会导致一定的系统开销

优先级调度算法

为每一个作业设置优先级,会以优先级来设置作业执行的先后顺序,有抢占式和非抢占式两种

有可能会发生低优先级永远不会被执行的情况

多级反馈队列调度算法

对以上调度算法的折中权衡


信号量机制

概述

信号量机制可以理解为一种变量,可以让这个信号量表示系统某种资源

原语是一种特殊的程序段,执行只能一气呵成,不能够中断,通过开中断和关中断指令实现

wait(S)、singnal(S)

这两个原语可以理解为函数,S其实就是信号量,可以理解为参数

wait和signal两个原语常常简称为==P、V操作==。因此可以简写为P(S)、V(S)

那么信号量理解为资源,我们可以使用原语对信号量进行操作

两种类型的信号量

1、整型信号量

整数类型的变量,用来表示系统中某种资源的数量

和普通整数有所区别,我们对这个信号量进行初始化、P、V三种操作,可以表示具体信号量的个数,来控制资源

2、记录型信号量

表示某种状态


信号量机制实现进程同步和互斥

信号量机制实现进程互斥

1、分析并发活动的临界区在什么资源上

2、设置互斥信号量 mutex,初始值为1

3、在临界区之前执行P操作(加锁)

4、临界区之后执行V操作(解锁)

注意,对于不同的临界资源需要设置不同的信号量,也就是加不同的锁

信号量机制实现进程同步

进程是同步的,但是会出现进程B必须在进程A之后执行,那么让异步的进程互相配合,有序推进就是进程同步

我们预想中,进程A先执行,进程B后执行

1、分析什么地方需要实现同步关系

2、设置同步信号量 S ,初始值为0

3、在任务A之后执行一个V操作

4、在任务B之前执行一个P操作

image-20210220113125234


几个问题

生产者消费者问题

生产者生产一个产品,放到缓冲区中。消费者从缓冲区中取走并消费产品。

当缓冲区满了,生产者会阻塞,缓冲区无产品,消费者会阻塞

缓冲区==必须互斥访问==,否则有可能产生问题,比如:

1、多个生产者去判断缓冲区,发现还有空间,但是生产产品之后发现可能已经被占用

2、多个消费者发现缓冲区内有产品,同时取走产品,可能会发现忽然没有的情况

一般来说,我们使用P、V操作来实现互斥操作

吸烟者问题

一个系统中有三个抽烟者进程和一个供应者进程

抽烟需要三个资源:烟草、纸、胶水,要将这三个材料合成为烟卷才能抽烟

三个抽烟者中,一个有烟草、一个有纸、一个有胶水,而供应者进程无限提供这三种材料

供应者每次供应两种材料,拥有剩下那种材料的吸烟者会回去这个资源并消费,并告诉供应者进程一个信号,供应者会放另外的两种材料,往复循环,让三个吸烟者轮流吸烟

其实我们可以看到,这其实是一种资源选择的问题,其实本质上也是生产者-消费者问题,但是和之前不太一样

从我们的角度来看,我们可以这样理解:现在供应者会提供三种选择,每一种选择都是一种组合:

1、纸+胶水

2、烟草+胶水

3、烟草+纸

读者-写者问题

现在有读和写两组并发进程

当两个或者两个以上的读进程同时访问共享数据时,不会产生副作用

因为读并不会改变数据

当两个或者两个以上的写进程同时访问数据,或者当读和写同时访问数据,可能会产生副作用

所以我们的策略是:可以让读进程同时操作,但是只要涉及到写进程,那么就读和其他写进程全部退出

哲学家进餐问题

一位哲学家要么在思考,要么在进餐,哲学家思考时不会影响他人

一个桌子上只有5支筷子,分别分散在哲学家的左右两边。

image-20210220164454569

哲学家在吃饭的时候,只能拿起自己左右两边的筷子才能吃饭,吃完饭之后放下筷子。假如筷子在其他人手上则需要等待

那么其实每一位哲学家都和自己左右两边的哲学家存在互斥关系,并且需要两个资源才能够正常进行吃饭,所以可能存在死锁问题

要避免死锁

1、对每一个进程加一些限制:比如最多允许四个哲学家同时进餐

2、要求奇数号码的哲学家先拿左边的筷子,然后拿右边的筷子,偶数号码的哲学家相反


管程

为什么要引入管程

我们之前一直在使用信号量,但是使用信号量的时候我们必须关注信号量的前后顺序,写代码十分困难

那么我们可以使用管程来解决这种困难

什么是管程

管程也是解决进程之间的互斥和同步的

可以这样理解:

我们的同步资源被管程完全管理,而且只能被管程管理,管程提供了一组操作这种资源的方法

每次只允许一个进程在管程内执行某个内部过程,也就是说同一时间只能有一个进程执行管程的方法

在Java中,就是体现在我们的多线程中了,比如synchronized关键字或者Lock锁,来定义一个资源互斥访问


死锁

用一句话来说,其实就是几个进程/线程 互相抱有对方需求的资源并且不放手,就会产生死锁

死锁

拆开来讲,死锁需要如下条件

1、互斥资源

2、并发环境下

3、每个人抱有一个资源,同时在等待另一个人手中的资源,但是每个人都不会将资源放手

4、循环等待

饥饿

某一个进程长期得不到资源,导致某些进程无法向前推进,注意要和死锁区分开


内存

内存概述

存储、内存、CPU

硬盘存储、内存、CPU可以看作是连接关系

在程序没有运行的时候是放到硬盘中的,程序运行的时候是放在内存中的,然后CPU得到内存中的程序去运行任务

为什么要让存储和内存划分开呢?因为内存比存储更快,所以我们需要内存,==程序执行前需要先放到内存中才能被CPU处理==

存储单元

那么在多道程序的环境下,系统中会有多个程序并发执行,也就是说会有多个程序的数据需要同时放到内存中。那么该如何区分各个程序的数据是放到什么地方呢?

内存中也有一个一个的小房间,每一个小房间其实就是一个存储单元。

假如计算机按照字节编址,则每一个存储单元的大小为1字节,假如16字节长的计算机按字编址,那么每一个存储单元大小为一个字,每一个字的大小为16个二进制位

逻辑地址和物理地址

现在宿舍四个人一起旅行,四个人的学号尾号为0、1、2、3

住酒店的时候四个人开了四个房号相连的房间,并且按照学号的顺序入住房间。

四个人的编号0、1、2、3是一种相对位置,而入住的房间是一个绝对位置

其实我们只需要知道每个人相对于其他人的相对位置,然后知道任意一个人的绝对位置,就可以推算出所有人的绝对位置

那么相对地址又被称为==逻辑地址==,绝对地址又被称为==物理地址==

逻辑地址转物理地址

逻辑地址转物理地址有一个专业的名字叫做装入,有三种方式:

1、绝对装入

程序在编译的时候就知道要放到内存中的哪个位置,这样在指令中就不应该写相对地址,而应该是绝对地址

2、静态重定位

又称为可重定位装入。他的地址其实逻辑地址。

所以这种装入的特点就是:在一个作业装入内存的时候,必须给他分配足够的内存空间(所有逻辑地址相加之后的)

假如没有足够的内存,那么就不能装入,==作业一旦装入内存之后,在运行期间就不能够再移动==

3、动态重定位

也是一种逻辑定位地址,但是它和静态重定位是不同的,==它允许程序在内存中进行移动==,现代结构基本都是这种方式


内存管理的概念

操作系统作为系统资源的管理者,需要对内存进行管理,包括

1、内存空间的分配与回收

2、虚拟技术,从逻辑上对内存空间进行扩充

3、逻辑地址到物理地址的转换(上面说的那三种转换方式)

4、内存保护,各个用户进程都在自己的内存空间内运行,不能干扰其他进程

具体的操作是:在内存中对应的进程放一个上限寄存器和一个下限寄存器,分别记录这个进程上下范围的界限

当进程要访问一个内存地址时,首先看一下是否在内存地址范围内

除了上面这种方式,还可以使用重定位寄存器和界地址寄存器

重定位寄存器存放的是进程的起始物理地址,界地址寄存器中存放的是进程的最大逻辑地址

通过这两个寄存器就可以算出一个进程使用的内存范围


覆盖与交换

我们对内存空间的扩充技术,有几种技术

程序大小超过物理内存总和时,出现了这种思想,也就是通过一种逻辑上的改变进行扩容

覆盖技术

将程序分为多个段,常用的常驻内存,不常用的需要时调入内存

内存中会分为固定区和若干个覆盖区,常用的调入固定区并且不再调出,不常用的需要时调入,不需要时调出

交换技术

交换技术也被称为对换技术,他的思想是:内存空间紧张的时候,系统将内存中某些进程暂时换出到外存,将外存中某些已经具备运行条件的进程换入内存,这其实是一种在内存和磁盘间进行调度的技术

暂时换出外存等待的进程被称为挂起状态,挂起状态也分为就绪挂起和阻塞挂起,所以进程/线程的状态可以细分为

image-20210220223235808

在具有对换功能的操作系统中,外存一般就是磁盘,而磁盘会分为文件区和对换区两个区域

文件区主要用于存放文件,主要追求存储空间的利用率,所以使用离散分配方式

对换区主要用于对换,只占用磁盘中很小的一部分,并且要追求换入换出的速度,所以使用连续分配方式

总之对换区虽然小,但是I/O的速度比文件区更快

当内存吃紧的时候可以选择换出,当内存空闲可以不使用换出

优先考虑阻塞进程和优先级低的进程,但是注意PCB还是要在内存中


内存的分配与回收

连续分配管理方式

连续分配:系统为用户进程分配的必须是连续的空间,这种分配方式有三种:

1、单一连续分配

内存被分为系统区和用户区

系统区通常位于内存的低地址部分,用于存放操作系统

用户区通常位于高地址部分,存放用户进程的相关数据

不管原来的用户程序有多大,内存中只能有一道用户程序,用户程序独占整个用户区空间

这样的优点是实现简单,没有外部碎片

缺点是只适用于单用户单任务的操作系统,会产生内部碎片

内部碎片的意思是:假如需要10M,但是区域中有12M,那就相当于浪费了2M,这个2M就是内部碎片

外部碎片的意思是:内存中的某些空间由于太小,难以利用

2、固定分区分配

将用户空间划分为多个固定大小的分区,在每一个分区中装入一道作业,是很早的适合多道程序的策略

固定分区分配有两种:分区大小相等、分区大小不等

固定分区分配需要建立一个叫做分区说明表的东西,用来实现各个分区的分配和回收,每一个表项对应一个分区,通常按照分区大小排列。每个表项包括对应分区的大小、起始地址、状态

固定分区分配还是没有外部碎片,但是有内部碎片

3、动态分区分配

不是预先划分区域,而是在进程装入内存的时候才会根据内存大小动态创建分区

不会导致内部碎片,但是会导致外部碎片,有一些空间太小而不能利用

非连续分配管理方式

我们之前的连续分配,无论是采用什么策略,都会出现这样那样的缺点,其根本原因是:

连续分配的策略必须为用户进程分配一块连续的内存空间

这样的策略导致了内存中必然会出现这样那样的问题,因此我们提出了一种解决方案:让进程占用不连续的内存空间

而进程的地址仍然可以采用动态重定位的方式来实现逻辑地址转换为物理地址


虚拟内存

我们之前讲的针对真实内存,其实要考虑的事情很多,比如内存的分配和回收,内存空间的逻辑扩容(覆盖、交换)

其实严格上来说,我们的虚拟内存技术也是内存空间扩容的一种

image-20210221173858061

我们首先来聊一聊之前的内存管理方式,不管是对内存的连续分配还是非连续分配,尽管他们的技术点在不断升级,但是其实还是逃脱不了两个问题:

作业必须一次性全部装入到内存中才能开始运行

那么这样其实会导致两个问题:

1、作业很大时,不能全部装入内存,这样会导致大作业无法运行

2、作业量很大时,内存无法容纳所有作业,这样会导致并发下降

作业会一直驻留到内存中,直到作业运行结束

事实上,在一个时间段内,只需要访问作业的一小部分数据既可以正常运行,但是因为作业是一次性加载的,所以这样会导致内存中驻留了大量的、暂时用不到的数据,虽然不能说是垃圾数据,但是还是浪费了宝贵的内存资源


局部性原理

局部性原理分为两个:

1、时间局部性

程序中的某条指令被访问,那么很有可能在不久之后再次被访问

2、空间局部性

假如程序中某个存储单元被访问了,那么在不久之后它附近的存储单元也很有可能被访问

为什么要说这个局部性原理呢?因为我们可以使用局部性原理来解决我们之前提到过的两个问题,其实高速缓存技术就是利用这个局部性原理

image-20210221174927501

虚拟内存

虚拟内存其实也就是利用的局部性原理,程序装入时,可以将程序中很快用到的部分装入内存,暂时用不到的留在外存,这样就可以让程序开始执行

在程序执行的过程中,当所访问的信息不在内存时,由操作系统负责将所需信息从外存调入内存,然后继续执行程序

假如内存空间不够,由操作系统负责将内存中暂时不用的信息换出外存

那么这样看来,其实在逻辑上来看用户有一个更大的内存,这就是虚拟内存


文件管理

文件管理概述

文件有如下结构

1、文件名

2、标识符

3、文件类型

4、文件位置

5、创建时间、大小、修改时间、作者、….

6、保护信息(不同用户对文件的权限是不同的)

文件内部的数据组织

文件内部的数据分为两种:

1、无结构文件:二进制或者字符流组成,比如txt文件

2、有结构文件:由各个记录组成,比如excel

文件之间的组成

文件夹、文件等等的组织信息其实类似一种树状的形式,文件夹其实也算一种特殊的有结构的文件

文件的逻辑结构

所谓的逻辑结构,其实是指在用户看来,文件内部的数据应该是如何被组织起来的

所谓的物理结构,其实是在操作系统看来,文件内部的数据是如何放在外存中的

这里我们要看文件的逻辑结构,其实可以分为两类:

1、无结构文件

2、有结构文件

  • 顺序文件
  • 索引文件
  • 索引顺序文件

有结构文件是我们主要要讨论的,无结构文件其实在我们看来没有什么内部结构

顺序文件

顺序文件中的记录是一个接一个的顺序排列,可以分为顺序存储形式的存储和链式存储形式的存储

顺序存储的,也就是逻辑上相邻,物理上也相邻

链式存储的,也就是逻辑上相邻,但是物理上不一定相邻,类似链表

索引文件

每一个文件都建立一个索引表,索引表中存放物理地址,索引文件就是逻辑文件

索引顺序文件

给文件分组,然后组中以链式排列,组和组以索引表的形式排列

其实就是数组+链表形式的哈希表


文件目录

文件目录由以下部分构成:

1、文件控制块:实现文件目录的关键数据结构

2、目录结构

  • 单级目录结构
  • 两级目录结构
  • 多级目录结构(树)
  • 无环图目录结构

3、索引结点:对文件控制块的优化


文件的物理结构

文件的物理结构分为三种:

1、连续分配:逻辑上的连续,物理上也必须连续

2、链接分配

  • 显式链接:同一个索引表存放文件中的块,但是和数组不同,物理地址不一定在一起
  • 隐式链接:除了文件的第一个部分之外,其他部分都会保存指向下一块的指针

3、索引分配:为文件建立索引表,分为逻辑块和物理块

image-20210221182754254

显式链接和索引分配不同

显示链接中,文件分配表是一个磁盘对应一张,而索引分配中是一个文件对应一张