无论是计算机考研、计算机软件水平考试、计算机操作系统期末考试还是其他计算机岗位考试,P、V原语操作都是一个常考点。下面笔者总结了关于P、V操作的一些知识。

信号量是最早出现的用来解决进程同步与互斥问题的机制(也可实现进程通信),包括一个称为信号量的变量及对它进行的两个原语操作。信号量为一个整数,我们设这个信号量为:sem。很显然,我们规定在sem大于等于零的时候代表可供并发进程使用的资源实体数,sem小于零的时候,表示正在等待使用临界区的进程的个数。根据这个原则,在给信号量附初值的时候,我们显然就要设初值大于零。

p操作和v操作是不可中断的程序段,称为原语。P,V原语中P是荷兰语的Passeren,相当于英文的pass, V是荷兰语的Verhoog,相当于英文中的incremnet。

且在P,V原语执行期间不允许有中断的发生。

对于具体的实现,方法非常多,可以用硬件实现,也可以用软件实现。这种信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。

首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:

 

P(S):①将信号量S的值减1,即S=S-1;

②如果S>=0,则该进程继续执行;否则该进程置为等待状态,排入等待队列。

V(S):①将信号量S的值加1,即S=S+1;

②如果S>0,则该进程继续执行;否则释放队列中第一个等待信号量的进程。

PV操作的意义:我们用信号量及PV操作来实现进程的同步和互斥。PV操作属于进程的低级通信。

什么是信号量?信号量(semaphore)的数据结构为一个值和一个指针,指针指向等待该信号量的下一个进程。信号量的值与相应资源的使用情况有关。当它的值大于0时,表示当前可用资源的数量;当它的值小于0时,其绝对值表示等待使用该资源的进程个数。注意,信号量的值仅能由PV操作来改变。

一般来说,信号量S>=0时,S表示可用资源的数量。执行一次P操作意味着请求分配一个单位资源,因此S的值减1;

当S<0时,表示已经没有可用资源,请求者必须等待别的进程释放该类资源,它才能运行下去。而执行一个V操作意味着释放一个单位资源,因此S的值加1;

若S<=0,表示有某些进程正在等待该资源,因此要唤醒一个等待状态的进程,使之运行下去

利用信号量和PV操作实现进程互斥的一般模型是:

进程P1                   进程P2                ……               进程Pn

……                     ……                  ……

P(S);                 P(S);                                 P(S);

临界区;                 临界区;                                 临界区;

V(S);                 V(S);                                 V(S);

……                     ……                  ……               ……

其中信号量S用于互斥,初值为1

使用PV操作实现进程互斥时应该注意的是:

(1)每个程序中用户实现互斥的P、V操作必须成对出现,先做P操作,进临界区,后做V操作,出临界区。若有多个分支,要认真检查其成对性。

(2)P、V操作应分别紧靠临界区的头尾部,临界区的代码应尽可能短,不能有死循环。

(3)互斥信号量的初值一般为1。

利用信号量和PV操作实现进程同步

PV操作是典型的同步机制之一。用一个信号量与一个消息联系起来,当信号量的值为0时,表示期望的消息尚未产生;当信号量的值非0时,表示期望的消息已经存在。用PV操作实现进程同步时,调用P操作测试消息是否到达,调用V操作发送消息。

利用信号量和PV操作实现进程互斥的一般模型是:

进程A                            进程B

....                            ....

L: P(信号量)                     L2:V(信号量)

....                            ....

使用PV操作实现进程同步时应该注意的是:

(1)分析进程间的制约关系,确定信号量种类。在保持进程间有正确的同步关系情况下,哪个进程先执行,哪些进程后执行,彼此间通过什么资源(信号量)进行协调,从而明确要设置哪些信号量。

(2)信号量的初值与相应资源的数量有关,也与P、V操作在程序代码中出现的位置有关。

(3)同一信号量的P、V操作要成对出现,但它们分别在不同的进程代码中。

【例1】生产者-消费者问题

在多道程序环境下,进程同步是一个十分重要又令人感兴趣的问题,而生产者-消费者问题是其中一个有代表性的进程同步问题。下面我们给出了各种情况下的生产者-消费者问题,深入地分析和透彻地理解这个例子,对于全面解决操作系统内的同步、互斥问题将有很大帮助。

(1)一个生产者,一个消费者,公用一个缓冲区。

定义两个同步信号量:

empty——表示缓冲区是否为空,初值为1。

full——表示缓冲区中是否为满,初值为0。

生产者进程

while(TRUE){

生产一个产品;

P(empty);

产品送往Buffer;

V(full);

}

消费者进程

while(TRUE){

P(full);

从Buffer取出一个产品;

V(empty);

消费该产品;

}

(2)一个生产者,一个消费者,公用n个环形缓冲区。

定义两个同步信号量:

empty——表示缓冲区是否为空,初值为n。

full——表示缓冲区中是否为满,初值为0。

设缓冲区的编号为1~n&61485;1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指针,指向下一个可用的缓冲区。

生产者进程

while(TRUE){

生产一个产品;

P(empty);

产品送往buffer(in);

in=(in+1)mod n;

V(full);

}

消费者进程

while(TRUE){

P(full);

从buffer(out)中取出产品;

out=(out+1)mod n;

V(empty);

消费该产品;

}

(3)一组生产者,一组消费者,公用n个环形缓冲区

在这个问题中,不仅生产者与消费者之间要同步,而且各个生产者之间、各个消费者之间还必须互斥地访问缓冲区。

定义四个信号量:

empty——表示缓冲区是否为空,初值为n。

full——表示缓冲区中是否为满,初值为0。

mutex1——生产者之间的互斥信号量,初值为1。

mutex2——消费者之间的互斥信号量,初值为1。

设缓冲区的编号为1~n&61485;1,定义两个指针in和out,分别是生产者进程和消费者进程使用的指针,指向下一个可用的缓冲区。

生产者进程

while(TRUE){

生产一个产品;

P(empty);

P(mutex1);

产品送往buffer(in);

in=(in+1)mod n;

V(mutex1);

V(full);

}

消费者进程

while(TRUE){

P(full);

P(mutex2);

从buffer(out)中取出产品;

out=(out+1)mod n;

V(mutex2);

V(empty);

消费该产品;

}

需要注意的是无论在生产者进程中还是在消费者进程中,两个P操作的次序不能颠倒。应先执行同步信号量的P操作,然后再执行互斥信号量的P操作,否则可能造成进程死锁。

【例2】桌上有一空盘,允许存放一只水果。爸爸可向盘中放苹果,也可向盘中放桔子,儿子专等吃盘中的桔子,女儿专等吃盘中的苹果。规定当盘空时一次只能放一只水果供吃者取用,请用P、V原语实现爸爸、儿子、女儿三个并发进程的同步。

分析在本题中,爸爸、儿子、女儿共用一个盘子,盘中一次只能放一个水果。当盘子为空时,爸爸可将一个水果放入果盘中。若放入果盘中的是桔子,则允许儿子吃,女儿必须等待;若放入果盘中的是苹果,则允许女儿吃,儿子必须等待。本题实际上是生产者-消费者问题的一种变形。这里,生产者放入缓冲区的产品有两类,消费者也有两类,每类消费者只消费其中固定的一类产品。

解:在本题中,应设置三个信号量S、So、Sa,信号量S表示盘子是否为空,其初值为l;信号量So表示盘中是否有桔子,其初值为0;信号量Sa表示盘中是否有苹果,其初值为0。同步描述如下:

int S=1;

int Sa=0;

int So=0;

main()

{

cobegin

father();

son();

daughter();

coend

}

father()

{

while(1)

{

P(S);

将水果放入盘中;

if(放入的是桔子)V(So);

else           V(Sa);

}

}

son()

{

while(1)

{

P(So);

从盘中取出桔子;

V(S);

吃桔子;

}

}

daughter()

{

while(1)

{

P(Sa);

从盘中取出苹果;

V(S);

吃苹果;

}

}

例题3 设公交车上,司机和售票员的活动如下:司机;启动车辆;正常行使,到站停车. 售票员;关车门,售票 开车门. 在汽车不断到站 停车行驶过程中这两个活动有什么同步关系? 用信号量和pv操作实现。

设信号量为s1(是否开车)和s2(是否停车),s1=1,s2=0;

司机进程:                      售票员进程:

begin                           begin

L1:                             L2:

P(S1);                          关闭车门;

启动车辆;                      V(s1);

正常行驶;                      售票;

V(s2);                        P(s2);

goto L1;                        开车门;

end;                           goto L2;

end;

思考题:

四个进程A、B、C、D都要读一个共享文件F,系统允许多个进程同时读文件F。但限制是进程A和进程C不能同时读文件F,进程B和进程D也不能同时读文件F。为了使这四个进程并发执行时能按系统要求使用文件,现用PV操作进行管理,请回答下面的问题:

(1)应定义的信号量及初值:                             。

(2)在下列的程序中填上适当的P、V操作,以保证它们能正确并发工作:

A()             B()            C()                D()

{              {               {                  {

[1];            [3];           [5];               [7];

read F;         read F;        read F;            read F;

[2];            [4];         [6];                 [8];

}              }              }                   }

思考题解答:

(1)定义二个信号量S1、S2,初值均为1,即:S1=1,S2=1。其中进程A和C使用信号量S1,进程B和D使用信号量S2。

(2)从[1]到[8]分别为:P(S1) V(S1) P(S2) V(S2) P(S1) V(S1) P(S2) V(S2)

信号量、PV操作是解决进程间的同步与互斥问题的。

★     做题时尤其要注意隐藏的同步、互斥问题。这些问题通常可以归入生产者-消费者问题和阅读者-写入者问题。

★     PV操作一定是成对出现的,但是这不意味着它会在一个进程内成对出现。

★     在互斥关系中,PV操作一定是在一个进程内成对出现。而且,信号一定大于0,具体多少视情况而定。而对于同步关系,则一对PV操作在两个进程或者更多的进程中出现。

★     对于同步关系,信号量可能为0,也可能不为0;用于同步的信号个数可能1个,也可能是多个。

★     对信号量为1的,应该先执行V操作。

★     在生产者-消费者问题中,要设置三个信号量:empty-空闲的缓存区数量,初值为n;full-已填充的缓存区数量,初值为0;mutex-保证只有一个进程在写入缓存区,初值为1。

★     在阅读者-写入者问题中,设置两个信号量:信号量access-控制写入互斥,初值为1;信号量rc-控制对共享变量ReadCount(读者统计值)的互斥访问。

最新文章

  1. Linux快速上手
  2. 序列化类型 System.Data.Entity.DynamicProxies 的对象时检测到循环引用
  3. ShopNc基本介绍
  4. Pop - Facebook 开源 iOS &amp; OS X 动画库
  5. iOS网络-05-AFNetwoking原理及常用操作
  6. [转]SQL中使用WITH AS提高性能-使用公用表表达式(CTE)简化嵌套SQL
  7. T-SQL通过触发器创建级联更新&#183;级联删除
  8. Spark中的键值对操作-scala
  9. plsql developer日期时间格式设置
  10. forEach、map、filter、some、every五个数组方法
  11. 探讨SQL Server并发处理存在就更新七种解决方案
  12. Angular 学习笔记 ( CDK - Layout )
  13. 深度解密Go语言之Slice
  14. phoenix 报错:type org.apache.phoenix.schema.types.PhoenixArray is not supported
  15. ggplot2 multiply graphs on one figure
  16. EntityFramework Core
  17. 【MLP】多层感知机网络
  18. 【树上DFS】Tree and Polynomials
  19. Android MVP 构架初试
  20. protobuf java学习

热门文章

  1. Shell教程
  2. Web Service
  3. FastCgi与PHP-fpm关系
  4. 通过配置Mysql参数提高写入速度
  5. 通过form表单的形式下载文件。
  6. font-size 兼容问题
  7. [LeetCode] Consecutive Numbers 连续的数字
  8. Linux 中 SVN 重启关闭
  9. [转]ExtJS Grid 分页时保持选中的简单实现方法
  10. 读取TDrawGrid之获取博易数据