操作系统学习笔记(day6)进程通信


先思考几个问题:

  1. 什么是进程通信?
  2. 进程间如何进行通信?
  3. 高级进程通信和低级进程通信的区别?高级通信机制有哪几类?
  4. 为什么要引入高级进程通信?

1. 进程通信概念

概念: 进程通信是指进程之间的信息交换

1.1 低级进程通信

低级进程通信: 由于进程的互斥与同步,需要在进程间交换一定的信息。

低级的原因:

  1. 效率低,生产者每次只能向缓冲池投放一个产品(消息),消费者每次只能从缓冲区中取得一个消息;
  2. 通信对用户不透明,OS只为进程之间的通信提供了共享存储器。

1.2 高级进程通信

在低级进程通信中,OS只为进程之间的通信提供了共享存储器,而关于进程之间通信所需之共享数据结构的设置、数据的传送、进程的互斥与同步,都必须由程序员去实现,显然,对于用户而言,这是非常不方便的。为了将管理进程之间传送大量数据的任务交给OS而不是程序员去处理,便引入了高级通信工具。

OS提供的高级通信工具的主要特点:

  1. 使用方便。OS隐藏了实现了进程通信的具体细节,向用户提供了一组用于实现高级通信的命令(原语),用户可方便地直接利用它实现进程之间的通信。或者说,通信过程对用户是透明的。这样就大大减少了通信程序编制上的复杂性

  2. 高效地传送大量数据。用户可直接利用高级通信命令(原语)高效地传送大量的数据。

2. 进程通信的类型

2.1 共享存储器系统(Shared-Memory System)

概念: 在共享存储器系统中,相互通信的进程共享某些数据结构共享存储区,进程之间能够通过这些空间进行通信。
据此,可以将其分为两种类型:
(1)基于共享数据结构的通信方式
在这种通信方式中,要求诸进程公用某些数据结构,借以实现诸进程间的数据交换。例如,在生产者-消费者问题中的有界缓冲区。
操作系统仅提供共享存储器,由程序员负责对公用数据结构的设置及对进程间同步的处理。这种通信方式仅适用于传递相对少量的数据,通信效率低下,属于低级通信

(2)基于共享存储区的通信方式
为了传输大量数据,在内存中划出了一块共享存储区域,诸进程可通过对该共享区的读或写操作交换信息,实现通信,数据的形式和位置甚至访问控制都是由进程负责,而不是OS。这种通信方式属于高级通信
需要通信的进程在通信前,先向系统申请获得共享存储区中的一个分区,并将其附加到自己的地址空间中,便可对其中的数据进行正常读、写,读写完成或不再需要时,将其归还给共享存储区。

2.2 管道(pipe)通信系统

概念: 所谓“管道”,是指用于连接一个读进程和一个写进程以实现它们之间通信的一个共享文件,又名pipe文件。

过程: 向管道(共享文件)提供输入的发送进程(即写进程)以字符流形式将大量的数据送入管道;而接受管道输出的接收进程(即读进程)则从管道中接收(读)数据。(能有效地传送大量数据)

为了协调双方的通信,管道机制必须提供以下三方面的协调能力:互斥、同步和确定对方是否存在。

互斥: 即当一个进程正在对pipe执行读/写操作时,其它(另一)进程必须等待。
同步: 指当写(输入)进程把一定数量(如 4KB)的数据写入pipe,便去睡眠等待,直到读(输出)进程取走数据后再把它唤醒。当读进程读一空pipe时,也应睡眠等待,直至写进程将数据写入管道后才将之唤醒。
确定对方是否存在: 只有对方已存在时才能进行通信。

2.3 消息传递系统(Message passing system)

在消息传递系统中,进程间的数据交换是以格式化的消息(message)为单位的。若通信的进程之间不存在可直接访问的共享空间,则必须利用操作系统提供的消息传递方法实现进程通信。进程通过系统提供的发送消息和接收消息两个原语进行数据交换。

举例,在计算机网络中,消息又称为报文;在微内核操作系统中,微内核与服务器之间的通信无一例外都是采用了消息传递机制;由于该机制能很好地支持多处理机系统、分布式系统和计算机网络,因此也成为这些领域最主要的通信工具。

  1. 直接通信方式,是指发送进程利用OS所提供的发送原语,直接把消息发送给目标进程;
  2. 间接通信方式,是指发送和接收进程,都通过共享中间实体(称为邮箱)的方式进行消息的发送和接收,完成进程间的通信。

* 2.4 客户机-服务器系统(Client-Server system)

客户机-服务器系统的通信机制,在网络环境的各种应用领域已成为当前主流的通信实现机制,主要的实现方法分为三类:套接字、远程过程调用和远程方法调用。

  • 套接字(Socket)
    一个套接字就是一个通信标识类型的数据结构,包含了通信目的的地址、通信使用的端口号、通信网络的传输层协议、进程所在的网络地址,以及针对客户或服务器程序提供的不同系统调用(或API函数)等,是进程通信和网络通信的基本构件。套接字是为客户/服务器模型而设计的。

    通常,套接字包括两类:
    (1)基于文件型:通信进程都运行在同一台机器的环境中,套接字是基于本地文件系统支持的。一个套接字关联到一个特殊的文件,通信双方通过对这个特殊文件的读写实现通信,其原理类似于前面所讲的管道。
    (2)基于网络型:通常采用的是非对称方式通信,即发送者需要提供接收者命名。过程: 通信双方的进程运行在不同主机的网络环境下,被分配了一对套接字,一个属于接收进程(或服务器端),一个属于发送进程(或客户端)。一般地,发送进程(或客户端)发送连接请求时,随机申请一个套接字,主机为之分配一个端口,与该套接字绑定,不再分配给其它进程。接收进程(或服务器端)拥有全局公认的套接字和指定的端口(如ftp服务器监听端口为21,Web或http服务器监听端口为80),并通过监听端口等待客户请求。接收进程(或服务器端)一旦收到请求,就接受来自发送进程(或客户端)的连接,完成连接,即在主机间传送的数据可以准确地发送到通信进程,实现进程间地通信;当通信结束时,系统通过关闭接受进程(或服务器端)的套接字撤销连接。

  • 远程过程调用和远程方法调用
    远程过程(函数)调用 RPC,是一个通信协议,用于通过网络连接的系统。该协议允许运行于一台主机(本地)系统上的进程调用另一台主机(远程)系统上的进程,而对程序员表现为常规的过程调用,无需额外地为此编程。如果涉及的软件采用面向对象编程,那么远程过程调用亦可称做远程方法调用

    负责处理远程过程调用的进程有两个:本地客户进程、远程服务器进程。
    这两个进程也被称为网络守护进程,主要负责在网络间的消息传递。
    一般情况下,两个进程都是处于阻塞状态,等待消息。

    Q:如何使远程过程调用看上去与本地过程调用一样,即希望实现RPC的透明性,使得调用者感觉不到此次调用的过程是在其他主机(远程)上执行的?
    A:RPC引入一个存根(stub) 的概念:在本地客户端,每个能够独立运行的远程过程都拥有一个客户存根(client stubborn),本地进程调用远程过程实际是调用该过程关联的存根;与此类似,在每个远程进程所在的服务器端,其所对应的实际可执行进程也存在一个服务器存根(stub)与其关联。
    本地客户存根与对应的远程服务器存根一般也是处于阻塞状态,等待消息。
    Q:远程过程调用的主要步骤?
    A:(1)本地过程调用者以一般方式调用远程过程在本地关联的客户存根,传递相应的参数,然后将控制权转移给客户存根;
    (2)客户存根执行,完成包括过程名和调用参数等信息的消息建立,将控制权转移给本地客户进程;
    (3)本地客户进程完成于服务器的消息传递,将消息发送到远程服务器进程;
    (4)远程服务器进程接受消息后转入执行,并根据其中的远程过程名找到对应的服务器存根,将消息转给该存根。
    (5)该服务器存根接到消息后,由阻塞状态转入执行状态,拆开消息从中取出过程调用的参数,然后以一般方式调用服务器上关联的过程;
    (6)在服务器端的远程过程运行完毕后,将结果返回给与之关联的服务器存根;
    (7)该服务器存根获得控制权运行,将结果打包为消息,并将控制权转移给远程服务器进程;
    (8)远程服务器进程将消息发送回客户端;
    (9)本地客户进程接收到消息后,根据其中的过程名将消息存入关联的客户存根,再将控制权转移给客户存根;
    (10)客户存根从消息中取出结果,返回给本地调用者进程,并完成控制权的转移。

消息传递通信的实现方式

分为直接间接两种通信方式。
如,直接消息系统、信箱通信。

  1. 直接消息传递系统
    在直接消息传递系统中采用直接通信方式,即发送进程利用OS所提供的发送命令(原语),直接把消息发送给目标进程。
    1)直接通信原语
    (1)对称寻址方式。该方式要求发送进程和接收进程都必须以显示方式提供对方的标识符。
    (2)非对称寻址方式 。 在某些情况下,接收进程可能需要与多个发送进程通信,无法事先指定发送进程。
    2)消息的格式
    在消息传递系统中所有的消息,必须具有一定的消息格式。
    3)进程的同步方式
    引入进程同步机制,是为了进程间能协调通信。
    不论是发送进程还是接收进程,在完成消息的发送或接收后,都存在两种可能性,即进程或者继续发送(或接收)或者阻塞。
    由此,我们可以得到三种情况:
    ①发送进程阻塞,接收进程阻塞。这种情况主要用于进程之间紧密同步,发送进程和接收进程之间无缓冲时。
    ②发送进程不阻塞、接收进程阻塞。这是一种应用最广的进程同步方式。平时,发送进程不阻塞,因而它可以尽快地把一个或多个消息发送给多个目标;而接收进程平时处于阻塞状态,直到发送进程发来消息时才被唤醒。
    ③发送进程和接收进程均不阻塞。这也是一种较常见地进程同步形式。平时,发送进程和接收进程都在忙于自己的事情,仅当发生某件事件使它无法继续运行时,才把自己阻塞起来等待。
    4)通信链路
    为使在发送进程和接收进程之间能进行通信,必须在两者之间建立一条通信链路。
    建立通信链路的两种方式:
    ①由发送进程在通信之前用显式的“建立连接”命令(原语)请求系统为之建立一条通信链路,在链路使用完后拆除链路。这种方式主要用于计算机网络中。
    ②发送进程无须明确提出建立链路的请求,只须利用系统提供的发送命令(原语),系统会自动地为之建立一条链路。这种方法主要用于单机系统中。
    根据通信方式的不同: 又可把链路分成两种:
    ①单向通信链路,只允许发送进程向接收进程发送消息,或者相反;
    ②双向通信链路,既允许由进程A向进程B发送消息,也允许进程B同时向进程A发送消息。
  2. 信箱通信
    信箱通信属于间接通信,即进程之间的通信,需要通过某种中间实体(如共享数据结构等)来完成。该实体建立在随机存储器的公用缓冲区上,用来暂存发送进程发送给目标进程的消息;接收进程可以从该实体中取出发送进程发送给自己的消息,通常把这种中间实体称为邮箱(或信箱),每个邮箱都有一个唯一的标识符。消息在邮箱中可以安全地保存,只允许核准的目标用户随机读取。
    1)信箱的结构
    信箱定义为一种数据结构。在逻辑上,可以将其分为两个部分:
    (1)信箱头,用以存放有关信箱的描述信息,如信箱标识符、信箱的拥有者、信箱口令和信箱的空格数等;
    (2)信箱体,由若干个可以存放消息(或消息头)的信箱格组成,信箱格的数目以及每格的大小是在创建信箱时确定的。
    2)信箱通信原语
    系统为邮箱通信提供了若干条原语,分别用于:
    (1)邮箱的创建和撤销。进程可利用邮箱创建原语来创建一个新邮箱,创建者进程应给出邮箱名字、邮箱属性(公用、私用或共享);对于共享邮箱,还应该给出共享者的名字。当进程不再需要读邮箱时,可用邮箱撤销原语将之撤销。
    (2)消息的发送和接收。当进程之间要利用邮箱进行通信时,必须使用共享邮箱,并利用提供的下述通信原语进行通信。
    Send(mailbox,message); 将一个消息发送到指定邮箱
    Receive(mailbax,message);从指定邮箱中接收一个消息
    3)信箱的类型
    邮箱可由操作系统创建,也可由用户进程创建。因此,可把邮箱分为以下三类:
    (1)私用邮箱。用户进程可为自己建立一个新邮箱,并作为该进程的一部分。邮箱的拥有者有权从邮箱中读取信息,其他用户则只能将自己构成的消息发送到该邮箱中。这种私用邮箱可采用单向通信链路的邮箱来实现。当拥有该邮箱的进程结束时,邮箱也随之消失。
    (2)公用邮箱。由操作系统创建,并提供给系统中的所有核准进程使用。核准进程既可把消息发送到该邮箱中,也可从邮箱中读取发送给自己的消息。显然,公用邮箱应采用双向通信链路的邮箱来实现。通常,公用邮箱在系统运行期间始终存在。
    (3)共享邮箱。由某进程创建,在创建时或创建后指明它是共享的,同时须指出共享进程(用户)的名字。邮箱的拥有者和共享者都有权从邮箱中取走发送给自己的消息。
    在利用邮箱通信时,在发送进程和接收进程之间,存在以下四种关系:①一对一关系。发送进程和接收进程可以建立一条两者专用的通信链路,使两者之间的交互不受其他进程的干扰。②多对一关系。允许提供服务的进程与多个用户之间进行交互,也称为客户/服务器交互。③一对多关系。允许一个发送进程与多个接收进程进行交互,使发送进程可用广播方式向接收者(多个)发送消息。④多对多关系。允许建立一个公用邮箱,让多个进程都能向邮箱中投递消息;也可从邮箱中取走属于自己的消息。

直接传递消息系统实例

在消息缓冲队列通信机制中,发送进程利用Send原语将消息直接发送给接收进程;接收进程则利用Receive原语接收信息。

  1. 消息缓冲队列通信机制中的数据结构

(1)消息缓冲区。在消息缓冲队列通信方式中,主要利用的数据结构是消息缓冲区。它可描述如下:

typedef struct message_buffer {
	int sender;								//发送者进程标识符
	int size;								//消息长度
	char* text;								//消息正文
	struct message_buffer *next;			//指向下一个消息缓冲区的指针
}

(2)PCB中有关通信的数据项。在操作系统中采用了消息缓冲队列通信机制时,除了需要为进程设置消息缓冲队列外,还应在进程的PCB中增加消息队列队首指针,用于对消息队列进行操作,以及用于实现同步的互斥信号量mutex资源信号量sm。在PCB中应增加的数据项可描述如下:

typedef struct processcontrol_block {
	...
	struct message_buffer *mq;			//消息队列队首指针
	semaphore mutex;					//消息队列互斥信号量
	semaphore sm;						//消息队列资源信号量
	...
}PCB;
  1. 发送原语

发送进程在利用发送原语发送消息之前,应先在自己的内存空间设置一发送区a,并把带发送的消息正文、发送进程标识符、消息长度等信息填入其中,然后调用发送原语,把消息发送给目标(接收)进程。发送原语首先根据发送区a中所设置的消息程度a.size来申请一缓冲区i,接着,把发送去a中的信息复制到缓冲区i中。为了能将i挂在接收进程的消息队列mq上,应先获得接收进程的内部标识符j,然后将i挂在j.mq上。由于该队列属于临界资源,故在执行insert操作的前后都要执行wait和signal操作。

发送原语描述:

void send(receive,a){				//receive 为接送进程标识符, a 为发送区首址
	getbuf(a.size,i);				//根据a.size申请缓冲区
	i.sender = a.sender;
	i.size = a.size;
	copy(i.text, a.text);			//将发送区a中的信息复制到消息缓冲区i中
	i.next = 0;
	getid(PCBset,receive.j);		//获得接收进程内部的标识符;
	wait(j.mutex);
	insert(&j.mq,i);				//将消息缓冲区插入消息队列;
	signal(j.mutex);
	signal(j.sm);
}
  1. 接收原语
    接收进程调用接收原语recevie(b),从自己的消息队列mq中摘下第一个消息缓冲区i,并将其中的数据复制到以b为首址的指定消息接收区内。接收原语描述如下:
void receive(b){
	j = internal name;						//j为接收进程内部的标识符
	wait(j.sm);								
	wait(j.mutex);
	remove(j.mq,i);							//将消息队列中第一个消息移出
	signal(j.mutex);
	b.sender = i.sender;
	b.size = a.size;
	copy(b.text,i.text);					//将消息缓冲区i中的信息复制到接收区b
	releasebuf(i);							//释放消息缓冲区
}
  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值