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

 更新时间:2025年07月04日 09:32:03   作者:爱吃芝麻汤圆  
这篇文章主要介绍了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:写入数据
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 是 披着文件外衣的内存管道,让进程可以通过文件路径名通信,数据 不落盘,速度极快!

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • linux中如何添加用户并赋予root权限详解

    linux中如何添加用户并赋予root权限详解

    这篇文章主要先是给大家介绍了linux中如何添加用户并赋予root权限,而后有详细的介绍了Linux系统用户组的管理,文中通过示例代码介绍的很详细,相信对大家的理解和学习具有一定的参考借鉴价值,有需要的朋友们下面来一起学习学习吧。
    2016-11-11
  • 在Linux环境下采用压缩包方式安装JDK 13的方法

    在Linux环境下采用压缩包方式安装JDK 13的方法

    JDK(Java Development Kit)是Sun公司(后被Oracle收购)推出的面向对象程序设计语言的开发工具包,拥有这个工具包之后我们就可以使用Java语言进行程序设计和开发。这篇文章主要介绍了在Linux环境下采用压缩包方式安装JDK 13,需要的朋友可以参考下
    2019-10-10
  • Linux有限状态机FSM的理解与实现

    Linux有限状态机FSM的理解与实现

    这篇文章主要帮助大家理解与实现Linux有限状态机FSM,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-06-06
  • Linux中的线程安全与线程同步详解

    Linux中的线程安全与线程同步详解

    这篇文章主要介绍了Linux中的线程安全与线程同步,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2025-04-04
  • crontab执行时间与系统时间不一致问题解决

    crontab执行时间与系统时间不一致问题解决

    这篇文章主要给大家介绍了关于crontab执行时间与系统时间不一致问题的解决方法,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-01-01
  • 在Linux系统中srv目录的作用详解

    在Linux系统中srv目录的作用详解

    在Linux系统中,/srv目录用于存放与系统服务相关的数据,如Web服务器、FTP服务器等,它提供了一种约定,用于将服务数据组织起来,便于管理和维护,通常,/srv目录的内容按照服务类型进行子目录划分,便于管理和备份
    2025-03-03
  • ubuntu20.04 LTS系统默认源sources.list文件的修改

    ubuntu20.04 LTS系统默认源sources.list文件的修改

    这篇文章主要介绍了ubuntu20.04 LTS系统默认源sources.list文件的修改,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-08-08
  • Linux系统设置开机自动运行脚本的方法实例

    Linux系统设置开机自动运行脚本的方法实例

    这篇文章主要给大家介绍了关于Linux系统设置开机自动运行脚本的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用Linux系统具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2020-06-06
  • 使用 Apache Web 服务器配置两个或多个站点的方法

    使用 Apache Web 服务器配置两个或多个站点的方法

    这篇文章主要介绍了使用 Apache Web 服务器配置多个站点的方法,本文给大家介绍的非常详细,具有一定的参考借鉴价值,需要的朋友可以参考下
    2018-10-10
  • Linux安装NodeJs并配合Nginx实现反向代理

    Linux安装NodeJs并配合Nginx实现反向代理

    本篇文章主要介绍了Linux安装NodeJs并配合Nginx实现反向代理,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-11-11

最新评论