对于进程间的通信,我在除了fork和exec系列函数和文件系统,在这里将要介绍进程间通信的其它一些方法,在这里将讨论的是管道、FIFO、消息队列、以及信号量和共享存储器
管道PIPE
管道是所有UNIX都提供此种通信机制,是一种很古老的技术,但是仍然有很大用处,在历史上管道都是半双工的,虽然现在有些系统已经实现了全双工,但是为了移植性,我们一般认为是半双工的;pipe技术只能实现的是有共同祖先的进程之间的传递,因为pipe是匿名的,在后面我们讲到的FIFO就可实现多个不相关的进程之间的通信。
在参数中fd[0]表示的是以读打开,而fd[1]表示的是以写打开。fd[1]的输出是fd[0]的输入。#include <unistd.h> int pipe( int fd[2]);
对于PIPE的用法一般是父进程调用pipe()函数,fork(),在依据通信的方向,关闭相应的描述符。过程如下图:
![]()
关闭相应的描述符后,实现父子进程通信,由父进程写, 子进程读取父进程写入的数据
![]()
在这个过程中我们需要注意:
当一端关闭后下面两条规则其作用:
1. 当读一个写端已经关闭的管道时,所有数据被读取完成后,read返回0,表示达到了文件结束处。
2. 当写一个读端已经关闭的管道,则产生SIGPIPE。如果忽略该信号并从处理程序返回,则write函数返回-1.
当多进程在写管道的时候,由系统规定了管道的缓冲区为PIPE_BUF,那么我们在一次写入大于PIPE_BUF的字节 数目,那么就有可能产生进程之间的数据的交错,造成数据的错误,那么我们一般都保证在每次写入的数据小于PIPE_BUF。
在很多情况下,我们都是使用下面这种情景:父进程调用pipe函数,fork()出两个进程,父子进程关闭相应的描述符,由子进程执行一个shell命令后,将执行结构写入到pipe中,父进程读取该结果。在这里系统为我们提供了两个库函数来实现上面的功能。
popen作用执行pipe函数,创建一个子进程,执行cmdstring的shell脚本,由于PIPE是单向的所以需要由type指定,是读还是写,但是不能指定为读写。#include <stdio.h> FILE* popen(char* cmdstring, char * type); int pclose( FILE * fp);
对于cmdstring是通过类似shell -sh -c cmdstring。
返回值:一个普通的文件流,但是关闭必须是pclose,而不是fclose。pclose函数,等待shell执行完毕,关闭popen产生的流,并且返回shell执行终止状态。
注意:popen函数不能被设置了set-user-id、set-group-id的程序调用,有可能造成不安全的情况发生。
为了实现两个相关进程的双向通信,我们应该将使用两个PIPE来间接实现。
FIFO
FIFO在一些时候被称为命名管道,和PIPE管道只能是相关进程使用相比,FIFO则可以在不相关进程之间也能进行交换数据。
我们知道FIFO仍然是一个文件,它是真实存在于计算机文件系统上面的。所以呢,就是说一般的文件I/O函数都是可以用于FIFO的。
对于FIFO函数的创建只能用下面这个函数,当创建成功后则可以使用open函数打开它:
#include <sys/stat.h> int mkfifo( const char* pathname, mode_t mode);
对于FIFO有两种用途:
- FIFO由shell命令使用以便将数据从一条管道线传送到另一条,为此无需创建中间临时文件。
- FIFO用于客户-服务器进程。