管程
信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
本节将从以下几个方面进行介绍--
--------------------------------------------------------------------------------
一. 管程的概念
1. 管程的形式
2. 管程的特点
--------------------------------------------------------------------------------
二. 实例
1. 生产者-消费者问题(有buffer)
2. 第一类读-写者问题
3. 哲学家问题
--------------------------------------------------------------------------------
一. 管程的概念
1. 管程的形式
管程作为一个模块,它的类型定义如下:
monitor_name = MoNITOR;
共享变量说明;
define 本管程内部定义、外部可调用的函数名表;
use 本管程外部定义、内部可调用的函数名表;
内部定义的函数说明和函数体
{
共享变量初始化语句;
}
--------------------------------------------------------------------------------
2. 管程的特点
从语言的角度看,管程主要有以下特性:
(1)模块化。管程是一个基本程序单位,可以单独编译;
(2)抽象数据类型。管程是中不仅有数据,而且有对数据的操作;
(3)信息掩蔽。管程外可以调用管程内部定义的一些函数,但函数的具体实现外部不可见;
对于管程中定义的共享变量的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间接访问这些变量。因此管程有很好的封装性。
为了保证共享变量的数据一致性,管程应互斥使用。 管程通常是用于管理资源的,因此管程中有进程等待队列和相应的等待和唤醒操作。在管程入口有一个等待队列,称为入口等待队列。当一个已进入管程的进程等待时,就释放管程的互斥使用权;当已进入管程的一个进程唤醒另一个进程时,两者必须有一个退出或停止使用管程。在管程内部,由于执行唤醒操作,可能存在多个等待进程(等待使用管程),称为紧急等待队列,它的优先级高于入口等待队列。
因此,一个进程进入管程之前要先申请,一般由管程提供一个enter过程;离开时释放使用权,如果紧急等待队列不空,则唤醒第一个等待者,一般也由管程提供外部过程leave。
管程内部有自己的等待机制。管程可以说明一种特殊的条件型变量:var c:condition;实际上是一个指针,指向一个等待该条件的PCB队列。对条件型变量可执行wait和signal操作:
wait(c):若紧急等待队列不空,唤醒第一个等待者,否则释放管程使用权。执行本操作的进程进入C队列尾部;
signal(c):若C队列为空,继续原进程,否则唤醒队列第一个等待者,自己进入紧急等待队列尾部。
--------------------------------------------------------------------------------
二. 实例
1. 生产者-消费者问题(有buffer)
问题描述:(见信号量部分)
解答:
管程:buffer=MODULE;
(假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
notfull,notempty:condition;
— notfull控制缓冲区不满,notempty控制缓冲区不空;
count,in,out: integer;
— count记录共有几件物品,in记录第一个空缓冲区,out记录第一个不空的缓冲区
buf:array [0..k-1] of item_type;
define deposit,fetch;
use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
procedure deposit(item);
{
if(count=k) monitor.wait(notfull);
buf[in]=item;
in:=(in+1) mod k;
count++;
monitor.signal(notempty);
}
procedure fetch:Item_type;
{
if(count=0) monitor.wait(notempty);
item=buf[out];
in:=(in+1) mod k;
count--;
monitor.signal(notfull);
return(item);
}
{
count=0;
in=0;
out=0;
}
进程:producer,consumer;
producer(生产者进程):
Item_Type item;
{
while (true)
{
produce(&item);
buffer.enter();
buffer.deposit(item);
buffer.leave();
}
}
consumer(消费者进程):
Item_Type item;
{
while (true)
{
buffer.enter();
item=buffer.fetch();
buffer.leave();
consume(&item);
}
}
--------------------------------------------------------------------------------
2. 第一类读-写者问题
问题描述:(见信号量部分)
解答:
管程:bulletin=MODULE;
(假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
r,w:condition;
— r控制读者使用黑板,w控制写者;
writing:boolean;
— 当前是否写者使用黑板
read_account:integer;
define start_read,end_read,start_write,end_write;
use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
procedure start_read();
{
if(writing) monitor.wait(r);
read_account++;
monitor.signal(r);
}
procedure end_read();
{
read_account--;
if (read_account=0) monitor.signal(w);
}
procedure start_write();
{
if((read_account<>;0) or writing) monitor.wait(w);
writing=true;
}
procedure end_write();
{
writing=false;
if (r<>;NULL) monitor.signal(r);
else monitor.signal(w);
}
进程:writer - 写者进程,reader - 读者进程
reader - (读者进程):
{
while (true)
{
bulletin.enter();
bulletin.start_read();
read();
bulletin.end_read();
bulletin.leave();
}
}
writer - (写者进程):
{
while (true)
{
bulletin.enter();
bulletin.start_write();
write();
bulletin.end_write();
bulletin.leave();
}
}
--------------------------------------------------------------------------------
3. 哲学家问题
问题描述:(见信号量部分)
解答:
管程:dining=MODULE;
(假设已实现一基本管程monitor,提供enter,leave,signal,wait等操作)
queue:array [0..4] of condition;
— 控制哲学家能否进食;
fork:array[0..4] of (free,use);
— 当前各个叉子的状态
define pickup,test,putdown;
use monitor.enter,monitor.leave,monitor.wait,monitor.signal;
procedure pickup(i:0..4);
{
if(fork=used) monitor.wait(queue);
fork=used;
}
procedure putdown(i:0..4);
{
fork=free;
monitor.signal(queue);
}
procedure test(i:0..4):boolean;
{
if(fork=use) ok=false
else { fork=use; ok=true; } ;
return ok;
}
管程:dining=MODULE;
philosopher(i:0..4);
with_two_forks:boolean;
{
while (true)
{
think();
with_two_forks=false;;
while(!with_two_forks) {
dining.enter();
dining.pickup(i);
if(dining.test((i+1) mod 5) with_two_forks=true;
else dining.putdown(i);
dining.leave();
}
eat();
dining.enter();
dining.putdown(i);
dining.putdown((i+1) mod 5));
dining.leave();
}
}
信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
本节将从以下几个方面进行介绍--
--------------------------------------------------------------------------------
一. 管程的概念
1. 管程的形式
2. 管程的特点
--------------------------------------------------------------------------------
二. 实例
1. 生产者-消费者问题(有buffer)
2. 第一类读-写者问题
3. 哲学家问题
--------------------------------------------------------------------------------
一. 管程的概念
1. 管程的形式
管程作为一个模块,它的类型定义如下:
--------------------------------------------------------------------------------
2. 管程的特点
从语言的角度看,管程主要有以下特性:
对于管程中定义的共享变量的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间接访问这些变量。因此管程有很好的封装性。
为了保证共享变量的数据一致性,管程应互斥使用。 管程通常是用于管理资源的,因此管程中有进程等待队列和相应的等待和唤醒操作。在管程入口有一个等待队列,称为入口等待队列。当一个已进入管程的进程等待时,就释放管程的互斥使用权;当已进入管程的一个进程唤醒另一个进程时,两者必须有一个退出或停止使用管程。在管程内部,由于执行唤醒操作,可能存在多个等待进程(等待使用管程),称为紧急等待队列,它的优先级高于入口等待队列。
因此,一个进程进入管程之前要先申请,一般由管程提供一个enter过程;离开时释放使用权,如果紧急等待队列不空,则唤醒第一个等待者,一般也由管程提供外部过程leave。
管程内部有自己的等待机制。管程可以说明一种特殊的条件型变量:var c:condition;实际上是一个指针,指向一个等待该条件的PCB队列。对条件型变量可执行wait和signal操作:
--------------------------------------------------------------------------------
二. 实例
1. 生产者-消费者问题(有buffer)
问题描述:(见信号量部分)
解答:
--------------------------------------------------------------------------------
2. 第一类读-写者问题
问题描述:(见信号量部分)
解答:
--------------------------------------------------------------------------------
3. 哲学家问题
问题描述:(见信号量部分)
解答: