操作系统原理 —— 什么是进程互斥? 以及进程互斥的实现方式(十四)

news2025/1/21 4:50:34

什么是进程互斥?

在操作系统中,有两种资源共享方式,一种是互斥共享方式,一种是同时共享方式。

互斥共享方式就是指在系统中的某些资源,虽然可以提供给多个进程使用,但一个时间段内只允许一个进程访问该资源。

这里就会有一个互斥的概念,一个时间段只允许一个进程访问,这就是进程互斥,

还有一个概念就是,我们把一个时间段内只允许一个进程使用的资源称之为临界资源,比如说摄像头、打印机都属于临界资源。

对于临界资源的访问,必须互斥的进行。进程互斥简单的来说:指当一个进程访问某些临界资源时,另一个想要访问该临界资源的进程必须等待。当前访问临界资源的进程访问结束,释放该资源之后,另一个进程才能去访问临界资源。

那既然有了这个互斥的概念,那各个进程又是如何保证对临界资源互斥访问呢? 在操作系统中,如果想要访问临界资源,有这么几个步骤:

1、 entry section; 进入区,负责检查是否可以进入临界区,如果可以进入,则设置正在访问临界资源的标志(可以理解为上锁),以阻止其他进程同时进入临界区。

2、critical section; 临界区,访问临界区资源的那段代码

3、exit section; 退出区,负责解除正在访问临界资源的标志,可以理解为解锁。

4、remainder section; 剩余区,做其他处理。

有了以上这几个动作之后,为了实现对临界资源的互斥访问,同时保证系统整体性能,还需要遵循以下原则:

1、空闲让进:临界资源空闲时,可以允许一个请求进入临界区的进程立即进入临界区。

2、忙则等待:当已有进程进入临界区时,其实试图进入临界区的进程必须等待。

3、有限等待:对请求访问的进程,应保证能在有限时间内进入临界区,保证不会饥饿。

4、让权等待:当进程不能进入临界区时,应立即释放 CPU 执行权,防止进程忙等待。

进程互斥软件的实现方式

单标志算法

算法思想:两个进程在访问完临界区后,会把使用临界区的权限转交给另一个进程,也就是说每个进程进入临界区的权限,只能被另一个进程赋予。

举个例子:

先设置一个单标志,定一个变量,int turn = 0; 这个变量表示一个标志,标志哪个进程可以进入临界区。

我们一起来看下两个进程的代码:

P0 进程:

// 先判断自己是否能进入临界区
while(turn != 0) // 进入区,如果不等于0,那么这个循环会一直阻塞在这里
critical section; // 进入临界区
turn = 1; // 退出区,这个时候需要把进入临界区的权限,转交给另一个进程
remainder section // 剩余区,做一些额外的事情

P1 进程:

// 先判断自己是否能进入临界区
while(turn != 1) // 进入区,如果不等于0,那么这个循环会一直阻塞在这里
critical section; // 进入临界区
turn = 0; // 退出区,这个时候需要把进入临界区的权限,转交给另一个进程
remainder section // 剩余区,做一些额外的事情

这两段代码,简单的模拟了一下,两个进程互斥的情况,首先 turn 的初始值为 0,假设 P1 进程先执行,那么会一直卡在 while 循环那, 一直等到 P1 的时间片用完了,发生调度,切换到 P0 进程开始执行。在这个时候 P0 可以进入临界区,并且执行完对应的代码之后,P0 会把临界区的权限,转交给另外一个进程,也是就是对应代码: turn = 1 ,当 P1 重新被 CPU 执行的时候,就可以进入临界区了。

因此,该算法可以实现:同一时刻最多只允许一个进程访问临界区。

但是大家思考一下,,这个算法有一个很明显的缺点,假设现在 P1 进程它执行完了,并且 P1 又把执行权转交给了 P0,但是 P0 它现在不需要使用临界资源,就会出现一种情况,临界资源是空闲的状态,P1 进程想要用,但是没权限,P0 又一直掌握了执行权,又不给 P1,所以违反了空闲让进原则。

双标志先检查算法

算法思想:设置一个布尔类型数组 flag[],数组中各个元素用来标记各进程想要进入临界区的意愿。比如 flag[0] = true 表示 P0 进程想要进入临界区,每个进程在进入临界区之前先检查当前有没有别的进程想要进入临界区,如果没有,则把自己对应的标志改为 true,之后开始临界区。

我们还是看看代码吧,有一个 boolean flag[2],初始值都是 false。

P0 进程:

while(flag[1]) // 进入区,先判断其他线程有没有想要使用临界资源的想法,如果有等待
flag[0] = true; // 进入区,如果没有,那么就把自己的对应的表示改为 true
critical section; // 进入临界区
flag[0] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

P1 进程:

while(flag[0]) // 进入区,先判断其他线程有没有想要使用临界资源的想法,如果有等待
flag[1] = true; // 进入区,如果没有,那么就把自己的对应的表示改为 true
critical section; // 进入临界区
flag[1] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

这种设计,就避免了刚刚单标志算法空闲让进的问题,但是大家仔细看看代码,在上面的代码中 进入区 有两行代码,这两个处理并没有保证原子性,在并发的情况下,就有可能 P0 进程 while 循环进去了还没来得及修改自己的标志,P1 进程也开始执行了,由于 P0 还没来及的修改自己的标志,所以 P1 的 while 循环也进去了,这样就会导致,两个进程都获得了临界资源,就违反了 忙则等待 的原则,因为两个进程都进入了临界区了。

双标志后检查算法

算法思想:该算法是基于双标志先检查算法的改进,只是在进入区两个操作调整了一下顺序,代码如下:
P0 进程:

flag[0] = true; // 进入区,先把自己的对应的表示改为 true
while(flag[1]) // 进入区,再判断其他线程有没有想要使用临界资源的想法,如果有等待
critical section; // 进入临界区
flag[0] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

P1 进程:

flag[1] = true; // 进入区,先把自己的对应的表示改为 true
while(flag[0])// 进入区,再判断其他线程有没有想要使用临界资源的想法,如果有等待
critical section; // 进入临界区
flag[1] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

这种算法存在很大的问题,还是在并发的场景下,P0进程把自己的标志修改为 true,此时 P1 同样也把自己的标志修改为 true,那么P0、P1 两个的 while 循环都会卡住,两个进程全部都不能进去临界区,并且临界资源是空闲的状态。

违反了空闲让进有限等待的原则,各个进程都长期无法反问临界资源而产生饥饿现象。

皮特森 Peterson 算法

算法思想:结合双标志、单标志的思想,如果双方都争着进入临界区,那可以让进程尝试相互谦让的做法,具体还是看代码把:

有一个布尔数组 boolean flag[2],初始值都是 false,还有一个标志 int turn = 0,表示优先让哪个进程进入临界区。

P0 进程:

flag[0] = true; // 进入区,先修改自己的对应的标志,表示自己想要进入临界区
turn = 1; // 进入区,可以优先让对方进入临界区
while(flag[1] && turn == 1)  // 如果对方想进,并且判断自己也可以优先对方先进入,那自己就进入等待
critical section; // 进入临界区
flag[0] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

P1 进程:

flag[1] = true; // 进入区,先修改自己的对应的标志,表示自己想要进入临界区
turn = 0; // 进入区,可以优先让对方进入临界区
while(flag[0] && turn == 0)  // 如果对方想进,并且判断自己也可以优先对方先进入,那自己就进入等待
critical section; // 进入临界区
flag[1] = false; // 退出区,使用完之后,把自己的标记成不需要使用了临界资源了
remainder section // 剩余区,做一些额外的事情

这个算法解决了前三种算法的缺点,遵循了空闲让进、忙着等待、有限等待三个原则,但是依然没有遵守让权等待的原则,如果自己获取不到进入临界区的权限,那么应该释放 CPU 的资源,而不是一直占有者。

好了,说完软件层面解决进程互斥的办法,在硬件上也有对应的解决办法,我们一起往下看:

互斥锁

解决临界区最简单的工具就是互斥锁,一个进程在进入临界区时应该获得锁,在退出临界区时要释放锁, 函数 acquire() 获得锁,release() 释放锁。

每个互斥锁有一个布尔变量 available,表示锁是否可用,如果锁是可用的,就会调用acquire()会成功,且锁不在可用。如果当一个进程尝试获取不可用的锁,会被阻塞,一直到锁被释放。

acquire()、release()的执行必须是原子操作,因此互斥锁通常采用硬件机制来实现。互斥锁主要缺点就是忙等待,当有一个进程在临界区中,任何其他进程在进入临界区时,必须连续循环调用 acquire(),这种方式也叫做自旋锁

进程互斥硬件的实现方式

硬件这一块,大家了解一下就好,这里我就不多说了
在这里插入图片描述

本章总结

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/527462.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

看懂身份证识别OCR:从算法到 API 使用

引言 身份证识别OCR API是一种基于 OCR 技术的身份证识别服务,可以将身份证上的各种信息自动识别并提取出来,包括姓名、身份证号码、性别、民族、出生年月日、地址、签发机关和有效期等。 这项技术可以大大提高身份验证和信息采集的准确性和效率&#…

SpringBoot——配置文件的作用

简单介绍: 之前我们介绍了有关于SpringBoot文件结构的resources文件夹下面的两个子文件夹的作用,也就是在我们访问页面的时候使用方式静态资源的地方,那么在这个文件夹下面还有一个文件叫做application.properties,这个文件就是项…

【容器化应用程序设计和开发】2.6 DevOps实践和持续集成/持续部署流程

往期回顾: 第一章:【云原生概念和技术】 第二章:2.1 容器化基础知识和Docker容器 第二章:2.2 Dockerfile 的编写和最佳实践 第二章:2.3 容器编排和Kubernetes调度 第二章:2.4 容器网络和存储 第二章&…

IP信息收集

1.DNS服务器类型 例如bbs.neko.com,本地DNS服务器,根DNS服务器,TLD(TOP Level DNS)顶级域名服务器解析neko.com,authoritative DNS server(解析bbs.三级域名) 两种查询方式&#x…

2023年5月深圳数据分析师需要哪些证书?当然CPDA

CPDA数据分析师认证是大数据方面的认证,助力数据分析人员打下扎实的数据分析基础知识功底,为入门数据分析保驾护航。 帮助数据分析人员掌握系统化的数据分析思维和方法论,提升工作效率和决策能力,遇到问题能够举一反三&#xff0c…

《针灸》笔记(倪海厦先生人纪系列针灸篇——任脉)

程序员上了年纪,各种职业病就来了,人工智能成为好工具的同时,自己的时间也多了一些。 了解他才能判断他的真伪,没学过就认为中医是糟粕的,请划走。 学到什么记什么,线上线下齐下手,自用笔记&…

JAVA项目通过IDEA如何构建可执行JAR

开发JAVA工程代码(main方法启动)如何构建出可以通过java -jar命令直接执行的jar?如果工程中依赖其他jar又如何处理?本文即针对以上两点通过IDEA开发工具来做一个简单的分析和尝试,测试如下四种方法,如有瑕疵请轻喷。 文字目录 1.…

命令行下载谷歌云盘超大文件

在这申请API:https://developers.google.com/oauthplayground/

GL绘制自定义线条3_自定义线帽

安卓Path搭配Paint可以设置线帽,我想能不能把我自己的线条绘制Demo也加上类似的功能。 线头规则描述: 1、设一个线宽一半的线段,坐标为(0, 0)到(-lineWidth / 2, 0)。 2、设步骤1的线段有一垂直于它的向量(0,1),然后传…

成功的项目管理的关键之一——时间计划

在现实的项目管理中,由于时间管理控制不力,导致项目拖期交付使用而使各相关方蒙受损失的案例屡见不鲜,究其原因,不完善的项目时间计划安排是一个重要的方面。成功的项目管理的关键之一就是成功的时间管理,而成功的时间…

Docker中如何限制容器可用的 CPU

默认情况下容器可以使用的主机 CPU 资源是不受限制的。和内存资源的使用一样,如果不对容器可以使用的 CPU 资源进行限制,一旦发生容器内程序异常使用 CPU 的情况,很可能把整个主机的 CPU 资源耗尽,从而导致更大的灾难。本文将介绍…

FileInputFormat的实现类

FileINputFormat的切片机制 FileInputFormat是MapReduce中用于处理文件输入的基类,它定义了输入文件的切片规则,并提供了默认的切片实现。具体来说,FileInputFormat会根据输入文件的大小和块大小等因素计算出每个切片的起始位置和长度&#…

【机器学习】多元线性回归详解和特征压缩

注意⚠️阅读本文前,你应该需要掌握:机器学习线性回归模型、高等数学微积分部分内容、线性代数矩阵部分内容 前情提要:https://blog.csdn.net/weixin_45434953/article/details/130593910 一、多元线性回归的假设函数 首先我们考虑以下的例…

linux Ubuntu Python 3.10 环境报错与解决方案集合

环境配置参考文章:使用Alpaca-Lora基于LLaMA(7B)二十分钟完成微调 1.报错.nvidia/cublas/lib/libcublas.so.11: undefined symbol: cublasLtHSHMatmulAlgoInit, version libcublasLt.so.11 解决方法: pip uninstall nvidia_cublas_cu112.CUDA版本对应…

Guitar Pro8优秀的自动扒谱软件

对于一些技术娴熟的音乐人来说,不仅需要演奏已有的乐谱,有时还需要从听到的其他音乐中将谱子扒下来。扒谱时可以借助扒谱软件,比如Guitar Pro,就是一款优秀的扒谱软件。下面就和大家分享一下guitar pro能自动扒谱吗,gu…

基于Java+SpringBoot+Vue餐厅点餐管理系统设计和实现

博主介绍:✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专…

spring boot与asp.net core区别联系

之前一直使用C#编写网站,最近也在了解学习java,根据目前我了解的和学习到的做一个总结分析,写的不好,大家见谅。 联系 名称javac#DIspringasp.net core、Autofac、UnityAOPspringasp.net coreORMmubatis、HibernateEntityFramew…

【MySQL】MySQL索引之最左前缀优化

文章目录 一、联合索引联合索引执行示例 二、索引的 order by优化MySQL中的排序方式数据准备无索引有索引where子句索引字段顺序不一致order by索引字段顺序不一致索引字段升降序不一致 三、总结 一、联合索引 对主键建立的索引叫做聚簇索引, 对普通字段建立的索引叫做二级索引…

Linux实操篇---常用的基本命令1(跟文件操作相关的命令)

一、常用的基本命令 1.常用的shell命令 Shell可以看作是一个命令解释器,为我们提供了交互式的文本控制台界面。 目前的发行版本:在bin/sh 最早的版本Unix:Bourne shell—>Bourne Again Shell 取了 B A Sh。因此目前Linux的发行版大多数…

PieCloudDB Database 与多家基础架构软件厂商完成产品兼容性认证

数据库作为数字经济建设的重要基础,扮演着产业数字化和数据价值释放的基石角色。然而,数据库的发展不能仅仅依赖于自身的技术和创新,也需要建立一个良好的生态系统,与各方合作共同推进数据库技术的进步与创新。 拓数派&#xff08…