107.管道(有名、无名管道)
目录
管道
? ? ? ? 管道是一种进程间通信的机制,允许一个进程的输出直接作为另一个进程的输入。在Linux 系统中,管道通过
pipe
系统调用来创建。一个典型的管道由两个文件描述符表示,分别用于读取和写入。因为管道数据是通过队列来维护的,我们先来分析一个管道中数据的特点:
- 管道对应的内核缓冲区大小是固定的,默认为4k(也就是队列最大能存储4k数据)
- 管道分为两部分:读端和写端(队列的两端),数据从写端进入管道,从读端流出管道。
- 管道中的数据只能读一次,做一次读操作之后数据也就没有了(读数据相当于出队列)。
- 管道是单工的:数据只能单向流动, 数据从写端流向读端。
- 对管道的操作(读、写)默认是阻塞的
- 读管道:管道中没有数据,读操作被阻塞,当管道中有数据之后阻塞才能解除
- 写管道:管道被写满了,写数据的操作被阻塞,当管道变为不满的状态,写阻塞解除。
// 读管道 ssize_t read(int fd, void *buf, size_t count); // 写管道的函数 ssize_t write(int fd, const void *buf, size_t count);
无名管道?
? ? ? ? 无名管道是一种最简单的管道形式,通过
pipe
系统调用创建。它是单向的,只能用于具有亲缘关系的进程之间的通信,通常在创建子进程前调用pipe
,然后通过fork
创建子进程,实现父子进程之间的通信。无名管道的创建
#include <unistd.h> int pipe(int pipefd[2]);//pipefd 是一个长度为 2 的整型数组,用于存储两个文件描述符 /*pipefd()成功返回 0,失败返回-1 fds[0]是管道读端的描述符 fds[1]是管道写端的描述符 */
无名管道的基本用法
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/wait.h> int main() { int pipefd[2]; // 创建管道 if (pipe(pipefd) == -1) { perror("pipe"); exit(EXIT_FAILURE); } // 创建子进程 pid_t pid = fork(); if (pid == -1) { perror("fork"); exit(EXIT_FAILURE); } if (pid > 0) { // 父进程 close(pipefd[0]); // 关闭管道读取端 const char *message = "Hello from parent!"; write(pipefd[1], message, strlen(message)); close(pipefd[1]); // 关闭管道写入端 } else if (pid == 0) { // 子进程 close(pipefd[1]); // 关闭管道写入端 char buffer[50]; ssize_t bytesRead = read(pipefd[0], buffer, sizeof(buffer)); if (bytesRead == -1) { perror("read"); exit(EXIT_FAILURE); } buffer[bytesRead] = '\0'; printf("Child received: %s\n", buffer); close(pipefd[0]); // 关闭管道读取端 } return 0; }
创建管道: 通过
pipe(pipefd)
创建了一个无名管道,pipefd[0]
是读取端,pipefd[1]
是写入端。创建子进程: 通过
fork()
创建了一个子进程。父进程和子进程都会执行后续的代码。父进程写入消息: 在父进程中,关闭了读取端
pipefd[0]
,然后使用write(pipefd[1], message, strlen(message))
向管道的写入端pipefd[1]
写入消息 "Hello from parent!"。子进程读取消息: 在子进程中,关闭了写入端
pipefd[1]
,然后使用read(pipefd[0], buffer, sizeof(buffer))
从管道的读取端pipefd[0]
读取消息。读取的内容被存储在buffer
中,并通过printf
打印出来。关闭文件描述符: 父子进程分别关闭了管道中不再需要的文件描述符。
这个例子中,父进程向子进程发送了一条消息,子进程接收到并打印了这条消息。需要注意的是,管道是一种半双工通信机制,数据是单向流动的。在实际应用中,可能需要创建两个管道来实现双向通信。此外,为了防止产生僵尸进程,通常在父进程中使用
wait
等待子进程的结束。
有名管道?
? ? ? ? 有名管道拥有管道的所有特性,之所以称之为有名是因为管道在磁盘上有实体文件, 文件类型为p ,有名管道文件大小永远为0,因为有名管道也是将数据存储到内存的缓冲区中,打开这个磁盘上的管道文件就可以得到操作有名管道的文件描述符,通过文件描述符读写管道存储在内核中的数据。
? ? ? ? 有名管道也可以称为 fifo (first in first out),使用有名管道既可以进行有血缘关系的进程间通信,也可以进行没有血缘关系的进程间通信。创建有名管道的方式有两种,一种是通过命令,一种是通过函数。
通过函数
#include <sys/types.h> #include <sys/stat.h> // int open(const char *pathname, int flags, mode_t mode); int mkfifo(const char *pathname, mode_t mode);
参数:
- pathname: 要创建的有名管道的名字
- mode: 文件的操作权限, 和open()的第三个参数一个作用,最终权限: (mode & ~umask)
- 返回值:创建成功返回 0,失败返回 -1
? ? ? ? 不管是有血缘关系还是没有血缘关系,使用有名管道实现进程间通信的方式是相同的,就是在两个进程中分别以读、写的方式打开磁盘上的管道文件,得到用于读管道、写管道的文件描述符,就可以调用对应的read()、write()函数进行读写操作了。
写管道的进程:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <fcntl.h> int main() { int fd = open("FIFO", O_WRONLY);// 打开有名管道 "FIFO",使用写入权限 assert(fd != -1); // 检查文件打开是否成功 printf("open FIFO success\n"); // 进入一个无限循环,提示用户输入数据 while (1) { printf("please input: ");// 提示用户输入 char buff[128] = {0}; fgets(buff, 128, stdin); 从标准输入中读取用户输入的数据,存储在 buff 缓冲区中 write(fd, buff, strlen(buff) - 1);// 将用户输入的数据写入到已打开的有名管道文件描述符 fd 中 // 检查用户输入是否为 "end",如果是,退出循环 if (strncmp(buff, "end", 3) == 0) { break; } } // 关闭有名管道文件描述符 close(fd); exit(0); }
这是一个用于向有名管道写入数据的程序。让我为您解释一下:
open("FIFO", O_WRONLY)
打开名为 "FIFO" 的有名管道,使用写入权限。如果该文件不存在,会创建它。进入一个无限循环,提示用户输入数据:
fgets(buff, 128, stdin)
从标准输入中读取用户输入的数据,存储在buff
缓冲区中。这里假设用户输入的数据不超过 128 个字符。
write(fd, buff, strlen(buff) - 1)
将用户输入的数据写入到已打开的有名管道文件描述符fd
中。这里使用strlen(buff) - 1
是为了去掉输入中的换行符。检查用户输入是否为 "end",如果是,退出循环。
关闭有名管道文件描述符 (
close(fd)
),退出程序。读管道的进程:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <string.h> #include <fcntl.h> int main() { // 打开有名管道 "FIFO",使用只读权限 int fd = open("FIFO", O_RDONLY); assert(fd != -1); // 检查文件打开是否成功 printf("open FIFO success\n"); // 进入一个无限循环,读取有名管道中的数据并输出 while (1) { char buff[128] = {0}; // 从有名管道中读取数据,存储在 buff 缓冲区中 int n = read(fd, buff, 127); // 如果读取到的数据小于等于 0,或者读取到的数据为 "end",则退出循环 if (n <= 0 || 0 == strncmp(buff, "end", 3)) { break; } // 输出读取到的数据 printf("%s\n", buff); } // 关闭有名管道文件描述符 close(fd); exit(0); }
这段代码是一个使用有名管道(FIFO)进行进程间通信的示例。有名管道是一种特殊的文件,可用于在不相关的进程之间传递数据。
- 打开有名管道文件 "FIFO",并使用只读权限打开,
open("FIFO", O_RDONLY)
。- 检查文件打开是否成功,如果失败则退出程序。
- 进入一个无限循环,用于读取有名管道中的数据并输出。
- 使用
read
从有名管道中读取数据,将其存储在buff
缓冲区中,最多读取 127 个字节。- 如果读取到的数据小于等于 0,或者读取到的数据为 "end",则退出循环。
- 输出读取到的数据。
- 关闭有名管道文件描述符。
- 退出程序。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!