跳转到主内容
极星编程网:以代码为星,赴技术山海!

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

目录匿名管道

1. 管道|的基本行为

示例 1(stdout 被管道传递)

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

2. 为什么|不处理 stderr

3. 原理

命名管道简介使用创建 FIFO FIFO 的特点如何使用 FIFO?

常见问题原理1. 普通文件 vs. 命名管道(FIFO)

2. 命名管道的工作原理

3. 关键点解析

4. 类比理解

总结匿名管道kubectl get pod -A | grep mysql上面命令行里的「|」竖线就是一个管道,在命令行(如 Linux Shell 或 Windows CMD/PowerShell)中,管道操作符|的作用是将前一个命令的标准输出(stdout)

传递给后一个命令的标准输入(stdin)

。但它默认不会传递标准错误(stderr)

,这是它的核心行为特点。

1. 管道|的基本行为

command1 | command2command1的stdout→ 作为command2的stdin。

command1的stderr→ 直接打印到终端,不会传递给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 的 FIFOpathname: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 #include #include

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

时:

磁盘上创建一个空文件(只有 inode,没有数据块)。

操作系统内核维护一个内存缓冲区(用于存储进程间传递的数据)。

(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 file,ref=1。

第二个进程打开时,ref=2(指向同一个内存缓冲区)。

关闭时ref--,ref=0时内核才释放资源。

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

FIFO 的设计目的是进程间通信,数据不需要持久化。

刷盘会拖慢速度,且毫无意义(通信完数据即可丢弃)。

(3)半双工通信同一时间只能单向流动(读或写)。

如果需要双向通信,需创建两个 FIFO(一个负责 A→B,一个负责 B→A)。

4. 类比理解

把 FIFO 想象成一条水管:写入端:进程 A 往水管里倒水(数据)。

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

特性:水管没有储水功能(数据不持久化)。

如果没人接水,倒水的人会等待(阻塞写入)。

如果没人倒水,接水的人会等待(阻塞读取)。

总结特性命名管道(FIFO)

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

简单来说:

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

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

您可能感兴趣的文章:Linux中的进程间通信之匿名管道解读Linux管道揭秘之匿名管道连接进程世界的方法Linux通过匿名管道进行进程间通信linux 匿名管道实例详解

相关文章