目录
1 匿名管道
1.1 创建匿名管道
1.1.1 pipe
1.2 匿名管道的使用
1.3 内核实现原理
1.3.1 内核数据结构
1.3.2 数据流动过程
1.3.3 阻塞与非阻塞
1.4 管道的五大特性和四种情况
2 命名管道
2.1 创建命名管道
2.1.1 mkfifo
2.2 命名管道的使用
2.3 命名管道的原理
2.4 删除命名管道
2.4.1 unlink
管道是一个非常古老的进程通信的方式。管道允许两个相关进程(通常是父子进程)通过一个单向或双向的数据通道交换数据
管道是一个基于文件系统的一个内存级的单向通信的文件,主要用来进行进程通信(IPC)
(1)管道使用样例
lz@VM-8-15-ubuntu:~/learn$ ls -l | grep pipe

1 匿名管道
匿名管道(Anonymous Pipe)是 Linux 中一种经典的 进程间通信(IPC) 机制,主要用于 父子进程 或 兄弟进程 之间的单向数据通信。其核心特点是 内存级高效传输、内核缓冲区管理 和 半双工通信
1.1 创建匿名管道
1.1.1 pipe
(1)功能
用于创建一个 匿名管道(Anonymous Pipe),实现父子进程间的单向通信
(2)函数原型
#include <unistd.h>
int pipe(int pipefd[2]);
(3)参数
- pipefd[2]:长度为 2 的整型数组,用于返回两个文件描述符:
- pipefd[0]:管道的 读端(用于 read())。
- pipefd[1]:管道的 写端(用于 write())。
(4)返回值
成功返回 0,失败返回 -1 并设置 errno。
1.2 匿名管道的使用
(1)父进程读数据,子进程写数据
#include <cstdio>
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main()
{
// 创建管道
int pipefd[2];
int n=pipe(pipefd);
// 创建子进程
pid_t pid = fork();
// 通信
if(pid == -1)
{
perror("fork");
return 1;
}
else if(pid == 0)
{
// 关闭读端
close(pipefd[0]);
// 子进程
int cnt=1;
while(1)
{
// 写入数据
std::string msg="my child process, pid = ";
msg+=std::to_string(pid);
msg += ", content = ";
msg+=std::to_string(cnt);
cnt++;
// 写入管道
int n = write(pipefd[1],msg.c_str(),msg.size());
sleep(1);
}
}
else
{
// 关闭写端
close(pipefd[1]);
// 父进程
char buff[1024]={0};
while(1)
{
// 读取数据
int n=read(pipefd[0],buff,sizeof(buff)-1);
buff[n]='\\0';
std::cout<<buff<<std::endl;
sleep(1);
}
}
// 等待子进程
pid_t status=waitpid(pid,NULL,0);
return 0;
}
注意事项:
(1)必须关闭未使用的端:
- 父进程不关闭读端 → 写满管道后 write() 阻塞。
- 子进程不关闭写端 → 读空管道时 read() 不会返回 EOF(返回0)。
(2)创建管道需要在创建子进程前,才能保证子进程与父进程通信的是同一个管道
(3)管道是字节流,无消息边界
1.3 内核实现原理
1.3.1 内核数据结构
管道在内核中由 pipe_inode_info 管理
struct pipe_inode_info {
struct page *pages[PIPE_DEF_BUFFERS]; // 环形缓冲区(默认16页)
unsigned int head; // 写指针
unsigned int tail; // 读指针
unsigned int readers; // 读端引用计数
unsigned int writers; // 写端引用计数
wait_queue_head_t wait; // 阻塞等待队列
// …
};
1.3.2 数据流动过程
(1)写入数据(write(fd[1], buf, size)):
- 检查缓冲区剩余空间,不足则阻塞(除非 O_NONBLOCK)。
- 数据从用户空间拷贝到内核环形缓冲区。
- 更新 head 指针,唤醒等待读的进程。
(2)读取数据(read(fd[0], buf, size)):
- 检查缓冲区数据量,为空则阻塞(除非 O_NONBLOCK)。
- 数据从内核缓冲区拷贝到用户空间。
- 更新 tail 指针,唤醒等待写的进程。
1.3.3 阻塞与非阻塞
(1)阻塞模式(默认):
- 读空管道时,read() 阻塞,直到有数据或写端关闭。
- 写满管道时,write() 阻塞,直到有空间。
(2)非阻塞模式
fcntl(pipefd[0], F_SETFL, O_NONBLOCK); // 读端非阻塞
fcntl(pipefd[1], F_SETFL, O_NONBLOCK); // 写端非阻塞
- 读空管道时,read() 返回 -1,errno=EAGAIN。
- 写满管道时,write() 返回 -1,errno=EAGAIN。

1.4 管道的五大特性和四种情况
(1)五大特性
- 常用于具有血缘关系的进程进行PIC
- 单向通信
- 匿名管道的生命周期随进程
- 匿名管道是面向字节流的
- 管道自带同步机制
(2)四种情况
- 写端不关,写端不写——管道里没有数据,读端就会处于阻塞状态
- 读端不关,读端不读——写端写满管道,就不会进行写入
- 写端不写,写端关闭——读端不会读到数据,read的返回值为0表示已经读到文件结尾
- 写端写入,读端关闭——操作系统会自动结束写端进程
2 命名管道
命名管道(Named Pipe,也称为
FIFO
,即
First In First Out
)是 Linux 中一种进程间通信(IPC, Inter-Process Communication)的方式。
它类似于匿名管道(
|
)
- 匿名管道 只能在父子进程或相关进程之间使用。
- 命名管道 通过文件系统中的一个特殊文件(FIFO 文件)进行通信,允许 无关联的进程 进行数据交换。
FIFO(命名管道)与pipe(匿名管道)之间唯⼀的区别在它们创建与打开的⽅式不同,⼀但这些⼯作完成之后,
它们具有相同的语义。命名管道主要解决毫无关系的进程之间,进行文件级进程通信
2.1 创建命名管道
(1)命令行创建
// 使用 mkfifo 命令创建 FIFO 文件
mkfifo mypipe
// 使用 mkfifo 命令创建 FIFO 文件,指定权限(如 660)
mkfifo -m 660 mypipe
2.1.1 mkfifo
(1)功能
mkfifo 用于创建一个 FIFO(命名管道),它是一种特殊的文件类型,允许不同进程通过文件系统进行通信。FIFO 遵循先进先出(First In First Out)原则。
(2)函数原型
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
(3)参数
- pathname : 创建的有名管道的名字
- mode : 创建文件的权限
(4)返回值
- 成功返回 0,失败返回 -1 并设置 errno。
2.2 命名管道的使用
命名管道的使用本质就是对一个打开的文件进行操作
示例一:⽤命名管道实现 server(服务端) & client(客户端) 通信
有名管道的学习代码,使用有名管道简单的实现服务端和客户端的进程通信 · d2c9787 · lv-zhuo/AsCent – Gitee.com
2.3 命名管道的原理
(1)FIFO 是一种特殊的 文件类型,但 不存储实际数据,仅作为数据通道。
(2)写入端 (>) 和读取端 (<) 必须同时存在,否则:
- 如果只有写入端,写入操作会 阻塞,直到有进程读取。
- 如果只有读取端,读取操作会 阻塞,直到有进程写入。

2.4 删除命名管道
(1)命令行删除
FIFO 文件只是普通文件,可以用 rm 删除
rm mkpipe
2.4.1 unlink
(1)功能
mkfifo 用于在当前目录下删除一个指定名字的文件
(2)函数原型
#include <fcntl.h>
#include <unistd.h>
int unlink(const char *pathname);
(3)参数
- pathname : 删除文件的名字
(4)返回值
- 成功返回 0,失败返回 -1 并设置 errno。
网硕互联帮助中心






评论前必须登录!
注册