当前位置: 代码网 > 服务器>服务器>Linux > Linux之匿名管道和命名管道详解

Linux之匿名管道和命名管道详解

2025年07月04日 Linux 我要评论
匿名管道kubectl get pod -a | grep mysql上面命令行里的「|」竖线就是一个管道,在命令行(如 linux shell 或 windows cmd/powershell)中,

匿名管道

kubectl get pod -a | grep mysql

上面命令行里的「|」竖线就是一个管道,在命令行(如 linux shell 或 windows cmd/powershell)中,管道操作符 | 的作用是将 前一个命令的标准输出(stdout) 传递给 后一个命令的标准输入(stdin)。但它默认 不会传递标准错误(stderr),这是它的核心行为特点。

1. 管道|的基本行为

command1 | command2
  • command1stdout → 作为 command2stdin
  • command1stderr → 直接打印到终端,不会传递给 command2

示例 1(stdout 被管道传递)

# ls 成功时,stdout(文件列表)会传递给 grep
ls /usr/bin | grep "python"

示例 2(stderr 未被管道传递)

# 如果目录不存在,错误信息会直接显示,不会传递给 grep
ls /nonexistent_dir | grep "error"

输出:

ls: cannot access '/nonexistent_dir': no such file or directory

(错误信息直接显示,grep 不会处理它)

2. 为什么|不处理 stderr

  • 设计初衷| 的职责是传递 正常输出,错误信息通常需要直接反馈给用户。
  • 分离数据流:stdout(正常输出)和 stderr(错误信息)是独立的流,| 默认只操作 stdout。

3. 原理

int pipe(int fd[2])

这里表示创建一个匿名管道,并返回了两个描述符,一个是管道的读取端描述符,另一个是管道的写入端描述符fd。注意,这个匿名管道是特殊的文件,只存在于内存,不存于文件系统中。

在这里插入图片描述

所谓的管道,就是内核里面的一串缓存。从管道的一段写入的数据,实际上是缓存在内核中的,另一端读取,也就是从内核中读取这段数据。另外,管道传输的数据是无格式的流且大小受限。

管道只能一端写入,另一端读出,所以上面这种模式容易造成混乱,因为父进程和子进程都可以同时写入,也都可以读出。那么,为了避免这种情况,通常的做法是:

  • 父进程关闭读取的 fd[o],只保留写入的 fd[1];
  • 子进程关闭写入的fd[1],只保留读取的 fd[o];

在这里插入图片描述

在shell 里面执行 a | b 命令的时候,a 进程和 b 进程都是shell 创建出来的子进程,a 和 b 之间不存在父子关系,它俩的父进程都是 shell。

请添加图片描述

命名管道

简介

  • 匿名管道,它的通信范围是存在父子关系的进程。因为管道没有实体,也就是没有管道文件,只能通过fork来复制父进程 fd文件描述符,来达到通信的目的。
  • 对于命名管道,它可以在不相关的进程间也能相互通信。因为命令管道,提前创建了一个类型为管道的设备文件,在进程里只要使用这个设备文件,就可以相互通信。
  • 不管是匿名管道还是命名管道,进程写入的数据都是缓存在内核中,另一个进程读取数据时候自然也是从内核中获取,同时通信数据都遵循先进先出原则,不支持iseek之类的文件定位操作。

使用

int mkfifo(const char *pathname, mode_t mode);

创建 fifo

使用 mkfifo 命令或 mkfifo() 函数创建:

mkfifo /tmp/myfifo  # 创建一个名为 /tmp/myfifo 的 fifo
  • pathname:fifo 的文件路径(如 /tmp/myfifo)。
  • mode:权限(如 0644,表示用户可读写,其他人只读)。

fifo 的特点

半双工通信

  • 同一时间只能 ,不能同时读写(类似单行道)。
  • 示例:
# 终端1:写入数据
echo "hello" > /tmp/myfifo

# 终端2:读取数据
cat /tmp/myfifo  # 输出 "hello"

阻塞机制

  • 如果没有进程在 ,写操作会 卡住,直到有进程来读。
  • 如果没有进程在 ,读操作会 卡住,直到有进程来写。

数据是流式的

  • 数据像水流一样,读完后会消失,不能像普通文件那样随意跳转(lseek 无效)。

如何使用 fifo?

(1)命令行测试

# 终端1:监听 fifo(读)
cat /tmp/myfifo

# 终端2:发送数据(写)
echo "hello fifo" > /tmp/myfifo

终端1 会显示 hello fifo

(2)c 语言示例

#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>

int main() {
    mkfifo("/tmp/myfifo", 0644);  // 创建 fifo

    int fd = open("/tmp/myfifo", o_wronly);  // 以写方式打开
    write(fd, "hello", 6);  // 写入数据
    close(fd);

    return 0;
}

常见问题

q1: fifo 和普通文件有什么区别?

  • fifo 是 内存中的管道,数据读完就没了;普通文件会持久化存储。

q2: fifo 和匿名管道(|)有什么区别?

  • 匿名管道只能用于 父子进程,fifo 可用于 任意进程

q3: 如果 fifo 已经存在,再创建会怎样?

  • mkfifo 会失败,并提示 file exists

原理

1. 普通文件 vs. 命名管道(fifo)

普通文件(如 a.txt):

  • 数据存储在 磁盘 上,打开时会加载到内存。
  • 修改后需要 写回磁盘(刷盘)。
  • 多个进程打开同一文件时,数据可能不同步(除非加锁)。

命名管道(fifo)

  • 本质是一个 内存缓冲区,只是伪装成文件(磁盘上只有文件名,没有实际数据块)。
  • 数据 不写入磁盘,直接在进程间流动。
  • 专为 进程间通信(ipc) 设计,速度快。

2. 命名管道的工作原理

(1)创建 fifo

当执行 mkfifo /tmp/myfifo 时:

  1. 磁盘上创建一个 空文件(只有 inode,没有数据块)。
  2. 操作系统内核维护一个 内存缓冲区(用于存储进程间传递的数据)。

(2)进程 a 写入数据

echo "hello" > /tmp/myfifo
  • 进程 a 打开 fifo 以写方式o_wronly)。
  • 数据 "hello" 被写入内核的 内存缓冲区(不刷盘!)。
  • 如果 没有进程在读取,进程 a 会 阻塞(卡住),直到有进程来读。

(3)进程 b 读取数据

cat /tmp/myfifo
  • 进程 b 打开 fifo 以读方式o_rdonly)。
  • 从内核缓冲区读取 "hello",数据被消费(缓冲区清空)。
  • 如果 没有进程在写入,进程 b 会 阻塞,直到有进程写入。

3. 关键点解析

(1)文件描述符与内核结构

每个进程打开 fifo 时,内核会 复用同一个 struct file(通过引用计数 ref 管理)。

  • 第一次打开时创建 struct fileref=1
  • 第二个进程打开时,ref=2(指向同一个内存缓冲区)。
  • 关闭时 ref--ref=0 时内核才释放资源。

(2)为什么数据不刷盘?

  • fifo 的 设计目的 是进程间通信,数据不需要持久化。
  • 刷盘会 拖慢速度,且毫无意义(通信完数据即可丢弃)。

(3)半双工通信

  • 同一时间只能 单向流动(读或写)。
  • 如果需要双向通信,需创建 两个 fifo(一个负责 a→b,一个负责 b→a)。

4. 类比理解

把 fifo 想象成 一条水管

写入端:进程 a 往水管里倒水(数据)。

读取端:进程 b 从水管接水(数据)。

特性

  • 水管没有储水功能(数据不持久化)。
  • 如果没人接水,倒水的人会等待(阻塞写入)。
  • 如果没人倒水,接水的人会等待(阻塞读取)。

总结

特性命名管道(fifo)普通文件
存储位置内存缓冲区磁盘
刷盘永不刷盘定期刷盘
多进程访问共享同一缓冲区可能数据不同步
阻塞行为读/写会阻塞无阻塞
用途进程间通信数据存储

简单来说:

fifo 是 披着文件外衣的内存管道,让进程可以通过文件路径名通信,数据 不落盘,速度极快!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持代码网。

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com