转载:
作者: 自由飞天 发表日期: 2000-06-19 10:59:52 返回《黑客也是侠》 快速返回
进程间通信与应用程序间通信及其实现技术
摘 要本文讨论了进程间通信与应用程序间通信的含义及相应的实现技术,并对这些技术的原理、特性等进行了深入的分析和比较。
---- 关键词 信号 管道 消息队列 共享存储段 信号灯 远程过程调用 Socket套接字 MQSeries
1 引言
---- 进程间通信的主要目的是实现同一计算机系统内部的相互协作的进程之间的数据共享与信息交换,由于这些进程处于同一软件和硬件环境下,利用操作系统提供的的编程接口,用户可以方便地在程序中实现这种通信;应用程序间通信的主要目的是实现不同计算机系统中的相互协作的应用程序之间的数据共享与信息交换,由于应用程序分别运行在不同计算机系统中,它们之间要通过网络之间的协议才能实现数据共享与信息交换。进程间通信和应用程序间通信及相应的实现技术有许多相同之处,也各有自己的特色。即使是同一类型的通信也有多种的实现方法,以适应不同情况的需要。
---- 为了充分认识和掌握这两种通信及相应的实现技术,本文将就以下几个方面对这两种通信进行深入的讨论:问题的由来、解决问题的策略和方法、每种方法的工作原理和实现、每种实现方法的特点和适用的范围等。
2 进程间的通信及其实现技术
---- 用户提交给计算机的任务最终都是通过一个个的进程来完成的。在一组并发进程中的任何两个进程之间,如果都不存在公共变量,则称该组进程为不相交的。在不相交的进程组中,每个进程都独立于其它进程,它的运行环境与顺序程序一样,而且它的运行环境也不为别的进程所改变。运行的结果是确定的,不会发生与时间相关的错误。
---- 但是,在实际中,并发进程的各个进程之间并不是完全互相独立的,它们之间往往存在着相互制约的关系。进程之间的相互制约关系表现为两种方式:
---- (1) 间接相互制约:共享CPU
---- (2) 直接相互制约:竞争和协作
---- 竞争——进程对共享资源的竞争。为保证进程互斥地访问共享资源,各进程必须互斥地进入各自的临界段。
---- 协作——进程之间交换数据。为完成一个共同任务而同时运行的一组进程称为同组进程,它们之间必须交换数据,以达到协作完成任务的目的,交换数据可以通知对方可以做某事或者委托对方做某事。
---- 共享CPU问题由操作系统的进程调度来实现,进程间的竞争和协作由进程间的通信来完成。进程间的通信一般由操作系统提供编程接口,由程序员在程序中实现。UNIX在这个方面可以说最具特色,它提供了一整套进程间的数据共享与信息交换的处理方法——进程通信机制(IPC)。因此,我们就以UNIX为例来分析进程间通信的各种实现技术。
---- 在UNIX中,文件(File)、信号(Signal)、无名管道(Unnamed Pipes)、有名管道(FIFOs)是传统IPC功能;新的IPC功能包括消息队列(Message queues)、共享存储段(Shared memory segment)和信号灯(Semapores)。
---- (1) 信号
---- 信号机制是UNIX为进程中断处理而设置的。它只是一组预定义的值,因此不能用于信息交换,仅用于进程中断控制。例如在发生浮点错、非法内存访问、执行无效指令、某些按键(如ctrl-c、del等)等都会产生一个信号,操作系统就会调用有关的系统调用或用户定义的处理过程来处理。
---- 信号处理的系统调用是signal,调用形式是:
---- signal(signalno,action)
---- 其中,signalno是规定信号编号的值,action指明当特定的信号发生时所执行的动作。
---- (2) 无名管道和有名管道
---- 无名管道实际上是内存中的一个临时存储区,它由系统安全控制,并且独立于创建它的进程的内存区。管道对数据采用先进先出方式管理,并严格按顺序操作,例如不能对管道进行搜索,管道中的信息只能读一次。
---- 无名管道只能用于两个相互协作的进程之间的通信,并且访问无名管道的进程必须有共同的祖先。
---- 系统提供了许多标准管道库函数,如:
pipe()——打开一个可以读写的管道;
close()——关闭相应的管道;
read()——从管道中读取字符;
write()——向管道中写入字符;
---- 有名管道的操作和无名管道类似,不同的地方在于使用有名管道的进程不需要具有共同的祖先,其它进程,只要知道该管道的名字,就可以访问它。管道非常适合进程之间快速交换信息。
---- (3) 消息队列(MQ)
---- 消息队列是内存中独立于生成它的进程的一段存储区,一旦创建消息队列,任何进程,只要具有正确的的访问权限,都可以访问消息队列,消息队列非常适合于在进程间交换短信息。
---- 消息队列的每条消息由类型编号来分类,这样接收进程可以选择读取特定的消息类型——这一点与管道不同。消息队列在创建后将一直存在,直到使用msgctl系统调用或iqcrm -q命令删除它为止。
---- 系统提供了许多有关创建、使用和管理消息队列的系统调用,如:
---- int msgget(key,flag)——创建一个具有flag权限的MQ及其相应的结构,并返回一个唯一的正整数msqid(MQ的标识符);
---- int msgsnd(msqid,msgp,msgsz,msgtyp,flag)——向队列中发送信息;
---- int msgrcv(msqid,cmd,buf)——从队列中接收信息;
---- int msgctl(msqid,cmd,buf)——对MQ的控制操作;
---- (4) 共享存储段(SM)
---- 共享存储段是主存的一部分,它由一个或多个独立的进程共享。各进程的数据段与共享存储段相关联,对每个进程来说,共享存储段有不同的虚拟地址。系统提供的有关SM的系统调用有:
---- int shmget(key,size,flag)——创建大小为size的SM段,其相应的数据结构名为key,并返回共享内存区的标识符shmid;
---- char shmat(shmid,address,flag)——将当前进程数据段的地址赋给shmget所返回的名为shmid的SM段;
---- int shmdr(address)——从进程地址空间删除SM段;
---- int shmctl (shmid,cmd,buf)——对SM的控制操作;
---- SM的大小只受主存限制,SM段的访问及进程间的信息交换可以通过同步读写来完成。同步通常由信号灯来实现。SM非常适合进程之间大量数据的共享。
---- (5) 信号灯
---- 在UNIX中,信号灯是一组进程共享的数据结构,当几个进程竞争同一资源时(文件、共享内存或消息队列等),它们的操作便由信号灯来同步,以防止互相干扰。
---- 信号灯保证了某一时刻只有一个进程访问某一临界资源,所有请求该资源的其它进程都将被挂起,一旦该资源得到释放,系统才允许其它进程访问该资源。信号灯通常配对使用,以便实现资源的加锁和解锁。
---- 进程间通信的实现技术的特点是:操作系统提供实现机制和编程接口,由用户在程序中实现,保证进程间可以进行快速的信息交换和大量数据的共享。但是,上述方式主要适合在同一台计算机系统内部的进程之间的通信。
3 应用程序间的通信及其实现技术
---- 同进程之间的相互制约一样,不同的应用程序之间也存在竞争和协作的关系。UNIX操作系统也提供一些可用于应用程序之间实现数据共享与信息交换的编程接口,程序员可以通过自己编程来实现。如远程过程调用和基于TCP/IP协议的套接字(Socket)编程。但是,相对普通程序员来说,它们涉及的技术比较深,编程也比较复杂,实现起来困难较大。
---- 于是,一种新的技术应运而生——通过将有关通信的细节完全掩盖在某个独立软件内部,即底层的通讯工作和相应的维护管理工作由该软件内部来实现,用户只需要将通信任务提交给该软件去完成,而不必理会它的具体工作过程——这就是所谓的中间件技术。
---- 我们在这里分别讨论这三种常用的应用程序间通信的实现技术——远程过程调用、会话编程技术和MQSeries消息队列技术。其中远程过程调用和会话编程属于比较低级的方式,程序员参与的程度较深,而MQSeries消息队列则属于比较高级的方式,即中间件方式,程序员参与的程度较浅。
---- 4.1 远程过程调用(RPC)
---- 远程过程调用是按下述方式工作的:当一个应用程序A需要与远程的另一个应用程序B交换信息或要求B提供协助时,A将在本地产生一个请求,通过通讯链路,通知B接收信息或提供相应的服务,B完成相关处理后将确认信息或结果返回给A。
---- RPC机制强调通信的两个应用程序所处的环境和平台中必须是相同的,而且必须同时处于运行状态。做远程调用时,两者必须先建立连接,而且通讯链路质量对它的效果影响很大。
---- RPC的优点是应用程序采用调用/返回方式通讯,拥有很高的潜在效率,但需要应用程序间的紧密藕合,通讯线路必须在通信期间一直保持良好的状态,而且必须进行大量的底层通讯的编程工作。
---- 4.2 会话编程
---- 会话编程类似于人们打电话,拨号——接通——说话——对方回答——挂机。基于TCP/IP协议的Socket编程就是一种典型的会话编程方式。它可适用于客户/服务通信方式,还能适用于点——点通信方式。
---- 下面,我们分别介绍服务器端和客户端的具体任务。
---- (1) 服务器端
---- 服务进程首先创建一个套接口,使用Socket()调用;然后,将该套接口与本机的IP地址和某一空闲端口相关联,使用Bind()调用;这时,服务端就可以用Listen()调用来侦听来自客户程序的数据;套接口一旦处于听模式,服务进程将可以接收一个连接,并允许传递数据,使用Accept()调用来完成;最后使用Read()调用来读入数据,同时,还可以用Write()调用来向发送进程写回一些数据,如确认信息或回显信息。
客户端
---- 客户进程也是首先创建一个套接口,使用Socket()调用;然后,客户进程就使用Connect()调用试图连接一个服务;连接成功之后,就可以利用Write()调用向服务器发送数据,同时,还可以使用Read()调用读取服务器写回的数据。
---- 目前的网络一般都支持TCP/IP协议,UNIX和WINDOWS也都提供相应的编程接口,用户可以随心所欲地编制出合乎自己要求的通信程序。现行大多数的应用程序间的通信采取的就是这种方式。但是,这种Socket编程技术,要求程序员必须熟悉相关概念,自己设计控制流程,客户和服务进程必须相互配合且必须都处于运行状态,技术上有一定的难度。
---- 4.3 MQSeries消息队列
---- 为了简化应用程序间的通信,使得通信既具有较高的可靠性,又保证实现的简单性,我们希望能有一种独立的通信软件,应用程序只需将任务提交给该软件,由该软件自动去完成信息的传递工作,这即是我们前面提到的中间件技术。IBM公司的MQSeries就是基于这种技术的商业化产品。
---- 应用程序A和B位于同一计算机,而应用程序C位于远程的其它计算机系统中。当应用程序A需要和B通讯时,它通过调用MQSeries接口将消息放入队列Q1,应用程序B在适当的时候读取该消息,或消息本身到达后唤醒应用程序B。当应用程序A需要和C通讯时,它通过相同的方式将消息放入队列Q2,应用程序C在适当的时候读取该消息。
---- 应用程序之间的消息传递是通过队列来实现的,是间接的。由于不存在直接连接,C关闭时A仍然能正常运行,不仅如此,当C不在运行时,消息还可以触发该程序。
---- MQSeries优点可以确保信息是永久的、可恢复的;确保信息成功发送且仅有一次发送,可以支持关键业务,如证券交易信息的传递;确保信息传递是保密的;同时,使用MQSeries,不需要应用程序和通讯介质以及远程应用程序之间的耦合,也不需要应用程序同时运行。MQSeries是应用程序间通信的首选技术。
---- MQSeries接口提供的调用主要有:
---- MQCONN——连接一个队列管理器,以后它发送和读入的消息的所有消息都由这个队列管理器管理;
---- MQOPEN——打开该应用程序所连接的队列;
---- MQPUT——将消息写入已打开的队列中;
---- MQGET——从该队列中读出消息;
---- MQINQ——获得关于队列的属性;
---- MQCLOSE——关闭队列(对队列执行完所有操作后);
---- MQPUT1——它执行三个操作,先调用MQOPEN打开队列,然后调用MQPUT写入一条消息,最后调用MQCLOSE关闭队列;
---- MQDISC——断开和队列管理器的连接(对队列管理器的所有操作完成后);
---- 4.4 三种实现技术的特性比较
表1清楚地列出了RPC、 Socket编程、MQSeries的不同特性。
比较项目 Socket编程 RPC MQSeries
属性 会话 远程调用 消息队列
类型 会话 调用/返回 队列
编程接口 非阻塞 阻塞 非阻塞
通信对方运行 是 是 否
应用程序类型 面向连接 面向连接 无连接
数据流模式 点-点,客户机/服务器 客户机/服务器 所有模式
逻辑路由 否 否 是
永久数据 否 否 是
表1 三种中间件的特性比较
4 结束语
---- 各种进程间通信和应用程序间通信的实现技术都具有自己的特点和使用范围。管道、消息队列、共享内存等技术最适用于同一计算机系统内部的进程间通信,以保证高效率。而远程过程调用、Socket会话编程、MQSeries则最适用于远程的应用程序之间通信,可以简化通信的编程,当然也保证通信的可靠性。尤其是MQSeries,它是一个比较完善的中间件产品,为许多的信息系统所选用。如我公司的帐务系统与各金融系统的话费信息的交换选择的就是MQSeries。有时,在一个信息系统里面,既存在进程间通信的需求,也存在应用程序间通信的需求,这时就必须分别选择两种不同的实现技术。因此,在实际信息系统建设的过程中,我们在选择哪种实现技术时,应根据信息系统的不同情况和不同需求,根据系统开发和维护的成本,选择一种或是几种实现技术,以求得整个系统的优化。