进程通信是指进程之间的信息交换。
由于进程的互斥与同步,需要在进程间交换一定的信息,不少学者也将它们归为进程通信,但只能把它们称为低级进程通信。它们之所以低级的原因在于:
在进程之间需要传送大量数据时,应当利用OS提供的高级通信工具,它具有以下特点:
1)、基于共享数据结构的通信方式。
这种方式主要是通过进程间共用的某些数据结构,借以实现各进程间的信息交换,适用于传递相对少量的数据,通信效率低下,属于低级通信。例如:在生产者-消费者问题中的有界缓冲区。
2)、基于共享存储区的通信方式。
这种方式是为了传输大量数据,一个进程在内存中划出一片共享区域,其他进程把该片内存映射到自己的地址空间,在对鞋这片空间时,就是在和其他进程进行通信。属于高级通信。
管道是一个线性字节数组,使用文件读写的方式进行访问。在创建管道时必须要提供目标进程,同时在读写管道时OS也需要进行协调:
1)、互斥,当有进程在进行读写操作时,其他进程必须等待。
2)、同步,写满管道或管道中有数据未被读出时,写进程等待,直到数据被读走;在管道中无数据时读进程等待,直到写进程将数据写入管道。
消息传递机制中,进程不必借助任何共享存储区或数据结构,而是以格式化的消息为单位,将通信的数据封装在消息中,并利用操作系统提供的通信命令在进程间进行传递,完成数据的交换。根据其实现方式,可分为以下两类:
1)、直接通信方式,发送进程利用OS提供的发送原语,直接把消息发给目标进程。
2)、间接通信方式,发送和接收进程都通过共享中间实体(称为邮箱)的方式进行消息的发送和接收。
客户机-服务器系统的通信机制,是当前网络环境的各个领域的主流通信实现机制,主要实现方法有以下三类:
1)、套接字(Socket)
一个套接字就是一个通信标识类型的数据结构,是为C/S模型而设计的。通常,套接字包括两类:
基于文件型:通信进程都运行在同一台机器中,套接字基于本地文件系统支持,一个套接字关联到一个特殊的文件,其原理类似管道。
基于网络型:该类型通常采用的是非对称方式通信,即发送者要提高接收者命名,可在不同的主机环境下进行。
2)、远程过程调用和远程方法调用
这两类方法都是通信协议,即通过网络连接,允许运行于一台主机系统上的进程调用另一台主机上的进程,对用户的表现就是常规的过程调用。它们的区别就是涉及的软件采用的是面向过程还是面向对象的编程方式。
管道通常指的是无名管道,是UNIX系统IPC最古老的形式。
从根本上说,管道是通过一个线性字节数组,类似文件,使用文件读写的方式进行访问,但却不是文件。
创建管道在命令行下和程序里是不同的。
在命令行下,只需要使用符号|
即可。如在Linux命令行下,我可以键入如下命令:
sort < a.txt | grep val
在两个命令“排序(sort)”和“查找(grep)”之间创建一个管道,数据从sort流向grep,sort的结果作为grep的输入。
上述命令行的意思是:对a.txt的内容进行排序,排序的结构作为grep的输入,在结果里找出所有包含字符串val的文本行。
在程序里创建管道需要使用系统调用popen()或者pipe()。popen需要一个目标进程作为参数,然后再调用该函数的进程和给出的目标进程之间创建一个管道,就像打电话时必须提供对方号码才能创建连接一样。
管道的一个重要特点是使用管道的两个进程间必须存在某种关系,例如,使用popen需要提供另一端进程的文件名,使用pipe的两个进程是父子进程。
管道详细介绍及Linux环境编程实例:Linux进程间通信之管道编程实例
有名管道可以在两个不相干的进程间进行管道通信。
有名管道,顾名思义就是一个有名字的通信管道,有名管道和文件系统共享一个命名空间,也就是有名管道的名字不能与文件系统里的任何文件名重复。
有名管道的名称由两部分组成:计算机名和管道名。
对于同一主机来讲运行由多个同一命名管道的实例并且可以由不同的进程打开,但是不同的管道都有属于自己的管道缓冲区和通信环境,互不影响。
有名管道可以支持多个客户端连接一个服务器端,命名管道客户端不但可以和本届上的服务器通信,也可以同主机上的服务器通信。
管道和有名管道虽然简单,无需特殊设计就可以和另外一个进程进行通信的优点,但缺点也很明显。首先管道和有名管道并不是所有操作系统都支持的,这样在其他操作系统上进行通信,管道机制就多半会力不从心。其次,管道通信需要在相关的进程间通信,或者需要直到按名字来打开,而这在某些时候会十分不便。
有名管道详细介绍及Linux环境编程实例:Linux进程间通信之有名管道编程
套接字的功能非常强大,可以支持不同层面、不同应用、跨网络的通信。
使用套接字进行通信需要双方均建立一个套接字,其中一方作为服务器方,另一方作为客户方。
之后客户端和服务器就可以通过send和recv命令在创建的通道上进行交流了。
需要注意,一般服务器套接字不收发数据,仅监听处理来自客户端的连接请求。每当有一个客户端连接请求时,服务器端就创建一个客户套接字,一旦客户套接字成功创建,与客户端通信的任务就交给这个刚刚创建的客户套接字,服务器套接字将继续监听处理客户端的连接请求。
套接字按照传送媒介是否为本地,可分为本地套接字和网域套接字。而网域套接字又按照其提供的数据传输特性分为几个大类:
管道和套接字的通信方式,首先需在实现通信的进程间建立连接,一旦建立,我们就想进行尽可能多的通信。而如果通信的信息量微小,如我们只是想通知某一个进程某个事件的发生,建立连接非常耗时,效率非常低下。其次,通信是自愿的,即乙方可以随意发送数据,对方却可以选择接收时机,即使对方充耳不闻,你也无可奈何。
为了解决上述存在的问题,我们就需要一种不同的机制来处理如下的通信需求:
信号机制就是为了应付上述需求。
在计算机里,信号就是一个内核对象,或者说一个内核数据结构。发送方将该数据结构的内容填好,并指明该信号的目标进程后,发出特定的软件中断。操作系统接受到特定的中断请求后,知道是有进程要发送信号,于是到特定的内核数据结构里找信号的接受方,并进行通知。接到通知的进程则对信号进行相应处理。
信号详细介绍及Linux环境编程示例:Linux进程间通信之信号
信号量主要应用在同步机制中。
在计算机里,信号量实际上就是一个简单整数。一个进程在信号变为0或者1的情况下推进,并且将信号变为1或者0来防止别的进程推进。当进程完成任务后,则将信号再改变为0或1,从而允许其他进程执行。
Linux环境信号量详细介绍及程序演示:Linux进程间通信之信号量
管道、套接字、信号、信号量虽然满足了多种通信需要,但还有一种需求未能满足,就是在两个进程需要共享大量数据。共享内存就是为了解决这一需求。
共享内存就是两个进程共同拥有同一片内存。这片内存中的任何内容二者均可访问。
要使用共享内存进行通信,一个进程首先创建一片内存空间专门作为通信用,而其他进程则将这片内存映射到自己的地址空间。这样在读写自己地址空间中对应的共享内存区域时,就是在和其他进程进行通信。
乍一看共享内存有点像管道,这是形似神不似。
首先,使用共享内存机制通信的两个进程必须在同一台物理机器上;其次,共享内存的访问是随机的,而不是只能从一端读,另一端写。
因此共享内存的灵活性比管道和套接字好,能够传递的数据复杂得多。
共享内存的缺点是管理复杂,且两个进程必须在同一台物理机器上才能使用,另外就是安全性脆弱。因为两个进程存在一片共享的内存,如果一个进程染色病毒,很容易就会传染给另一个进程。
Linux环境共享内存详细介绍及程序演示:Linux进程间通信之共享内存
消息队列是一列具有头和尾的消息排列。新来的消息放在队列尾部,而读取消息则从队列头部开始,如图6-12所示。
乍一看,这不是管道吗?一头儿读、一头儿写?没错。这的确看上去像管道。但它不是管道。首先它无需固定的读写进程,任何进程都可以读写(当然是有权限的进程)。其次,它可以同时支持多个进程,多个进程可以读写消息队列。即所谓的多对多,而不是管道的点对点。另外,消息队列只在内存中实现。
最后,它并不是只在UNIX和类UNIX操作系统实现。几乎所有主流操作系统都支持消息队列。