Linux之匿名管道和命名管道

匿名管道

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. 半双工通信

    • 同一时间只能 ,不能同时读写(类似单行道)。
    • 示例:
      # 终端1:写入数据
      echo "Hello" > /tmp/myfifo
      
      # 终端2:读取数据
      cat /tmp/myfifo  # 输出 "Hello"
      
  2. 阻塞机制

    • 如果没有进程在 ,写操作会 卡住,直到有进程来读。
    • 如果没有进程在 ,读操作会 卡住,直到有进程来写。
  3. 数据是流式的

    • 数据像水流一样,读完后会消失,不能像普通文件那样随意跳转(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 从水管接水(数据)。
  • 特性
    • 水管没有储水功能(数据不持久化)。
    • 如果没人接水,倒水的人会等待(阻塞写入)。
    • 如果没人倒水,接水的人会等待(阻塞读取)。

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

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

参考文档

https://blog.csdn.net/daaikuaichuan/article/details/82827994

https://xiaolincoding.com/os/4_process/process_commu.html#%E7%AE%A1%E9%81%93

https://blog.csdn.net/m0_56069910/article/details/135726854

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值