命名管道
一、特点
有名字 :在文件系统中以特殊文件形式存在(Linux/Unix 叫 FIFO 文件,Windows 是命名管道)
跨进程 :不要求进程有父子 / 兄弟关系,任何进程知道名字就能通信
半双工:数据单向流动(一端写、一端读),要双向通信需创建两个管道
二、本质
命名管道 = 操作系统内核开辟的一段【内存缓冲区】 + 文件系统中一个【名字入口】
它不是真正的文件 ,只是借用文件系统的名字,让任意进程都能找到它。
三、核心
1. 它在内核里,不在磁盘上
- 数据不存硬盘 ,只存在内核缓冲区(重点)
- 读写速度极快,和文件 IO 完全不是一回事
- 关闭后数据消失,不持久
2. 为什么叫 "命名"?
因为:
- 匿名管道只能父子进程用
- 命名管道在文件系统有路径名 (如
/tmp/mypipe) - 任何进程只要知道这个名字,就能打开通信
这就是它能跨进程、跨终端、无亲缘关系通信的根本原因。
四、见识命名管道
1. 命令mkfifo

2. C语言封装的mkfifo函数
c++
int mkfifo(const char* pathname, mode_t mode);
(1)功能 :在文件系统中创建一个 命名管道(FIFO)特殊文件
(2)返回值:
0:创建成功-1:创建失败(比如文件已存在、权限不够等)
(3)参数 :const char* pathname
作用:给命名管道起名字 + 放哪里
例如:
c++
mkfifo("./n_pipe", 0666);
/home/zincsweet/VSCode_remote/Linux2_vsc/test_named_pipe/n_pipe就是管道名字- 本质:文件路径字符串
- 任意进程通过这个路径就能找到同一个管道
(4)参数 :mode_t mode
作用:设置管道的访问权限(和 chmod 一样)
常用写法:
c++
0666 所有进程可读可写(最常用)
0644 自己可读写,别人只读
注意:
- 前面的 0 不能丢,表示八进制
- 权限最终 =
mode & ~umask(系统会做权限掩码)
五、代码运用
1. 初步创建命名管道



函数补充(1) access
c++
#include <unistd.h>
int access(const char *pathname, int mode);
作用:检查当前进程 对某个文件 / 路径 是否有 某种权限 / 是否存在,它不打开文件 ,只是做检查。
参数说明
-
pathname:如"./fifo" -
mode:(检查存不存在,还是权限)-
F_OK:文件是否存在 -
R_OK:是否可读 -
W_OK:是否可写 -
X_OK:是否可执行
-
返回值
- 0 :检查 成功(存在 / 有权限)
- -1 :检查 失败(不存在 / 没权限)
常见用法:
c++
// 判断管道是否存在
if (access("./fifo", F_OK) == -1) {
// 不存在 → 创建
mkfifo("./fifo", 0666);
}
函数补充(2) unlink
c++
#include <unistd.h>
int unlink(const char *pathname);
作用:删除一个文件(包括:普通文件、软链接、命名管道 FIFO) ,就是代码里的 rm 文件名 命令。
返回值
- 0:删除成功
- -1:删除失败(文件不存在、权限不够等)
和命名管道的关系
mkfifo 创建的管道文件会一直留在文件系统里,程序退出也不会消失。
所以:
- 程序启动前 :用
access判断是否存在 - 程序退出后 :用
unlink删除管道文件 - 否则下次运行可能出问题
2. 完善管道的查存、销毁


3. 打开管道


4. 信息收发



测试代码



六、完整代码

cpp
// 核心文件./pipe.hpp
#pragma once
#include <iostream>
#include <string>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <cstring>
const std::string gcommfile = "./fifo";
#define FIFO_READ 1
#define FIFO_WRITE 2
class Fifo {
public:
Fifo(const std::string& commfile = gcommfile)
: _commfile(commfile),
_mode(0666),
_fd(-1)
{}
// 1. 创建命名管道
void Build() {
if (IsExist()) {
std::cout << "命名管道已存在" << std::endl;
return;
}
int n = mkfifo(_commfile.c_str(), _mode);
if (n == -1) {
std::cerr << "创建命名管道失败: " << strerror(errno) << std::endl; // strerror(errno) 获取错误信息
} else {
std::cout << "命名管道创建成功" << std::endl;
}
}
// 2. 打开管道
void Open(int open_mode) {
// 管道为保障通信(读、写)双方同时存在,一端打开管道时会阻塞,直到另一方打开管道
// 判断打开模式
if (open_mode == FIFO_READ) {
_fd = open(_commfile.c_str(), O_RDONLY);
}
else if (open_mode == FIFO_WRITE) {
_fd = open(_commfile.c_str(), O_WRONLY);
}
else {
std::cerr << "无效的打开模式" << std::endl;
return;
}
if (_fd == -1) {
std::cerr << "打开命名管道失败: " << strerror(errno) << std::endl; // strerror(errno) 获取错误信息
} else {
std::cout << "命名管道打开成功" << std::endl;
}
}
// 信息通信
void Send(const std::string& msgin) {
if (_fd == -1) {
std::cerr << "管道未打开,无法发送消息" << std::endl;
return;
}
ssize_t n = write(_fd, msgin.c_str(), msgin.size());
if (n == -1) {
std::cerr << "发送消息失败: " << strerror(errno) << std::endl;
}
}
int Recv(std::string& msgout) {
if (_fd == -1) {
std::cerr << "管道未打开,无法接收消息" << std::endl;
return -1;
}
char buffer[1024];
ssize_t n = read(_fd, buffer, sizeof(buffer) - 1);
if (n == -1) {
// 接收消息失败
return -1;
} else if (n == 0) {
// 管道已关闭
return 0;
}
else {
buffer[n] = '\0'; // n 是实际读取的字节数,确保字符串正确结束,用n下标设置'\0'
msgout = buffer;
return n;
}
}
// 3. 销毁管道
void Destroy() {
// unlink() 函数用于删除一个文件或命名管道
int n = unlink(_commfile.c_str());
if (n == -1) {
std::cerr << "销毁命名管道失败: " << strerror(errno) << std::endl; // strerror(errno) 获取错误信息
} else {
std::cout << "命名管道销毁成功" << std::endl;
}
}
~Fifo() {}
private:
bool IsExist() {
// access() 函数用于检查文件是否存在
return access(_commfile.c_str(), F_OK) == 0;
}
private:
std::string _commfile;
mode_t _mode = 0666;
int _fd;
};
cpp
// 文件./client.cpp
#include <iostream>
#include "pipe.hpp"
int main() {
// 打开管道
Fifo fileclient;
fileclient.Open(FIFO_WRITE);
while (true) {
std::cout << "Client@请输入要发送的消息: ";
std::string msg;
std::getline(std::cin, msg);
fileclient.Send(msg);
}
return 0;
}
cpp
// 文件./server.cpp
#include <iostream>
#include "pipe.hpp"
int main() {
// 创建管道 && 打开管道
Fifo pipefifo;
pipefifo.Build();
pipefifo.Open(FIFO_READ);
std::string msg;
while (true) {
int n = pipefifo.Recv(msg);
if (n == -1) {
std::cerr << "client:接收消息失败" << std::endl;
break;
}
else if (n == 0) {
std::cout << "client:管道已关闭" << std::endl;
break;
}
else {
std::cout << "client:收到消息: " << msg << std::endl;
}
}
pipefifo.Destroy();
return 0;
}