在计算机操作系统中,PV操作是进程管理中的难点。
首先应弄清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操作实现进程同步时应该注意的是:

(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-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-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);
              吃苹果;
            }

 
思考题:

四个进程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原语对信号量的操作可以分为三种情况:

1)              把信号量视为一个加锁标志位,实现对一个共享变量的互斥访问。

实现过程:

P(mutex);           // mutex的初始值为1

访问该共享数据;

V(mutex);

非临界区

2)              把信号量视为是某种类型的共享资源的剩余个数,实现对一类共享资源的访问。

实现过程:

P(resource);          // resource的初始值为该资源的个数N

使用该资源;

V(resource);

非临界区

3)              把信号量作为进程间的同步工具

实现过程:

临界区C1;    P(S);

V(S);           临界区C2;

下面用几个例子来具体说明:

例1:某超市门口为顾客准备了100辆手推车,每位顾客在进去买东西时取一辆推车,在买完东西结完帐以后再把推车还回去。试用P、V操作正确实现顾客进程的同步互斥关系。

分析:把手推车视为某种资源,每个顾客为一个要互斥访问该资源的进程。因此这个例子为PV原语的第二种应用类型。

解:semaphore  S_CartNum;   // 空闲的手推车数量, 初值为100

void  consumer(void)           // 顾客进程
{
        P(S_CartNum);

买东西;

结帐;

V(S_CartNum); 
}

例2:桌子上有一个水果盘,每一次可以往里面放入一个水果。爸爸专向盘子中放苹果,儿子专等吃盘子中的苹果。把爸爸、儿子看作二个进程,试用P、V操作使这四个进程能正确地并发执行。

分析:爸爸和儿子两个进程相互制约,爸爸进程执行完即往盘中放入苹果后,儿子进程才能执行即吃苹果。因此该问题为进程间的同步问题。

解:semaphore  S_PlateNum;  // 盘子容量,初值为1

semaphore  S_AppleNum;   // 苹果数量,初值为0

void  father( )                // 父亲进程
{

while(1)

{
        P(S_PlateNum);

往盘子中放入一个苹果;

V(S_AppleNum);


}

void  son( )   // 儿子进程
{

while(1)

{
        P(S_AppleNum);

从盘中取出苹果;

V(S_PlateNum);

吃苹果;


}

另附用PV原语解决进程同步与互斥问题的例子:

经典IPC问题如:生产者-消费者,读者-写者,哲学家就餐,睡着的理发师等可参考相关教材。

一、两个进程PA、PB通过两个FIFO(先进先出)缓冲区队列连接(如图)

PA

PB

Q1

Q2

PA从Q2取消息,处理后往Q1发消息,PB从Q1取消息,处理后往Q2发消息,每个缓冲区长度等于传送消息长度. Q1队列长度为n,Q2队列长度为m. 假设开始时Q1中装满了消息,试用P、V操作解决上述进程间通讯问题。

解:// Q1队列当中的空闲缓冲区个数,初值为0
semaphore  S_BuffNum_Q1;

// Q2队列当中的空闲缓冲区个数,初值为m 
semaphore  S_BuffNum_Q2;

// Q1队列当中的消息数量,初值为n 
semaphore  S_MessageNum_Q1;

// Q2队列当中的消息数量,初值为0 
semaphore  S_MessageNum_Q2;

void  PA( )
{

while(1)

{
                P(S_MessageNum_Q2);

从Q2当中取出一条消息;

V(S_BuffNum_Q2);

处理消息;

生成新的消息;

P(S_BuffNum_Q1);

把该消息发送到Q1当中;

V(S_MessageNum_Q1);


}

void  PB( )
{

while(1)

{
                P(S_MessageNum_Q1);

从Q1当中取出一条消息;

V(S_BuffNum_Q1);

处理消息;

生成新的消息;

P(S_BuffNum_Q2);

把该消息发送到Q2当中;

V(S_MessageNum_Q2);


}

二、《操作系统》课程的期末考试即将举行,假设把学生和监考老师都看作进程,学生有N人,教师1人。考场门口每次只能进出一个人,进考场的原则是先来先进。当N个学生都进入了考场后,教师才能发卷子。学生交卷后即可离开考场,而教师要等收上来全部卷子并封装卷子后才能离开考场。

(1)问共需设置几个进程?

(2)请用P、V操作解决上述问题中的同步和互斥关系。

解:semaphore  S_Door;          // 能否进出门,初值1

semaphore  S_StudentReady;    // 学生是否到齐,初值为0

semaphore  S_ExamBegin;   // 开始考试,初值为0

semaphore  S_ExamOver;    // 考试结束,初值为0

int  nStudentNum = 0;          // 学生数目

semaphore  S_Mutex1         //互斥信号量,初值为1

int  nPaperNum = 0;       // 已交的卷子数目

semaphore  S_Mutex2         //互斥信号量,初值为1

void  student( )
{

P(S_Door);

进门;
        V(S_Door);

P(S_Mutex1);

nStudentNum ++;         // 增加学生的个数

if(nStudentNum == N)  V(S_StudentReady);

V(S_Mutex1);

P(S_ExamBegin);         // 等老师宣布考试开始

考试中…

交卷;

P(S_Mutex2);

nPaperNum ++;      // 增加试卷的份数

if(nPaperNum == N)  V(S_ExamOver);

V(S_Mutex2);

P(S_Door);

出门;

V(S_Door);

}

void  teacher( )
{

P(S_Door);

进门;
        V(S_Door);

P(S_StudentReady);//等待最后一个学生来唤醒

发卷子;

for(i = 1; i <= N; i++)    V(S_ExamBegin);

P(S_ExamOver);         // 等待考试结束

封装试卷;

P(S_Door);

出门;
        V(S_Door);

}

三、某商店有两种食品A和B,最大数量均为m个。 该商店将A、B两种食品搭配出售,每次各取一个。为避免食品变质,遵循先到食品先出售的原则。有两个食品公司分别不断地供应A、B两种食品(每次一个)。为保证正常销售,当某种食品的数量比另一种的数量超过k(k<m)< font="">个时,暂停对数量大的食品进货,补充数量少的食品。

(1) 问共需设置几个进程?

(2) 用P、V操作解决上述问题中的同步互斥关系。

解:semaphore  S_BuffNum_A;  //A的缓冲区个数, 初值m

semaphore  S_Num_A;          // A的个数,初值为0

semaphore  S_BuffNum_B;  //B的缓冲区个数, 初值m

semaphore  S_Num_B;          // B的个数,初值为0

void  Shop( )
{

while(1)

{
                P(S_Num_A);

P(S_Num_B);

分别取出A、B食品各一个;

V(S_BuffNum_A);

V(S_BuffNum_A);

搭配地销售这一对食品;


}

// “A食品加1,而B食品不变”这种情形允许出现的次数(许可证的数量),其值等于//k-(A-B),初值为k

semaphore  S_A_B;

// “B食品加1,而A食品不变”这种情形允许出现的次数(许可证的数量),其值等于//k-(B-A),初值为k

semaphore  S_B_A;

void  Producer_A ( )
{

while(1)

{
                生产一个A食品;

P(S_BuffNum_A);

P(S_A_B);

向商店提供一个A食品;

V(S_Num_A);

V(S_B_A);


}

void  Producer_B ( )
{

while(1)

{
                生产一个B食品;

P(S_BuffNum_B);

P(S_B_A);

向商店提供一个B食品;

V(S_Num_B);

V(S_A_B);


}

四:在一栋学生公寓里,只有一间浴室,而且这间浴室非常小,每一次只能容纳一个人。公寓里既住着男生也住着女生,他们不得不分享这间浴室。因此,楼长制定了以下的浴室使用规则:(1)每一次只能有一个人在使用;(2)女生的优先级要高于男生,即如果同时有男生和女生在等待使用浴室,则女生优先;(3)对于相同性别的人来说,采用先来先使用的原则。

假设:

(1)当一个男生想要使用浴室时,他会去执行一个函数boy_wants_to_use_bathroom,当他离开浴室时,也会去执行另外一个函数boy_leaves_bathroom;

(2)当一个女生想要使用浴室时,会去执行函数girl_wants_to_use_bathroom,当她离开时, 也会执行函数girl_leaves_bathroom;

问题:请用信号量和P、V操作来实现这四个函数(初始状态:浴室是空的)。

解:信号量的定义:

semaphore  S_mutex;     // 互斥信号量,初值均为1

semaphore  S_boys; // 男生等待队列,初值为0

semaphore  S_girls;   // 女生等待队列,初值为0

普通变量的定义:

int  boys_waiting = 0;     // 正在等待的男生数;

int  girls_waiting = 0; // 正在等待的女生数;

int  using = 0;      // 当前是否有人在使用浴室;

void  boy_wants_to_use_bathroom ( )
{

P(S_mutex);

if((using == 0) && (girls_waiting == 0))

{

using  =  1;

V(S_mutex);

}

else

{

boys_waiting ++;

V(S_mutex);

P(S_boys);

}

}

void  boy_leaves_bathroom ( )
{

P(S_mutex);

if(girls_waiting  >  0)  // 优先唤醒女生

{

girls_waiting --;

V(S_girls);

}

else  if(boys_waiting  >  0)

{

boys_waiting --;

V(S_ boys);

}

else    using  =  0;         // 无人在等待

V(S_mutex);

}

void  girl_wants_to_use_bathroom ( )
{

P(S_mutex);

if(using == 0)

{

using  =  1;

V(S_mutex);

}

else

{

girls_waiting ++;

V(S_mutex);

P(S_girls);

}

}

void  girl_leaves_bathroom ( )
{

P(S_mutex);

if(girls_waiting  >  0)  // 优先唤醒女生

{

girls_waiting --;

V(S_girls);

}

else  if(boys_waiting  >  0)

{

boys_waiting --;

V(S_ boys);

}

else    using  =  0;         // 无人在等待

V(S_mutex);

}

转 信号量与PV操作的更多相关文章

  1. 信号量与PV操作

    在计算机操作系统中,PV操作是进程管理中的难点.首先应弄清PV操作的含义:PV操作由P操作原语和V操作原语组成(原语是不可中断的过程),对信号量进行操作,具体定义如下:    P(S):①将信号量S的 ...

  2. Pintos修改优先级捐赠、嵌套捐赠、锁的获得与释放、信号量及PV操作

    Pintos修改优先级捐赠.嵌套捐赠.锁的获得与释放.信号量及PV操作 原有的优先级更改的情况下面没有考虑到捐赠的情况,仅仅只是改变更改了当前线程的优先级,更别说恢复原本优先级了,所以不能通过任何有关 ...

  3. 信号量和PV操作写出Bakery算法的同步程序

    面包店烹制面包及蛋糕,由n个销售员卖出.当有顾客进店购买面包或蛋糕时,应先在取号机上取号,然后等待叫号,若有销售员空闲时便叫下一号,试用信号量和PV操作写出Bakery算法的同步程序. 设计要求 1) ...

  4. 用信号量及其PV操作处理实际问题

    43.现有3个生产者P1.P2.P3,他们都要生产橘子汁,每个生产者都已分别购得两种不同的原料,待购齐第三种原料后就可配制成橘子汁装瓶出售.有一供应商能源源不断的供应糖.水.橘子精,但每次只拿出一种原 ...

  5. Operating System-进程/线程内部通信-信号量、PV操作的实现和应用(解决哲学家进餐和生产者消费者问题)

    本文主要内容: 信号量的实现 利用信号量解决哲学家用餐问题 利用信号量解决生产者消费者问题 一.信号量的实现 1.1 信号量结构 typedef struct { int value; struct ...

  6. Operating System-进程/线程内部通信-信号量和PV操作

    本文介绍操作系统进程管理的两个核心概念: 信号量 PV操作 一.信号量介绍 1.1 信号量引入 信号量(Semaphore)1965年由Dijkstra引入的.信号量一般由一个值是一个变量,其值有可能 ...

  7. 整型信号量和PV操作(计算机操作系统)

    在整型信号量机制中,信号量被定义为一个整形变量.除初始化外,仅能通过两个标准的原子操作Wait(S)和Signal(S)来访问.其通常分别被称为P.V操作. 描述如下: P操作:S=S-1:如果S小于 ...

  8. 【转】进程同步之信号量机制(pv操作)及三个经典同步问题

    原文地址:http://blog.csdn.net/speedme/article/details/17597373 上篇博客中(进程同步之临界区域问题及Peterson算法),我们对临界区,临界资源 ...

  9. OS__信号量(semaphore)PV操作

    信号量的概念 1.信号量的类型定义 信号量(semaphore)的数据结构为记录型数据结构一个值和一个指针,指针指向等待该信号量的下一个进程.信号量的值与相应资源的使用情况有关,在操作系统中,信号量用 ...

随机推荐

  1. Linux安装Tomcat-Nginx-FastDFS-Redis-Solr-集群——【第八集之安装Nginx】

    1,务必保证安装Nginx之前,所需的环境必须安装完备.  gcc 安装nginx需要先将官网下载的源码进行编译,编译依赖gcc环境,如果没有gcc环境,需要安装gcc:yum install gcc ...

  2. Noj - 在线强化训练4

    状态 题号 竞赛题号 标题 × 1092 A 童年的回忆——计算24 × 1145 B 求图像的周长 × 1144 C 农场灌溉问题 × 1202 D 数独游戏 × 1243 E 循环赛日程表 × 1 ...

  3. Service工作原理

    --摘自<android插件化开发指南> 一.在新进程启动Service 第一步:APP向AMS发送一个启动Service的消息 通过AMN/AMP把要启动的Service信息发送给AMS ...

  4. POJ 3189 Steady Cow Assignment 【二分】+【多重匹配】

    <题目链接> 题目大意: 有n头牛,m个牛棚,每个牛棚都有一定的容量(就是最多能装多少只牛),然后每只牛对每个牛棚的喜好度不同(就是所有牛圈在每个牛心中都有一个排名),然后要求所有的牛都进 ...

  5. B. Divisiblity of Differences

    B. Divisiblity of Differencestime limit per test1 secondmemory limit per test512 megabytesinputstand ...

  6. unity3d俄罗斯方块源码教程+源码和程序下载

    小时候,大家都应玩过或听说过<俄罗斯方块>,它是红白机,掌机等一些电子设备中最常见的一款游戏.而随着时代的发展,信息的进步,游戏画面从简单的黑白方块到彩色方块,游戏的玩法机制从最简单的消方 ...

  7. Is there a TRY CATCH command in Bash

    Is there a TRY CATCH command in Bash? No. Bash doesn't have as many luxuries as one can find in many ...

  8. python中正则表达式 re.findall 用法

    在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配. 其中,re.findall() 函数可以遍历匹配,可以获取字符串中所有匹配的字符串,返回一个列表. 在python源代码中 ...

  9. 利用cookie实现iframe刷新时停留在当前页面

    这段时间第一次用iframe,发现问题还挺多,这次主要解决了一个用cookie实现iframe刷新时停留在当前页面,具体步骤如下: 1.必须在每一个页面中记录下当前的url并存入cookie中,具体代 ...

  10. 如何调用wasm文件?

    如果用C/C++导出wasm模块,方法名会默认带_前缀:如果是asm.js转成了wasm模块,方法名就不带_前缀. 一.c到js 二.wasm和js 三.小尝试 这里主要汇集了自己初学webAssem ...