CHS_05.2.3.4_1+信号量机制
- 知识总览
- 信号量机制
- 信号量机制——整型信号量
- 信号量机制——记录型信号量
- 知识回顾
在这个小节中 我们会学习信号量 机制这个极其重要的知识点
知识总览
在考研当中 我们需要掌握两种类型的信号量 一种是整形信号量 另一种是记录型信号量
我们会在后面分别展开讲解 那么在正式聊本节的内容之前
我们先用之前学过的内容来引出两个问题 之前咱们学过 在进程互斥当中有四种
软件实现方式和三种硬件实现方式 其中单标志法 双标志先检查和双标志后检查
这三种方法都存在着比较严重的一些问题的隐患 大家
尝试回忆一下 还能不能想起来呢 那么在双标志先检查法当中 我们之前聊过造成双标志先检查法的问题的主要原因在于
他在进入区的检查和上锁这两个操作是无法一气呵成的
中间有可能先执行了检查就进行进程切换 所以在这种情况下 如果两个进程并发执行的话 那么就有可能导致两个进程同时进入临界区的问题
那么我们提出第一个问题 能不能用某种方法能够让检查和上锁这两个操作都可以一气呵成呢 从而避免这个双标志检查先检查法的这个严重问题呢
第二个问题 除了这三种问题比较大的算法之外 Peterson算法还有后面的三种硬件实现方式其实问题都不大
但是这些方式也都无法实现让权等待这个原则 那么第二个问题就是我们能不能用某种方式实现真正的让这些
并发执行的进程能够遵守让权等待的原则呢 这是第二个问题 其实这两个问题在
很早很早以前 1965年有一个荷兰学者叫做Dijkstra 如果学过数据结构的同学 对这个名字可能并不陌生 他提出了一个
很好的解决方式解决方案叫做信号量机制 那么信号量机制就可以比较好的解决
信号量机制
进程互斥和进程同步的问题 我们先来看一下对信号量机制的一些文字性的描述
用户可以通过操作系统提供的一对原语来对信号量进行操作
然后就实现进程互斥 进程同步这样的事情 这儿提到的信号量 其实我们可以把它理解为 简单的理解为 它就是某一种变量
它可以是一个整数 也可以是比较复杂的记录型的 这种数据结构的变量 一个数据 一个信号量 它可以用来表示系统当中的某种资源的数量
比如说一个系统当中有一台打印机 那么我们就可以为这个打印机设置一个和它对应的信号量
让这个信号量的值 把它初始值设为一那么就表示这个系统当中打印机这种资源的数量是有一个
所以只是信号量的用法 那么之后我们还会根据更多的例子来让大家加深对这这段话的理解
这提到的原语 咱们在之前也介绍过 它其实就是一种特殊的程序段 就是由操作系统提供的一种
在执行的过程当中不可以被中断 只能一气呵成的这种程序
而这个原语的具体实是怎么实现的 咱们之前也介绍过 那还记得刚开始开篇的时候咱们提过的问题吗
双标志先检查法的问题主要在于他在进入区当中的检查和操作
检查和上锁这两个操作无法一气合成 那么既然原语拥有这种
一气呵成不可被中断的特性 那如果说我们把检查和上锁这两个操作都放在一个原语当中完成
那么是不是就可以避免它无法一气合成的这种问题了呢 所以这就是为什么信号量机制会想到用原语来解决问题的一个原因
那这儿提到的一对原语指的是wait原语和signal原语
这两个原语 其实我们就把它理解为是一种一种我们自己写的程序段
那这个wait其实就是函数明以后有一个函数明 然后这个s括号里的这个s 我们就把它理解为是一种函数 调用锁有的一个参数
那这个这个地方大家如果学过数据结构 或者自己有一些编程基础的话 应该不难理解
另外呢 wait和signal这两个原语 或者说这两个程序 这两个函数也可以简称为p v操作
这个pv操作我在自己复习的时候一直不明白他的来历 然后之前查了一下才知道他其实是来自两个荷兰语 具体什么意思 有兴趣的同学大家可以去查一下 在我们的考研当中 经常会把wait和signal这两个
函数这两个原语把它简写为p和v 这样的一种简写的形式 说了这么多 其实这整块想要告诉大家的无非就是两件事
第一信号量 它是一种一种变量 可以用信号量来表示我们系统当中某种资源的数量
第二我们可以用系统提供的一对原语 wait原语和signal原语来对信号量
进行操作 那具体是怎么操作呢 我们之后的讲解还会展开这 我们
注意 这个地方的有一句话信号量这种变量 它可以是一个整数 也可以是一个更复杂的记录型的变量
那么 根据这个问题 我们就引伸出了两种类型的信号量 一种叫整形信号量 一种叫记录型信号量
信号量机制——整型信号量
我们先来看第一种整形信号量 整形信号量其实就是用一个整数
来表示某一种系统资源的数量 那么它和普通的整形变量有什么区别呢
普通的整形变量 我们可以对它进行加减乘除各种各样的操作运算
但是对于作为信号量来说的这种整形变量来说 我们只能对这种信号量
进行三种操作 一种是初始化 另一种是p操作 还有一种是v操作
所以按照他的定义 如果说一个计算机系统当中有一台打印机 那么我们就可以定义一个和打印机这种系统资源对应的
整形信号量 我们把它这个变量名设置为s s等于一也就是说 这个系统当中原本是有一台打印机这种系统资源的
那我们可以通过wait和signal 或者说p v这两个操作
对s这个信号量进行一些更改 一些操作 那具体wait和signal做了一些什么事情呢 我们直接来看一个例子
如果一个进程p零他想要使用打印机这种资源 那么由于这种资源是有限的 他只有一个
并且我们需要互斥的访问这个打印机 所以在使用打印机资源之前
进程p零他必须要先执行一个wait原语对
信号两s进行操作 那wait原语当中做了两件事情 第一件事
就是这句代码 他会检查当前剩余的资源数量是否还足够
如果s小于等于零 就说明现在系统当中已经没有这种资源了
那么 这个进程就会被一直卡在这个循环下不去 但是由于p 零进程
执行wait原语的时候 这个s的值是一所以这个循环并不会卡住它 它会跳出这个循环 然后执行下一句
下一句是把s的值减一也就是说 这个打印机资源已经被分配给p零进程了 因此系统当中打印机资源的数量s要减一
所以只是这一步的意思 所以s就变为了零这个值 那么当p 零在访问打印机资源的过程当中 假如说发生了进程切换
有另外的进程 比如说进程p一它也想使用打印机 这种
资源 那么他在使用之前先执行wait这个源语 不过由于此时s的值已经是零也就是说此时系统当中已经没有打印机资源了 所以p一这个进程在执行这个循环的时候 由于满足这个循环的条件 所以他会
一直循环 一直循环 循环等待 直到p零这个进程把打印机资源释放了
那其他的进程也一样 即使他们都想争着使用打印机资源 但是由于这个循环是过不了的 所以他们会一直循环等待
那一直到p零使用完打印机资源之后 他又执行了signal原语
c的原语 做的事情很简单 其实就是把这个s的值加一 也就是告诉把这个打印机还给系统
并且把打印机对应的这个信号量的值加一也就表示打印机资源的数量变多了
那当打印当s等于一之后 p 一或者说其中的某一些某一个正在等待打印机的进程
这个循环就可以被跳过 然后再继续执行下面的循下面的语句 于是p一进程就可以开始使用打印机 然后一直到p一再把打印机释放
然后又把这个打印机资源交给别的进程使用 那这就是整形信号量做的一个事情
其实通过对比大家会发现整形信号量在wait这个原语当中的这两个操作
其实逻辑上来看 和双标志先检查法当中先检查后上锁其实是以做的 其实做的是一样的事情 大家可以对比着来
串联一下这两个知识点 那么 因为它是用一个原语来实现的
检查和上锁 所以他这两个操作仪器合成就避免了双标志先检查法那种
就是两个进程同时进入临界区的问题 第二点 在整形信号量机制当中我们可以看到
这个地方有一个while循环 如果说此时一个进程暂时进不了临界区 暂时使用不了这个资源 那么它会一直卡在这个循环 这因此会发生进程一直占用处理机忙等的这种情况
并不满足让权 等待的原则 那这个地方可能会有一个疑问 如果一个进程暂时进不了临界区 也就意味着他他被卡在wait这个原语的
这个while循环里 那么既然wait原语它是不可被中断的 那么也就意味着当前正在执行while循环的这个进程
是不是一直不会被切换呢 这个地方确实是一个让人感觉不太严谨的地方
那有兴趣的同学可以自己下去研究一下 但是我们在很多经典的教材当中 其实他们都是这么写的 所以这个地方我们姑且认为他没有问题 不会导致一个进程一直占用处理机的情况吧
那在整形信号量当中 其实比较容易考察的是它存在的问题 这一点经常会把这个
整形信号量和记录型的信号两座对比 那么他俩的区别就在于整形信号量不满足让权等待会发生忙等
所以 这个地方是大家需要重点关注的 而刚才提出的问题 有兴趣的同学可以自己下去研究一下
信号量机制——记录型信号量
那接下来我们介绍第二种信号量 叫做记录型信号量 刚才的整形信号量有一个很大的缺陷 就是
如果一个进程暂时进不了临界区 那个资源数 系统的那种资源暂时不够的话 它会一直占用处理机 一直循环检查
从而导致忙等的情况不满足让权 等待的原则 所以后来人们又提出了记录型信号量
就是为了解决刚才锁说的那个问题 这种信号量是用一个记录型的数据结构来表示
其中value表示的是当前这种系统资源的剩余数量 比如说刚才咱们提到打印机
第二个比较重要的是 在这种信号量当中 他还会保持一个
指向等待这种系统资源的等待 对列指向等待它的那些进程
那具体的 咱们之后用一个具体的例子来再展开细料 对于记录型信号量的位置操作是这样的
首先执行了wait操作 就意味着某一个进程它需要使用与这个信号量对应的那种系统资源
所以我们会把这个资源的数量 也就是它的value的值做一个简易的操作
当他简易之后 又会对这个value的值进行一个检查 如果说我们在执行了简易操作之后 导致这个value的值已经小于零了
那么就说明他在简易之前其实已经没有这种系统资源了
因此 在这个时候 是没有系统资源可以分配给当前申请这种资源的那个进程的
因此 在这个地方需要执行一个block原语 这个原语的作用就是
当前的这个进程阻塞起来 主动的阻塞放弃处理机并且把它挂到
这个信号量对应的这个队列等待队列 当中这是wait操作 而sign的操作是当一个进程它在使用完这种系统资源的时候会执行的一个原语操作
首先是会把这种资源的数量进行一个加一的操作 如果value的值再加一之后仍然小于等于零
那么就说明在这个进程释放这个资源之前依然还有别 依然还有一些进程是true于等待对列的
所以就需要再调用一个wakeup原语从s 也就是从这个信号量对应的等待对列当中唤醒其中的某一个进程
然后让他从阻塞态回到就绪态并且把这他锁申请的 他锁等待的这个资源分配给他
所以这就是wait原语和signal原语要做的两件事情 他们做的一个共同点是
不管怎么说 肯定是对vidvalue的值先减减或者先加加 然后之后还会再对value的值做做一个检查来再来判断是否需要对进程进行阻塞 或者是否需要唤醒某一个进程
那具体我们来看一个例子 如果说一个计算机当中 计算机系统当中有两台打印机
那么我们可以把初始的这个信号量的初始值把它设置为二就是
这个value值设置为二而刚开始等待这个打印机资源的等待对列肯定是空的
各个进程在使用这个打印机资源之前 需要先用wait源语来申请打印机资源
在使用完之后 又需要执行signal原语来释放一个打印机资源 所以锁有各个进程的代码大概是这个样子 这些省略号就是其余的部分
那假如说刚开始cpu是为p零这个进程服务的 那么当他执行到wait原语的时候 首先会执行的事情是value简简
对吧 所以这个s点value这个值 它会由2-为一之后
按系统判断 此时是有打印机资源的 所以会把其中的一个打印机分配给p 零进程 然后p零可以往下开始使用打印机
之后切换到了p 一进程cpu为p一进程服务 那么p一进程 同样的 他执行wait原语的时候 其实也是在申请一个打印机资源 那首先做的事情是会让s点value的值做一个简易的操作
所以 这个值从一变为了零之后 系统会把打印机分配给p一进程 然后p一进程可以开始使用打印机
那么我们可以看到 当这个value的值变为了零的时候 此时系统当中的锁有的打印机刚好就已经全部分配给了
某一些进程说明资源恰好分配完毕 那么接下来如果cpu再为p 二进程执行
而p二进程同样需要使用打印机资源 他在执行为操作的时候同样首先
肯定是让这个信号量的value值做一个简易的操作 所以它会由零这个value会由零变为负一
当value的值在简易之后小于零那么就说明
呃 此时系统当中已经没有多余的资源可以分配给这个进程了
因此 这个进程会主动的在wait源语当中主动的执行一个block源语
也就是把自己阻塞的原语 所以p二进程 它会被挂到打印机这种资源的等待对列里
那在这个地方 我们会发现 当这个value的值等于负一的时候 是有一个进程在等待打印机资源
刚好就是这个复数的绝对值 接下来cpu再转向为p 三进程服务
那么 同样的 他在执行为他的原语的时候 首先是对value值减一
同样的 当他简易之后小于零所以也会知道 此时这种资源打印机资源已经分配完毕 所以p三进程也会主动的执行block源语
因此 p三也会被插入到等相应的等待对列的对位 就是这个样子
那么 由于p二和p三都不能执行 因此接下来cpu只能为p零或者p一服务
那假设接下来cpu是为p零进程服务的 p 零在使用完打印机之后
执行了一个signal原语 还记得signal原语做了什么事情吗 首先 他会让这个value的值做一个加一的操作
所以value会从负二变成负一而如果value的值在加一之后 它依然是小于等于零的
那么就说明此时在等待队列当中依然还有一些进程正在等待 所以毗邻进程在signal原语当中他会主动的
执行一个vcup原语 用来唤醒信号量对应的等待对列当中的某对头的进程 也就是p二
所以p二进程会从阻塞队列的放回就绪队列 并且
会把p零刚刚才释放的这个打印机资源分配给p 二就是这个样子
那么接下来毗邻在执行了其他语句之后就执行完毕 那之后
如果说cpu在接着为p二这个进程服务p二就可以得以开始使用打印机资源
然后在使用完了之后 会对打印机资源进行释放 执行一个signal原语 那么同样的 他首先是会对
value的值进行加一的操作 那由于它加一之后 它的值依然是小于等于零的
所以说明此时在等待队列当中还是有进程正在等待这种资源 所以他也会执行一个vcup原语
来唤醒此时处于等待队列对头的这个进程 也就是p三进程
因此 在执行的这个vcup原语之后p 三进程会从阻塞态
变回就绪态 并且这个p二刚才释放的这个打印机资源会被分配给p三
然后p三的信息会从这个等待对列当中消失 这样的话 这个等待对列就变为了空的状态
那接下来 皮尔在执行完剩余的代码 然后就结束了 在之后如果说cpu又回到了为p衣服 那么p当他使用完打印机资源之后 又会对打印机进行释放
那此时他会对首先是会对value的值进行一个加一的操作 于是从value的值从零变为一
而由于加一之后他已经大于零了 所以说明此时在等待队列当中已经没有进程
在等待了 所以p一进程 他在执行signal操作的时候并不需要执行vcup原语
那接下来系统会回收这个分配给p的打印机资源 让p得以继续往下执行
那最后还有p三这个进程没有结束 所以cpu会p三进程服务 那么p三在使用完打印机之后也是会
对打印机资源进行释放 同样的 这个value的是会加一变为二然后之后
系统回收打印机资源 然后p三得以顺利的执行 最后结束
那么这就是一个记录型信号量的一个具体的例子 相信根据通过刚才的动画 大家应该能够比较形象的理解
好的 那么我们再结合刚才的wait和signal的这个代码 再来
进行一个总结 wait和signal这两个原语分别可以用于对系统资源的申请和释放的时候
那么 这个value的初值其实是表示系统当中某一种资源的数目
如果对一个信号两s进行一次p操作 也就是申请这个
信号量s对应的那种系统资源的话 那么就意味着这个p操作
会导致系统的这种剩余资源数会减一所以我们需要首先进行一个
value减减这样一个操作 那如果说在他简易之后 发现他的值已经小于零了
那么就意味着这种资源其实之前就已经分配完毕了 所以他需要主动的调用block源语来进行自我阻塞
这会导致这个进程的状态从运行态变为阻塞态 并且这个进程相关的信息会被挂到
s这个信号量对应的等待对列当中 用于之后的唤醒工作
那么 可以看到 这种机制其实遵循了锁谓的让权等待原则 因为当一个进程他暂时申请得不到他锁申请的这种系统资源的时候
他会主动的进行自我阻塞 主动的放弃cpu 所以就不会出现
之前的那些解决方案当中 忙等的这种现象 另外呢 如果对一个信号量s进行v操作
那么 就意味着需要释放一个与s这个信号量相对应的那种资源
所以我们此时需要对这个value的值进行加一的操作 那如果说加一之后 它仍然是小于等于零的
就意味着 此时在s对应的等待对列当中依然还有进程
在等待这种资源的分配 所以就需要调用vc up源语从这个队列的对头当中把对头的那个进程给唤醒
也就是让他从阻塞态重新回到就绪态并且把那个资源分配给这个进程
所以这就是记录型信号量在p操作和v操作当中做的事情 那么我们再来
简单的回顾一下这个小节 讲了整形信号量和记录型信号量 其中整形信号量比较容易考察的是它存在的问题 也就是不满足让权等待的原则 有可能会出现忙等的现象
而记录行信号量可以说是操作系统这门课当中最重要的知识点
知识回顾
在大题和小题当中都会有很高的概率会考察记录型信号量 那么大家需要自己
动手写一下记录型信号量的pv操作 还有在什么条件下需要执行block和vcup这两个原语
一定不能用死经背的方式 需要把这个地方彻底的理解 那从刚才的讲解我们也知道 这种记录性的信号量其实很方 可以很方便的用于实现系统资源的申请和释放这样的一个操作
那除此之外 记录型信号量还可以实现进程互斥 进程同步这两个咱们之前提过的问题 但这两个问题咱们会放到下个小节的视频当中再进行讲解
另外 我们需要强调的一点是 如果说在题目当中出现了对某一个信号量s的p操作和v操作 那如果说题目中没有特别说明的话
这个信号量指的都是记录型的信号量 也就是说 如果说这个p操作
暂时得不到他锁申请的资源的话 那么这个进程不会忙等 而是会进入到阻塞的状态
好的 那么以上就是这个小节的全部内容
推荐一个零声学院免费公开课程,个人觉得老师讲得不错,分享给大家:Linux,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK等技术内容,立即学习