管道是Linux中最常用的进程间通信 IPC 机制。使用管道时,一个进程的输出可以成为另外一个进程的输入。
当输入/输出的数据量特别大时,管道的这种 IPC 机制就非常有用。
在Linux中,通过将两个file结构指向同一个临时的VFS节点,这个VFS节点又指向同一个物理页而实现管道。
若要创建一个简单的管道,可以使用系统调用pipe(),它接受一个参数,也就是一个包括两个整数的数组。如果系统调用成功,此数组将包括管道使用的两个文件描述符,一个为读端,一个为写端。pipe管道是半双工的,数据只能向一个方向流动,需要双方通信时,就建立两个管道。pipe管道只能用于父子进程或者兄弟进程之间。
头文件: #include <unistd.h>
函数原型: int pipe( int filedes[2])
filedes: 管道文件描述符,当为0时,pipe端代码工程为读端;当为1时,pipe端代码工程为写端;
filedes[0]: 管道的读端
filedes[1]: 管道的写端
函数返回值:成功为 0 ;错误为 1 。
#include <stdio.h> #include <unistd.h> #include <string.h> /*父子进程通过无名管道进行通信*/ int main(int argc, char *argv[]) { //1, 创建无名管道 int fd[2]; char buf[64]; int ret = pipe(fd); if(-1 == ret) { perror("pipe failed"); return -1; } //2, 创建子进程 pid_t pid = fork(); if(-1 == pid) { perror("fork faied"); return -1; } //3, 父进程循环从键盘获取数据写入无名管道 --> 写入端 fd[1] if(pid > 0) { while(1) { memset(buf, 0, sizeof(buf)); fgets(buf, sizeof(buf), stdin); write(fd[1], buf, sizeof(buf)); } } //4, 子进程循环从无名管道中读取数据 if(0 == pid) { while(1) { memset(buf, 0, sizeof(buf)); read(fd[0], buf, sizeof(buf)); //默认是阻塞的 printf("readbuf:%s", buf); } } return 0; }
命名管道与一般管道要一些不同点
1) 命名管道是在文件系统中作为一个特殊的设备文件而存在的。
2) 不同管道的进程之间可以通过命名管道共享数据。
3) 当共享命名管道的进程执行完所有的 I/O 操作后,命名管道将继续保存在文件系统中,以便以后使用。
4) 普通管道只能由父子兄弟相关进程使用,它共同的祖先进程创建了管道。但是,通过命名管道,不相关的进程也能交换数据。
5) 一旦用mkfifo函数创建了一个命名管道,就可以 open 打开它。一般的文件 I/O 函数( close , read , write , unlink )都可用于命名管道。
函数头文件:
#include <sys/types.h>
#include <sys/stat.h>
函数原型:
int mkfifo(const char * pathname,mode mode)
pathname: 管道文件名
mode: 管道创建方式
函数返回值:
成功: 0
失败: -1
代码例程:
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define FIFO_PATH "/home/gec/myfifo" /*有名管道读端程序*/ int main(int argc, char *argv[]) { //1, 判断有名管道是否存在 --> 不存在则创建 if( access(FIFO_PATH, F_OK) != 0) //access返回值不等于0,表示文件不存在 { int ret = mkfifo(FIFO_PATH, 0777); if(-1 == ret) { perror("mkfifo failed"); return -1; } } //2, 直接打开有名管道 --> open() int fd = open(FIFO_PATH, O_RDWR); if(-1 == fd) { perror("open failed"); return -1; } //3, 循环获取读取管道中数据 --> "EXIT\n" char buf[128]; while(1) { memset(buf, 0, sizeof(buf)); read(fd, buf, sizeof(buf)); if( strcmp("EXIT\n", buf) == 0 ) break; printf("readbuf:%s", buf); } //4, 关闭管道文件 close(fd); return 0; }
#include <stdio.h> #include <string.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #define FIFO_PATH "/home/gec/myfifo" /*有名管道写端程序*/ int main(int argc, char *argv[]) { //1, 判断有名管道是否存在 --> 不存在则创建 if( access(FIFO_PATH, F_OK) != 0) //access返回值不等于0,表示文件不存在 { int ret = mkfifo(FIFO_PATH, 0777); if(-1 == ret) { perror("mkfifo failed"); return -1; } } //2, 直接打开有名管道 --> open() int fd = open(FIFO_PATH, O_RDWR); if(-1 == fd) { perror("open failed"); return -1; } //3, 循环获取数据写入管道 char buf[128]; while(1) { memset(buf, 0, sizeof(buf)); fgets(buf, sizeof(buf), stdin); write(fd, buf, sizeof(buf)); if( strcmp("EXIT\n", buf) == 0 ) break; } //4, 关闭管道文件 close(fd); return 0; }