《新编操作系统习题与解析》试读:3.2 知识点2:进程通信
3.2 知识点2:进程通信
3.2.1 要点归纳
1. 什么是进程通信
操作系统内并发执行的进程可以是独立进程或协作进程。独立进程是指它不影响其他进程也不被其他进程所影响。显然不与任何其他进程共享数据的进程是独立进程。协作进程是指它影响其他进程或被其他进程所影响,显然与其他进程共享数据的进程是协作进程。
协作进程间的信息交换称为进程通信。前面介绍的进程互斥与同步就是一种进程间的通信方式。由于进程互斥与同步交换的信息量较少且效率较低(P、V操作只能传递信号,信号本身不包含任何数据),因此称这两种通信方式为低级进程通信方式,相应地也将P、V原语称为低级进程通信原语。本节介绍的进程通信为高级进程通信方式,这种通信方式是指进程之间以较高的效率传送大量数据。
2. 进程通信的类型
目前,高级进程通信方式可以分为三大类:共享存储器系统、消息传递系统以及管道通信系统。
(1)共享存储器系统
为了传输大量数据,在存储器中划出一块共享存储区,诸进程可以通过对共享存储区进行读或写来实现通信,如图3.32所示。进程在通信前,应向系统申请建立一个共享存储区,并指定该共享存储区的关键字。若该共享存储区已经建立,则将该共享存储区的描述符返回给申请者。然后,申请者把获得的共享存储区附接到本进程的地址空间上。这样,进程便可以像读写普通存储器一样地读写共享存储区。
(2)消息传递系统
在消息传递系统中,进程间的数据交换以消息为单位,程序员直接利用系统提供的一组通信命令(原语)来实现通信。操作系统隐藏了通信的实现细节,大大简化了通信程序编制的复杂性,因而获得了广泛的应用,如图3.33所示。在微内核操作系统中,微内核和服务器之间的通信都是采用消息传递机制。
图3.32 共享内存 图3.33 消息传递
(3)管道通信系统
管道是用于连接读进程和写进程以实现它们之间通信的共享文件,又称为pipe文件。它是读写进程的一个特殊文件,允许按照先来先服务的方式传送数据,也能使进程同步执行。管道是单向的,发送进程(即写进程)视管道文件为输出文件,以字符流的形式把大量数据送入管道;接收进程(即读进程)视管道文件为输入文件,从管道中接收数据,所以也称为管道通信,如图3.34所示。
管道的实质是一个共享文件,即利用辅助存储器来进行数据通信,因此管道通信基本上可借助于文件系统的机制来实现,包括管道文件的创建、打开、关闭和读写。但是写进程和读进程之间的相互协调单靠文件系统机制是解决不了的,读写进程相互同步,为此管道机制必须提供以下三方面的协调能力:
互斥。当一个进程正在对管道执行读/写操作时,其他进程必须等待。
同步。当写进程把一定数量的数据写入管道,便去睡眠等待,直到读进程取走数据后,再把它唤醒。当读进程读一空管道时,也应睡眠等待,直到写进程将数据写入管道后,才将它唤醒。
确定对方是否存在。只有确定对方存在时,才能进行通信。
图3.34 管道通信
在UNIX系统中,将管道分为有名管道和无名管道两种,前者通过相关系统调用建立有名称的文件,后者是通过相关系统调用建立的临时文件。
3.消息传递通信的实现方法
在消息传递系统中,源进程可以直接或间接地将消息传送给目标进程。所谓消息是指一组信息,通常由消息头和消息正文组成,前者包含消息在传输时所需要的控制信息,如源进程名、目标进程名、消息长度、消息类型和消息编号等,后者是发送进程实际上所发送的数据。在通信时,发送进程利用发送原语Send向接收进程发送一个消息,而接收进程则利用接收原语Receive接收来自发送进程的一个消息。发送原语的主要工作是申请分配一个消息缓冲区,然后将消息正文传送到该缓冲区中,并向缓冲区中填写消息头,再将该消息缓冲区挂到接收进程的消息链上。接收原语的主要工作是把消息链上的消息逐个读入到接收进程的接收区中并进行处理。
消息传递系统因其实现方式不同又可以分为直接通信方式和间接通信方式。
(1)直接通信方式
发送进程直接把消息发送给接收进程,并将它挂在接收进程的消息缓冲队列上,接收进程从消息缓冲队列中取得消息。在直接通信方式中,Send和Receive原语的使用格式如下:
Send(Receiver,mesaage); //发送一个消息message给接收进程Receiver
Receive(Sender,message); //接收Sender进程发送的消息message
发送进程发送消息的过程如下:
发送进程先在自己的内存空间设置一个发送区a,把发送进程自己的标识符、发送消息的长度、发送消息的正文等信息填入发送区a中。
调用发送原语,把消息发送给接收进程。
消息发送原语的处理过程如下:
根据发送区a中所设置的消息长度申请一个相应大小的消息缓冲区。
把发送区a中的信息复制到消息缓冲区中。
将消息缓冲区挂在接收进程PCB的消息队列上。由于消息缓冲区队列属于临界资源,故在插入一个消息缓冲区操作的前后,都要执行P、V操作,对该队列进行互斥访问控制。
接收进程接收消息的过程如下:
接收进程首先准备好一个存放消息的接收区b。
调用接收原语,从自己的消息队列中取下第一个消息缓冲区,并将它复制到接收区b内。
释放该消息缓冲区。
消息接收原语的处理过程如下:
查看接收消息队列中是否有消息,若没有则阻塞等待。
封锁接收者的消息链,摘下消息链中的第1个消息缓冲区后释放接收者消息链。
复制缓冲区中的内容到接收区内。
释放消息缓冲区。
两通信进程必须满足下列条件:
在发送进程把消息写入缓冲区和把缓冲区挂入消息队列时,应禁止其他进程对缓冲区消息队列的访问。同理,接收进程取消息时也禁止其他进程访问缓冲区消息队列。
当缓冲区中没有信息存在时,接收进程不能接收到任何消息。
例如,生产者-消费者的通信过程可分别描述如下:
producer() //生产者进程
{ while(true)
{ 生产一个产品nextp;
…
Send(consumer,nextp); //向消费者进程发消息
}
}
consumer() //消费者进程
{ while(true)
{ Receive(producer,nextc);//接收生产者进程的消息
…
消费nextc中的产品;
}
(2)间接通信方式
间接通信方式指进程之间的通信需要通过作为共享数据结构的实体。该实体用来暂存发送进程发给目标进程的消息;接收进程从该实体中取得对方发送给自已的消息。通常把这种中间实体称为信箱(mailbox)。消息在信箱中可以安全的保存,只允许核准的目标用户随时读取。所以这种通信方式也称为信箱通信方式,消息可被理解成信件。信箱通信方式广泛应用于计算机网络中,现在称为电子邮件系统。
发送信件过程:如果指定信箱未满,则将信件送入信箱中由指针所指示的位置,并释放等待该信箱中信件的等待者;否则发送信件者被置成等待信箱有空闲空间状态。
接收信件过程:如果指定信箱中有信,则取出一封信件,并释放等待信箱的等待者,否则接收信件者被置成等待信箱中信件的状态。
系统为信箱通信提供了若干条原语,用于信箱的创建、撤销和消息的发送、接收等。
信箱的创建和撤销。进程可利用信箱的创建原语来建立一个新信箱,创建者进程应给出信箱名、信箱属性(公用、私用或共享)。对于共享信箱,还应给出共享者的名称。当进程不再需要读信箱时,可用信箱撤销原语将其撤销。
消息的发送和接收。进程之间利用信箱进行通信时,其Send和Receive原语的使用格式如下:
Send(mailbox,mesaage); //发送一个消息message到信箱mailbox
Receive(mailbox,message); //从信箱mailbox接收一个消息message
信箱可由操作系统创建,也可由用户进程创建,创建者是信箱的拥有者。这样把信箱分为以下三类:
私用信箱。用户进程为自已建立一个新信箱,并作为该进程的一部分。信箱的拥有者有权从信箱中读取消息,其他用户则只能将自已构成的消息发送到该信箱中。当拥有该信箱的进程结束时,信箱也随之消失。
公用信箱。它由操作系统创建,并提供系统中的所有核准进程使用。核准进程既可把消息发送到该信箱中,也可从信箱中读取发送给自已的消息。通常公用信箱在系统运行期间始终存在。
共享信箱。它由某个进程创建,在创建时或创建后指明它是可共享的,同时须指出共享进程的名称。信箱的拥有者和共享者都有权从信箱中取走发送给自已的消息。
在利用信箱通信时,在发送进程和接收进程之间存在以下4种关系:
一对一关系。这时可为发送进程和接收进程建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰。
多对一关系。允许提供服务的进程与多个用户进程之间进行交互,也称为客户/服务器交互。
一对多关系。允许一个发送进程与多个接收进程之间进行交互,使发送进程可用广播方式向接收者发送消息。
多对多关系。允许建立一个公用信箱,让多个进程都能向信箱中投递消息,也可从信箱中取走属于自已的消息。
4.消息传递通信的相关问题
(1)通信链路
为使在发送进程和接收进程之间能够进行通信,必须在两者之间建立一条通信链路。通信链路是发送进程采用显式“建立连接”原语建立的,或者发送进程利用系统提供的发送原语由系统自动建立的。
根据连接方式,将通信链路分为:
点-点连接通信链路。一条通信链路只连接两个进程。
多点连接链路。一条通信链路连接多个进程。
根据通信方式,将通信链路分为:
单向通信链路。只允许发送进程向接收进程发送消息,或者相反。
双向通信链路。允许两进程间互发消息。
根据通信链路的容量,将通信链路分为:
无容量通信链路。该通信链路上没有缓冲区,不能暂停任何消息。
有容量通信链路。该通信链路上设有缓冲区,可以暂停消息。
(2)消息传递通信的进程同步方式
两个进程间的消息通信就隐含着某种程度的同步。不论发送进程还是接收进程,在完成消息的发送或接收后,都存在继续发送(接收)或阻塞两种可能性。主要有以下3种情况:
发送进程阻塞,接收进程阻塞。这种情况用于两个进程之间紧密同步(发送进程和接收进程之间无缓冲时)。平时这两个进程都处于阻塞状态,直到有消息传递时才被唤醒。
发送进程不阻塞,接收进程阻塞。这是一种应用最广的进程同步方式。由于发送进程不阻塞,所以可把一条或多条消息发送给目标进程;而接收进程通常是服务器进程,平时处于阻塞状态,直到发送进程发来消息时才被唤醒。
发送进程和接收进程均不阻塞。这也是一种较常见的进程同步方式。平时发送进程和接收进程都在忙自已的事件,仅当发生某事件使它无法继续运行时,才把自已阻塞起来等待。如发送进程和接收进程共同联系一个能容纳n条消息的消息队列,发送进程可连续发送消息至消息队列,而接收进程可连续地从消息队列获取消息,仅当消息队列中的消息数达到n条时,发送进程才会被阻塞。类似地,仅当消息队列中没有消息时,接收进程才会被阻塞。
3.2.2 例题解析
1. 单项选择题
【例3-2-1】信箱通信是一种 通信方式。
A. 直接通信 B. 间接通信 C. 低级通信 D. 信号量
解:消息传递通信有两种方式,即直接和间接通信方式,而信箱通信属于后者。本题答案为B。
【例3-2-2】在间接通信时,用Send(N,M)原语发送信件,其中N表示 。
A. 发送信件的进程名 B. 接收信件的进程名
C. 信箱名 D. 信件内容
解:Send(N,M)原语中N表示信箱,M表示发送的消息。本题答案为C。
【例3-2-3】用信箱实现进程间相互通信的通信机制要有两个通信原语,它们是 。
A. “发送”原语和“执行”原语 B. “就绪”原语和“执行”原语
C. “发送”原语和“接收”原语 D. “就绪”原语和“接收”原语
解:用信箱实现进程间相互通信的通信机制要有两个通信原语,它们是“发送”原语(Send)和“接收”原语(Receive)。本题答案为C。
【例3-2-4】在实现进程通信时,会导致调用Send原语的进程被设置成“等信箱”状态的原因是 。
A. 指定的信箱不存在 B. 调用时没有设置参数
C. 指定的信箱中无信件 D. 指定的信箱中存满了信件
解:Send原语用于发送信件,在指定的信箱满时设置成“等信箱”状态。本题答案为D。
【例3-2-5】在消息缓冲通信中,消息队列属于 资源。
A. 临界 B. 共享 C. 永久 D. 可剥夺
解:在消息缓冲通信中,消息队列属于临界资源,在消息缓冲区操作的前后,都要执行P、V操作,对该队列进行互斥访问控制。本题答案为A。
【例3-2-6】在消息缓冲通信机制中,使用的临界资源是 。
A. 信箱 B. 消息队列中的某个消息缓冲区
C. 管道 D. 整个消息队列
解:解释同上例。本题答案为D。
2. 填空题
【例3-2-7】最基本的通信原语有两条,它们是 ① 原语和 ② 原语。
解:本题答案为:①Send ②Receive。
3. 判断题
【例3-2-8】判断以下叙述的正确性。
(1)进程通信只能采用P、V操作。
(2)进程间通过信件交换信息,可实现进程同步。
(3)P、V操作可看成是进程之间的一种通信方式。
(4)进程之间采用高级通信方式时,进程间利用P、V操作来交换信息。
(5)在管道通信中,管道是一个普通的共享文件。
解:(1)错误。P、V操作属于进程低级通信,还可以采用高级通信。
(2)正确。
(3)正确。
(4)错误。
(5)错误。在管道通信中,管道是一个特殊的共享文件。
4. 问答题
【例3-2-9】进程之间有哪些基本的通信方式?它们分别有什么特点?适用于哪些场合?
解:进程通信根据交换信息量的多少分为高级通信和低级通信。低级通信一般只传送一个或几个字节的信息,以达到控制进程执行速度的作用(如P、V操作),其效率低,对用户不透明,所有的操作都必须由程序员来实现;而高级通信工具则可弥补这些缺陷,用户可直接利用操作系统所提供的一组通信命令,高效地传送大量的数据。高级通信的目的不是为了控制进程的执行速度,而是为了交换信息。
高级进程通信方式大致归为如下三类:
共享存储器。在内存中分配一片空间作为共享存储区。需要进行通信的进程把它附加到自己的地址空间中,不需要则把它取消。
管道文件。用于连接读、写进程以实现通信的共享文件。
消息传递。以消息为单位在进程间进行数据交换。
【例3-2-10】简要比较消息队列与管道通信机制不同点。
解:管道通信的管道是指用于连接读进程和写进程,以实现它们之间通信的共享文件,又称pipe文件,所以管道通信是基于文件系统的。而消息队列通信机制属于消息传递系统通信机制,存在通信链路,有消息的格式,有若干缓冲队列,采用独特的发送原语Send和接收原语Receive进行消息传递。
【例3-2-11】什么是管道?无名管道和有名管道的主要差别是什么?
解:管道是指能够连接一个写进程和一个读进程的,并允许它们以生产者-消费者方式进行通信的一个共享文件,又称为pipe文件。
无名管道是一个临时文件,是利用系统调用pipe()建立起来的临时文件,没有路径名,只有调用pipe的进程及其子孙进程才能识别此文件描述符而利用该文件(管道)进行通信;有名管道是利用mknod系统调用建立的,是可以在文件系统中长期存在的,即有路径名的文件,其他进程可以知道其存在,并利用该路径名来访问该文件。