操作系统-关于叫号类的PV操作总结
原文 http://blog.csdn.net/u011240016/article/details/53395370
时间 2016-11-29
先分析一道题目:
面包师傅有很多面包,由n个推销人员推销。每个顾客进店后取一个号,并且等待叫号。当一个销售人员空闲下来时,就叫下一个号。设计一个使销售人员和顾客同步的算法。
分析:需要理解的是这个场景中的过程,抽象出进程的概念,再用信号量约束。
在这个场景中,主要是用号码进行约束。当取号时,这个号码只能给到一个人,因此要互斥访问。当按照号码叫号时,因为有n个推销员,所以号码也需要被互斥访问。
所以针对顾客取的号,和销售员待叫的号,需要设置两个变量控制访问。
即:
int i = 0, j = 0; semaphore mutex_i = 1,mutex_j = 1; Consumer() { 进入面包店; P(mutex_i); //互斥访问号码i 取号 i; i++;//正常逻辑下,号码要自增 V(mutex_i); //释放,可用于下一次的叫号控制 等待叫号i并购买面包 } Seller() { while(1) { P(mutex_j);//互斥访问当前叫号 if(j < i)//只要有未叫的号,就不停止 { 叫号j; j++; V(mutex_j); 销售面包; } else { V(mutex_j); 休息; } } }
单独看这个,容易形成的思维定势是见到号码,一定对号码进行互斥访问,其实不然,只要找到场景中能够约束进程或过程同步或互斥的变量都可以,并不是片面的都是对号码进程控制。
再举一个类似但不相同的控制思路。
(2011.45)某银行提供一个服务窗口和10个供顾客等待的座位。顾客到达银行时,若有空闲座位,则到取号机上取一个号,等待叫号。取号机每次仅允许一个顾客使用。当营业员空闲时,通过叫号选取一位顾客,并为其服务。顾客和营业员的活动过程描述如下:
cobegin { process 顾客i { 从取号机获取一个号码; 等待叫号; 获取服务; } process 营业员 { while(1) { 叫号; 为客户服务; } } } coend
思考:在这样的场景中,需不需要可以对叫的号码和取的号码进行互斥访问?
为什么要对获取号码进行互斥访问?答案是不让人取同一个号码。
为什么要对叫的号进行互斥访问?答案是防止不同的人叫了同一个号。
OK,回到这里的场景,控制叫号机的互斥访问就能控制取号的不同。
因为只有一个窗口服务,因此不存在多人叫同一个号的情况,因此不用对叫的号进行互斥访问。
推演来看,如果是多个服务窗口,就需要对叫的号进行互斥访问。也即添加一个mutex进行互斥即可。
再看座位,我们想为什么在生产者消费者的模型中,要设置一个full和一个empty跟踪,明明已经知道了总数,设置一个full或者empty不就可以了吗?是的,当然也行,只不过用的是总数减去其中一个得到另一个换算,思想是一个意思:用变量跟踪状态。
那无论是缓冲区还是座位,只要两个进程改变的方向不一样,那么就设full和empty来跟踪即可。
顾客和营业员之间是同步关系,在第一个例子中,其实用的是文字描述两个进程之间的同步,而没有用信号量做,所以不是严谨的答案,只是作为一个引例说明号码这件事情。
这里严格来做,就需要设置一个同步变量,同步变量的初始值为0,区别于互斥。
这样,可以抽出两个简化模型:
- 互斥:取号机,mutex=1控制
- 同步:顾客需要获得座位等待叫号。营业员空闲时,选取一个顾客为其服务。空座位的有无影响等待顾客数量。顾客的有无决定营业员是否可以服务。设置信号量和full来实现两个同步关系。设置service构成顾客与营业员何时开始服务的同步关系。
semaphore empty = 10; //空位数初值 semaphore full = 0; //已使用 semaphore mutex = 1; //互斥访问叫号机 semaphore service = 0; //控制服务同步关系 process 顾客i { P(empty); //等空位,在营业员那边V,所以是一个同步关系 P(mutex); 从取号机取号; V(mutex); V(full);//营业员那边在P,也是一个同步关系 P(service); //营业员那边在V,同步关系 } process 营业员 { while(1) { P(full); V(empty); V(service); 为顾客服务; } }