OS-2-3-进程同步
2021-08-17 21:39:38 0 举报
AI智能生成
操作系统 第二章 第三节 同步-互斥机制的实现方式,信号量机制及其应用 知识点梳理 个人理解
作者其他创作
大纲/内容
经典问题
生产者消费者问题
资源
初始为空,大小为n的缓冲区
关系<br>
缓冲区空时,生产者必须先放入,消费者才能消耗:同步关系
缓冲区满时,消费者必须先取走,生产者才能放入:同步关系<br>
生产者和消费者访问缓冲区:互斥关系
实现<br>
semaphore mutex=1; //即产品池的访问权<br>semaphore empty=0; //即空缓冲区的数量<br>semaphore full=0; //即产品的数量<br>
Producer()
while(1) {<br> Produce();<br> P(empty);<br> P(mutex);<br> PutInto();<br> V(mutex);<br> V(full);<br>}<br>
Consumer()<br>
while(1) {<br> P(full);<br> P(mutex);<br> Get();<br> V(mutex);<br> V(full);<br> Consume();<br>}<br>
多生产者-多消费者问题
资源
初始为空,大小为1的缓冲区
关系
缓冲区空时,生产者必须先放入,消费者才能消耗:同步关系
缓冲区满A时,消费者A必须先取走,生产者才能放入:同步关系
缓冲区满B时,消费者B必须先取走,生产者才能放入:同步关系
生产者AB、消费者AB对缓冲区的访问:互斥关系
实现
semaphore mutex=1;<br>semaphore A=0;<br>semaphore B=0;<br>semaphore empty=1;<br>
可以分析得:任何时刻A、B、empty最多只有1个为1,<br>即<font color="#F44336">最多只有一个进程不被阻塞</font>,因此可以去掉mutex<br>
如果缓冲区大小大于1,此时上述情况不总成立,mutex不可去
Class ProducerA<br>
while (1) {<br> ProduceA();<br> P(empty);<br> P(mutex);<br> PutInto();<br> V(mutex);<br> V(A);<br>}<br>
Class ProducerB
while (1) {<br> ProduceB();<br> P(empty);<br> P(mutex);<br> PutInto();<br> V(mutex);<br> V(B);<br>}<br>
Class ConsumerA
while (1) {<br> P(A);<br> P(mutex);<br> Get();<br> V(mutex);<br> V(empty);<br> Consume();<br>}<br>
Class ConsumerB
while (1) {<br> P(B);<br> P(mutex);<br> Get();<br> V(mutex);<br> V(empty);<br> Consume();<br>}<br>
吸烟者问题<br>单万能生产者-多消费者问题<br>
资源
大小为1的缓冲区
关系
缓冲区内有A时,消费者A才会取走A:同步关系
缓冲区内有B时,消费者B才会取走B:同步关系
缓冲区内有C时,消费者C才会取走C:同步关系
消费者完成消费时,生产者才为下一个消费者提供资源:同步关系
轮流关系是盲的,无需区分是谁完成了<br>
生产者和消费者ABC访问缓冲区:互斥关系
缓冲区大小为1,可去<br>
实现
semaphore mutex=1;<br>semaphore A=0;<br>semaphore B=0;<br>semaphore C=0;<br>semaphore finish=0;<br>
Class <font color="#F44336">MultiProducer</font><br>
while(1) {<br> ProduceA();<br> PutInto();<br> P(finish);<br> <br> ProduceB();<br> PutInto();<br> P(finish);<br><br> ProduceC();<br> PutInto();<br> P(finish);<br>}<br>
也可以用变量和循环控制
根据题目要求,最好用变量
Class ConsumerA<br>
while(1) {<br> P(A);<br> Get();<br> Consume();<br> V(finish);<br>}<br>
Class ConsumerB<br>
while(1) {<br> P(B);<br> Get();<br> Consume();<br> V(finish);<br>}<br>
Class ConsumerC<br>
while(1) {<br> P(C);<br> Get();<br> Consume();<br> V(finish);<br>}<br>
读者写者问题
资源
一个文件
关系
写者修改文件:互斥关系<br>
写者写时不允许读者读:互斥关系
<font color="#F44336">写者欲写时,阻止后来的读者进入读状态:互斥关系</font>
核心<br>
<font color="#F44336">利用count取消互斥</font><font color="#4CAF50"></font>
<font color="#4CAF50">用额外的信号量保证count互斥</font>
用PV包裹“读写非互斥量的代码段”可使其具备互斥性
<font color="#2196F3">用w保证写者不饥饿<br></font>
任何针对互斥量m的一组PV操作实际上阻断了<br>其他进程进入其被针对m的PV操作包裹的代码段<br>
亦可认为:<br>本进程一组对m的PV包裹的代码段执行<br>优先于其他进程被对m的PV包裹的代码段<br>
当进程A用P(m)V(m)包裹P(n)V(n),<br>而进程B用P(m)V(m)仅包裹P(n)时,<br>表明进程A执行P(n)V(n)所包裹的代码段<br>优先于进程B执行P(n)V(n)所包裹的代码段<br>
所谓优先,是当B类进程间不存在互斥关系,<br>而A类B类间存在互斥关系时,A类可以阻断<br>后来的B类进程资源请求,从而“强行插队”。<br>
实现
semaphore rw = 1;<br><font color="#F44336">int count = 0;<br><font color="#4CAF50">semaphore countMutex = 1;</font><br><font color="#2196F3">semaphore w = 1;</font></font><br>
Class Writer<br>
while(1) {<br> <font color="#2196F3">P(w);</font><br> P(rw);<br> Write();<br> V(rw);<br> <font color="#2196F3">V(w);</font><br>}<br>
Class Reader<br>
while(1) {<br> <font color="#2196F3">P(W);</font><br> <font color="#4CAF50">P(countMutex);</font><br> <font color="#F44336">if (count == 0)<br> <font color="#212121">P(rw);</font><br> count++;<br> <font color="#4CAF50">V(countMutex);</font><br></font> <font color="#2196F3">V(w);</font><br> Read();<br> <font color="#F44336"><font color="#4CAF50">P(countMutex);</font><br> count--;<br> if(count == 0)<br> <font color="#212121">V(rw);</font><br> <font color="#4CAF50">V(countMutex);</font></font><br>}<br>
哲学家进餐问题
资源
不同的临界资源
关系
当进程拿到两个资源时,进程开始工作
进程工作结束后,释放占用的资源
进程获取资源是逐个的
进程获取不到需要的资源时,进程进入等待,不释放已经获得的资源
核心<br>
死锁隐患
解决死锁
实现1<br>最多4人同时拿筷子<br>(总有一人可以吃饭)<br>
semaphore maxEater = 4;<br>semaphore chopstick[5] = [1, 1, 1, 1, 1 ];<br>
Class Philosopher<br>
while (1) {<br> P(maxEater);<br> P(chopstick[this]);<br> P(chopstick[(this + 1) % 5]);<br> Eat();<br> V(chopstick[this]);<br> V(chopstick[(this + 1) % 5]);<br> V(maxEater);<br> Think();<br>}<br>
实现2<br>奇偶位哲学家拿筷子策略不同<br>总有人一根筷子都拿不到<br>
semaphore chopstick[5] = [1, 1, 1, 1, 1 ];<br>
Class Philosopher<br>
while (1) {<br> if(this % 2 == 0) {<br> P(chopstick[this]);<br> P(chopstick[(this + 1) % 5]);<br> }<br> else {<br> P(chopstick[(this + 1) % 5]);<br> P(chopstick[this]);<br> }<br> Eat();<br> V(chopstick[this]);<br> V(chopstick[(this + 1) % 5]);<br> Think();<br>}<br>
实现3<br>只允许一人拿筷子<br>不能全拿别人都别想拿<br>
semaphore mutex = 1;<br>semaphore chopstick[5] = [1, 1, 1, 1, 1 ];<br>
Class Philosopher<br>
while (1) {<br> P(mutex);<br> P(chopstick[this]);<br> P(chopstick[(this + 1) % 5]);<br> V(mutex);<br> Eat();<br> V(chopstick[this]);<br> V(chopstick[(this + 1) % 5]);<br> Think();<br>}<br>
概念<br>
临界资源
临界资源的访问必须是互斥的
同步<br>直接制约关系<br>
为了完成某种任务的多个进程,需要在某种条件下协调工作顺序,<br>必须进行等待或传递信息形成的制约关系<br>
将条件是否存在(事件是否发生)视为一种资源,可以由<br>先执行者提供条件资源,后执行者消耗条件资源<br>
直接制约关系来源于合作关系
互斥<br>间接制约关系<br>
一个程序访问临界资源时,其他意图访问此临界资源的进程必须等待,直到该资源被释放<br>
间接制约关系来源于竞争关系
互斥实现
基本原则:
空闲让进
临界资源空闲时,立即允许一个进程进入临界区
忙则等待
有进程进入临界区时,令其他试图访问的进程等待
有限等待<br>
保证进程不饥饿
让权等待
进不了临界区的进程必须释放处理机,避免忙等
互斥机制程序结构
负责实现互斥的部分是进入区和退出区<br>
进程访问临界资源<br>的<font color="#F44336">代码段</font>称为临界区<br>
<font color="#0D47A1">do</font> {<br> entry section; //进入区:负责检查是否可以进入临界区;进入时对临界资源上锁,阻止其他进程进入临界区<br> critical section; //临界区:<br> exit section; //退出区:负责临界资源的解锁<br> remainder section; //剩余区<br>} <font color="#0D47A1">while</font> {true}<br>
实现进程互斥<br>软件方法<br>
单标志法
双标志先检查
双标志后检查
Peterson算法
实现进程互斥<br>硬件方法<br>
中断屏蔽
进程进入临界区前执行关中断指令
简单高效
只适用于内核进程,不适用于用户进程
不适用于多处理机系统
TestAndSet<br>(TS指令/TSL指令)<br>
TSL:TestAndSetLock,别称
该指令由硬件实现,执行过程不可中断。
逻辑实现
bool TestSet (bool *lock) {<br> bool old = *lock;<br> *lock = true;<br> return old;<br>}<br>
while (TestSet(&lock));<br>//临界区代码段<br>lock = false;//解锁<br>//剩余区代码段<br>
没有逻辑漏洞,适用于多处理机环境
不满足让权等待
卡在循环,造成忙等
SWAP指令<br>(XCHG指令)<br>
该指令用硬件实现,执行过程不可中断
逻辑实现
bool old = true;<br>while (old) Swap(&lock, old);<br>//临界区代码段<br>lock = false;<br>//剩余区代码段<br>
不满足让权等待
信号量
背景
所有的解决方案均无法实现“让权等待”,都会卡循环
双标志先检查法甚至因为操作不具有原子性无法实现互斥
机制
用户通过OS提供的一对原语来对信号量进行操作
信号量是一类数据,表征系统中某类资源的存量
若进入区和退出区的操作通过原语实现,可以实现原子性
PV原语
wait(S)原语
P操作
Proboren<br>
signal(S)原语
V操作<br>
Verhogen
将条件是否存在(事件是否发生)视为一种资源,可以由先执行者<br>进行V操作以提供条件资源,后执行者进行P操作消耗条件资源<br>
分类
整型信号量
相比普通int,int信号量只有“初始化”,“P操作”,“V操作”3种操作
原语实现
void wait(int S) { while (S <= 0); S -= 1; }<br>
void signal(int S) { S += 1; }<br>
应用<br>
进入区使用wait(S),退出区使用signal(S)。<br>
不满足组让权等待<br>
记录型信号量
数据定义<br>
Class semaphore { int value; Process* L; }<br>
L为等待该资源的剩余进程队列<br>
原语实现
void wait(semaphore S) { S.value--; if (S.value < 0) block(S.L); }<br>
void signal(semaphore S) { S.value++; if (S.value <= 0) wakeup(S.L); }<br>
应用<br>
当进程调用wait(S)申请资源时,若资源不能满足,则block自身
注意block和wakeup的参数都是对应资源的等待队列S.L
必然先S.value++/S.value--<br>
适用于实现进程同步、互斥
应用
进程互斥
根据具体资源被进程访问的情况(代码),划定临界区
设置互斥信号量mutex,初值为1,表示资源的存量
根据资源存量不同,初值可以不同
在进程临界区前后分别加入P(mutex);和V(mutex);
进程同步
需要保证两个进程中两段代码的先后关系<br>
设置同步信号量S,初值为0,表示一种只有某段代码执行才能产生的资源
在需要先执行的代码段后加V(S),后执行的代码段前加P(S)
实现进程前驱关系<br>
每一对前驱关系都是个进程同步关系
前VP后,一对VP:先完成V,P就会发生<br>
考察
操作顺序
实现互斥的P操作一定要放在实现同步的P操作之后。<br>即:进程内实现同步的P操作必须放在互斥的PV对之外
同步P在互斥PV外
V操作不会直接导致任何阻塞,颠倒V操作的顺序不会造成死锁
尽量缩短临界区的长度,操作能不放进临界区就不要放进去
管程
管程的应用1<br>生产者消费者问题<br>
典型管程的定义<br>
Monitor PCMnt {<br> private condition notFull, notEmpty;<br> private int count = 0;<br> private Buffer buffer;<br><br> public void insert(Item item) {<br> if(count == buffer.size) wait(notFull);<br> count++;<br> InsertItem();<br> if(count == 1) signal(notEmpty);<br> }<br> <br> public Item Get() {<br> if(count == 0) wait(notEmpty);<br> count--;<br> if(count == buffer.size - 1) signal(notFull);<br> return GetItem();<br> }<br>}<br>
采用管程后生产者消费者的定义<br>
Class Producer<br>
while (1) {<br> PCMnt.Insert(Produce());<br>}<br>
Class Consumer<br>
while (1) {<br> Consume(PCMnt.Get());<br>}<br>
基本概念<br>
背景
解决信号量机制编程困难、易出错
定义<br>
一种高级的同步机制
在Pascal语言中被引入
组成结构
管程私有的共享数据结构说明
操作上述数据结构的一组过程(方法)
(管程私有)初始化上述数据结构的方法
管程的名字
管程的基本特征<br>
管程的成员只能被管程的方法访问
进程仅能使用管程的方法访问管程的成员
<font color="#F44336">进程只能互斥地使用管程的任意方法</font>
此为管程的精髓
管程的意义<br>
在管程中设置条件变量、等待/唤醒操作以解决同步问题
<font color="#F44336">进程互斥访问管程的特性是由编译器实现的</font><br>
这一层解决了互斥访问就无需再管程内部再实现互斥
进程无需关注自身逻辑是否满足同步互斥关系
0 条评论
下一页