OS复习笔记ch5-4-2

news2024/11/24 19:35:43

引言

承接上文我们介绍了信号量机制和应用信号量机制实现的进程同步和互斥,这一节我们将围绕一些经典问题对信号量机制展开更深入地探讨。

读者/写者问题

ppt

读者/写者问题与我们之前遇到的问题类型不同,它描述的是:

有读者和写者两组进程,它们共同访问同一个文件。

对于读者,它可以与多个读者共同读取文件(因为不会修改到文件);

对于写者,它不能与其他任何进程共同访问文件(如果另一进程是写,则可能覆盖同一内容;如果是读,则可能修改正在读的内容)。

这里的互斥问题是读写和写写互斥的问题,但与之前不同的是,除了实现读写和写写的互斥,我们还要实现读读的“不互斥”,即满足”读写互斥,写写互斥,读读允许“

Version1

首先准备一个信号量 rw = 1 表示当前是否有进程在访问文件(注意一开始是没有这样的进程的,1 表示的不是进程数目,是使用互斥信号量给定的初始值,代表任何时刻只有一个进程可以访问)。

在不考虑“读读不互斥”的情况下,我们的伪代码是这样的:

Writer(){               Reader(){
    while(1){             while(1){
        P(rw)                P(rw)
        写文件               读文件  
        V(rw)                V(rw)
    }                     }
}                      }

这个代码可以实现读写互斥和写写互斥,但显然无法实现“读读不互斥”,因为每个读进程之间也会受到 rw 的影响,使得某一时刻只能有一个读进程访问文件。

Version2

我们考虑从读进程入手,做一些改进。这里读和读不能同时进行的本质原因在于,所有的读进程都会经历“检查并上锁”这个步骤,而一个读进程进入后就会马上检查并上锁,导致另一个也想要进入的读进程被阻塞,所以我们考虑:能不能不要让所有的读进程都经历“检查并上锁”这一步骤?也就是说,某些进程可以跳过 P 操作,直接进入临界区,这样一来,这些进程就不存在在 P 操作这里被阻塞的可能性。

什么样的进程可以跳过 P 操作呢?


就是中间的那些读进程。因为一开始肯定要有读进程上锁、最后肯定要有读进程解锁,所以上锁和解锁的任务交付给第一个和最后一个进程,而中间的那些进程来去自如,只需要负责读文件,不需要参与上锁和解锁。
为了区分读进程的次序,我们准备一个 count = 0 的变量,它表示的是当前有多少个读进程正在访问文件。然后在读文件的前后,我们分别对 count 进行加一和减一的操作,每次读文件开始之前 count 会加一,所以在此之前如果变量为 0 ,说明当前读进程是第一个读进程;同理,每次读文件之后 count 会减一,所以在此之后如果变量为 0 ,说明当前读进程是最后一个读进程;

此时伪代码如下:

Reader(){
    while(1){
        if(count==0)
            P(rw)
        count++
        读文件
        count--
        if(count==0)
            V(rw)
    }
}

但是这样会产生一些问题。比方 1 号读进程首先进入并上锁,然后在 P 操作之后、count 加一变成 1 之前,进程切换到 2 号读进程,那么 2 号读进程就会卡在 P 操作这个地方,陷入阻塞,显然这时候无法实现我们想要的“读读不互斥”;又比方说,1 号读进程在 count 减一变成 0 之后、释放 rw 之前,进程切换到了 2 号读进程,那么 2 号同样又会被卡在 P 操作这里。所以我们还要进行改进。

问题其实就出在,对 count 的检查和赋值不是一个原子操作,这导致的结果是,如果在检查和赋值之间的空隙,进程发生切换,则必然会使得另一进程陷入阻塞。那么能不能让这两个操作一气呵成呢?事实上,可以把 count 当作是一个互斥访问的资源,对 count 的访问是互斥的,也就说明一个时间段内只能有一个读进程去访问它,即使这个过程中切换到了其它进程,那个进程也会被阻塞,从而保证只有一个进程可以访问 count,而这个访问就是检查和赋值,这种情况下,检查和赋值一定是不会被中断的。

准备一个互斥信号量 mutex = 1 表示对 count 的互斥访问,将检查和赋值封装在一个 PV 操作里。伪代码如下:

Reader(){
    while(1){
        P(mutex)
        if(count==0)
            P(rw)
        count++
        V(mutex)
        读文件
        
        P(mutex)
        count--
        if(count==0)
            V(rw)
        V(mutex)
    }
}

现在我们再来跑一下过程。
假设还是 1 号读进程运行到 P 操作的时候,进程切换到了 2 号读进程,那么由于互斥信号量 mutex 的存在,导致 2 号进程进入了 mutex 对应的阻塞队列 —— 是的,这时候看起来 2 号进程还是被阻塞了,不过我们要关注到的是,阻塞它的信号量是 mutex,不是 rw。这意味着,在进程重新切换回 1 号进程的时候,1 号进程一旦执行了 V(mutex),就可以将 2 号进程唤醒并送到就绪队列了。也就是说,尽管 2 号进程还是经历了“阻塞”这个过程,但是这个过程只是为了确保 1 号进程检查和上锁两个操作的原子性,一旦操作完成,2 号进程马上就被唤醒了。而之前那种情况不同,之前的情况是,导致 2 号进程被阻塞的是信号量 rw,除非 1 号进程读完后释放,否则 2 号进程会一直处于阻塞状态。这就是说,2 号进程永远不可能与 1 号进程同时读文件,但是改进后是可以的。

但事实上还有另一个问题更加严重——“读写不公平”。也就是说,这样的代码本质上是对读进程更有利的。


因为对读进程来说,一旦第一个读进程进来了,中间即使穿插再多的读进程,也都是允许的,他们根本不受到 rw 这个“锁”的限制;而对于写进程,它的运气就没这么好了,写进程只要想进来,就必须通过 rw 这个“锁”,而这个“锁” 实际上又掌握在最后一个读进程手里 —— 这就是说,万一读进程源源不断进来,始终轮不到最后一个读进程解锁,那么写进程就只能陷入无尽的等待了。

Version3

既然 rw 这把锁无法做到公正对待每一个进程,那我们就考虑在外层加一把“更大、更公正的锁”——所有的进程,无论读还是写,无一例外必须先通过这把“锁”的检查。为此,我们准备一个新的互斥信号量 w = 1,并将 Writer 和 Reader 的一些关键操作封装在 w 的一对 PV 操作里。此时,伪代码如下:

//用于实现对共享文件的互斥访问
semaphore rw = 1; //记录当前有几个读进程在访问文件
semaphore mutex = 1;//用于保证对count变量的互斥访问
semaphore w = 1//用于实现写优先
int count = 0; //用于记录读进程的次序

writer() {
    while(1){
        P(w);
        P(rw)
        
        写文件
        
        V(rw);
        V(w);
    }
}

reader() {
    while(1)
        P(w);
        P(mutex);
        if(count==0)	P(rw);
        Count++;
        V(mutex);
        V(w);
        
        读文件
        
        P(mutex); 
        count--;
        if(count==0)	V(rw);
        V(mutex);
    }
}

我们来跑一下流程。
假设首先来到 1 号读进程,那么它就会执行 P 操作上锁,这个过程中即使有写进程想进来,也会被送到 w 对应的阻塞队列。在 1 号读进程执行到 V 操作之后,写进程才会被唤醒并送到就绪队列,之后就轮到写进程执行了,而写进程虽然通过第一个 P 操作,但是被卡在了第二个 P 操作(读进程尚未释放 rw),所以他来到了 rw 对应的阻塞队列。

注意!重点来了,如果这时候 2 号读进程也想要访问文件,那么在以前,它是不需要通过任何检查就可以直接来读文件的,并且直到 2 号读进程释放 rw 之后,写进程才能真正来执行写文件的操作。但是现在由于我们加了一把“更大的锁w,导致 2 号进程必须先通过 w 的检查,而由于写进程抢先在他之前上了锁,所以 2 号读进程被送到了 w 对应的阻塞队列。也就是说,现在的情况是:写进程等着 1 号读进程释放 rw,而 2 号读进程等着写进程释放 w,1 号读进程是让一切正常进行下去的关键。在处理机又来到 1 号读进程并执行 V(rw) 之后,写进程从 rw 的阻塞队列被唤醒,继续往下执行写文件的操作。而在写进程真正执行完之后,w 才能得到释放,由此又唤醒了 w 阻塞队列中的 2 号读进程,2 号读进程来到处理机运行。

如果换一种情况,是按照 写者 — 读者 — 写者的顺序,那么由于读者在第二个写者之前,所以是读者作为阻塞队列队头,第二个写者则次之,在后续执行过程中,根据队列“先进先出”的原则,也会是读者先于第二个写者访问文件。

综上,这种情况下,实际上谁先到、谁就在后续过程中先被执行(而不是像之前,无论写进程先还是后,读进程都可以“无视规则”抢先一步执行)。由此,我们就实现了“读写公平”。

总结

上文根据王道课上给的案例进行的分析,博主自身在理解这里的时候感觉不是很好记忆,自己结合老师上课的讨论摸索了一套方法解决这个问题。(信号量命名和王道不一致)

首先是解决读者优先问题,设置一个balance信号量(也有写写、写读互斥的作用),解决读写互斥放一个mutex信号量(读的时候不能写),读读允许同样设置一个count计数,配套设置cmutex解决count计数的原子操作。

具体实现思路如下

  1. 先设置好信号量初值
semaphore mutex = 1;
semaphore cmutex = 1;
semaphore balance = 1;
int count = 0;

  1. 然后对写者进程进行分析
writer() {
	while(1) {
		P(balance); // 读写平衡
		P(mutex); // 读写互斥
		写文件...
		P(mutex); 
		V(balance); 
	}
}

  1. 对于读者进程进行分析
reader() {
	while(1) {
		P(balance); // 读写平衡
		P(cmutex);  // count操作互斥
		if(count == 0) P(mutex); //读写互斥
		count--;
		V(cmutex);
		V(balance);
		读文件...
		P(cmutex); // count操作互斥
		count--;
		if(count == 0) V(mutex);
		V(cmutex);
		
	}
}

  • 读写互斥→mutex(斥)
  • 读读允许→cmutex、count(允)
  • 解决读者优先→balance(平衡)

由于读者写者问题确实有他自身的复杂性,所以一定要有自己的理解,不然很容易就不记得该如何解决,按照演绎的顺序就是三个信号量(一斥二允三平衡)+一个计数器count。

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

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

相关文章

分布式版本控制工具git

1 安装Git 在linux上我们建议你用二进制的方式来安装git,可以使用发行版包含的基础软件包管理工具来安装,如果你是 是CentOS或者Fedora的操作系统,可以使用yum命令来安装git: $ sudo yum install git如果你是ubuntu或者是Debian…

语音识别-paddlespeech-流程梳理

上一次研究语音识别是21年年底的事情了,记得当时是先进行了语音识别的应用,然后操作了模型的再次训练;两年过去,关于ASR相关流程忘得差不多了,这次基于paddlespeech的代码,进行了流程的梳理,关于…

从零创建一个vue2项目

标题从零创建一个vue2项目,项目中使用TensorFlow.js识别手写文字 npm切换到淘宝镜像 npm config set registry https://registry.npm.taobao.org安装vue/cli -g npm install -g vue/cli检查是否安装成功 vue -V创建项目 vue create 项目名安装TensorFlow npm …

AI试衣IDM-VTON,Windows11本地安装配置记录!

昨天我们已经介绍过IDM-VTON这个开源项目了。 通过这个软件可以轻松实现一键换衣服。 昨天,简单演示了一下在线使用。 今天,来演示如何安装到本地电脑上! 本地配置会有一定的专业性,懂的人可以参考下。 不懂得直接拉到最后&am…

绘制一个单级放大电路原理图过程,保姆级教程

新手在学习pads的使用最好最快的方法就是实际上手去画原理图,画PCB图,在这个过程中,就能够更快速得掌握PADS软件的使用。 本篇就是对于实际画原理图过程的一个记录,手把手教学,如果有纰漏或者有更好的一些技巧&#xf…

购买正版SOLIDWORKS软件有哪些好处?

在工程设计和制造领域,SOLIDWORKS软件以其强大的功能和广泛的应用而备受推崇。购买正版SOLIDWORKS软件,不仅是对知识产权的尊重,更是对企业和个人长远发展的明智选择。下面,我们将探讨购买正版SOLIDWORKS软件所带来的诸多好处。 …

vue vite 环境非构建包编译 React 报错定位

1. 背景 众所周知,vite 在构建生态的位置,vue 与之更是密切,主流的 vue 库几乎都与 vite 捆绑。 但有些 UI 库 如 private/ui 并没进行行编译,而是直接将源码发布到了 npm 中,无法实现兼容化,需要消费方去…

LabVIEW学习记录3 - 自定义函数

LabVIEW学习记录3 - 自定义函数 一、LabVIEW学习记录二、自定义函数及函数调用 一、LabVIEW学习记录 【labVIEW】学习记录LabVIEW学习记录2 - MySQL数据库连接与操作LabVIEW学习记录 - 实时显示时间LabVIEW学习记录4-局部变量、全局变量、共享变量 二、自定义函数及函数调用 …

【vue2项目经验总结:部署到服务器之后出现所有数据渲染失败的问题】

原因是因为在没部署到服务器之前前端为了解决跨域问题使用了代理,但是在项目部署到服务器之后,前端通常不再需要使用代理,因为代理的作用是在开发过程中帮助前端应用程序与后端服务进行通信,解决跨域访问等问题。在开发阶段&#…

海洋环境保护论文阅读记录

海洋环境保护 论文1:Critical role of wave–seabed interactions in the extensive erosion of Yellow River estuarine sediments 波浪-海床相互作用在黄河河口广泛侵中的关键作用 estuatine 河口的,港湾的 erodibility侵蚀度 sediment erodibility …

Springboot HelloWorld

新建一个maven工程 引入依赖项 <modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.2.11.RELEASE</version><…

LLM实战:LLM微调加速神器-Unsloth + LLama3

1. 背景 五一结束后&#xff0c;本qiang~又投入了LLM的技术海洋中&#xff0c;本期将给大家带来LLM微调神器&#xff1a;Unsloth。 正如Unsloth官方的对外宣贯&#xff1a;Easily finetune & train LLMs; Get faster with unsloth。微调训练LLM&#xff0c;可以显著提升速…

(项目)-KDE巡检报告(模板

金山云于12月26日对建行共计【30】个KDE集群,合计【198】台服务器进行了巡检服务。共发现系统风险【135】条,服务风险【1912】条,服务配置风险【368】条。 一、系统风险 1、风险分析(图片+描述) (1)磁盘使用率高 问题描述多个集群的多台服务器磁盘使用率较高,远超过…

UniAD大模型开路,智能车驶入AGI时代

作者 |老缅 编辑 |德新 在刚刚结束不久的北京车展上&#xff0c;除一众明星车型亮相&#xff0c;供应链企业也开始大秀肌肉&#xff0c;其中尤其以端到端大模型为代表&#xff0c;焕新一代的智驾技术栈掀起了新一轮热潮。 作为首个提出感知决策一体化自动驾驶通用模型的公司&…

计算机网络(第八版 谢希仁 编著) 期末复习大纲

一.每章总结 第一章&#xff1a;分组交换&#xff0c;计网定义、范围划分&#xff0c;性能指标&#xff0c;五层体系结构&#xff0c;TCP/IP体系结构 第二章&#xff1a;物理层&#xff0c;码元&#xff0c;基带调制(数字信号->数字信号&#xff0c;也叫编码)&#xff0c;带…

mysql group by 细节介绍

mysql中group by的用法是配合聚合函数&#xff0c;利用分组信息进行统计&#xff0c;语句如“select name,sum(id) from test group by name,number”。 先来看下表1&#xff0c;表名为test&#xff1a; 执行如下SQL语句&#xff1a; SELECT name FROM test GROUP BY name 你…

专业音频修复软件:iZotope RX 11 for Mac 激活版

iZotope RX 专为满足后期制作专业人士的苛刻需求而设计的一款专业音频修复软件。iZotope RX 10添加了新的特性和功能&#xff0c;以解决当今后期项目中存在的一些最常见的修复问题&#xff0c;使其成为音频后期制作的最终选择。虽然包含许多其他新功能&#xff0c;但这里是新的…

Postman基础功能-断言与日志

若能脱颖而出&#xff0c;何必苦苦融入。大家好&#xff0c;在 API 测试的领域中&#xff0c;Postman 是一款极为强大且广泛使用的工具。其中&#xff0c;断言和日志调试功能扮演着至关重要的角色。 一、介绍 断言允许我们在测试过程中验证 API 的响应是否符合预期。通过设定各…

数字人解决方案——AniTalker声音驱动肖像生成生动多样的头部说话视频算法解析

1.概述 AniTalker是一款先进的AI驱动的动画生成工具&#xff0c;它超越了简单的嘴唇同步技术&#xff0c;能够精准捕捉并再现人物的面部表情、头部动作以及其他非言语的微妙动态。这不仅意味着AniTalker能够生成嘴型精准同步的视频&#xff0c;更重要的是&#xff0c;它还能够…

使用LangChain和Neo4j快速创建RAG应用

大家好&#xff0c;Neo4j 通过集成原生的向量搜索功能&#xff0c;增强了其对检索增强生成&#xff08;RAG&#xff09;应用的支持&#xff0c;这标志着一个重要的里程碑。这项新功能通过向量索引搜索处理非结构化文本&#xff0c;增强了 Neo4j 在存储和分析结构化数据方面的现…