1、定义
有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO的文件形式(特殊文件形式)存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径就能够彼此通过 FIFO相互通信,因此,通过FIFO不相关的进程也能交换数据。
一旦打开了 FIFO,就能在它上面使用与操作匿名管道和其他文件的系统调用一样的I/O系统调用了 (如read ( ). write()和close())。与管道一样, FIFO也有一个写入端和读取端,并且从管道中读取数据的顺序与写入的顺序是一样的。FIFO 的名称也由此而来:先入先出。
2、有名管道与匿名管道的区别
1. FIFO在文件系统中作为一个特殊文件存在,但FIFO中的内容却存放在内存中。
2.当使用FIFO 的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。
3. FIFO 有名字,不相关的进程可以通过打开有名管道进行通信(无亲属关系也可)。
3、有名管道的使用
创建fifo文件 1.通过命令: mkfifo 名字 2.通过函数:int mkfifo(const char *pathname, mode_t mode); #include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *pathname, mode_t mode); 参数: - pathname: 管道名称的路径 - mode: 文件的权限 和 open 的 mode 是一样的,是一个八进制的数 返回值:成功返回0,失败返回-1,并设置错误号
4、有名管道通信案例
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <string.h> //往有名管道中写数据 int main() { // 1.判断文件是否存在 int ret = access("test", F_OK); if(ret == -1) { printf("管道不存在,创建管道\n"); // 2.创建管道文件 ret = mkfifo("test", 0664); if(ret == -1) { perror("mkfifo"); exit(0); } } // 3.以只写的方式打开管道 int fd = open("test", O_WRONLY); if(fd == -1) { perror("open"); exit(0); } // 写数据 for(int i = 0; i < 100; i++) { char buf[1024]; sprintf(buf, "hello, %d\n", i); printf("write data : %s\n", buf); write(fd, buf, strlen(buf)); sleep(1); } close(fd); return 0; }
#include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> // 从管道中读取数据 int main() { // 1.打开管道文件 int fd = open("test", O_RDONLY); if(fd == -1) { perror("open"); exit(0); } // 读数据 while(1) { char buf[1024] = {0}; int len = read(fd, buf, sizeof(buf)); if(len == 0) { printf("写端断开连接了...\n"); break; } printf("recv buf : %s\n", buf); } close(fd); return 0; }
说明:只有读端和写段都被打开时才能正常通信;在两个终端(两个进程分别运行两个函数);
5、注意事项
读管道:
管道中有数据,read返回实际读到的字节数
管道中无数据:
管道写端被全部关闭,read返回0,(相当于读到文件末尾)
写端没有全部被关闭,read阻塞等待
写管道:
管道读端被全部关闭,进行异常终止(收到一个SIGPIPE信号)
管道读端没有全部关闭:
管道已经满了,write会阻塞
管道没有满,write将数据写入,并返回实际写入的字节数。
6、有名管道案例
(1) 进程A:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> int main() { // 1.判断有名管道文件是否存在 int ret = access("fifo1", F_OK); if(ret == -1) { // 文件不存在 printf("管道不存在,创建对应的有名管道\n"); ret = mkfifo("fifo1", 0664); if(ret == -1) { perror("mkfifo"); exit(0); } } ret = access("fifo2", F_OK); if(ret == -1) { // 文件不存在 printf("管道不存在,创建对应的有名管道\n"); ret = mkfifo("fifo2", 0664); if(ret == -1) { perror("mkfifo"); exit(0); } } // 2.以只写的方式打开管道fifo1 int fdw = open("fifo1", O_WRONLY); if(fdw == -1) { perror("open"); exit(0); } printf("打开管道fifo1成功,等待写入...\n"); // 3.以只读的方式打开管道fifo2 int fdr = open("fifo2", O_RDONLY); if(fdr == -1) { perror("open"); exit(0); } printf("打开管道fifo2成功,等待读取...\n"); char buf[128]; // 4.循环的写读数据 while(1) { memset(buf, 0, 128); // 获取标准输入的数据 fgets(buf, 128, stdin); // 写数据 ret = write(fdw, buf, strlen(buf)); if(ret == -1) { perror("write"); exit(0); } // 5.读管道数据 memset(buf, 0, 128); ret = read(fdr, buf, 128); if(ret <= 0) { perror("read"); break; } printf("buf: %s\n", buf); } // 6.关闭文件描述符 close(fdr); close(fdw); return 0; }
(2) 进程B:
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> int main() { // 1.判断有名管道文件是否存在 int ret = access("fifo1", F_OK); if(ret == -1) { // 文件不存在 printf("管道不存在,创建对应的有名管道\n"); ret = mkfifo("fifo1", 0664); if(ret == -1) { perror("mkfifo"); exit(0); } } ret = access("fifo2", F_OK); if(ret == -1) { // 文件不存在 printf("管道不存在,创建对应的有名管道\n"); ret = mkfifo("fifo2", 0664); if(ret == -1) { perror("mkfifo"); exit(0); } } // 2.以只读的方式打开管道fifo1 int fdr = open("fifo1", O_RDONLY); if(fdr == -1) { perror("open"); exit(0); } printf("打开管道fifo1成功,等待读取...\n"); // 3.以只写的方式打开管道fifo2 int fdw = open("fifo2", O_WRONLY); if(fdw == -1) { perror("open"); exit(0); } printf("打开管道fifo2成功,等待写入...\n"); char buf[128]; // 4.循环的读写数据 while(1) { // 5.读管道数据 memset(buf, 0, 128); ret = read(fdr, buf, 128); if(ret <= 0) { perror("read"); break; } printf("buf: %s\n", buf); memset(buf, 0, 128); // 获取标准输入的数据 fgets(buf, 128, stdin); // 写数据 ret = write(fdw, buf, strlen(buf)); if(ret == -1) { perror("write"); exit(0); } } // 6.关闭文件描述符 close(fdr); close(fdw); return 0; }
说明:只有读端和写段都被打开时才能正常通信;在两个终端(两个进程分别运行两个函数);