转自:http://blog.csdn.net/Xiejingfa/article/details/50819660
进程间通信,英文又称作IPC(InterProcess Communication)就是在不同的进程之间交换信息。我们知道,进程的用户是相空间互独立的,不能相互访问,所以为了使得进程间可以相互通信,操作系统需要提供一种介质使通信双方都可以访问。类似这样的介质有很多,比如磁盘上的文件,我们只要协调好进程访问文件的顺序,一个进程先“写”,一个进程后“读”,这样,两者就可以相互交换信息了。又比如我们经常使用的数据库,也可以作为信息交换的媒介。但是这些通信手段效率都太低了,需要在系统级别提供高效的进程间通信手段。今天我们就来介绍一种最古老的进程间通信方式 – 管道。
1、什么是管道?
如果你熟悉Linux系统,一定不会对管道感到陌生。在Linux系统中,我们经常通过符号“|”来使用管道,用以连接两个或多个命令。那管道是什么?实际上,管道是进程与进程间的数据流通道,它使得数据可以以一种“流”的形式在进程间流动。管道也是Unix/Linux系统中一种最常见的进程间通信方式,它在两个通信进程之间实现一个数据流的通道从而进行信息传递。管道通信具有以下特点:
1,管道是没有名字的,因此有称为匿名管道。它在系统中是没有实名的,不能在文件系统中看到管道。
2,在大多数系统中,管道通信是半双工的,也就是说数据只能在一个方向上流动。
3,管道中传送的是无格式的字节流。
4,管道的缓冲区是有限的,它的大小在include/linux/limits.h中由PIPE_BUF定义。
5,管道只能用于具有亲缘关系的进程间进行通信,如父子进程或者兄弟进程。
2、管道的创建
Linux下管道是通过pipe函数创建的,函数原型如下:
#include <unistd.h>
intpipe(intpipefd[2]);// 若成功则返回0,出错则返回-1
其中参数pipefd[2]是一个长度为2的文件描述符数组,pipefd[0]是读端,pipefd[1]是写端。
管道的关闭则使用close函数。
#include <unistd.h>
intclose(intfd);
管道关闭时,需要将pipefd[0]和pipefd[1]都关闭掉。
我们先用一段代码演示一下管道的创建:
在上面的例子中,我们使用pipe()函数创建了一个管道。到这里你可能会感到奇怪:不是说管道是进程间通信的方式吗?在单个进程中创建管道,然后从一段写入从另一端读出有什么意义?下面我们重点讲解一下管道如何实现进程间通信。
3、管道的读写
正如你所想的,在单个进程中的创建管道几乎没有意义。前面我们说过管道只能用于父子进程或者兄弟进程间进行通信,通常我们先创建pipe,接着调用fork函数,从而创建从父子进程或者兄弟进程间的ipc通道。
为什么这样就能创建进程通信的管道呢?其中的功臣就是fork函数。fork函数创建出来的子进程是父进程的一个拷贝,子进程从父进程中得到数据段和堆栈段的拷贝。这样,父子都拥有事先创建管道的文件描述符,都可以往管道中读写信息。一般用于文件的IO函数如read、write等都可以用于管道的读写。
经过fork函数调用之后,父子进程间存在两种数据流向的管道:一个是从父进程到子进程的数据通道,另一个是从子进程到父进程的数据通道。管道只支持半双工通信,所以我们必须关闭其中一个数据通道!!
比如:如果你想建立一个父进程到子进程的数据通道,就要在父进程中关闭读端,在子进程中关闭写端。
通常,利用pipe和fork的组合,我们可以构造父子进程或者兄弟进程间任意方向的的数据通道。
上面的例子演示了利用管道在父进程到子进程的通信,而管道在兄弟进程间通信则需要在父进程调用两次fork函数创建两个子进程,在两个兄弟子进程中维护通信管道。
4、管道读写的规则
管道写入的规则:程序向管道写入数据时,如果缓冲区有空闲空间,写进程立即向管道写入数据。如果缓冲区已满,那么写操作就一直阻塞到读进程读走数据为止。
管道读取的规则:如果请求的字节数大于PIPE_SIZE,则返回管道中现有的字节数。如果请求的字节数不大于PIPE_SIZE,则返回请求的字节数或现有数据的字节数(管道中的数据量小于请求的数据量)。
如果读写一端已经被关闭的管道是呢?
如果读一个写端已经被关闭的管道时,当所有的数据都被读取后,read返回0,表示文件结束。
如果写一个读端已经被关闭的管道,则产生SIGPIPE信号。如果忽略该信号或从处理程序中返回后,write函数返回-1,并且errno被设置为EPIPE。