有名管道(如何创建,基本读写,进程间通信)

您所在的位置:网站首页 管道符号的作用有哪些呢 有名管道(如何创建,基本读写,进程间通信)

有名管道(如何创建,基本读写,进程间通信)

2024-07-08 08:16| 来源: 网络整理| 查看: 265

1.1 有名管道概述

有名管道,也被称为FIFO(First In First Out),是一种在文件系统中创建的特殊文件。它允许不相关的进程通过文件系统进行通信,而不需要这些进程之间存在亲缘关系或共享内存。

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同, 其特点是:

1、半双工,数据在同一时刻只能在一个方向上流动。

2、写入FIFO中的数据遵循先入先出的规则。

3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格 式,如多少字节算一个消息等。

4、FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,所以有名管道可以实现不相关进程间通信,但FIFO中的内容却存放在内存中。

5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。

7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用。

8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信。

那么再具体说明一下和管道的异同

共同特点

半双工通信:数据在同一时刻只能在一个方向上流动,即只能单向传输数据。先进先出(FIFO)原则:写入的数据按照先入先出的规则进行读取,确保数据的有序性。无格式数据传输:传输的数据是无格式的字节流,读写双方需要事先约定数据格式。基于内存的缓冲区:数据在内存中进行传输和缓冲,提高了数据传输效率。一次性读取操作:从管道或FIFO中读取数据是一次性操作,数据一旦被读取就会被抛弃,为新数据腾出空间。 不同之处 可见性与持久性:命名管道(FIFO)在文件系统中可见,并作为特殊文件存在,即使创建进程退出后仍然保留,以便后续使用。而管道(pipe)则不可见,且在使用它的进程退出后自动消失。通信范围:命名管道(FIFO)允许不相关的进程通过打开管道文件进行通信,而管道(pipe)通常用于具有亲缘关系的进程之间的通信,如父子进程。缓冲区大小:管道(pipe)的缓冲区大小在不同系统中可能有所不同,而命名管道(FIFO)作为文件系统中的特殊文件,其缓冲区大小可能受到文件系统的限制。有名与无名:命名管道(FIFO)有具体的名称,可以通过名称来引用和访问;而管道(pipe)通常是匿名的,没有具体的名称标识。 1.2 有名管道的创建 

方法1:用过shell命令mkfifo创建有名管道

 mkfifo 文件名

 

ls -l命令用于显示文件和目录的详细信息。 

方法2:使用函数mkfifo

#include #include int mkfifo(const char *pathname, mode_t mode); 功能: 创建一个有名管道,产生一个本地文件系统可见的文件pathname 参数: pathname:有名管道创建后生成的文件,可以带路径 mode:管道文件的权限,一般通过八进制数设置即可,例如0664 返回值: 成功:0 失败:‐1

写一个案例

#include #include #include #include #include #include #include int main() { //通过mkfifo函数创建有名管道 if(mkfifo("fifo_file", 0664) == -1) { //printf("errno = %d\n", errno); //如果管道文件已经存在,不需要报错退出,直接使用即可,所以需要在错误输出之前把 //因为文件存在的错误排除 if(errno != EEXIST) { perror("fail to mkfifo"); // exit(1); } } return 0; }

执行结果

1.3 有名管道的基本读写操作 

由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道进行操作, 但是不能使用lseek修改管道文件的偏移量

注意:有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的操作实质就是对内核空间的操作 

案例

#include #include #include #include #include #include #include #include #define FIFONAME "fifo_file" int main(int argc, char const *argv[]) { //通过mkfifo函数创建有名管道 if(mkfifo(FIFONAME, 0664) == -1) { if(errno != EEXIST) { perror("fail to mkfifo"); exit(1); } } //对有名管道进行操作 //管道后写入的数据会保存在之前写入数据的后面,不会替换 //如果管道中没有数据了,读操作会阻塞 //通过open函数打开管道文件并得到文件描述符 int fd; fd = open(FIFONAME, O_RDWR); if(fd == -1) { perror("fail to open"); exit(1); } //通过write函数向管道中写入数据 if(write(fd, "hello world", strlen("hello world")) == -1) { perror("fail to write"); exit(1); } write(fd, "nihao beijing", strlen("nihao beijing")); //通过read函数读取管道中的数据 char buf[32] = ""; if(read(fd, buf, sizeof(buf)) == -1) { perror("fail to read"); exit(1); } printf("buf = [%s]\n", buf); if(read(fd, buf, sizeof(buf)) == -1) { perror("fail to read"); exit(1); } printf("buf = [%s]\n", buf); //使用close函数关闭文件描述符 close(fd); return 0; }

运行结果

 1.4 有名管道实现进程间通信

由于有名管道在本地创建了一个管道文件,所以不相关的进程间也可以实现通信

1.4.1 send #include #include #include #include #include #include #include #include int main(int argc, char const *argv[]) { //如果没有创建有名管道,则创建有名管道 //为了实现两个进程都可以收发数据,所以需要创建两个有名管道 if(mkfifo("myfifo1", 0664) == -1) { if(errno != EEXIST) { perror("fail to mkfifo"); exit(1); } } if(mkfifo("myfifo2", 0664) == -1) { if(errno != EEXIST) { perror("fail to mkfifo"); exit(1); } } //打开两个有名管道并得到文件描述符 int fd_w, fd_r; if((fd_w = open("myfifo1", O_WRONLY)) == -1) { perror("fail to open"); exit(1); } if((fd_r = open("myfifo2", O_RDONLY)) == -1) { perror("fail to open"); exit(1); } char buf[128] = ""; ssize_t bytes; while(1) { fgets(buf, sizeof(buf), stdin); buf[strlen(buf) - 1] = '\0'; //send进程负责将数据写入myfifo1,接着从myfifo2中读取数据 if((bytes = write(fd_w, buf, sizeof(buf))) == -1) { perror("fail to write"); exit(1); } if((bytes = read(fd_r, buf, sizeof(buf))) == -1) { perror("fail to read"); exit(1); } printf("from recv: %s\n", buf); } return 0; }

这段代码实现了一个简单的双向通信机制,其中"myfifo1"管道用于发送数据,"myfifo2"管道用于接收数据。通过这种方式,两个不相关的进程可以互相发送和接收数据,而不需要直接引用对方。

1.4.2  recv #include #include #include #include #include #include #include #include int main(int argc, char const *argv[]) { if(mkfifo("myfifo1", 0664) == -1) { if(errno != EEXIST) { perror("fail to mkfifo"); exit(1); } } if(mkfifo("myfifo2", 0664) == -1) { if(errno != EEXIST) { perror("fail to mkfifo"); exit(1); } } int fd_w, fd_r; if((fd_r = open("myfifo1", O_RDONLY)) == -1) { perror("fail to open"); exit(1); } if((fd_w = open("myfifo2", O_WRONLY)) == -1) { perror("fail to open"); exit(1); } char buf[128] = ""; ssize_t bytes; while(1) { if((bytes = read(fd_r, buf, sizeof(buf))) == -1) { perror("fail to read"); exit(1); } printf("from send: %s\n", buf); fgets(buf, sizeof(buf), stdin); buf[strlen(buf) - 1] = '\0'; write(fd_w, buf, sizeof(buf)); } return 0; }

我创建了两个有名管道"myfifo1"和"myfifo2",并分别以只读和只写的方式打开它们。在无限循环中,代码首先从"myfifo1"中读取数据,并将其打印出来。然后,它从标准输入读取一行数据,并将其写入到"myfifo2"中。通过这种方式,两个进程可以互相发送和接收数据。运行结果



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3