管道是Linux系统中最古老的进程间通信(IPC,InterProcess Communication)手段,它把一个程序的输出直接连接到另一个程序的输出。
1.无名管道
无名管道是Linux中管道通信的一种原始方法。
特点
- 它只能用于具有亲缘关系的进程之间的通信
- 它是一个半双工的通信模式,它具有固定的读端和写端
- 管道也可以看做一种特殊的文件,对他的读写也可以直接使用普通的read(),write()等函数。但它不是普通的文件,并不属于其他文件系统,并且只能存在与内存中。
- 一个进程项管道中写的内容被管道另一端的进程读出。写入的内容每次都添加到管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。
创建
管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fd[0],fd[1],其中fd[0]固定于读管道,fd[1]固定于写管道,这样就构成了一个半双工的通信。
创建管道可以用pipe()来实现:
#include <unistd.h>
int pipe(int fd[2]);
用pipe()创建的管道两端处于一个进程中,由于管道是主要用于在不同进程间通信的,因此这在实际应用中没有太大意义。实际上,通常先创建一个管道,再调用fork()函数创建一个子进程,该子进程会继承父进程所创建的管道。
父子进程分别拥有自己的读写通道,为了实现父子进程之间的读写,只需把无关的读端或写端的文件描述符关闭即可。
![](https://img-my.csdn.net/uploads/201209/16/1347796977_3645.jpg)
注意事项
- 只有在管道的读端存在时,向管道写入数据才有意义,否则,向管道写入数据的的进程将收到内核传来的SIGPIPE信号。
- 向管道写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取缓冲区中的数据,那么写操作将会一直阻塞。
- 管道的容量非常有限,在<limits.h>头文件中定义的常量PIPE_BUF规定了管道一次传送的最大字节数,在现有的大多数系统中为4096字节.
2.标准流管道
基于流的管道主要用来创建一个链接到另一进程的管道,这里另一进程也局势一个可以进行一定操作的可执行文件。标准流管道就将一系列的创建过程合并到一个函数popen中完成,他完成的工作有以下几步:
- 创建一个管道。
- 建立一个子进程
- 在父子进程中关闭不需要的文件描述符。
- 执行exec函数族调用
- 执行函数中所指定的命令
#include <stdio.h>
FILE *popen(const char *cmd, const char *type)
int pclose()
关键代码
menset(buf, 0, sizeof(buf));
FILE *fp = popen("uname -a", "r");
fread(buf, sizeof(char),sizeof(buf)-1, fp)
或
fgets(buf, sizeof(buf), fp);
注意:
用popen创建的管道必须使用标准I/O函数进行操作,但不能使用read(),write()一类不带缓冲的I/O函数。
3.有名管道FIFO
有名管道(FIFO)的创建可以使用函数mkfifo(),该函数类似于open操作,可以指定管道的路径和打开的模式(用户也可以在命令行中使用"mknod 管道名 p"来创建有名管道)。
在创建管道成功之后,就可以使用open(),read()和write()这些函数,与不同文件不同的是阻塞问题。
对于阻塞问题:
读进程:
- 若该管道是阻塞打开,且当前FIFO内没有数据,则对读进程而言将一直阻塞到有数据写入。
- 若该管道是非阻塞打开,则不论FIFO内是否有数据,读进程都会立即执行读操作。即如果米有数据,则读函数将立即返回0
写进程:
- 若该管道是阻塞打开,则写操作将一直阻塞到数据可以被写入
- 若该函数是非阻塞打开,而不能写入全部数据,则读操作进行部分写入或者调用失败
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode)
mode:
- O_RDONLY :读管道
- O_WRONLY:写操作
- O_RDWR:读写管道
- O_NONBLOCK:非阻塞
- O_CREAT:如果该文件不存在,就新创建一个新的文件
- O_EXCL:如果配合)、O_CREAT时文件存在,那么返回错误信息(EEXIST)
可以通过access(FILENAME, F_OK);来判断文件是否存在
open() write()
open() read()