在 Linux 系统中,通道(Channel)通常指代进程间通信所使用的管道机制。最典型的两种形态是匿名管道(pipe)与命名管道(FIFO)。它们都由内核提供缓冲区,数据从一端写入、另一端读出,整个过程不落地到磁盘,因此比普通文件读写更轻量。很多后台服务和 Shell 命令组合都依赖这种机制完成数据接力。

匿名管道的工作原理与创建方式
匿名管道是 Linux 中最基础的进程间通信手段之一,它通过 pipe() 系统调用创建,返回两个文件描述符:一个用于读,一个用于写。管道在内核中开辟一块环形缓冲区,写入端将数据 copies 进内核空间,读取端再从内核空间取走,用户态程序只看到两个 fd,感知不到中间细节。由于管道没有名字,只能用于具有亲缘关系的进程,例如父进程创建管道后 fork() 出子进程,双方关闭不需要的方向,从而形成单向数据流。
在使用匿名管道时,必须注意描述符的关闭逻辑。若父进程只负责写入,就应关闭读端;子进程只负责读取,就应关闭写端。如果某一方未关闭多余的描述符,可能导致 read() 永远不返回 EOF,因为内核认为还有写入者存在。以下 C 代码展示了父进程向子进程发送字符串的基本做法:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main() {
int fd[2];
pipe(fd); // 创建匿名管道,fd[0]读,fd[1]写
pid_t pid = fork();
if (pid == 0) {
// 子进程
close(fd[1]); // 关闭写端
char buf[64];
read(fd[0], buf, sizeof(buf));
printf("child got: %sn", buf);
close(fd[0]);
} else {
// 父进程
close(fd[0]); // 关闭读端
const char *msg = "hello pipe";
write(fd[1], msg, strlen(msg) + 1);
close(fd[1]);
}
return 0;
}
匿名管道默认是阻塞的:当缓冲区满时写操作会休眠,当缓冲区空时读操作会等待。这种同步特性简化了数据流控制,但也可能引发死锁,比如父子进程都等待对方先动作。在 Shell 中,ls | grep txt 就是匿名管道的典型应用,ls 的标准输出接到管道写端,grep 的标准输入接到管道读端,两个进程并行运行,由内核调度数据流动。
命名管道(FIFO)的文件系统形态与跨进程通信
命名管道在文件系统中以特殊节点存在,类型为 p,可通过 mkfifo 命令或 mkfifo() 函数创建。与匿名管道不同,它有路径名,任意进程只要有权限就能打开这个节点进行通信,不再要求父子关系。命名管道仍然只借内核缓冲区传输数据,文件本身不保存内容,ls 看到的大小通常为 0。这一特性让它非常适合作为两个独立服务之间的临时数据桥。
打开命名管道的行为具有阻塞语义:默认情况下,只读打开会等待某个进程以写方式打开,只写打开会等待读方出现,双方会合后才继续。若指定 O_NONBLOCK 标志,则打开动作立即返回,但后续读写仍需遵循管道空满规则。下面示例展示如何用 C 创建并在不同进程中使用 FIFO:
// 写进程
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <string.h>
int main() {
mkfifo("/tmp/myfifo", 0666); // 创建命名管道
int fd = open("/tmp/myfifo", O_WRONLY);
write(fd, "fifo data", 9);
close(fd);
return 0;
}
命名管道在 Shell 脚本里也很常见,例如用 mkfifo /tmp/log_pipe 建立通道,让日志采集进程读取,业务进程向其中写入,彼此解耦。它的缺点是权限配置不当可能被其他用户读取,且通道生命周期需手动清理,否则残留节点会占用目录。相比 TCP 套接字,FIFO 省去了协议栈开销,但仅限单机,无法跨主机。
通道的阻塞、缓冲区与数据完整性要点
无论是匿名还是命名管道,底层都依赖有限的内核缓冲区,早期 Linux 默认 64KB,后续版本可调整。当写端速度远超读端,缓冲区写满后写调用阻塞,这是天然的背压机制。若写端全部关闭,读端 read() 返回 0 表示 EOF;若读端全部关闭,写端继续写会收到 SIGPIPE 信号,默认终止进程,因此网络服务中常忽略该信号并检测写错误。
数据完整性方面,管道是字节流而非消息边界,多次写入可能被一次读走,或一次写入分多次读。如果需要区分记录,必须在应用层加分隔符或长度头。原子性方面,当写入量小于 PIPE_BUF(通常 4096)时,内核保证写入原子,不会与其他进程写交叠;超过该值则可能被拆分。以下 Shell 片段演示两个进程并发写小数据时的安全边界:
mkfifo /tmp/p for i in 1 2 3; do echo "msg-$i" > /tmp/p & done cat /tmp/p
理解这些细节后,就能在日志汇聚、流式处理、父子任务编排中合理使用 Linux 通道。它虽简单,却构成了大量命令行组合与守护进程协作的隐形骨架。掌握其阻塞点与关闭规则,可以避免多数静默卡死与数据截断问题。
linuxpipenamed_pipe修改时间:2026-08-17 20:42:34