107.管道(有名、无名管道)

2023-12-11 12:04
文章标签 管道 有名 107 无名

本文主要是介绍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,失败返回-1fds[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;
}
  1. 创建管道: 通过 pipe(pipefd) 创建了一个无名管道,pipefd[0] 是读取端,pipefd[1] 是写入端。

  2. 创建子进程: 通过 fork() 创建了一个子进程。父进程和子进程都会执行后续的代码。

  3. 父进程写入消息: 在父进程中,关闭了读取端 pipefd[0],然后使用 write(pipefd[1], message, strlen(message)) 向管道的写入端 pipefd[1] 写入消息 "Hello from parent!"。

  4. 子进程读取消息: 在子进程中,关闭了写入端 pipefd[1],然后使用 read(pipefd[0], buffer, sizeof(buffer)) 从管道的读取端 pipefd[0] 读取消息。读取的内容被存储在 buffer 中,并通过 printf 打印出来。

  5. 关闭文件描述符: 父子进程分别关闭了管道中不再需要的文件描述符。

这个例子中,父进程向子进程发送了一条消息,子进程接收到并打印了这条消息。需要注意的是,管道是一种半双工通信机制,数据是单向流动的。在实际应用中,可能需要创建两个管道来实现双向通信。此外,为了防止产生僵尸进程,通常在父进程中使用 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);
}

这是一个用于向有名管道写入数据的程序。让我为您解释一下:

  1. open("FIFO", O_WRONLY) 打开名为 "FIFO" 的有名管道,使用写入权限。如果该文件不存在,会创建它。

  2. 进入一个无限循环,提示用户输入数据:

    • fgets(buff, 128, stdin) 从标准输入中读取用户输入的数据,存储在 buff 缓冲区中。这里假设用户输入的数据不超过 128 个字符。

    • write(fd, buff, strlen(buff) - 1) 将用户输入的数据写入到已打开的有名管道文件描述符 fd 中。这里使用 strlen(buff) - 1 是为了去掉输入中的换行符。

    • 检查用户输入是否为 "end",如果是,退出循环。

  3. 关闭有名管道文件描述符 (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)进行进程间通信的示例。有名管道是一种特殊的文件,可用于在不相关的进程之间传递数据。

  1. 打开有名管道文件 "FIFO",并使用只读权限打开,open("FIFO", O_RDONLY)
  2. 检查文件打开是否成功,如果失败则退出程序。
  3. 进入一个无限循环,用于读取有名管道中的数据并输出。
  4. 使用 read 从有名管道中读取数据,将其存储在 buff 缓冲区中,最多读取 127 个字节。
  5. 如果读取到的数据小于等于 0,或者读取到的数据为 "end",则退出循环。
  6. 输出读取到的数据。
  7. 关闭有名管道文件描述符。
  8. 退出程序。

这篇关于107.管道(有名、无名管道)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/480622

相关文章

Redis 管道的神奇力量

今天我们要来探索一个 Redis 中非常强大且实用的特性——管道(Pipeline)。如果你想让你的 Redis 操作更加高效,那么这篇文章绝对值得一读。 一、Redis 管道是什么 Redis 管道是一种在客户端和服务器之间批量执行命令的技术。它允许客户端将多个命令一次性发送到服务器,而不是逐个发送并等待每个命令的响应。服务器会按照顺序执行这些命令,并将所有命令的响应一次性返回给客户端。

【Linux】Linux 管道:进程间通信的利器

文章目录 Linux 管道:进程间通信的利器1. 什么是管道?2. 管道的分类2.1 匿名管道(Unnamed Pipe)2.2 命名管道(Named Pipe,FIFO) 3. 管道的局限性4. 结论 Linux 管道:进程间通信的利器 在 Linux 系统中,管道(Pipe)是进程间通信(IPC, Inter-Process Communication)的重要机制之一。

Linux管道式操作命令

Linux管道(Pipe)是一种将一个命令的输出作为另一个命令输入的技术。管道操作符是|。这种机制非常强大,因为它允许你将多个简单的命令组合成复杂的操作,实现数据的流式处理。 以下是一些常见的管道式操作命令的例子: 1. 查找特定进程 使用ps命令列出所有进程,然后用grep命令过滤出特定的进程。 ps aux | grep nginx 这个命令会列出所有与nginx相关的进程。 2

redis管道piplines优化springboot业务性能

redis本身执行指令的性能非常的高,单台数十万并发不成问题,但是如果一个请求处理流程过长,需要频繁的操作redis,此时无论如何都无法提高系统的并发,归根结底还是网络带来的性能损耗过大,为了降低网络开销,redis支持piplines和lua脚本的方式进行批量处理和返回值,只需要一次网络请求就可以发送数百条操作指令。以下我将对着两种方式的应用场景进行详细的描述。  上图是piplines的

传统管道,匿名管道

二、传统的进程间通信-管道文件     管道是UNIX系统中最古老的进程间通信技术,古老意味着所有系统都支持,早期的管道是半双工通信,现有的系统管道是全双工通信     管道就是一种特殊的文件,数据在文件中是流动的,读取之后就自动消失,如果文件中没有数据则会阻塞     有名管道:基于有文件名的管道文件的通信         编程模型             进程A

吃透Redis系列(三):Redis管道,发布/订阅,事物,过期时间 详细介绍

Redis系列文章: 吃透Redis系列(一):Linux下Redis安装 吃透Redis系列(二):Redis六大数据类型详细用法 吃透Redis系列(三):Redis管道,发布/订阅,事物,过期时间 详细介绍 吃透Redis系列(四):布隆(bloom)过滤器详细介绍 吃透Redis系列(五):RDB和AOF持久化详细介绍 吃透Redis系列(六):主从复制详细介绍 吃透Redi

Linux | 匿名管道和命名管道:进程间通信数据流的桥梁

目录 1、进程间通信目的 2、管道——匿名管道和命名管道 匿名管道 匿名管道的示例代码:将数据写入管道、子进程从管道读取数据并将其输出到bash中 父子进程通过匿名管道建立通信 重点:管道的五个特点 命名管道(也称为FIFO) a. 创建命名管道 - mkfifo() b. 使用open函数打开命名管道文件 c. 读写命名管道- read() 和 write() d. 关闭和

【进程间通信】管道应用场景---简易进程池

#include<iostream>#include<vector>#include<string>#include<cstring>#include<cstdlib>#include<unistd.h>#include<sys/stat.h>#include<sys/wait.h>//把5个子进程要管理起来,要先描述再组织const int processnum=5;//先

【Rust光年纪】极致性能体验:数据管道实现、虚拟化列表和网格布局美化完全攻略

优秀开源库大揭秘:内存分析、数据处理、页面滚动监测、图片延迟加载全指南 前言 在当今的软件开发环境中,存在着各种各样的库和工具,它们为开发人员提供了丰富的功能和技术支持。本文将介绍几个与内存分析、数据处理、页面滚动状态监测、图片延迟加载、虚拟化长列表和网格布局美化相关的优秀库,帮助开发人员更好地理解和利用这些工具。 欢迎订阅专栏:Rust光年纪 文章目录 优秀开源库大揭秘:内

Lesson_for_java_day17--java中的IO流(IO基本流、键盘输入、管道流、文件及文件夹操作、Properties类、切割文件、记录软件运行次数)

IO基本流: 字符流:读操作:Reader 字符输入流 抽象类Reader r = new FileReader(File f);构造方法:FileReader(File file) ;FileReader(String fileName) ;方法://读取一个字符并以整数的形式返回(0~255),//如果返回-1已到输入流的末尾。int read() throws IOException