
在操作系统的寂静星辰之间,进程如宇宙中的恒星各自运转,孤独却不懈。而在某一刻,它们渴望对话,渴望将彼此的思想穿越内存的风暴,投递至彼此心中。于是,
进程间通信(IPC)如星际信使般诞生。而在众多信使中,有一种古老而优雅的方式被称为:命名管道(Named Pipe,亦称FIFO)。
这便是我们今天的主角——在系统的寂静深处低语的“命名管道”。
命名管道,是一种半双工通信机制,允许两个不具亲缘关系的进程通过内核中一个具名的管道文件进行通信。
正如信使需要地址,命名管道也需栖身于文件系统中,等待两端的进程将心语倾诉或倾听。
与匿名管道(pipe)不同,命名管道拥有一个路径名,存在于文件系统中,通常通过命令 mkfifo 或系统调用mkfifo()创建。即便通信双方毫无血缘(非父子进程),只要知晓这路径,便能互通心声。
那么如何给 匿名管道 起名字呢?
结合文件系统,给匿名管道这个纯纯的内存文件分配 inode,将文件名与之构建联系,关键点在于不给它分配 Data block,因为它是一个纯纯的内存文件,是不需要将数据刷盘到磁盘中的
可以将命名管道理解为 “挂名” 后的匿名管道,把匿名管道加入文件系统中,但仅仅是挂个名而已,目的就是为了让其他进程也能看到这个文件(文件系统中的文件可以被所有进程看到)
因为没有 Data block,所以命名管道这个特殊文件大小为 0
命令管道的创建依赖于函数 mkfifo,函数原型如下

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);关于 mkfifo 函数

/home/xxx/namePipeCode/fifo,也可以传递相对路径 ./fifo,当然绝对路径更灵活,但也更长
mode_t 其实就是对 unsigned int 的封装,等价于 uint32_t,而 mode 就是创建命名管道时的初始权限,实际权限需要经过 umask 掩码计算
不难发现,mkfifo 和 mkdir 非常像,其实 mkfifo 可以直接在命令行中运行
创建一个名为fifo的命名管道文件
mkfifo fifo
p是管道文件,大小为0

这个管道文件也非常特殊:大小为 0,从侧面说明 管道文件就是一个纯纯的内存级文件,有自己的上限,出现在文件系统中,只是单纯挂个名而已
可以直接在命令行中使用命名管道:
echo 可以进行数据写入,可以重定向至 fifocat 可以进行数据读取,同样也可以重定向于 fifo打开两个终端窗口(两个进程),即可进行通信

命名管道遵循管道的四种场景,因此在写端未写入数据时,读端会阻塞
当然也可以通过程序实现两个独立进程 IPC
思路:创建 服务端 server 和 客户端 client 两个独立的进程,服务端 server 创建并以 读 的方式打开管道文件,客户端 client 以 写 的方式打开管道文件,打开后俩进程可以进程通信,通信结束后,由客户端关闭 写端(服务端 读端 读取到 0 后也关闭并删除命令管道文件)
注意:
server 创建管道文件 客户端(写端)写入数据unlink 命令管道文件名 //删除管道文件为了让服务端和客户端能享有同一个文件名,可以创建一个公共头文件 common.h,其中存储 命名管道文件名及默认权限等公有信息
公共资源
common.h
#pragma once
#include <iostream>
#include <string>
std::string fifo_name = "./fifo"; //管道名
uint32_t mode = 0666; //权限服务端(写端) server.cc 提供文件拷贝服务
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"
using namespace std;
int main()
{
// 服务端
// 1、打开文件
int wfd = open(fifo_name.c_str(), O_WRONLY);
if (wfd < 0)
{
cerr << "open fail! errno: " << errno << " | " << strerror(errno) << endl;
exit(0);
}
// 2、打开源文件
FILE *fp = fopen("file.txt", "r");
if (fp == NULL)
{
cerr << "fopen fail! errno: " << errno << " | " << strerror(errno) << endl;
exit(0);
}
// 3、读取源文件数据
char buff[1024];
int n = fread(buff, sizeof(char), sizeof(buff), fp);
//IPC区域
// 4、写入源文件至命名管道
write(wfd, buff, strlen(buff));
cout << "服务端已向管道写入: " << n << "字节的数据" << endl;
//IPC区域
fclose(fp);
fp = nullptr;
close(wfd);
return 0;
}客户端(读端) client.cc 从服务端中拷贝文件(下载)
#include <iostream>
#include <cstring>
#include <cerrno>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "common.h"
using namespace std;
int main()
{
// 客户端
// 1、创建命名管道文件
int ret = mkfifo(fifo_name.c_str(), mode);
if (ret < 0)
{
cerr << "mkfifo fail! errno: " << errno << " | " << strerror(errno) << endl;
exit(0