会合                                  

                     返回本讲概述

会合是适用于不具有公共内存的分布式系统的同步机制。
本节将从以下几个方面进行介绍--

一. 会合的提出

二. 会合的概念

三. 实例

一. 会合的提出

在不具有公共内存的分布式操作系统中,要使用P.V操作或管程
机制存在着十分大的问题--信号灯量和管程共享变量存放在何
处?如果分步式系统中有两个主机H1和H2,它们之间并没有公共
内存,H1中有进程P1,H2中有进程P2,如果有一个信号量(或管
程共享变量)S,那S放在何处呢?如果放在H1中,显然P2无法访
问到S;同理,如果放在H2中,S对P1又是不可访问的。显然,P.V
操作和管程都是以进程所操作的对象为核心的,而这些对象在无
公共内存的分布式操作系统中的存储是一个不可克服的困难。因
此,自然希望在分布式操作系统中的同步机制应该避开这些被动
的成分,而以主动成分--进程直接进行相互作用。因而,产生了
会合这一种同步机制。

返回


二. 会合的概念
1. 会合的一般形式

一个进程可以有许多入口,一个入口对应一段程序,一个进程可
以调用另一个进程的入口。当一个进程调用另一个进程的入口,
而且被调用的进程已准备好接受这个调用时,会合就发生了。当
调用者发出调用请求时,被调用的进程未准备接受这个调用时,
则调用者等待;反之,当被调用者准备接受调用,而当前尚无调
用者时,则被调用者等待。即先到达会合处等待后到达者。当多
个进程调用同一个进程的同一个入口时,被调用者按先来先服务
(FCFS)的次序接受调用。入口处可以携带调用参数,还可以有
返回参数,以实现信息的交换。被调用者可以选择会合的入口。
下图为会合的一般形式。

返回


2. 会合的核心语句

(1) 调用语句
<进程名称> <进程名称> .<入口名称> [<实际参数表><实际参数表>]
如:Buffer.read(a);
操作:将自身放入等待链,并唤醒相应进程;调用<进程名称>确定
的入口,即进入相应的FIFO队列。

(2) 接受语句
accept <入口名称> <入口名称>[<形参表><形参表>]
[do <语句序列><语句序列>]
例如:accept read(OUT:int a)
         do a = buffer[i];
IN:输入参数,OUT:输出参数的标识
语义流程图如下:



(3) 选择语句
select [when (<布尔表达式><布尔表达式>) do]
<接受语句> <接受语句>
[<语句序列><语句序列>]
{or [when (<布尔表达式><布尔表达式>) do]
<接受语句><接受语句> <接受语句>
[<语句序列><语句序列>]}
[else <语句序列><语句序列>]
说明:当布尔表达式的值为真时,则对应的入口可被调用,反之不
然;当选择语句发生时,其包含when的布尔表达式均被计算。
语义流程图如下:


:由于在分布式操作系统中没有公共内存,因此参数全为值参
而且不可为指针。

返回


三. 实例
1. 生产者-消费者问题(有buffer)
问题描述:                                                              

有三台主机,在一台主机上有进程(生产者)生产item,并放置在另一台主机的缓冲区里,第三台主机上有进程(消费者)从缓冲区中得到item,并进行处理。如果缓冲区满,则生产者不能再生产,直到缓冲区有空位,如果缓冲区空时,消费者也不能从缓冲区中得到item,直到缓冲区不空。

解决方法:


进程:Buffer,Producer,Consumer
Producer(生产者进程):
Item_Type item;
{
    while (true)
   {
       produce(&item);
       Buffer.Set(item);
   }
}

Consumer(消费者进程):
Item_Type item;
{
   while (true)
  {
      Buffer.Get(item);
      Consume(item);
   }
}

Buffer(缓冲区的管理进程):
Item_Type buffer[max_num];
int in , out ;
{
     in = out = 0;
     while (ture)
     {
         select
         when (in<out+max_num) do {
           accept Set(IN:Item_Type item) do
           {
               buffer[in % max_num]=item;
               in++;
           }
         }
         or when (out<in) do {
            accept Get(OUT:Item_Type item) do
            {
               item = buffer[out % max_num];
               out++;
            }
         }
     }
}

例程演示

返回


2. 第一类读-写者问题
问题描述:

有三类主机,在第一类主机上的写者向第二台主机写信息,第三类主机上的读者从第二台主机上读得信息。如果有读者在读,则写者就不能写;如果有写者在写时新来的写者也不能写,即只有当只有一个写者时此写者才能写。如果有写者,则读者不能读。

进程:Reader,writer,Server
Reader(读者进程):
Item_Type tiem;
{
    while (true)
    {
        Serve.readbegin();
        read(item);
        sever.readend();
     }
}

Writer(写者进程):
Item_type item;
{
   while (true)
   {
        Server.writebegin();
        write(&item);
        Server.writeend();
    }
}

Server(服务器进程):
int readcount;
bool nonwriter;
{
   readcount = 0;
   nonwriter = true; 
   while (true)
   {
        select
        when (nonwriter) do
        {
           accept readbegin() do readcount++;
         }
         or when (nonwrite && (readcount == 0)) do
            accept writebegin() do nonwrite = false;
         or when (true) do
            accept writeend() do nonwriter = true;
         or when (true) do
            accept readend() do readcount--;
   }
}

例程演示

返回


3. 哲学家问题
问题描述:

有五个哲学家,围坐在一个圆桌前,每个人的左右都只有一只筷子,桌上总共只有五只筷子,而哲学家就餐时需要两只筷子,即其左右的筷子,而一次只能取一只筷子,所以五个哲学家不能同时就餐,设计一个算法模拟哲学家就餐的问题。

进程:Philosopher[i],fork
思路:哲学家有三种状态:思考(Thinking)、饥饿(hungry)、进食
(Eating)。当饥饿时,通知其左筷子,若其右筷子无人用,并且其左筷子
无人准备用时,才可以取得其左筷子。例如:Philosopher[0]先hungry,
则fork[0]测试fork[1]是否可用,若无人用则将fork[1].ready_user
=true,fork[0].R_user=false,则其可获得fork[0] 和fork[1],而若
Philosopher[1] hungry,则其左筷子fork[1]被使用或准备被使用,故
Philosopher[1]的调用被挂起在hungry的入口;若Philosopher[4]
hungry,由于其右叉子fork[1]被使用,故其在getL()以取得fork[4]
时就被挂起等待。

例程演示

返回