管程 信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因 此后来又提出了一种集中式同步进程一一管程。其基本思想是将共享变量和对它们的操作集中在 一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修 改,易于保证正确性。 本节将从以下几个方面进行介绍-一 一,管程的概念 1.管程的形式 2.管程的特点 二.实例 1.生产者-消费者问题(有buffer) 2. 第一类读-写者问题 3.哲学家问题 一,管程的概念 1,管程的形式 管程作为一个模块,它的类型定义如下: monitor_name MoNITOR; 共享变量说明: define本管程内部定义、外部可调用的函数名表: use本管程外部定义、内部可调用的函数名表; 内部定义的函数说明和函数体 共享变量初始化语句:
管程 信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因 此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在 一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修 改,易于保证正确性。 本节将从以下几个方面进行介绍-- -------------------------------------------------------------------------------- 一. 管程的概念 1. 管程的形式 2. 管程的特点 -------------------------------------------------------------------------------- 二. 实例 1. 生产者-消费者问题(有 buffer) 2. 第一类读-写者问题 3. 哲学家问题 -------------------------------------------------------------------------------- 一. 管程的概念 1. 管程的形式 管程作为一个模块,它的类型定义如下: monitor_name = MoNITOR; 共享变量说明; define 本管程内部定义、外部可调用的函数名表; use 本管程外部定义、内部可调用的函数名表; 内部定义的函数说明和函数体 { 共享变量初始化语句; } --------------------------------------------------------------------------------
2.管程的特点 从语言的角度看,管程主要有以下特性: (1)模块化。管程是一个基本程序单位,可以单独编译: (2)抽象数据类型。管程是中不仅有数据,而且有对数据的操作: (3)信息掩蔽。管程外可以调用管程内部定义的一些函数,但函数的具体实现外部不 可见: 对于管程中定义的共享变量的所有操作都局限在管程中,外部只能通过调用管程的某些函数来间 接访问这些变量。因此管程有很好的封装性。 为了保证共享变量的数据一致性,管程应互斥使用。管程通常是用于管理资源的,因此管程中 有进程等待队列和相应的等待和唤醒操作。在管程入口有一个等待队列,称为入口等待队列。当 一个已进入管程的进程等待时,就释放管程的互斥使用权:当已进入管程的一个进程唤醒另一个 进程时,两者必须有一个退出或停止使用管程。在管程内部,由于执行唤醒操作,可能存在多个 等待进程(等待使用管程),称为紧急等待队列,它的优先级高于入口等待队列。 因此,一个进程进入管程之前要先申请,一般由管程提供一个enter过程:离开时释放使用权, 如果紧急等待队列不空,则唤醒第一个等待者,一般也由管程提供外部过程1eave。 管程内部有自己的等待机制。管程可以说明一种特殊的条件型变量:varc:condition:实际上 是一个指针,指向一个等待该条件的PCB队列。对条件型变量可执行wait和signal操作: wit(c):若紧急等待队列不空,唤醒第一个等待者,否则释放管程使用权。执行本 操作的进程进入C队列尾部: signal(c):若C队列为空,继续原进程,否则唤醒队列第一个等待者,自己进入紧 急等待队列尾部。 二.实例 I.生产者-消费者问题(有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;
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; f while (true) produce(&item); buffer.enter(); buffer.deposit(item); buffer.leave(); consumer(消费者进程): Item_Type item; while (true) buffer.enter(); item=buffer.fetch(); buffer.leave();
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);
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;
} 进程: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();
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(); } }