操作系统(上)

109 阅读57分钟

操作系统概念和功能

最常用的操作系统当然是Windows,当然除其之外还有更多的操作系统,如Mac、Linux等

image-20230805133356337

一台电脑如果只有硬件,那么就被称为裸机,其上安装操作系统,操作系统可以和用户和应用程序也就是软件打交道,用户也可以和软件打交道

image-20230805133453215

操作系统是指控制和管理整个计算机系统的硬件和软件资源(操作系统是系统资源的管理者)以提供给用户和其他软件方便的接口和环境(向上层提供方便易用的服务),是计算机系统中最基本的系统软件,其是计算机系统中最基本的系统软件(是最接近硬件的一层软件

image-20230805133900335

其作为系统资源的管理者,提供文件、设备、存储器和处理机四大管理功能

假设有下面的一个场景,用户需要使用QQ和朋友视频聊天,那么首先要找到QQ的执行文件,这个过程称为文件管理,执行一个程序前需将该程序放到内存中才能被CPU处理,这个过程就是存储器管理,而对于的经常需要被处理机处理,这个过程就是处理机管理,和朋友进行视频聊天需要将摄像头设备分配给进程,这个过程称为设备管理

image-20230805134255460

操作系统使用封装思想,将晦涩难懂的硬件功能封装为简单易用的服务并提供给用户

image-20230805134419818

现在很多现代操作系统都提供GUI,也就是图形化用户接口,用户可以根据形象的图形界面进行操作

image-20230805134627630

不过一般也提供命令接口方式来进行交互,其特点是用户说一句系统就跟着做一句,打开cmd进行命令的就是命令接口方式,不过其是细分下的联机命令接口,也是交互式命令接口

image-20230805134829951

脱机命令接口指的是类似于是bat的文件,其就是将一个个交互式命令接口写在一个名为bat的文件里,点击之后会自动执行该文件的所有交互命令,其又被称为批处理命令接口,特点是用户说一堆,系统跟着做一堆

image-20230805135038066

操作系统还提供策划给你续接口,该接口一般给程序员使用,在程序中可以通过系统调用来使用程序接口,比如说写C语言函数,就可以其函数调用该程序接口,该接口会和硬件交互并实现指定功能,在C语言函数底层就使用到了操作系统提供的显式相关的系统调用,其类似于函数调用,是应用程序请求操作系统服务的唯一方式

值得一提的是在有的教材中系统调用又被称为广义指令

image-20230805135203204

程序接口和命令接口有时也统称为用户接口

image-20230805135307009

通常我们将覆盖了软件的机器称为扩充机器,也称为虚拟机,类比汽车的话,操作系统做的功能就是将一个个独立的硬件联系起来,令其能形成一个整体给用户执行,当然实际肯定不是这样的,这么说是为了方便理解

image-20230805135545476

四大特征

操作系统的四大特征分别是并发、共享、虚拟和异步,其中并发和共享是两个最基本的特征,二者互为存在条件

image-20230805143307047

并发指的是时间在宏观上是同时发生的,但是在微观上时交替发生的,而并行指的是两个或多个时间在同一时刻同时发生,两者的概念注意区分

image-20230805143413477

N核CPU在同一时刻只能执行N个程序,第N个之后的各个程序只能并发地执行,并发性是操作系统最基本的特性

image-20230805143538333

共享即是资源共享,有两种资源共享方式,分别是互斥共享和同时共享,前者指的是在一个时间段内只允许一个进程访问放共享资源,比如说QQ和微信在同一时间里只能有一个使用摄像头,而后者允许在一个时间段内允许多个进程同时访问共享资源,比如QQ和微信同时发同一个文件,宏观上看就是在同时读取并发送文件,微观上其实是在交替访问硬盘文件并发送文件资源

image-20230805144203228

如果失去了并发行,那么系统中就永远只有一个程序正在运行,此时共享性就没有了意义,因此并发性和共享性互为存在条件

image-20230805144450770

虚拟指的是把一个物理上的实体变为若干个逻辑上的对应物,物理实体是实际存在的,而逻辑上的对应物则是用户感受到的,当然说这些云里雾里的没意思,我们直接举例子

比如我们的电脑只有4GB内存,但是GTA5都要4GB了,那不是运行性GTA5之后就啥也不能运行了?然而实际上的情况却是我们不但可以运行GTA5,还可以运行其他软件,这就是因为利用了虚拟技术里的空分复用技术

image-20230805144820506

而我们只有一个CPU的主机也可以同时打开多个软件,有种六核CPU同时运行这些软件的感觉,这是虚拟技术中时分复用技术的功劳

image-20230805144938515

最后值得一提的是,如果失去了并发性那么虚拟性也就同样失去了其意义

image-20230805145033543

异步指的是在多道程序环境下允许多个程序并发执行,但是由于资源有限,因此进程的执行并不是一直推进的,而是以一个不可预知的速度向前推进,这就是进程的异步性

image-20230805145443050

最后我们来看看总结

image-20230805145543226

发展与分类

操作系统简称OS,其发展经历了四个阶段,我们接着一个个说

image-20230805153129951

首先是手工操作阶段,该阶段下使用纸带来传输数据,用户独占全机不说,实际对计算机的利用率还特别低

image-20230805153346424

后来引入了脱机输入/输出技术,简单来说就是允许多个用户通过外围机器将纸带的输入传入到磁带中,计算机通过磁带进行读写,其速度比纸带机快很多,但仍不够好

image-20230805153540180

后来引入了批处理系统,首先是单道批处理系统,其使用磁带机传入传出数据,其优点是速度有所提升,缺点是CPU仍然有大量时间空闲,利用率不够高

image-20230805153701846

为了解决单道批处理系统的劣势,出现了多道批处理系统,其可以令程序并发执行,共享计算机资源,使得资源利用率大幅提升,其最大的缺点是没有人机交互系统

image-20230805153838888

为了实现人机交互系统,因此出现了分时操作系统,其可以令计算机以时间片为单位轮流为各个用户提供服务,解决了人机交互问题,但是无法处理一些紧急任务

image-20230805154000278

为了实现处理紧急任务的需求,因此出现了实时操作系统,可以优先相应一些紧急任务,这些紧急任务不需要时间片排队,其主要特点是及时性和可靠性

实时操作系统分类硬实时操作系统和软实时操作系统,前者必须要在严格的规定时间内完成任务处理,比如说在导弹控制系统和自动驾驶系统中,后者则能接受偶尔违反时间规定,比如火车订票系统

image-20230805154054083

最后就是到我们的熟知的网络操作系统、分布式操作系统和个人计算机操作系统了,这些知道就好,基本不考

image-20230805154117146

运行机制

程序运行时需要将应用程序的指令通过编译器翻译为二进制指令,这种指令称为机器指令,是CPU可以识别执行的最基本命令

image-20230805162037526

普通程序员写的程序是应用程序,而微软和苹果一帮人负责实现操作系统的人写得是内核程序,多个内核程序组成了操作系统内核,简称内核,内核是操作系统中最核心的部分,也是最接近硬件的部分

image-20230805162144713

应用程序只能使用非特权指令,比如说加减指令,而操作系统内核作为管理者有时会让CPU执行一些特权指令,这些指令影响比较大,只允许管理者,也就是操作系统内核来使用

CPU设计和生产时就划分了特权指令和非特权指令,因此CPU执行前一条指令前就能判断出其类型

image-20230805162302048

CPU有内核态和用户态两种状态,处于内核态时说明此时运行的是内核程序,此时可以执行特权指令,反之处于用户态时说明运行的是应用程序,只能执行非特权执行,内核态又称核心态和管态,而用户态简称目态

image-20230805162719249

从内核态到用户态会执行一条特权执行,修改CPU中PSW的标志位为用户态,从用户态到内核态由中断引发,硬件自动完成从用户态到内核态的变态过程

image-20230805163209713

最后我们来看看总结

image-20230805163251166

中断和异常

中断会使得CPU从用户态转为内核态,使操作系统重新夺回对CPU的使用权,其是操作系统夺回CPU使用权的唯一方式,如果没有中断,那么CPU会一直运行一个应用程序,那么就无法实现并发

image-20230807165105029

中断分为内中断和外中断,前者与当前指令的指令有关,其中断信号来源于CPU内部,而后者则与当前执行的指令无关,其中断信号来源于CPU外部

image-20230807165148172

内中断和当前执行的指令有关,中断信号来源于CPU内部

内中断的一个例子是之前我们提到过的在用户态发现了内核态 才允许执行的特权指令,此时会触发内中断,同时在发现了程序异常时也会触发内中断,比如说除数为0,经典的数字异常

image-20230807165248914

有时应用程序想要请求操作系统内核的服务,此时会发送一条特殊的指令——陷入指令,该指令会引发一个内部中断信号,要注意的是,陷入指令本身并不是特殊指令,毕竟这个指令是要在用户态中运行的,如果是特殊指令那就根本运行不了了都

image-20230807165430450

外中断的例子有时钟中断,这个中断信号来自于外部,和CPU当前执行的指令无关,时钟部件每50ms发送一个中断信号给cpu,一般接收到中断信号后会从用户态转化到内核态执行完这个命令之后再转化为用户态,一般来说这个常用于想要使用并发的时候,每个程序分配一定的时间,当时间到达时时钟部件就会发送中断信号给CPU令其将时间片分配给其他应用程序

当然,像是其他IO内核程序也是可以发送中断信号的,同样是外中断信号,最后值得一提的是CPU每执行完一条指令都会例行检查是否有外部的中断信号

image-20230807165941048

内中断一般也称异常,外中断一般会直接称中断,我们这里使用的中断分类是广义的中断,包括内中断和外中断,而狭义上的中断只指外中断,内中断直接称异常

内中断除了陷入、故障之外,还有终止,终止指的是应用程序发生了致命错误,都无法修复了,那操作系统会直接终止该应用程序,如果是故障的话,那么可能应用程序的问题被内核程序修复之后会继续令其运行

image-20230807170146843

中断的原理是当出现中断信号时,其会根据中断向量表找到中断处理程序执行来进行对应的处理,显然,中断处理程序一定是内核程序,需要运行在内核态

image-20230807170413152

最后我们来看看总结

image-20230807170441850

系统调用

操作系统为用户和计算机硬件之间的接口向上提供一些简单易用的服务,主要包括命令接口和程序接口,其中程序接口由一组系统调用组成,系统调用是给应用程序使用的接口,可以简单理解为一种可以调用的特殊函数,应用程序可以通过系统调用来请求获得操作系统内核的服务

image-20230807173133057

理论上应用程序也可以绕过库函数直接进行系统调用,但是由于现在基本都使用高级语言来变成了,所以一般来说都是先调用C库函数,由C库函数进行系统调用,当然C库函数中也存在不涉及系统调用该的库函数,比如说取绝对值的函数

image-20230807173312498

为什么要存在系统调用呢?因为加入没有系统调用,那么操作系统内核就无法对共享资源进行统一管理,这样会出现各种问题

image-20230807173524548

凡是与共享资源有关的操作,都必须通过系统调用的方式向操作系统提出服务请求,由操作系统代为完成,这样能保证系统的稳定性和安全性

image-20230807173614873

一般来说,应用程序要进行系统调用,需要发出陷入指令,再次之前还需要传入参数给CPU,参数可能会致命系统调用什么类型的服务,如fork,当陷入指令被CPU读取之后发生异常,用户态转化为核心态,此时其会根据寄存器保存的之前应用程序保存的参数来确定用户需要那种系统调用服务

image-20230807174727150

而转化为高级语言中,则是代码调用库函数,库函数执行后面的一系列操作

值得一提的是陷入指令又称trap指令和访管指令,都是一个意思,叫法不同而已

image-20230807174939803

最后来看看总结

image-20230807174955676

体系结构

之前我们介绍过的操作系统的体系结构了,但是其实在操作系统结构中在可以继续细分为内核和非内核,非内核功能有GUI一类的非操作系统的联系紧密的,最基本最核心的功能,而内核结构中则与其反之

内核结构的功能比如说是时钟管理、中断处理、原语等

image-20230808004357500

原语是一种特殊的程序,其程序运行具有原子性,必须一次完成,不可中断,运行时间较短的同时也被调用比较频繁

内核系统中还包括对系统资源进行管理的功能,这些功能更多是对数据结构的操作,不会直接涉及硬件,内核结构因此又衍生出两种结构,一种是将这些不直接涉及硬件的功能也包括功能中,这种内核结构称为大内核,另外一种是将这些功能排除出内核结构中,这种结构称为微内核

image-20230808004417364

值得一提的是,状态切换的过程是有成本的,而且还不小,频繁进行状态切换是会降低系统性能的,而微内核又不包括对系统资源管理的功能,因此可能在执行应用程序时因为频繁的状态切换导致效率降低

image-20230808004611155

最后来看看总结

image-20230808004626351

不过现在操作系统后来又增加了分层结构、模块化、外核这三个结构,所以我们也是来一起讲一下

image-20230808005950100

首先是分层结构,这种结构就像计网的分层结构,最底层的是硬件结构,最高层是用户接口,每层可调用更低一层

image-20230808010731228

模块化指的是将内核划分为多个模块,分为主模块和可加载内核模块,主模块是必须要有的模块,而可加载模块则是锦上添花的模块,可选择性加载

image-20230808010756229

外核这种结构真的是非常少见,直接不提了

下面是所有结构的特点和优缺点总结

image-20230808010150739

操作系统引导

操作系统引导指的是开机的时候让你的操作系统运行起来

image-20230808012515624

总所周知,系统文件放置在C盘,那么可以称C盘位活动分区,在次盘前存有主引导记录MBR,其内部包含磁盘引导程序和分区表

image-20230808012645971

同时活动分区可继续细分,分为引导记录PBR,其负责找到启动管理器,根目录和其他,根目录就是C盘打开之后的目录

image-20230808012821005

主存中有RAM和ROM,RAM是运行时的内存,断点后所有数据都会消失,而ROM内部固定有数据,断电后也不会消失,其包含ROM引导程序,就是自举程序

当主机通电时,CPU启动会先从固定地址中取指令,执行ROM的引导程序,当然要先进行硬件自检,别特么内存条没插就想着开机,然后再开机,自举程序会寻找磁盘的主引导记录MBR,其内部的磁盘引导程序会找寻到引导记录PBR,然后会找到其下的启动管理器,上面所说的内容都是一个个软件,一个个加载到RAM中启动,最后到达启动管理器,然后完成开机所需要一切初始化动作

image-20230808013452360

值得一提的是下面的内容就是启动管理器的磁盘内容

image-20230808013530865

虚拟机

虚拟机指的是使用虚拟化技术将一台物理机器化为多台虚拟机器,每台虚拟机器都可以独立运行一个操作系统

第一个类虚拟机会直接在硬件上运行一个虚拟机管理程序,会将CPU时间片分配给各个系统,磁盘和内存则是直接分配对应空间,第二类虚拟机是直接分配在宿主的操作系统上,宿主可以自己运行自己的应用的同时,由运行的虚拟机管理程序来管理各个虚拟机

image-20230808020945517

第一类虚拟机会将权限分类各个层级,这样可以让虚拟机管理程序是受理层级为0的权限请求,因为其本身就是处于0层的,能提高效率

image-20230808022150021

最后来看看两个虚拟机管理程序的对比

image-20230808022130412

总的来说,虽然第二类虚拟机性能更擦,但是其可迁移性更好,选择哪种就根据自己的需求来吧(个人用一般都是第二种其实)

进程的概念、组成、特征

程序是一系列可执行的指令集合,是静态的可执行文件,而进程是动态的,是程序的一次执行过程,同一个程序可以对应多个进程

image-20230808032224671

当进程被创建时操作系统会为该进程分配一个唯一ID,称为PID,除此之外还会记录进程的所属用户ID,进程被分配了多少资源和其运行情况,这些信息都被保存PCB数据结构,也就是进程控制块

image-20230808032652096

当进程创建时,操作系统就会给其创建PCB,当其进程结束时,就会回收其PCB

image-20230808032740151

进程的由PCB,程序段和数据段组成,注意除了PCB是给操作系统用的,其他都是给进程自己用的

image-20230808032858944

一个进程实体由PCB、程序段、数据段组成,当程序要运行时,首先其会进行编译形成一个可执行文件exe存放到硬盘中(当然,有的已经编译完了可以直接用了,那就不用走这个步骤了),执行该exe文件会创建进程,创建进程就会给其分配PCB,CPU会根据其中的指令一个个运行,这些指令集合就是程序段,而进程运行时当然会用到参数,这些参数的集合就成为数据段,其包括进程运行过程中产生的各种数据

image-20230808033352016

最后值得一提的是,其实进程是动态的,更确切的说我们说进程的组成其实是进程实体或进程映像的组成,当然一般来说我们不会特别区分这个

image-20230808033658651

进程有动态性、并发性、独立性、异步性、结构性这五大特征,了解即可

image-20230808033802250

最后我们来看看总结

image-20230808033832950

状态与转换、组织

进程刚创建时处于创建态,该阶段操作系统会为其分配资源、初始化PCB,当进程创建完毕后处于就绪态时,说明该进程已经具备了运行条件,只是由于没有空闲的CPU,因此暂时不能运行

image-20230808041618844

当其获得了CPU上的时间片时,其进入运行态,如果其向操作系统请求其他服务而该服务正好忙无法处理请求时会导致进程无法继续向下执行

image-20230808041719626

当进程等待某个事件的发生时,操作系统会令该进程移除CPU并令其会进入阻塞态,当CPU空闲时会选择CPU会选择领一个就绪态的进程在CPU上执行

image-20230808041947637

当一个进程执行完之后,会进入终止态,操作系统会在CPU中移除该进程并回收其内存空间,之后这个进程就消失了

image-20230808042141214

下面是五种状态模型转换的图,这里值得一提的是,当CPU处于空闲时,进程可能从就绪态转化到运行态,但是进程也可能从运行态直接转化到就绪态,常发生在CPU时间片抢夺不到的情况下或者CPU时间片被操作系统分配给了其他进程

注意,运行态到阻塞态是进程主动请求发生的,而从阻塞态到就绪态则是被动发生的

image-20230808042825943

注意阻塞态又称等待态,创建态又称新建态,终止态又称结束态,别名还是要记住的,进程的不同状态使用PCB中的state变量来表示

image-20230808043037565

进程状态的组织一般通过指针和链表来实现,可以看到执行指针只指向执行态的进程,其他亦然,不同指针指向一个对于状态的进程队列,一般来说,就绪状态下的进程链表会将优先级高的链表放在队头,阻塞队列一般会根据不同的阻塞队列继续细分,比如说等待打印机的或者是等待磁盘的阻塞队列

image-20230808043207762

还有使用索引的,这个方式也是很简单,下图就是,不过很少用就是了

image-20230808043237217

最后是总结

image-20230808043304592

image-20230808043332163

进程控制

进程控制的是对系统中的所有进程实事有效管理,其具有创建、撤销进程,实现进程状态转换等功能

image-20230808050648235

进程控制使用原语实现,原语具有原子性的特点,其执行过程是一气呵成,不可中断的

image-20230808050908223

其原子性的实现使用关中断和开中断这两个特权指令实现,前者会令内核程序不再没执行一个步骤就检查中断信号,开中断指令则会回复检查,这两个指令实现了原语和原子性

image-20230808051123854

创建进程使用创建原语,中间的过程自己看,值得一提的是作业调度中的作业指的是等待CPU执行的任务

image-20230810033928092

进程终止使用撤销原语,终止一个进程的同时会通知其下所有的子进程

image-20230810034405727

进程要实现阻塞和就绪的状态变换分别需要使用阻塞原语和唤醒原语,注意进程因为什么事情阻塞就会因为什么事情被唤醒,因此阻塞原语和唤醒原语必须成对使用

image-20230810034613548

进程状态的切换需要使用切换原语,其切换时首先做的是将进程的运行环境存入到PCB中,运行环境信息又称为进程上下文Context

image-20230810035246275

当CPU执行指令时,其会设置多个寄存器用于存放程序运行过程中产生的数据

image-20230810034802092

有程序状态寄存器(PSW)、程序计数器(PC)、指令寄存器(IR)、以及通用寄存器,他们各有作用,由于这个作用和我们的课程主题关系不大就不深入谈了

image-20230810035024364

当进程要进入阻塞状态时,其会保存起程序运行环境,这里指的是就是PSW、PC和通用寄存器一类的内容,会保存到PCB中,当要再次使用的时候会重新放到这些寄存器中,因为这个寄存器是公用的,所以只保存数值方便重复利用

image-20230810035209792

最后我们来看看总结

image-20230810035404588

进程通信

进程间通信IPC指的是两个进程之间产生数据交互,比如说B站的视频分享到微信上

image-20230810042218138

进程是分配系统资源的单位,各进程拥有的内存地址空间是相互独立的,同时出于安全考虑,一个进程不允许直接访问另一个进程的地址空间,进程间的通信有三种方式,分别是共享存储、消息传递、管道通信这三种

image-20230810043030472

共享存储指的是两个进程约定好一个共享存储区,其可以随意往该存储区中写和读,同时为了避免出错,各个进程对共享空间的访问应该是互斥的,这里的互斥实现使用系统内核提供的同步互斥工具

注意各个机场南可以使用mmap函数将申请的共享存储区映射到自己的地址空间,内部是通过增加页表项(段表项)实现的,这里我们不深入谈

image-20230810043323961

其实共享存储还分两种方式,一种是基于数据结构的共享,另一种是基于存储区的共享,上面的共享方式是后者,而前者的共享方式是约定好往共享空间只能放入什么类型的数据,这种共享方式共享速度慢、限制多,是低级通信费方式,返回存储区的共享则是高级通信方式

image-20230810043505157

第二种方式是消息传递方式,进程间的数据以格式化的消息为单位,通过操作系统提供的发送和接收消息这两个原语进行数据交换,一个消息由消息头和消息体组成,前者存有发送进程的id,接收进程的id等内容,消息体那就是存放数据的内容

消息传递有两种方式,分别是直接通信方式和间接通信方式,前者指的是直接一个进程直接将消息发送到另一个进程,而后者指的是进程之间通过信箱收发消息

image-20230810043924829

下图中橙色块的内容是操作系统内核的地址空间,这里可以看到在操作系统内核中保存有消息队列,内部存放一个个消息,一个进程要发送消息给另一个进程时就构建自己的消息然后发送到内核中,内核会将该队列的一个个信息发送给对应的进程

image-20230810044124480

而间接通信方式指的是在操作系统内核空间中创建信箱,然后不同进程间通过信箱即可实现数据的收发,同时可以多个进程网同一个信息中收发消息

image-20230810044450083

第三种方式是管道通信,其会在进程间创建一个管道,管道实现了半双工通信,同一时刻数据只能从一端传向另一端,如要实现双向同时通信则需要设置两个管道

这时有聪明的同学就会问,这个方式和共享存储区方式不一样吗?看起来是这样的,但是实际却是不同的,因为共享存储区的方式允许进程随意在各个位置上收发,而管道如果前面没写满,那么就必须先写前面,收也是同理,这其实就像是队列,不过这里的内部其实更像是循环队列,同时这里进程也需要互斥访问管道

当管道满时则阻塞写进程,空时则阻塞读进程,管道读取时如果多个进程读取同一个管道可能会导致错乱,因此有两种解决方式,一种是允许多个写进程,一个读进程,另一种多个写进程,多个读进程(由操作系统轮流令读取进程读取数据),解题时我们推荐以前者为准,但是实际上则是以后者为准,因为Linux就是采取后者的方案

image-20230810045401614

最后我们来看看总结

image-20230810045614833

最后值得一提的是,只要管道没满,写进场就可以写数据,反之只要管控不空,读进程可以往管道读数据

image-20230810045727711

线程的概念

在没有引入进程之前,系统中的各个程序只能串行执行,进入进程之后就可以并发执行了,但是进程中还希望实现多个功能,比如说在QQ这个进程中我们希望一边和人视频的同时另一边还可以进行文字聊天

image-20230810154212927

为了实现令一个进程同时执行多个服务,因此引入了线程这个概念来增加并发度,传统情况下进程是程序执行流的最小单位,但是引入线程之后线程久成了执行流的最小单位了

image-20230810154536893

我们可以简单将线程理解为轻量级线程,其是一个基本的CPU执行单元同时也是程序执行流的最小单位,引入线程之后进程内的各多线程可以并发,进一步提升了系统的并发度,并且引入线程之后进程只作为除CPU之外的资源的分配单元

image-20230810154716161

简单来说就是进程负责分配资源,线程负责执行任务进行调度,切换线程的开销要远比切换进程的小

image-20230810154937922

最后我们来看看线程具有什么属性,这些我们后续会细讲,我们这里直接看图先了解即可

image-20230810155221721

实现方式和多线程模型

早期的操作系统只支持进程,当时的线程由线程库实现,一个内核态下的进程对于一个用户态下的线程,线程库对于多个用户级线程

image-20230810161821194

在早期的操作系统中,所有的线程管理工作都由应用程序负责,线程切换在用户态下即可完成,同时操作系统是意识不到有线程的存在的,这种方式的优点是线程管理系统开销小,效率高,而缺点是并发度不高且多个线程不可在多和处理机上并行运行,因为说到底操作系统只知道进程,那么他就只会分配时间片给一个进程,那这样分配不到进程不管有几个CPU都是无法令线程并行运行的

image-20230810162221856

而内核级线程(简称KLT,又称内核支持的线程),内核级线程的管理工作由操作系统内核完成,所以进行线程切换时操作系统必须在核心态下才能完成,每个KLT有一个TCB,也就是线程控制块,这个类似于进程的PCB

这种方式的优点是并发能力强且可在多和处理机上并发执行,缺点是线程管理的成本高开销大,主要是一个用户进程会占用多个内核级线程,而线程又要频繁切换导致成本变高

image-20230810162556033

为了解决其缺点因此内核级线程下其实还有三种模型,第一种是一对一模型,其实就是我们上面的介绍的那种,这里不赘述了

image-20230810162806266

第二种是多对一模型,可以看到多个用户级线程对于一个线程库并对于一个内核级线程,其优点是开销小效率高,缺点是某些情况下并发度不高且多个线程不可在多核处理机上并发运行

注意,操作系统只能看得见内核级线程,因此只有内核级线程才是处理机分配的单位,这里的情况其实跟之前进程的是情况是一样的,所以用户线程不可并发运行

image-20230810163028096

最后一种模型是多对多模型,在这种模型下n个用户线程通过线程库映射到m个内核级线程下(n>=m),优点是折中,各方面的缺点都不明显

注意内核级线程只有全部内核级线程都阻塞了,我们才能说这个进程阻塞了

image-20230810163211646

最后我们看看总结

image-20230810163251832

状态与转换

线程的状态与状态转换和进程完全一抹一样,只不过线程只考虑运行、阻塞、就绪这三种状态,其他状态不考虑

image-20230811132241085

线程中的TCB和进程中的PCB也非常类似,比如两者内部的TID就和PID类似,其他也保存了程序执行到哪里的程序计数器PC,存放线程运行的中间结果的其他寄存器,存放函数调用信息的堆栈指针等,而上面的介绍的程序计数器PC、其他寄存器以及堆栈指针就是线程切换时需要保存或恢复的存在于TCB中的三大重要数据(当然TCB还存在其他数据内容就是,我们这里没提)

而其组织与控制指的是TCB根据一种规则将多个PCB组织成一张表,这个表称为线程表,就这样,没别的内容了

image-20230811132701011

调度的概念与层次

调度指的是CPU由于资源有限,很多任务无法同时处理,因此要制定某种规则来决定处理任务的顺序

image-20230811160042703

用户向系统提交一个作业指的是用户绕那个操作系统启动一个程序,也就是启动一个进程,而可能同时存在多个作业需要处理而由于内存不足够导致作业无法执行形成一个作业后备队列(启动进程需要将该作业从外存放到内存),而高级调度(作业调度)指的是按一定规则的从外存的作业后备队列中挑选一个作业调入内存并创建进程,这个过程中每个作业只调入调出一次,作业调入时建立PCB、调出时撤销PCB

image-20230811160217925

而低级调度指的是按照某种策略从就绪队列中选一进程并将处理机分配给它,进程调度是操作系统中的最基本的一种调度,一般的操作系统中都必须配置进程调度,且其频率很高,一般几十毫秒就调用一次,这当然频率要高,因为这个是给不同的进程分配CPU时间片的,频率不高还怎么制造宏观上同时运行的假象?

image-20230811160438490

而当内存不够时,CPU可能将某些进程数据调出外存,等内存空闲或者是进程需要时再重新调入内存,暂时调出外存的进程处于挂起状态,而被挂起的进程PCB会组成挂起队列

中级调度按照策略决定将哪个挂起进程重新载入内存,一个进程可能会被多次调出调入,因此发生的频率比高级调用更高

image-20230811160606015

之前我们讲过进程五状态模型的牛头图,而刚刚我们讲到了就绪挂起和阻塞挂起这两种状态,那么就有七状态模型,就是再加入就绪挂起和阻塞挂起这两种状态

创建态、就绪态、运行态的进程都有可能因为内存不足被CPU转换为就绪挂起态,而阻塞挂起状态只可能从阻塞态转换过来,而阻塞挂起状态的进程如果对应的唤醒事件发生了,也会转换到就绪挂起状态,同样等待内存空间分配的激活,当激活时则转换到就绪态准备执行

最后值得一提的是有些操作系统是会将就绪挂起和阻塞挂起这两种状态的进程继续细分的,这个就具体问题具体分析了

image-20230811160749076

最后我们来看看这三种调度方式的对比,从高往低发生频率越来越高,而只有低级调度是面向内存和CPU的,其他都是面向外存到内存,其中高级调度是面向作业的,也就是要执行的任务,而中级调度是面向进程的,也就是要不要挂起的任务

image-20230811161022606

最后我们来看看总结

image-20230811161115550

时机、切换、过程、方式

进程调度指的是按某种算法从就绪队列中选择一个进程为期分配CPU,需要进行进程调度的情况有进程主动放弃CPU和被动放弃CPU

不过在一些情况下是不能进行进程的调度和切换的,这些情况有在处理中断的过程中、进程在系统内核程序临界区中、在原子操作过程中这三种情况

image-20230811164601141

进程在操作系统内核程序临界区中不能进行进程调度与切换,但是处于临界区时是可以的,注意操作系统内核程序临界区和临界区是两种概念,前者指的是访问临界资源的代码,而后者指的是用于访问某种内核数据结构的内容(比如进程的就绪队列),临界资源在这里指的是一个时间段内只允许一个进程使用的资源

进程如果访问后者,那么操作系统只会令其尽快完成操作释放资源,这样不会影响正常的进程切换调度,而前者比如说是进程访问打印机一类的IO设备,的确一个时间内只有一个进程能访问,但是这个资源并不是非常重要,不会影响操作系统内核,因此即使有处于临界区的进程也可以实现进程的调度与切换

image-20230811164937549

不过有的系统中只允许进程主动放弃处理机,有的则允许进程主动方式的同时还可以强制剥夺一个进程在处理机上的运行时间

image-20230811165020125

前者指的是非剥夺调度方式,也称非抢占方式,该方式只允许进程主动方式处理机,即使有更紧迫的任务来了,那只要当前进程没主动放弃那就不会去处理更紧急的任务,优点是实现简单,系统开销小,缺点是无法及时处理紧急任务,适合于早期的批处理系统

而后者指的是剥夺调度方式,又称抢占方式,指的是即使有一个进程在处理机上执行,如果有一个更重要或更紧迫的进程需要CPU会立刻暂停该进程并处理更紧急的进程,其优点是可以优先处理更紧急的进程,也可以实现让各个进程按时间片轮流执行的功能,适合现在的分时操作系统、实时操作系统

image-20230811165124024

注意狭义的进程调度与进程切换是有区别的,前者指的是从就绪独立额中选中一个要执行的进程,我们暂且称为进程选择吧,而后者指的是一个进程让出处理机由另一个进程占用处理机的过程

广义的进程调度是包含了进程选择和进程切换的,而狭义的进程调度只包括了进程选择了,进程切换无非做的事就是对原来进程的各种数据的保存和对新的进程的各种数据的恢复

值得一提的是进程的切换是有代价的,国语频繁的进程切换会使得整个系统的效率降低

image-20230811165425453

最后我们来看看总结

image-20230811165655982

调度器和闲逛进程

调度器程序决定进程的调度,比如让进程从就绪态转为运行态或者反之,调度时机指的是会触发调度程序的事件,包括但不限于创建新进程、进程退出以及运行的进程堵塞

值得一提的是非抢占式调度策略下只有运行进程阻塞或退出才会触发调度程序工作,而在抢占式调度策略中,每个时钟中断或k个时钟中断都会触发

image-20230812010521957

在不支持内核级线程的操作系统下,调度程序处理的对象是进程,反之则是内核线程

image-20230812010554081

当没有其他就绪进程时,调度程序会运用闲逛进程(idle),该进程的特点是优先级最低且能耗低,甚至可以是0地址指令,不过其同样也占用一个完整的指令周期,指令周期的末尾会例行检查中断,这样如果说没有什么进程要执行了,重复运行这个闲逛进场也可以通过不断触发检查中断的方式令CPU及时发现要执行的进程

image-20230812010654020

评价指标

CPU利用率指的是CPU忙碌的时间/总时间,而某个设备的利用率也是同理计算(如果要考察多道程序并发的情况,我们可以用甘特图来辅助计算,具体案例我们后来再详谈)

image-20230812012137249

系统吞吐量指的是计算机在单位时间内能处理多少作业的能力,公式为系统吞吐量=总共完成了多少道作业/总共花了多少时间

image-20230812012232821

周转时间指的是从作业被提交到系统开始到作业完成为止的这段时间间隔,那当然就是作业完成时间-作业提交时间就是答案了,同时平均周转时间各个作业的周转时间之和/作业数即可

image-20230812012411831

而带权周转时间则是表示作业周转时间中用于工作时间的比例,公式是作业周转时间/作业时间运行时间,其结果必然大于等于1,同时结果越小代表运行的效果越好

也有平均带权周转时间,就是将各个作业的带权周转时间加起来然后除于作业数而已

image-20230812012556375

计算机用户当然是希望自己的作业尽可能少的进行等待,尽可能让自己的作业快点运行,所以有等待时间这个指标

对于进程来说,等待时间指的是进程建立之后的等待时间,在等待I/O完成时的期间进程是被IO设备服务的,此时不计入等待时间,而对于作业来说,不仅要考虑建立进程后的等待时间,还要加上作业在外存后备队列中等待的时间

一般来说,调度算法只会影响作业或进程的等待时间,而不会影响作业被处理的时间,同样这里也有平均等待时间的概念,计算方式和之前差不多

image-20230812012825185

响应时间指的是用户提交请求到首次产生响应的时间

image-20230812012903599

最后我们来看看总结,这里带权周转时间比较容易忘,我们这里只要记住厕所排队的案例就不容易忘了

image-20230812012944441

早期调度算法

调度算法一共有三个,分别是先来先服务(FCFS)算法,短作业优先(SJF)算法,高响应比优先(HRRN)

image-20230812015142870

先来先服务算法非常简单,就是先到达的服务先服务,后到达的服务就乖乖等待,我们先来介绍这个算法在非抢占式情况的执行结果

通过下图我们可以看到执行顺序就是P1234,非常简单,我们画出对于的坐标图就可以利用公式进行对应的计算就可以得到我们想要的结果,不过我们要注意的是我们这里的进程都是纯运算的进程,如果说一个进程到达只是又有计算,又有IO操作的进程,那么计算等待时间时还需要额外减去IO操作的时间

image-20230812015709175

这个算法的思想是出于公平考虑的,其可以用于作业角度也可以用于进程调度,用于前者时考虑哪个作业先到达后备队列,用于后者时考虑哪个进程先到达就绪队列,这个算法一般是非抢占式的算法,所以我们这里就不介绍其抢占是的实现了,其有点事公平且算法实现简单,缺点是这个算法对短作业的用户体验很烂,不会的导致饥饿现象

image-20230812015942429

然后我们来介绍短作业优先(SJF)算法的非抢占式实现,可以看到在非抢占式视线里,这里按照服务时间进行排序执行,最后得到结果是各方面均是FCFS更低

值得一提的是,我们这里严格来说用于进程调度的算法应该是短进程优先调度算法(SPF),因为我们这里是使用了非抢占式实现

image-20230812020502078

然后是抢占式算法的实现,这里全称是最短剩余时间优先(SRTN)算法,我们这里计算时要按照每时刻来计算,不断计算进程的等待时间和完成还所需的时间,最后将得到的结果构建一个坐标接着通过计算得到所需的结果

image-20230812020833577

可以看到对比非抢占式的短作业优先算法,这个抢占式的实现的这个几个指标要更低,也就是其效果会更加好

image-20230812021922782

值得一提的是,如果题目中未特别说明,则所提到了SJF算法都默认是非抢占式的,同SJF调度算法的平均等待时间、平均周转时间最少这个说法是不够准确的,除非加上所有进程同时可运行或所有进程几乎同时到达的前提条件或者是直接说这个是抢占式的短作业or进程优先调度算法

如果选择题中有这个选项,那么一定要判断其他选项是否有更加明确的正确答案,有则选择,无则选这个

image-20230812022228972

最后SJF算法追求最少的平均等待时间,算法规则是拥有最短服务时间的进程优先得到服务,其优点是用于几乎最短的平均等待时间和平均周转时间,但是其缺点时对长作业不利,可能会出现长作业迟迟得不到处理而导致的饥饿甚至是饿死的现象

image-20230812022404040

FCFS和SJF算法前者利于长作业后者利于短作业,都各自有缺点,为了克服其缺点因此设置了高响应比优先算法(HRRN)

image-20230812022834086

简单来说就是每个时刻对进程计算其响应比,响应比的公式为等待时间+要求服务时间/要求服务时间,这个值一定是>=1的,然后值越高的进程优先级越高,其实就是说等待时间越长而运行时间越短的进程优先执行的意思

计算方式就是每个时刻计算响应比,找到响应比最高的进程令其进行服务,注意这个算法只有非抢占的实现,所以只要一个进程被服务,就必须等到起服务完才能进行下一个进程的服务,同样也是得到横坐标图然后利用公式计算结果

image-20230812023103402

该算法的思想是综合考虑进程的等待时间和要求服务的时间来选择谁执行,由于等待时间在响应比的分子中,等待时间越久的进程也有机会获得服务,所以也不会出现饥饿现象

image-20230812023208991

最后我们看看总结,这里值得一提的是上面上面介绍的这三种的算法的交互性都很糟糕,只适合用于早期的批处理系统,现在早不用了,不过FCFS算法还是常结合其他的算法使用的,现在也扮演者很重要的角色,就它是例外

image-20230812023318642

现代调度算法

而为了让调度算法适合交互式系统,还研究出了另外三个算法,分布式时间片轮转(RR)算法、优先级调度算法、多级反馈队列调度算法

image-20230812033237839

时间片轮转算法是要确定一个时间片大小,然后进程每次到达之后就给其运行,当运行到指定的时间片之后就将其移除CPU令其进入等待队列队尾,然后让其他先进来的进程继续执行相同时间片的时间,比如我们这里设定时间片大小为2,那么0时刻首先是P1执行,然后过了2个时间块之后到P2执行,接着还是到P1执行,因为P1先退出先到队尾,执行完之后到P3,P3执行一个时间就直接让P2执行了

image-20230812033653841

之后都是大差不差的内容,最后将得到的结果化为横坐标图即可

image-20230812034002848

当然有聪明的同学可能会问这一次怎么不计算各种结果了?这其实是因为在现在计算机的系统里我们更加关注响应时间,其他的什么等待时间这些反而是小事了,不是我们关注的重点,我们这里只关注响应时间和处理时间,其他的我们一概忽略

image-20230812034016322

如果说我们设定时间片为5,那么就能得到下面的结果

image-20230812034223093

可以看到其执行结果和先来现先服务算法几乎一模一样,这其实就是该算法的一个特性,如果时间片设置太大,那么其会退化为先来先服务算法,会增加进程响应时间,但是太小也不行,这样会导致进程切换过于频繁,所以实际上时间片也不能太小

一般来说时间片要设置为让切换进程的开销不超过1%,这样会比较好

image-20230812034642170

时间片轮转算法的思想是公平轮流地为各个进程服务,只能用于进程调度且属于是抢占式算法,由时钟装置发出时钟中断来通知CPU时间片已到来执行抢占,其有点是公平响应快,适用于分时操作系统,但是缺点是由于高频率的进程切换会导致额外的开销且不区分任务的紧急程度,不会导致饥饿

image-20230812034801788

第二种是优先级调度算法,该算法是通过优先级来判断哪个进程优先执行,我们先来看看非抢占式的优先级调度算法的实现

不过我们这里首先要注意的是,给进程分配优先数的时候,优先数和优先级的关系是要看题目的,不是说优先数越高优先级就越高的,有可能是反过来的,主要看题目怎么设置

这个非抢占式的就很简单了,根据优先数和到达时间分析一个个执行对应的进程即可

image-20230812035120974

而在抢占式的优先级调度算法中,我们则需要每个时刻中都进行进程优先级的分析,一旦有更高优先级的进程进入了,那么就要将服务器让给其使用,直到进程运行结束

image-20230812035415171

这里我们要补充的是就绪队列未必只有一个,这个是可以按照不同优先级来自己组织的,同时根据优先级是否可以动态改变,可将优先级分为静态优先级和动态优先级,前者是创建进程是确定的一个不变值,而后者创建时也有初始值,但是后续会根据情况动态跳转优先级

一般来说,对于静态优先级,我们常认为系统进程比用户进程的优先级更高,前台进程比后台进程的优先级更高,值得一提的是操作系统更加偏好I/O型(也称IO繁忙型)的进程,简单来说就是这一类进程在操作系统中一般占有高优先级,因为IO设备可以和CPU并行工作,如果优先让IO繁忙型机场南先运行,那么就可以让IO设备也忙起来,尽早投入工作,提高资源利用率

而如果采用动态优先级,我们可以从多个角度考虑来动态提升某些进程的优先级,比如在资源利用率、处理时间、等待时间等

image-20230812035747189

优先级调度算法会优先处理优先级高的进程,其既可以用于作业角度,也可以用于进程调度,甚至可以用于IO调度,其有抢占式和非抢占式,优点是可以优先区分紧急、重要程度的不同进程并基于执行,适用于实时操作系统,缺点是可能导致饥饿

image-20230812035859141

前面这么多算法都各有各的缺点,因此产生了集百家之长的多级反馈队列调度算法

image-20230812040040944

这个多级反馈队列调度算法比较麻烦,直接说定义怕不是给大伙们都整不会了,我们还是先来说例子吧,这样好理解

首先,这个队列要先创建一个多级队列,这个队列会对于一个时间片,优先级越高的队列对应的时间片就越低,当进程首先到达时,先进入第一级队列,执行时间片,执行之后如果还没令进程执行完,那么就进入二级队列,下级队列的进程无论是否正在执行,只要更高级别的队列出现了新的进程,那么就一定会优先服务新的更高进程,进程就这样不断执行并向下排队,当其到达最低层级的队列执行之后还没有将进程执行完,就会再一次回到这个最低队列继续执行

image-20230812040458317

这个算法的优点很多,这里就不集中介绍了,值得一提的我们之前说过操作系统是IO偏好型的,如果我们希望在这个算法中实现这个需求,那么我们可以让IO进程不管处理多少次都永远处于第一级队列,这样就可以让IO进程优先被执行了

这个算法的最大缺点是有可能会导致饥饿

image-20230812040657795

最后我们来看看总结,这三种算法比起之前学过的三种算法来说过更加适合交互式系统

image-20230812040832686

多级队列调度指的是系统按进程类型设置多个队列,进程创建成功后就根据进程的类型插入到具体的某个队列,不同的队列优先级不同,这样能让重要的进程优先被执行,比如说系统进程肯定是优先级最高的,所以要第一个执行

当然如果只是这样分配,那需要打字的时候CPU一直在执行系统进程不响应用户的打字需求那肯定也是不行的,所以一般会给队列进行时间片划分,比如最高队列占有的时间片是一般,剩下的时间片底下两个自己分

然后是不同的队列也可以使用不同的调度策略,基本就这些

image-20230812045422330

进程的同步与互斥

进程具有异步性的特征,异步性指的是各个并发执行的进程以各自独立的,不可预知的速度往前推进

image-20230812141851384

但有时我们就是需要让我们的进程推进符合我们的预期,最简单的例子就是管道的写读数据,我们肯定是期望读进程在写进程之后的,为了实现这种效果,也就是解决异步问题,我们就需要使用进程同步,同步也称直接制约关系

image-20230812142002180

进程的并发需要共享的支持,而共享资源的方式有两种分别是互斥共享方式和同时共享方式,前者指的是系统中的某些资源在某意思时刻只允许一个进程访问资源,其资源称为临界资源,许多物理设备,比如摄像头打印机都属于是临界资源,而后者指的是同一时间段允许多个进程同时访问的资源,注意这里的同时是宏观上的同时,可能在微观层面其实多个进程还是采取轮询的方式实现多个进程对资源的访问的

image-20230812142203659

对临界资源的互斥访问,逻辑上可以分为进入区、临界区、退出区和剩余区四个部分,其中进入区检查进程是否可以进入临界区,若可以则进入且设置正在访问临界资源的标志,以阻止其他进程进入临界区,这个过程可以理解为上锁,而临界区指的是访问临界资源的代码,如果是打印机互斥资源则临界区是执行打印要使用的代码,而退出区则是用于解除正在访问临界资源标记,可以理解为解锁,剩余区可以做其他处理

最后值得一提的是临界区也可称为临界段

image-20230812142347274

临界资源的互斥访问需要遵循空闲让进、忙则等待、有限等待和让权等待这四个原则

空闲让进指的是临界区空闲则允许请求进程立即进入临界区,忙则等待指的是对已有进程进入临界区时,其他还想进入的进程必须等待,有限等待指的是对请求访问的进程要保证在有限时间内能进入临界区(这样设置是为了防止出现饥饿),让权等待指的是当进程不能进入临界区时要立即释放处理机,防止进程忙等待(忙等待指的是进程也没做什么事,就是在等待,但是又不退出处理机导致处理机一直被占用,那处理机就没办法给其他进程服务)

image-20230812142614811

最后我们来看看总结

image-20230812142643165

互斥软件实现方法

进程互斥的软件实现方法有四种,分布式单标志法、双标志线检查法、后标志后检查法以及Peterson(皮特森)算法

image-20230812145628336

如果进程之间没有互斥方法,那么就会导致进程混着使用一个临界资源,这样会导致预期之外的后果,比如说打印机例子,总不能打印两个进程的内容到一张纸上吧,这不特么搞笑呢

image-20230812150108317

首先是单标志法,该算法的逻辑是设定一个int值表示允许进入临界区的进程号,当一个进程运行完毕之后会修改int值,代表其已经执行完了允许其他进程执行,这样即使在进程执行过程中其他进程抢占了时间片尝试执行,因为int值不相等也会导致另外的进程执行不了,最后还是会让第一个进程继续执行

该算法可以实现同一时刻只允许一个进程访问临界区

image-20230812150502335

但是这个该算法可能违背了空闲让进的原则,存在一种情况是P1已经完成了进程并谦让给P0,但是P0迟迟不执行自己的内容,那么P1就没办法执行了,因为P0没执行完

image-20230812150744354

第二种方法是双标志检查法,该算法逻辑是设置一个布尔型数组flag,数组下包代表各个进程,值代表各进程想进入临界区的意愿

当一个进程想要执行时,其会首先判断相邻的进程是否想要执行,如果想要执行,那么自己就先等待,不想要自己就将自己的执行意愿设置为true,然后开始执行代码,执行完之后将自己的执行意愿设置为false

这个算法的主要问题是违反忙则等待原则,可能会出现一种情况,当出现并发导致执行顺序为1526时,可能会出现两个进程都同时都被执行的情况,导致这个问题的主要原因是因为进入区的检查和上锁并不是原子性的

image-20230812151220099

为了解决上面的问题,因此出现了双标志后检查法,该算法的逻辑是当一个进程想要执行时,会先将自己的对应flag值设置为true,然后再检查其他进程是否想要执行,如果想要执行自己就先等待,反之则进入执行并在执行结束后将自己的执行意愿设置为false

这个算法的确是可以解决上面违反忙则等待的问题,但是这个算法又违背了空闲让进和有限等待的原则,在进程并发运行导致执行顺序为1526时,可能会导致两个进程长期无法访问临界资源而产生饥饿现象

image-20230812151444540

最后我们来介绍下皮特森(Peterson)算法,该算法的逻辑是如果一个进程想要执行,那么首先其会将该进程的执行意愿也就是flag数组对于下标的值设置为true,然后间turn值设置为1,接着如果其他进程的想要执行的意愿为ture且自己的turn值为1时,就进入等待

这代表这个进程想要执行,但是同时如果其他进程想要进行,那么他会让其他进程先执行,不去争抢,这种算法遵循了空闲让进、忙则等待、有限等待三个原则,但是没有遵守让权等待的原则,即使在并发情况下也能正常执行

虽然还是有缺点,但是已经比之前的三个解决方案好不少了

image-20230812152637723

最后我们来看看总结

image-20230812152725279

互斥硬件实现方法

进程互斥的硬件实现方法有三种,分别是中断屏蔽方法、TestAndSet(简称TS/TSL指令)、Swap指令(简称XCHG指令)

image-20230812162522673

中断屏蔽方法其实我们之前也讲过,其实就是加上关中断和开中断,实现原理和原语的实现效果相同,其优点是简单高效,缺点是不适用于多处理机切值适用于操作系统内核进程

image-20230812162647471

TestAndSet指令有的地方也称为TestAndSetLock(TSL)指令,其实用硬件实现的,执行过程中不允许被中断

其逻辑是首先将当前的锁传入通过TSL方法传入到While循环中,会将当前锁的值保存且无论其有无加锁都给赋值为true,可以理解为不管有没有加锁反正就再次上锁,这样在while循环中如果锁修改前的值为ture就在while循环中等待,因为这说明已经有进程在执行了,反之则直接进入执行,当进程执行完之后还会将lock赋值为false,这个动作其实就是解锁

这个算法的有点是实现简单,且不用严格检查,适用于多处理机环境,缺点是不满足让权等待原则,可能导致忙等

image-20230812163227364

对于Swap指令,也称Exchange指令,简称XCHG指令,其是使用硬件实现的,执行的过程不允许被中断,其实他的代码虽然和TSL指令的不同,但是内部逻辑都是一样,这里就不多提了,自己看吧

image-20230812163511359

最后我们来看看总结

image-20230812163540358

互斥锁

互斥锁是解决临界区的最简单工具了,其逻辑是一个进程在进入临界区时获得锁,退出时释放锁,采用硬件机制来实现

其主要缺点是忙等待,为了克服该缺点所以互斥锁常用于多处理机系统,这样一个线程可以在一个处理器上等待,不会影响到其他线程的执行

需要连续忙等的互斥锁我们称为自旋锁,比如TSL、swap、单标志法等

image-20230812170341517

值得一提的是忙等并不是一直等着,如果进程时间片用完,还是会下处理机的,只是其返回让权等待原则而已,而且忙等也未必时坏事,因为忙等是不用切换进程,如果在多处理器的系统上上锁的时间很短,那么等待的代价就很低,忙等效率反而更高

但注意忙等肯定是要使用在多处理器系统的,单处理器系统发生忙等的话进程永远都等不到可执行的资源,那就只是干等着而已

image-20230812170525830