3.5.1 进程通信的类型 1、进程通信的定义 进程之间的信息交换。 2、按进程通信机制发展来分 ◆ 低级进程通信机制 ◇少量的信息交换,没有专门的通信机制,如信号量机制(同步和互斥);◇效率低,通信对用户不透明。 ◆ 高级进程通信机制 大量的信息交换,有专门的通信机制。◇共享存储器系统(Shared-Memory System) 相互通信的进程共享某些数据结构和共享存储区,进程之间能够通过它们进行通信。 ▲ 基于共享数据结构的通信方式 要求各进程公用某些数据结构,进程通过它们交换信息。效率低,只适于少量数据传递。 ▲ 基于共享存储区的通信方式 在存储器中划出一块共享存储区,各进程通过对共享存储区中的数据进行读或写来实现通信。◇消息传递系统(Message passing system) 进程间的数据交换以消息(Message—在计算机网络中称为报文)为单位,程序员直接利用系统提供的一组通信命令(原语)来实现通信。能够通过它们进行通信。目前系统中的主要进程通信方式。 不论是单机系统、多机系统,还是计算机网络,消息传递机制都是用得最广泛的一种进程间通信的机制。在消息传递系统中,进程间的数据交换,是以格式化的消息(message)为单位的;在计算机网络中,又把message称为报文。程序员直接利用系统提供的一组通信命令(原语)进行通信。操作系统隐藏了通信的实现细节,大大减化了通信程序编制的复杂性,而获得广泛的应用。消息传递系统的通信方式属于高级通信方式。又因其实现方式的不同而进一步分成直接通信方式和间接通信方式两种。 ▲ 直接通信方式 发送进程直接将消息发送给接收进程,并将其挂在接收进程的消息缓冲队列上。接收进程从消息缓冲队列中取得消息。 ▲ 间接通信方式 发送进程将消息发到某种中间实体(一般称为信箱)中,接收进程从中取得消息。◇管道(Pipe)通信 所谓“管道”,是指用于连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又名pipe文件。向管道(共享文件)提供输入的发送进程(即写进程), 以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程),则从管道中接收(读)数据。由于发送进程和接收进程是利用管道进行通信的,故又称为管道通信。这种方式首创于UNIX系统,由于它能有效地传送大量数据,因而又被引入到许多其它操作系统中。 为了协调双方的通信,管道机制必须提供以下三方面的协调能力:① 互斥,即当一个进程正在对pipe执行读/写操作时,其它(另一)进程必须等待。 ② 同步,指当写(输入)进程把一定数量(如4 KB)的数据写入pipe,便去睡眠等待, 直到读(输出)进程取走数据后,再把他唤醒。当读进程读一空pipe时,也应睡眠等待,直至写进程将数据写入管道后,才将之唤醒。③ 确定对方是否存在,只有确定了对方已存在时,才能进行通信。 3.5.2 消息传递通信的实现方法 1、直接通信方式 是指发送进程利用OS所提供的发送命令,直接把消息发送给目标进程。此时,要求发送进程和接收进程都以显式方式提供对方的标识符。通常,系统提供下述两条通信命令(原语): Send(Receiver, message); 发送一个消息给接收进程。Receive(Sender, message); 接收Sender发来的消息。 如原语Send(P2,m1)表示将消息m1发送给接收进程P2;而原语Receive(P1,m1)则表示接收由P1发来的消息m1。 在某些情况下,接收进程可与多个发送进程通信,因此,它不可能事先指定发送进程。例如,用于提供打印服务的进程,它可以接收来自任何一个进程的“打印请求”消息。对于这样的应用,在接收进程接收消息的原语中的源进程参数,是完成通信后的返回值,接收原语可表示为: Receive (id, message); 我们还可以利用直接通信原语,来解决生产者-消费者问题。当生产者生产出一个产品(消息)后,便用Send原语将消息发送给消费者进程;而消费者进程则利用Receive原语来得到一个消息。如果消息尚未生产出来,消费者必须等待,直至生产者进程将消息发送过来。生产者-消费者的通信过程可分别描述如下: repeat … produce an item in nextp; … send(consumer, nextp);until false;repeat receive(producer, nextc); … consume the item in nextc;until false; 2、间接通信方式 ◆ 信箱的创建和撤消 进程可利用信箱创建原语来建立一个新信箱。创建者进程应给出信箱名字、信箱属性(公用、私用或共享);对于共享信箱,还应给出共享者的名字。当进程不再需要读信箱时,可用信箱撤消原语将之撤消。 ◆ 消息的发送和接收 当进程之间要利用信箱进行通信时,必须使用共享信箱,并利用系统提供的下述通信原语进行通信。 Send(mailbox, message); 将一个消息发送到指定信箱Receive(mailbox, message); 从指定信箱中接收一个消息 ◆ 示例 ◆ 信箱分类 信箱可由操作系统创建,也可由用户进程创建,创建者是信箱的拥有者。据此,可把信箱分为以下三类。 ◇ 私用信箱 用户进程可为自己建立一个新信箱,并作为该进程的一部分。信箱的拥有者有权从信箱中读取消息,其他用户则只能将自己构成的消息发送到该信箱中。这种私用信箱可采用单向通信链路的信箱来实现。 当拥有该信箱的进程结束时,信箱也随之消失。◇ 公用信箱 它由操作系统创建,并提供给系统中的所有核准进程使用。核准进程既可把消息发送到该信箱中,也可从信箱中读取发送给自己的消息。显然,公用信箱应采用双向通信链路的信箱来实现。通常,公用信箱在系统运行期间始终存在。◇ 共享信箱 它由某进程创建,在创建时或创建后,指明它是可共享的,同时须指出共享进程(用户)的名字。信箱的拥有者和共享者,都有权从信箱中取走发送给自己的消息。 ◆ 发送进程和接收进程之间的关系 在利用信箱通信时,在发送进程和接收进程之间,存在以下四种关系: ◇ 一对一关系 这时可为发送进程和接收进程建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰。◇多对一关系 允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服务器交互(client/server interaction)。◇一对多关系 允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方式,向接收者(多个)发送消息。◇多对多关系 允许建立一个公用信箱,让多个进程都能向信箱中投递消息;也可从信箱中取走属于自己的消息。 3.5.3 消息传递系统实现中的若干问题 1、通信链路(communication link) ◆ 建立方式: ◇ 显式建立链路◇ 隐式建立链路 ◆ 分类: ◇ 按连接方法 ▲ 点—点连接通信链路 一条链路只连接两个结点(进程); ▲ 多点连接链路 指用一条链路连接多个(n>2)结点(进程)。◇ 按通信方式 ▲ 单向通信链路 只允许发送进程向接收进程发送消息; ▲ 双向链路 既允许由进程A向进程B发送消息,也允许进程B同时向进程A发送消息。◇ 按通信线路的容量 ▲ 无容量通信链路 在通信链路上没有缓冲区,不能暂存任何消息。 ▲ 有容量通信链路 在通信链路中设置了缓冲区,能暂存消息。缓冲区数目愈多,容量越大。 2、消息的格式(Message Format) 3、进程同步方式(Process Synchronous Mode) ◆ 发送进程阻塞、 接收进程阻塞——汇合◆ 发送进程不阻塞、 接收进程阻塞◆ 发送进程和接收进程均不阻塞 3.5.4 消息缓冲队列通信机制 1、消息缓冲队列通信机制中的数据结构 ◆ 消息缓冲区 Struct message buffer {sender; /*发送者进程标识符*/ size; /*消息长度*/ text; /消息正文*/ netxt; /指向下一个消息缓冲区的指针*/ } ◆ PCB中有关通信的数据项 在设置消息缓冲队列的同时,还应增加用于对消息队列进行操作和实现同步的信号量,并将它们置入进程的PCB中。 Struct process_control_block {… mq; /*消息队列队首指针*/ mutex; /*消息队列互斥信号量*/ sm; /*消息队列资源信号量*/ … } 2、发送原语与接受原语 发送进程在利用发送原语发送消息之前,应先在自己的内存空间,设置一发送区a,见下图所示,把待发送的消息正文、发送进程标识符、消息长度等信息填入其中,然后调用发送原语,把消息发送给目标(接收)进程。发送原语首先根据发送区a中所设置的消息长度a.size来申请一缓冲区i,接着,把发送区a中的信息复制到缓冲区i中。为了能将i挂在接收进程的消息队列mq上,应先获得接收进程的内部标识符j,然后将i挂在j.mq上。由于该队列属于临界资源,故在执行insert操作的前后,都要执行wait和signal操作。 Send(receiver,a) Receive(b) {getbu(a.size,i); {j=internal name; i.sender=a.sender; wait(j.sm; i.size=a.size; wait(j.mutex); i.text=a.text; remove(j.mq,i); i.next=0; signal(j.mutex); getid(PCB set,receiver,j); b.sender=i.sender; wait(j.mutex); b.size=i.size; insert(j.mq,i); b.text=i.text; signal(j.mutex); } signal(j.sm); 接收原语 } 发送原语