2.4、进程同步

进程同步的主要任务是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。

进程同步是指系统中多个进程发生的事件存在某种时序关系(即表示多个进程之间的执行是有先后顺序的),需要相互协作,共同完成一个任务。

1、两种形式的制约关系

在多道程序环境下,当程序并发执行时,由于资源共享和进程合作,使同处于一个系统中的诸进程之间可能存在着以下两种形式的制约关系:

(1) 间接相互制约关系

同处于一个系统中的进程,通常都共享着某种系统资源,如共享CPU、共享I/O 设备等。所谓间接相互制约即源于这种资源共享,例如,有两个进程A和B,如果在A进程提出打印请求时,系统已将惟一的一台打印机分配给了进程B,则此时进程A只能阻塞;一旦进程B将打印机释放,则A进程才能由阻塞改为就绪状态。

(2) 直接相互制约关系

这种制约主要源于进程间的合作。例如,有一输入进程A通过单缓冲向进程B 提供数据。当该缓冲空时,计算进程因不能获得所需数据而阻塞,而当进程A把数据输入缓冲区后,便将进程B唤醒;反之,当缓冲区已满时,进程A因不能再向缓冲区投放数据而阻塞,当进程B将缓冲区数据取走后便可唤醒A。

2、临界资源

许多硬件资源如打印机、磁带机等,都属于临界资源(Critical Resource),诸进程间应采取互斥方式,实现对这种资源的共享。

生产者-消费者(producer-consumer)问题是一个著名的进程同步问题它具体是一个什么样的问题呢?该问题是:生产者进程会出现缓冲区满的情况下继续向缓存区写入数据,消费者进程会出现在缓冲区为空的情况下向缓冲区读取数据。所以,生产者-消费者问题的解决方案关键需要处理的地方是:要保证生产者在缓冲区满的情况下不在向缓冲区写入数据,消费者在缓冲区空的情况不向缓冲区读取数据。

它描述的是:有一群生产者进程在生产产品,并将这些产品提供给消费者进程去消费。为使生产者进程与消费者进程能并发执行,在两者之间设置了一个具有n个缓冲区的缓冲池,生产者进程将它所生产的产品放入一个缓冲区中;消费者进程可从一个缓冲区中取走产品去消费。尽管所有的生产者进程和消费者进程都是以异步方式运行的,但它们之间必须保持同步,即不允许消费者进程到一个空缓冲区去取产品,也不允许生产者进程向一个已装满产品且尚未被取走的缓冲区中投放产品。

我们可利用一个数组来表示上述的具有n 个(0,1,…,n-1)缓冲区的缓冲池。用输入指针in 来指示下一个可投放产品的缓冲区,每当生产者进程生产并投放一个产品后,输入指针加1;用一个输出指针out来指示下一个可从中获取产品的缓冲区,每当消费者进程取走一个产品后,输出指针加1。由于这里的缓冲池是组织成循环缓冲的,故应把输入指针加1 表示成 in:= (in+1)mod n;输出指针加1 表示成out:= (out+1) mod n。当 (in+1) mod n=out时表示缓冲池满;而in=out则表示缓冲池空。此外,还引入了一个整型变量counter,其初始值为0。每当生产者进程向缓冲池中投放一个产品后,使counter 加1;反之,每当消费者进程从中取走一个产品时,使counter 减1。

指针in和out初始化为1。在生产者和消费者进程的描述中,noop是一条空操作指令,whilecondition do no-op语句表示重复的测试条件(condication),重复测试应进行到该条件变为false(假),即到该条件不成立时为止。在生产者进程中使用一局部变量nextp,用于暂时存放每次刚生产出来的产品;而在消费者进程中,则使用一个局部变量nextc,用于存放每次要消费的产品。

producer: repeat

produce an item in nextp;

…

while counter = n do no-op;

buffer[in]:= nextp;

in := in+1 mod n;

counter := counter+1;

until false;

consumer: repeat

while counter = 0 do no-op;

nextc := buffer[out];

out := (out+1) mod n;

counter := counter-1;

consumer the item in nextc;

until false;

虽然上面的生产者程序和消费者程序在分别看时都是正确的,而且两者在顺序执行时其结果也会是正确的,但若并发执行时就会出现差错,问题就在于这两个进程共享变量counter。生产者对它做加1操作,消费者对它做减1操作,这两个操作在用机器语言实现时。

常可用下面的形式描述(这里需要特别注意的是变量加1和变量减1操作用机器语言表示时其形式,引入了一个中间寄存器变量,我们将变量的值赋给中间寄存器变量,然后对中间寄存器变量进行加1或减1操作,最后再把中间寄存器变量的值赋给该变量。我们不是直接对该变量进行加1或减1操作。也就是说一个变量加1或减1操作在操作系统中它是被分成了三步进行计算:第一步将该变量的值赋给中间寄存器变量;第二步对该中间寄存器变量进行加1或减1操作;第三步将中间寄存器变量赋给该变量。这一点需要特别注意,这对于进程同步时进行变量值的计算值非常重要,要记得将其拆分为三个步骤):

register1:=counter;             register2:=counter;

register1:=register1+1;       register2:=register2-1;

counter:=register1;             counter:=register2;

假设counter 的当前值是5。如果生产者进程先执行左列的三条机器语言语句,然后消费者进程再执行右列的三条语句,则最后共享变量counter 的值仍为5;反之,如果让消费者进程先执行右列的三条语句,然后再让生产者进程执行左列的三条语句,则counter 值也还是5,但是,如果按下述顺序执行:

register1:=counter;      (register1=5)

register1:=register1+1; (register1=6)

register2:=counter;      (register2=5)

register2:=register2-1; (register2=4)

counter:=register1;      (counter=6)

counter:=register2;      (counter=4)

正确的counter 值应当是5,但现在是4。读者可以自己试试,倘若再将两段程序中各语句交叉执行的顺序改变,将可看到又可能得到counter=6的答案,这表明程序的执行已经失去了再现性。为了预防产生这种错误,解决此问题的关键是应把变量counter作为临界资源处理,亦即,令生产者进程和消费者进程互斥地访问变量counter。

3、临界区

人们把在每个进程中访问临界资源的那段代码称为临界区(critical section)(这里需特别注意:临界区是一段代码,它是进程中访问临界资源的那段代码。)。显然,若能保证诸进程互斥地进入自己的临界区,便可实现诸进程对临界资源的互斥访问。为此,每个进程在进入临界区之前,应先对欲访问的临界资源进行检查,看它是否正被访问。如果此刻该临界资源未被访问,进程便可进入临界区对该资源进行访问,并设置它正被访问的标志;如果此刻该临界资源正被某进程访问,则本进程不能进入临界区。因此,必须在临界区前面增加一段用于进行上述检查的代码,把这段代码称为进入区(entry section)。相应地,在临界区后面也要加上一段称为退出区(exit section)的代码,用于将临界区正被访问的标志恢复为未被访问的标志。进程中除上述进入区、临界区及退出区之外的其它部分的代码,在这里都称为剩余区。这样,可把一个访问临界资源的循环进程描述如下:

repeat

entry section              (进入区)

critical section;       (临界区)

exit section                (退出区)

remainder section;  (剩余区)

until false;

4、同步机制应遵循的规则

为实现进程互斥地进入自已的临界区,可用软件方法,更多的是在系统中设置专门的同步机构来协调各进程间的运行。所有同步机制都应遵循下述四条准则:

(1) 空闲让进。当无进程处于临界区时,表明临界资源处于空闲状态,应允许一个请求进入临界区的进程立即进入自己的临界区,以有效地利用临界资源。

(2) 忙则等待。当已有进程进入临界区时,表明临界资源正在被访问,因而其它试图进入临界区的进程必须等待,以保证对临界资源的互斥访问。

(3) 有限等待。对要求访问临界资源的进程,应保证在有限时间内能进入自己的临界区,以免陷入“死等”状态。

(4) 让权等待。当进程不能进入自己的临界区时,应立即释放处理机,以免进程陷入“忙等”状态。

5、信号量机制(是一种进程同步工具)

1965 年,荷兰学者Dijkstra提出的信号量(Semaphores)机制是一种卓有成效的进程同步工具。信号的类型有以下几种:

(1)整型信号量(表示该信号量是一个整型变量)

最初由Dijkstra 把整型信号量定义为一个用于表示资源数目的整型量S(即也就是说它是一个整型变量,但又与一般的整型变量不同),它与一般整型量不同,除初始化外,仅能通过两个标准的原子操作(Atomic Operation)wait(S)和signal(S)来访问。很长时间以来,这两个操作一直被分别称为P、V操作。wait(S)和signal(S)操作可描述为:

wait(S):  while S <= 0 do no-op;

S := S - 1;

signal(S): S := S + 1;

wait(S)和signal(S)是两个原子操作,因此,它们在执行时是不可中断的。亦即,当一个进程在修改某信号量时,没有其他进程可同时对该信号量进行修改。此外,在wait操作中,对S值的测试和做S:=S-1操作时都不可中断。

(2)记录型信号量(表示该信号量是一个记录型变量(也就是结构体型变量))

在整型信号量机制中的wait操作,只要是信号量S≤0,就会不断地测试。因此,该机制并未遵循“让权等待”的准则,而是使进程处于“忙等”的状态。记录型信号量机制则是一种不存在“忙等”现象的进程同步机制。但在采取了“让权等待”的策略后,又会出现多个进程等待访问同一临界资源的情况。为此,在信号量机制中,除了需要一个用于代表资源数目的整型变量value外,还应增加一个进程链表指针L,用于链接上述的所有等待进程。记录型信号量是由于它采用了记录型的数据结构而得名的。它所包含的上述两个数据项可描述为:

type semaphore = record

value: integer;

L: list of process;

end

相应地,wait(S)和signal(S)操作可描述为:

procedure wait(S)

var S:semaphore;

begin

S.value := S.value - 1;

if  S.value <0 then block(S.L);

end

procedure signal(S)

var S: semaphore;

begin

S.value := S.value + 1;

if  S.value <=0 then wakeup(S.L);

end

在记录型信号量机制中,S.value的初值表示系统中某类资源的数目,因而又称为资源信号量。对它的每次wait操作,意味着进程请求一个单位的该类资源,使系统中可供分配的该类资源数减少一个,因此描述为S.value:=S.value-1;当S.value<0时,表示该类资源已分配完毕,因此进程应调用block原语,进行自我阻塞,放弃处理机,并插入到信号量链表S.L 中。可见,该机制遵循了“让权等待”准则。此时S.value的绝对值表示在该信号量链表中已阻塞进程的数目。对信号量的每次signal操作,表示执行进程释放一个单位资源,使系统中可供分配的该类资源数增加一个,故S.value:=S.value+1操作表示资源数目加1。若加1后仍是S.value≤0,则表示在该信号量链表中,仍有等待该资源的进程被阻塞,故还应调用wakeup原语,将S.L链表中的第一个等待进程唤醒。如果S.value的初值为1,表示只允许一个进程访问临界资源,此时的信号量转化为互斥信号量,用于进程互斥。

(3)AND型信息量

上述的进程互斥问题,是针对各进程之间只共享一个临界资源而言的。在有些应用场合,是一个进程需要先获得两个或更多的共享资源后方能执行其任务。假定现有两个进程A和B,他们都要求访问共享数据D和E。当然,共享数据都应作为临界资源。为此,可为这两个数据分别设置用于互斥的信号量Dmutex和Emutex,并令它们的初值都是1。相应地,在两个进程中都要包含两个对Dmutex 和Emutex 的操作,即

process A:                     processB:

wait(Dmutex);           wait(Emutex);

wait(Emutex);            wait(Dmutex);

若进程A和B按下述次序交替执行wait操作:

process A: wait(Dmutex);于是Dmutex = 0

process B: wait(Emutex);于是Emutex = 0

process A: wait(Emutex);于是Emutex = -1     A阻塞

process B: wait(Dmutex);于是Dmutex = -1    B阻塞

最后,进程A和B处于僵持状态。在无外力作用下,两者都将无法从僵持状态中解脱出来。我们称此时的进程A和B已进入死锁状态。显然,当进程同时要求的共享资源愈多时,发生进程死锁的可能性也就愈大。

AND 同步机制的基本思想是:将进程在整个运行过程中需要的所有资源,一次性全部地分配给进程,待进程使用完后再一起释放。只要尚有一个资源未能分配给进程,其它所有可能为之分配的资源也不分配给它。亦即,对若干个临界资源的分配,采取原子操作方式:要么把它所请求的资源全部分配到进程,要么一个也不分配。由死锁理论可知,这样就可避免上述死锁情况的发生。为此,在wait 操作中,增加了一个“AND”条件,故称为AND 同步,或称为同时wait 操作,即Swait(Simultaneous wait)定义如下:

Swait(S1,S2,…,Sn)

if Si >= 1 and … and Sn >= 1 then

for i := 1 to n do

Si := Si - 1;

endfor

else

place the process in the waitingqueue associated with the first Si found                 withSi < 1,and set theprogram count of this process tothe beginning of Swait operation

endif

Ssignal(S1,S2,…,Sn)

for i := 1 to n do

Si := Si + 1;

Remove all the process waitingin the queue associated with Si into the ready queue.

endfor;

(4)信号量集

在记录型信号量机制中,wait(S)或signal(S)操作仅能对信号量施以加1 或减1 操作,意味着每次只能获得或释放一个单位的临界资源。而当一次需要N个某类临界资源时,便要进行N次wait(S)操作,显然这是低效的。此外,在有些情况下,当资源数量低于某一下限值时,便不予以分配。因而,在每次分配之前,都必须测试该资源的数量,看其是否大于其下限值。基于上述两点,可以对AND信号量机制加以扩充,形成一般化的“信号量集”机制。Swait操作可描述如下,其中S为信号量,d为需求值,而t为下限值。

Swait(S1,t1,d1,…,Sn,tn,dn)

if Si >= t1and … and Sn >= tn then

for i := 1 to ndo

Si := Si - di;

endfor

else

Place theexecuting process in the waiting queue of the first Si with Si<ti and setits program counter to the beginning of the Swait Operation.

endif

Ssignal(S1,d1,…,Sn,dn)

for i := 1 to n do

Si := Si + di;

Remove all the process waiting in the queueassociated with Si into the ready queue

endfor;

下面我们讨论一般“信号量集”的几种特殊情况:

1) Swait(S,d,d)。此时在信号量集中只有一个信号量S,但允许它每次申请d 个资源,当现有资源数少于d时,不予分配。

2) Swait(S,1,1)。此时的信号量集已蜕化为一般的记录型信号量(S>1时)或互斥信号量(S=1时)。

3) Swait(S,1,0)。这是一种很特殊且很有用的信号量操作。当S≥1 时,允许多个进程进入某特定区;当S 变为0 后,将阻止任何进程进入特定区。换言之,它相当于一个可控开关。

注意点:信号量实现进程同步和实现进程互斥的区别???

1、信号量实现进程的同步

利用信号量能方便的实现进程的同步。设S为两个并发进程P1和P2的公共信号量,初值为0。P1执行的程序中有一条语句S1,P2执行的程序中有一条语句S2。而且,只有当P1执行语句S1后,P2才能开始执行S2语句。对这种简单的同步问题,很容易使用信号量解决。其描述如下:

main()

{

semaphore S = 0; // 信号量S置初值0

cobegin

P1();

P2();

coend

}

P1()

{

...

S1;

V(S);     // 这个进程执行V操作

...

}

P2()

{

...

P(S);      // 这个进程执行P操作

S2;

...

}

同步的实现由一个进程P1对一个信号量进行P操作后,只能由另一个进程P2对同一个信号量进行V操作,使P1能继续前进,在这种情况下,P1要同步等待P2。若进程P2也要同步等待P1,则要设置另一个信号量。(实现进程的同步的信号量的P操作和V操作是在不同的进程中,这一点需要特别注意!!!)

2、信号量实现互斥

利用信息量可以方便的实现进程互斥,设S为两个进程P1、P2实现互斥的信号量,由于每次只允许一个进程进入临界区,所以S的初值应为1(即可用资源个数为1)。只须把临界区置于P(S)和V(S)之间,即可实现两个进程的互斥。互斥访问临界区的描述如下:

main()

{

semaphoreS = 1; // 信号量S置初值0

cobegin

P1();

P2();

coend

}

P1()

{

...

P(S)

进程1的临界区

V(S);

...

}

P2()

{

...

P(S);

进程2的临界区

V(S);

...

}

两个或多个进程使用的初值为1的信号量可保证同时只有一个进程能够进入临界区。如果每个进程在进入临界区前都执行一个P操作,并在退出时执行一个V操作,则能实现互斥。

互斥的实现是不同进程对同一个信号量进行P、V操作,一个进程在成功的对信号量执行了P操作后进入临界区,并在退出临界区后,由该进程本身对该信号量执行V操作,表示当前没有进程进入临界区,可让其他进程进入。(实现进程的互斥的信号量的P操作和V操作是在同一个的进程中,这一点需要特别注意!!!)

第二章 进程管理(3)——进程同步相关推荐

  1. 操作系统:第二章 进程管理3 - 进程同步与互斥

    本文已收录至 Github(MD-Notes),若博客中有图片打不开,可以来我的 Github 仓库:https://github.com/HanquanHq/MD-Notes,涵盖了互联网大厂面试必 ...

  2. 操作系统第二章 进程管理

    写在前面:本文参考王道论坛的 操作系统考研复习指导单科书 文章目录 第二章 进程管理 进程同步 读者写者问题 哲学家就餐问题 练习题 哲学家就餐:加碗(2019真题) 既是生产者又是消费者 和尚取水( ...

  3. 【王道】操作系统OS第二章进程管理(二[1])

    本笔记结合<2023王道操作系统考研复习指导>食用 操作系统OS第二章进程管理 本笔记结合<2023王道操作系统考研复习指导>食用 1.进程 1.1.进程的组成 1.1.1.P ...

  4. 操作系统 课堂笔记 第二章 进程管理

    第二章 进程管理 2.1 本章导学 基本内容: (1)进程的基本概念. (2)进程控制. (3)进程间互斥与同步. (4)进程通信. (5)进程调度. (6)进程死锁. (7)线程. 学习重点: (1 ...

  5. 笔记篇:操作系统第二章 进程管理

    笔记篇:操作系统第二章 进程管理 目录 笔记篇:操作系统第二章 进程管理 2.1 进程的基本概念 2.1.1 程序的顺序执行及其特征 2.1.2 前驱图 2.1.3 程序的并发执行及其特征 2.1.4 ...

  6. 操作系统:第二章 进程管理2 - 处理机调度

    本文已收录至 Github(MD-Notes),若博客中有图片打不开,可以来我的 Github 仓库:https://github.com/HanquanHq/MD-Notes,涵盖了互联网大厂面试必 ...

  7. 操作系统:第二章 进程管理1 - 进程、线程

    本文已收录至 Github(MD-Notes),若博客中图片打不开,可以来我的 Github 仓库,包含了完整图文:https://github.com/HanquanHq/MD-Notes,涵盖了互 ...

  8. (王道408考研操作系统)第二章进程管理-第三节10:经典同步问题之哲学家进餐问题

    本文接: (王道408考研操作系统)第二章进程管理-第三节6:经典同步问题之生产者与消费者问题 ((王道408考研操作系统)第二章进程管理-第三节7:经典同步问题之多生产者与多消费者问题 (王道408 ...

  9. (王道408考研操作系统)第二章进程管理-第三节8:经典同步问题之吸烟者问题

    本文接: (王道408考研操作系统)第二章进程管理-第三节6:经典同步问题之生产者与消费者问题 ((王道408考研操作系统)第二章进程管理-第三节7:经典同步问题之多生产者与多消费者问题 文章目录 一 ...

  10. (王道408考研操作系统)第二章进程管理-第三节7:经典同步问题之多生产者与多消费者问题

    注意:生产者与消费者问题Linux系统编程专栏有案例讲解 Linux系统编程39:多线程之基于阻塞队列生产者与消费者模型 Linux系统编程40:多线程之基于环形队列的生产者与消费者模型 本文接:(王 ...

最新文章

  1. 40个出色的Wordpress cms插件
  2. R语言使用yardstick包的pr_curve函数评估二分类(binary)模型的性能、并使用autoplot函数可视化模型的PR曲线(precision recall)
  3. frida hook so导出函数
  4. C#中怎样获取System.Drawing.Color的所有颜色对象并存到数组中
  5. 高通骁龙cpu排行_最新手机性能排行榜出炉:高通骁龙865霸榜,前十不见华为!...
  6. 当启动文档转换负载平衡器服务时出现如下问题The system cannot find the file specified的解决方案...
  7. Object-C语法
  8. 【托马斯微积分】(12版)阅读笔记2:极限
  9. 关于系统集成的设计方案(一)
  10. 技嘉主板bios怎么进入,如何进入技嘉主板的bios
  11. 2021-01-26
  12. 牛客网Verilog快速入门题目收获——异步复位的串联T触发器(VL2)
  13. 雷电模拟器打开应用权限_雷电模拟器超级用户权限怎么设置,是什么,怎么关闭【获取超级用户权限】通知提示,如何自动允许...
  14. Unitimes三周年重磅第二弹 Gitlab中国线上首秀
  15. 海康大华安防网络摄像头Onvif、RTSP网络无插件直播流媒体服务解决方案EasyNVR表单重复提交的优化方案
  16. 前缀码的判断(个人摸索的小技巧)
  17. 联想台式计算机配置单,联想台式电脑报价 联想电脑配置参数
  18. html css x y相对定位坐标,HTML与CSS之相对定位、绝对定位
  19. 915Resolution补丁——支持“GM965”,G33, GM45 (GMA 4500MHD), GMA3150
  20. javascript 标记_如何使用JavaScript更改Google Maps标记的颜色

热门文章

  1. 相机技术--监控摄像机焦距与视角(视场大小)的具体选择
  2. 低保真原型vs高保真原型,哪一种更适合你的设计?
  3. 【深入浅出Spring原理及实战】「开发实战系列」带你看看那些可能你还不知道的Spring特殊技巧和想不到的招数
  4. 菜鸟在学编程__LSJ
  5. Java模拟Post 提交表单数据
  6. 2021计算机一级新增知识点,2021年全国计算机等级考试改革有哪些内容
  7. c语言中英文字母的符号,C语言中的符号(国外英文资料).doc
  8. php如何给导航加链接,修改phpcms导航链接的方法
  9. STM32之SPI和W25Q128
  10. 微信小程序上传文件报错: errMsg: “uploadFile:fail createUploadTask:fail invalid url“