在os的内部,不同的进程之间其实还是会有互相交互的实时运作,这些实时运作的调度会影响系统内部某些资源数据的共享问题。

什么场景下os的执行会有数据共享问题

例如a进程访问了x001这块内存区域做了些许修改,然后cpu分片给了b进程继续执行,接下来b进程也对这块内存区域做了修改,这会导致一些共享的内存区域中的数据发生变化。不好确保接下来的a进程继续访问内存区域的时候不受影响。

即然并行有这么多风险,为什么还要使用并行计算这种模式呢

并行计算的优势在于:

共享资源

多个不同的任务共用相同的内存资源,可以减少开销。

任务处理高效

io操作和cpu的计算可以重叠,多个处理器将程序分成多个部分执行。

模块化设计

例如一份大的计算任务,可以拆分为多个小的计算任务然后并行计算,提高执行效率。

操作系统内部的一些基本概念

临界区

访问共享资源的代码我们一般称之为临界区,这里我介绍一个例子来解释下什么是临界区:


例如这段程序代码当中,假设线程A执行A模块,线程B执行B模块,A,B线程同时并发执行的时候,对于 i 的加减操作就是一个对于临界区的访问操作。

具体的执行结果可能是A先执行完毕,也有可能是B先执行完毕,还有一种就是谁也没有结束执行,一直在执行。(例如先执行一轮codeA的i+1,接着时间片切换到执行codeB的i-1,不断循环导致没有一方能结束)

互斥

当一个进程在访问临界区的时候,不允许其他的进程访问共享资源,此时临界区只允许有一个进程存在。

死锁

两个或者以上的进程,在相互等待对方完成特定任务而最终没法将自身任务执行下。

饥饿

一个可以执行的进程,一直被调度器所忽略,导致一直处于可执行状态但是却没有执行。

ps:通常当多个进/线程访问相同的内存资源区域时候就会出现数据安全性访问的问题,我们一般称之为竞态条件的不确定性。

共享资源访问的安全性问题

为了避免临界区访问的时候出现竞态问题,重点还是得设计一种机制,每次都只允许有一个请求访问到公共资源模块才行。

信号量

信号量的本质可以理解为一个整形的数字(sem),对于这个数字的访问,程序内部只是提供了两个原子操作,分别是:

  • P():sem -1,如果sem<0,进入等待状态。

  • V():sem +1,如果sem<=0,则同时会唤醒一个等待的P。

可能观看文字还不太好接受这个概念,下边我通过一张绘图来介绍下:

如上图的右边有两个箭头代表可以同时处理的任务数目,(由于我们一开始设置的sem初始值为2,所以允许同时有2个请求进入临界区)图中的左边则是一条等待任务处理的队列,其内部包含了正在待执行的任务信息。每当有一个请求进入的时候,就会执行一次sem-1的P()操作。

下边,假设当有两个请求同时进入了临界区,则需要执行两次的P()操作,对应的sem也就变成了0

此时如果再有更多的请求进来的话,则执行P()操作,sem继续减少,此时sem<0,新的请求就会进入堵塞状态,需要进入到等待队列WaitQueue中来。

随着request的处理完毕之后,会执行V()操作,sem继续增加,此时sem>=0,那么这时候会顺带唤醒队列里的请求进入临界区。

通过这几张图的解释,相信你应该已经对基本的信号量有了一个清晰的认识。

发明信号量这个名词的科学家其实是一名荷兰人(大名鼎鼎的荷兰计算机科学家 Dijkstra ),P和V分表都是荷兰用语,代表的意思是V verhoog 增加,P prolaag 减少。当sem<=0的时候,再执行P()操作会引起堵塞情况,通常我们的等待队列都是采用了FIFO的设计思路,从而保证后续对于请求的唤醒具有公平性。

信号量的一些应用场景

信号量一般分为三种类型:
二进制类信号量

sem为0或者1,当sem==1则允许请求进入临界区。

一般/计数信号量

sem可以取任何非负数,当sem==0的时候就会堵塞。

条件信号量

按照不同的条件来控制临界区的访问。
二进制类信号量伪代码思路

初始化信号量:

mutex = new Semaphore(0);

对mutex元素做操作:

mutex -> P();
......
mutex -> V();

在操作系统底层,二进制类信号量有什么具体的应用场景吗?
假设有这么一个案例,如下图所示:

线程A需要执行一段methodA,线程B需要执行methodB,对于函数A,B内部分别要求,method_A_2执行之前,需要依靠method_B_1先执行完成。(例如method_A_2需要访问某些资源信息,method_B_1中负责对一些资源信息进行初始化操作)

此时利用mutex->P()和mutex->V()去协调同一个二进制信号量的值就很好地实现了两个线程之间的通信。进程也是类似的案例(在linux系统中线程本质就是轻量级的线程)

ps:关于进程的互相通信部分可以参考之前的文章:OS中的进程

二进制类信号量的sem值只会有0和1,但是在一些更加复杂的条件下,这类设计就存在一定的局限性了。
下边我们再来看一个案例,经典的生产者和消费者问题:
假设有一个共享缓冲区,生产者专门往其中写入数据,消费者专门读取这块缓冲区的数据内容。


场景条件:
1.一次只能有一个线程访问Buffer这个临界区。
2.当Buffer写满了之后Producer需要等待Consumer去进行消费。
3.当Buffer清空之后Consumer需要等待Producer写入新的数据。

这里面如果采用了之前的二进制信号量机制来设计就没法满足一次有多个生产者进入到buffer区域,同时及时是将二进制信号量的sem调大似乎也并不满足当前只允许一种类型的角色访问Buffer临界区的设计。

所以这个时候可以采用条件信号量来进行管理,例如下边这段伪代码:

Class Buffer {mutex = new Semphore(1);fullBuffer = new Semphore(0);emptyBuffer = new Semphore(n); //n对应consumer的数量
}
​
Buffer::Deposit(c){emptyBuffer -> P();mutex -> P();//将数据写入到Buffer中mutex -> V();fullBuffer -> V();
}
​
Buffer::Remove(c){fullBuffer -> P();mutex -> P();//将Buffer中的数据移除mutex -> V();emptyBuffer -> V();
}


这段代码我大概解释下,假设我们设置了10个消费者,并且初始化Buffer中的数据正好满足10次的消费请求。那么在执行Remove函数的时候,fullBuffer信号量的值就会从sem=10一直做sem-1操作。此时如果当sem==0,并且没有生产者去触发Deposit则会导致消费者进入等待环节。

同时二进制信号量mutex的使用也能保证同一时刻只能有一个线程访问Buffer区域,能够较好地控制对于某块内存区域的写入和移除操作。

所以说合理地使用多个信号量可以处理一些较为复杂的约束场景。但是直接地使用信号量其实很容易会导致一些异常的发生,所以直接上手的难度比较高,因此os底层也基于信号量的机制封装了一套操作机制,这套机制我们统一称之为管程

管程

对于管程的理解,大致可以认为是基于信号量的基础做了一层封装,专门用于访问一些共享变量的函数,让调用方使用起来更加简单和清晰。例如JDK内部对于并发模块的实现wait(),notify(),notifyAll()这些函数就是采用了管程技术来控制的。

管程 英文名为monitor,翻译过来就是指监视器的意思,一次只允许有一个进程访问临界区,如果同时有多个访问,则将多余的访问挂起。

管程的组成

  • 一把锁

  • 0或者多个条件变量

一把锁

管程为了保证对于共享资源的访问一次只能有一个元素,所以才引入了锁的机制,没有抢到锁的请求则需要进入到入口等待队列。

0或者多个条件变量

当获取到了锁的请求进入到了临界区模块中之后,有可能还需要做多个条件的判断,如果没有满足其中的某一条条件则需要进入到条件等待队列中。
说了这么多,你可能还是对管程没有一个特别清晰的认识,下边我还是打算通过绘图的方式来和你解释一下:

管程的内部其实本质还是信号量来实现,当获取到锁成功的请求会进入到管程的内部,其余则会留在入口处的等待队列中。进入内部的线程会依次判断是否满足条件A,条件B,条件C,如果均满足则正常执行程序中的函数,正常访问临界区的资源,否则会进入到条件队列中进行等待。

假设线程T1进入了管程中,发现不满足条件C,进入到了条件等待队列C中,之后线程T2进入到了管程中并且通知线程T1,告之已满足条件C,那么线程T1需要重新进入入口队列重新等待。

这里我罗列一段Java程序的简单代码案例来介绍下:


public class BlockedQueue<T>{final Lock lock =new ReentrantLock();// 条件变量:队列不满  final Condition notFull =lock.newCondition();// 条件变量:队列不空  final Condition notEmpty =lock.newCondition();
​// 入队void enq(T x) {lock.lock();try {while (队列已满){// 等待队列不满 notFull.await();}  // 省略入队操作...//入队后,通知可出队notEmpty.signal();}finally {lock.unlock();}}// 出队void deq(){lock.lock();try {while (队列已空){// 等待队列不空notEmpty.await();}// 省略出队操作...//出队后,通知可入队notFull.signal();}finally {lock.unlock();}  }
}

代码中的Condition有多个,分别是notEmpty和notFull,你会发现他们的定义都和os底层的信号量非常相似。

计算机原理探险系列(十)信号量和管程的一些理解相关推荐

  1. 计算机原理探险系列(一)CPU

    cpu制作短视频链接: https://www.bilibili.com/video/BV1tr4y1K7Bs?from=search&seid=12992058713602054171 CP ...

  2. 计算机原理探险系列(二)-- 网络传输

    各位亲爱的读者们大家好,在上一篇文章中我们着重对计算机底层对cpu原理进行了相关分析,那么这篇文章中我们主要会对计算机模块对网络进行一次深入挖掘. 什么是TCP 简单来说就是一种面向连接的稳定,可靠的 ...

  3. 计算机原理探险系列(九)CPU调度机制

    前边有一篇文章中我有提及到过关于操作系统内部的上下文切换部分,以及一些进程和线程的设计模型.今天这篇文章将会从CPU如何分配资源给到不同的进程使用的角度出发. 为什么会有CPU调度 当我们的进程或者线 ...

  4. 计算机多媒体应用技术ppt课件ppt,多媒体计算机技术原理及应用十二课件.ppt

    <多媒体计算机技术原理及应用十二课件.ppt>由会员分享,提供在线免费全文阅读可下载,此文档格式为ppt,更多相关<多媒体计算机技术原理及应用十二课件.ppt>文档请在天天文库 ...

  5. ELK系列(十五)、Elasticsearch核心原理一篇全搞定

    目录 Lucene 介绍 核心术语 如何理解倒排索引? 检索方式 分段存储 段合并策略 Elasticsearch 核心概念 节点类型 集群状态 3C和脑裂 1.共识性(Consensus) 2.并发 ...

  6. 亮考帮优秀作业计算机操作原理,罗教授科技教学系列十∣“对分课堂”教学法...

    原标题:罗教授科技教学系列十∣"对分课堂"教学法 罗伟 博士/副教授 万礼豪程资深教学顾问 武汉轻工大学旅游管理系主任 FD-QM高等教育在线课程质量评审师 还记得那句广告词吗?& ...

  7. 【k8s系列十四】nginx-ingress原理

    一. 现存的问题 现在所有的app再上线之前都必须进行加密,这里的加密不是说对应用加密,而是对请求加密,比如https加密请求.如果我们使用的是clusterIp类型, clusterIp是四层协议, ...

  8. 计算机基础知识第十讲,计算机文化基础(第十讲)学习笔记

    计算机文化基础(第十讲)学习笔记 采样和量化PictureElement Pixel(像素)(链接: 采样的实质就是要用多少点(这个点我们叫像素)来描述一张图像,比如,一幅420x570的图像,就表示 ...

  9. 计算机原理与应用简称,基础知识-计算机原理与应用.ppt

    基础知识-计算机原理与应用 第1章 基础知识 本章内容: 单片机的概念 单片机的发展.基本的结构和特点 单片机的应用模式和领域 MCS-51单片机等. 1.1 计算机的一些概念 1.2 单片机 单片机 ...

最新文章

  1. cocos2d-x游戏开发系列教程-坦克大战游戏之坦克的显示
  2. Python语言学习:三种随机函数random.seed()、numpy.random.seed()、set_random_seed()及random_normal的简介、使用方法(固定种子)详细攻略
  3. c++内存,堆和栈的区别
  4. 如何查看Linux是32位还是64位
  5. server 2008R2 AD域环境中DHCP服务器的授权步骤
  6. 树莓派 小屏幕_树莓派学习手动积累(1)
  7. apply、call、callee、caller初步了解
  8. telnet IP不通/sybase central工具无法连接到数据库
  9. java继承类长方形面积_java_java用接口、多态、继承、类计算三角形和矩形周长及面积的方法,本文实例讲述了java用接口、多 - phpStudy...
  10. linux 常见路径,linux中目录与路径常见相关命令
  11. 一名软件测试工程师的一天24小时(每天在忙什么)
  12. 数字逻辑educoder实训项目 logisim实现 交通灯系统设计超详细实验步骤,绝对完整
  13. 【雷达信号处理】脉冲多普勒PD及其MATLAB实现
  14. Rockstar Games遭黑客攻击,《侠盗猎车手6》90个开发视频外泄
  15. 基于python和高德地图租房系统的设计与实现
  16. Educational Codeforces Round 132 (Rated for Div. 2) 题解(A~D)
  17. 【JavaScript】笑话生成器
  18. 苹果侧边滑动返回_一个丝滑的全屏滑动返回手势
  19. UMLChina建模知识竞赛第3赛季第13轮:SysML和系统工程知识
  20. 【Benewake(北醒)】 单点系列标品介绍

热门文章

  1. 2022年计算机二级c++考试题库软件
  2. 如何实现全链路营销效果跟踪?
  3. 数据权限这样设计,领导直呼666!
  4. 微信公众号 网页支付的实现
  5. 新托福写作:三选一题型写法
  6. 计算机操作题ppt考试题库,计算机二级考试MSOffice考试题库ppt操作题附答案
  7. 自学java,学多久可以自己找到工作?
  8. Vue_02 快速入门 基础语法1
  9. Python函数之六:递归函数
  10. 四种类型项目管理生命周期