同步机制(上)
Posted chaunceyctx
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了同步机制(上)相关的知识,希望对你有一定的参考价值。
进程特性:
并发:
1.进程的执行是间断性的(进程由于调度问题导致可能中间被打断)
2.进程的相对执行速度不可预测
共享:进程/线程之间的制约
不确定性:进程执行的结果与其执行的相对速度有关因此是不确定的
由于并发,进程可能会出现与时间有关的错误:
进程get,copy,put并发执行,f s t g为缓冲区,其中s t只能存放一个数据
假如get进程执行了两次(由于并发是可能发生的),则s中数据就被覆盖掉
假如刚开始就执行put进程,那么g缓冲区中就存了一个空数据
这些都表明了进程执行结果的正确性与时间有关
g1:第一次执行get进程,c1同理,分支表示两者没有先后顺序,但都要执行
只有当这三个进程满足这样的前驱图时,才能保证结果的正确性。
进程互斥:
竞争条件:
spooling技术应用于打印机时会出现这样的场景:
spooler directory保存着要打印文件的文件名,out指明要打印文件的位置,in执行文件添加进目录的位置
两个进程A和进程B,首先进程A先运行,A会把所要打印的文件名放入7位置,但是就在A刚想把in递增时,由于进程调度被换下CPU,此时进程B上CPU,也往目录中放要打印文件的文件名,但是此时in所指的7位置处已经存了进程A所要打印文件的文件名,就会把A进程保存的信息覆盖掉,这就出现了问题。
竞争条件:两个或多个进程读写某些共享数据,而最后的结果取决于进程运行的精确时序
进程互斥:由于各进程要求使用共享资源(变量、文件等),而这些资源需要排他性使用,导致了各进程之间竞争使用这些资源,这样的关系叫进程互斥
临界资源:系统中某些资源一次只允许一个进程使用,称这样的资源为临界资源或互斥资源或共享变量
临界区:进程中对临界资源操作的程序片段
临界区原则:
1.没有进程在临界区时,想进入临界区的进程可进入
2.不允许两个进程同时处于其临界区中
3.临界区外运行的进程不得阻塞其他进程进入临界区
4.不得使进程无限期等待进入临界区、
ps.图中A进程已进入临界区,A进程在临界区期间,进程B也想进入临界区,此时B会被阻塞直到A离开临界区。
实现进程互斥的方案(软件实现)
实现1:
free: 临界区空闲标志
true: 有进程在临界区
false:无进程在临界区
初值:free为false
但是此方案会出现问题首先假设P进程先执行,free为false,此时while循环跳过,还没重新给free赋值,此时由于进程调度换成进程Q上CPU,free还是false,那么Q进程跳过while循环,进到临界区中,之后再度由于进程调度,P进程上CPU,此时是从free = true开始执行,也进入了临界区,这就不符合临界区使用规则。
解决方法:
问题在于,进临界区前忘记上锁,所以将while(free)和free = true两个语句绑定成原语即可
实现2:
turn: 谁进临界区的标志 true: P进程进临界区 false: Q进程进临界区
turn初值任意
问题:假设turn初始为false,那么P无法进入临界区,假如Q也不想进入临界区的话,那么临界区中始终没有进程,这也违反了规则。
实现3:
pturn, qturn: 初值为false
P进入临界区的条件: pturn∧ not qturn
Q进入临界区的条件: not pturn∧ qturn
问题:
P进程先执行,执行到pturn = true完成,由于进程调度换成Q进程上CPU,最终Q进程因为pturn为true(忙等待:进程死循环换也需要CPU),也进不了临界区,之后切到P也由于qturn为true,也进不了临界区,最终临界区中没有进程。
解决方法:
P进程中pturn = true和while(qturn)合并成原语,Q进程中同理
实现4(DERKKER算法):
在实现3基础上加了turn变量来改进
执行过程:初始假设turn值为1,qturn,pturn都为false,首先假设执行P进程,pturn变为true,qturn为false,跳过while循环进入临界区,之后turn变成2,Q进程执行时,进入循环,但是turn为2,跳过if,进入临界区。
如何解决实现3中的问题?
pturn = true执行完后切换到Q进程,qturn变为true,进入while循环,turn为1,通过if发现turn为1,此时应该p进程进入临界区,而不是q进程,所以把qturn置为false,然以后进入while(turn == 1)一直循环等到时间片用完切换到进程P。由于P进程中qturn为false,跳过while,进入临界区,最终可正确执行。
实现4(PETERSON方法)
进程只要调用函数enter_region和函数leave_region即可
enter_region函数,参数是进程号,假设此时有两个进程要进入临界区,process为0,other为1,把兴趣数组中第0个设成TRUE,表明0进程想进入临界区,turn = 0.如果此时由于进程调度,换成1进程执行,那么turn又变成1,但是此时turn == process&&兴趣数组第一项为TRUE,就一直进行循环直到使用完时间片。切换到0进程,由于turn = 1 不等于 0 所以,最终0进程先进入临界区,实现了先来先进顺序。
进程互斥的硬件实现:
开关中断”指令:
执行“关中断”指令
临界区操作
执行“开中断”指令
优缺点:
简单,高效
代价高,限制CPU并发能力(临界区大小)
不适用于多处理器(禁止中断只能禁止一个处理器)
适用于操作系统本身,不适于用户进程
测试并加锁指令:
enter_region:
复制锁到寄存器并将锁置1
判断寄存器内容是否是零?
若不是零,跳转到enter_region
返回调用者,进入了临界区
leave_region:
在锁中置0
返回调用者
交换指令:
enter_region:
给寄存器中置1 交换寄存器与锁变量的内容 判断寄存器内容是否是零? 若不是零,跳转到enter_region 返回调用者,进入了临界区
leave_region:
在锁中置0
返回调用者
进程同步:
概念:指系统中多个进程中发生的事件存在某种时序关系,需要相互合作,共同完成一项任务。具体地说,一个进程运行到某一点时,要求另一伙伴进程为它提供消息,在未获得消息之前,该进程进入阻塞态,获得消息后被唤醒进入就绪态
生产者和消费者问题:
问题:
1.一个或多个生产者生产某种类型的数据放置在缓冲区中
2.有消费者从缓冲区中取数据,每次取一项
3.只能有一个生产者或消费者对缓冲区进行操作
要解决的问题:
1.当缓冲区已满时,生产者不会继续向其中添加数据
2.当缓冲区为空时,消费者不会从中移走数据
解决方案:
count为缓冲区中数据数量
问题:
当消费者中if(count == 0)时,若因为进程调度被切换,生产者进程上CPU,到最后执行wake_up,但是此时消费者进程并没有被睡眠,这就会出现问题。
信号量:
概念:一个特殊变量,用于进程间传递信息的一个整数值
定义如下:
struc semaphore
{
int count;
queueType queue;
}
对信号量可以实施的操作:初始化、P和V(P、V分别是荷兰语的test(proberen)和increment(verhogen))
同时P、V操作为原语操作(primitive or atomic action)
P(s) { s.count --; if (s.count < 0) { 该进程状态置为阻塞状态; 将该进程插入相应的等待队列s.queue末尾; 重新调度; } }
V(s) { s.count ++; if (s.count < = 0)//等于0表明之前s.count为-1,说明有进程处于阻塞状态也需要被唤醒,所以也要算上等于0 { 唤醒相应等待队列s.queue中等待的一个进程; 改变其状态为就绪态,并将其插入就绪队列; } }
用PV操作解决进程互斥基本过程:
分析并发进程的关键活动,划定临界区
设置信号量 mutex,初值为1
在临界区前实施 P(mutex)
在临界区之后实施 V(mutex)
用PV操作解决生产者消费者问题:
执行过程:生产者进程先检查缓冲区是否已满(P(&empty)看empty是否为0,为0表明缓冲区中没有空位置),然后是信号量减一,目的是为了生产者与消费者不能同时执行还有不能多个生产者往缓冲区中生产数据,之后生产结束,mutex加一和full加一,目的是为了让消费者能执行,消费者执行,先检查缓冲区中是否有数据(P(&full)看full是否为0,为0表明缓冲区中没有数据),然后是信号量减一,目的是为了生产者与消费者不能同时执行还有不能多个消费者者从缓冲区中读数据,之后消费结束,mutex加一和empty加一(消耗完了,空位多出来了),目的是为了让生产者能执行。
进阶:
生产者中两个PP位置调换顺序如何?有影响,假设都是生产者执行,最终导致empty为0,那么最后生产者就阻塞在empty,但是没有释放mutex,所以消费者由于mutex也被阻塞。
生产者中两个VV位置调换顺序如何?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使消费者上CPU时间变晚了。
消费者中两个PP位置调换顺序如何?假如一上来消费者执行,mutex减1,full由于为0,减一为-1,消费者阻塞,然后生产者由于mutex为0也阻塞,最终两者都阻塞,导致了死锁问题
消费者中两个VV位置调换顺序如何?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使消费者上CPU时间变晚了。
生产后立即插入缓冲区和取出后立即消费有什么影响?结果没有影响,但是拉长了临界区(p(mutex)v(mutex)之间),使下一个进程上CPU时间变晚了。
读者写者问题:
多个进程共享一个数据区,这些进程分为两组:
读者进程:只读数据区中的数据
写者进程:只往数据区写数据
满足条件:
允许多个读者同时执行读操作
不允许多个写者同时操作
不允许读者、写者同时操作
读者执行情况:
1.无其他读者、写者,该读者可以读
2.若已有写者等待,但有其他读者正在读,则该读者也可以读
3.若有写者正在写,该读者必须等待
写者执行情况:
1.无其他读者、写者,该写者可以写
2.若有读者正在读,该写者等待
3.若有其他写者正在写,该写者等待
解决方案1:
void reader(void) { while (TRUE) { …… P (w); 读操作 V(w); …… } } void writer(void) { while (TRUE) { …… P(w); 写操作 V(w); …… } }
但是该方案满足不了许多读者同时读的情况。
方案2:
要想满足多个读者可以同时读的情况,新增加一个rc变量,规定只有第一个读者将信号量减1,其他读进程不做操作,这样就可让多个读进程同时执行,又由于w被第一个读进程置为0,写进程无法写,之后rc由于读进程减少而减少,最终为0,此时将w加一。总的来说就是:一旦某个读进程开始读防止写进程写就第一个读进程将w减1,那么接下来其他读进程就可以执行,直到最后一个读完数据的进程结束后,缓冲区没有新内容可读了,w变成1让写进程写。由于多个进程对rc进行了操作,那么rc也就成了临界资源,所以对rc加以保护,在设计rc资源头尾加上另一个信号量mutex的PV操作。
作者水平有限,文章肯定有错还请各位指点!!!感谢!!!
以上是关于同步机制(上)的主要内容,如果未能解决你的问题,请参考以下文章
[Go] 通过 17 个简短代码片段,切底弄懂 channel 基础
[工作积累] UE4 并行渲染的同步 - Sync between FParallelCommandListSet & FRHICommandListImmediate calls(代码片段