网站备案主体是,网站建设简历模板,html5 网站自适应,龙华区是深圳最差的区文章目录 命名管道什么是命名管道**命名管道 vs. 无名管道**如何创建命名管道 用命名管道实现进程间通信MakefileComm.hppServer.hppClient.hppServer.cppClient.cpp 效果总结 命名管道
什么是命名管道
命名管道#xff0c;也称为 FIFO#xff08;First In First Out#… 文章目录 命名管道什么是命名管道**命名管道 vs. 无名管道**如何创建命名管道 用命名管道实现进程间通信MakefileComm.hppServer.hppClient.hppServer.cppClient.cpp 效果总结 命名管道
什么是命名管道
命名管道也称为 FIFOFirst In First Out是一种 进程间通信IPC 机制它允许不相关的进程即没有父子关系的进程通过文件系统中的特殊文件进行数据传输。 命名管道 vs. 无名管道
类型说明适用场景匿名管道pipe() 创建仅限于父子进程之间通信适用于父进程创建子进程并通信命名管道mkfifo() 创建存在于文件系统中可用于任意进程间通信适用于独立进程间通信 如何创建命名管道 手动创建命名管道
mkfifo FIFO这个FIFO也是一个文件被操作系统特殊标记过是管道文件。 在C语言库中有一个函数也是mkfifo这个接口解决了进程间通信的问题。
用命名管道实现进程间通信 我们创建一下文件Client是模拟的客户端用于发送请求server是服务端用于接收请求Comm用于存储全局变量还有一些都要用到的函数Makefile用来编译可执行程序首先我们将Makefile完善。
Makefile
SERVERserver
CLIENTclient
ccg
SERVER_SRCServer.cc
CLIENT_SRCClient.cc.PHONY:all
all:$(SERVER) $(CLIENT)$(SERVER):$(SERVER_SRC)$(cc) -o $ $^ -stdc11
$(CLIENT):$(CLIENT_SRC)$(cc) -o $ $^ -stdc11.PHONY:clean
clean:rm -f $(SERVER) $(CLIENT)由于在Makefile中只能创建一个可执行文件所以我们需要加上这一段
这一段表示all这个伪目标依赖下面两个可执行程序所以不得不创建下面两个可执行程序来完成这个伪目标了。
Comm.hpp
#pragma once#include iostream
#include string
#include unistd.h
#include thread
#include fcntl.h
#include sys/types.h
#include sys/stat.h
using namespace std;//双方通信使用的文件
const string gpipefile ./fifo;
const mode_t gmode 0600;//允许拥有者写所属组和other什么权限都没有
const int gdefulted -1;
const int gsize 1024;
const int gForRead O_RDONLY;
const int gForWrite O_WRONLY;//打开管道
int OpenPipe(int flag)
{//以只读方式打开int fd open(gpipefile.c_str(),flag);if(fd 0){cerropen errorendl;return false;}return fd;
}
void ClosePipeHelper(int fd)
{if(fd 0) close(fd);
}由于客户端和服务器都要打开管道和关闭管道所以我们将这两个接口放在Comm中但是因为这两个端的打开方式不一样所以我们用一个参数来代替调用这个函数的时候只需要传递调用的方式就行了。
Server.hpp
#pragma once
#include Comm.hppclass Init
{
public://创建文件Init(){umask(0);//先将缺省权限清零int n mkfifo(gpipefile.c_str(),gmode);if(n 0)//创建管道文件失败{cerrmkfifo errorendl;return;}coutmkfifo successendl;}//销毁文件~Init(){//删除当前路径下的文件int n unlink(gpipefile.c_str());//判断是否删除成功if(n 0){cerrunlink errorendl;return;}coutunlink successendl;}
};Init init;class Server
{
public://初始化Server():_fd(gdefulted){}bool OpenPipeForRead(){_fd OpenPipe(gForRead);if(_fd 0)return false;return true;}//读取管道//string *out:输出型参数//const string 输入型参数//string 输入输出型参数 int RecvPipe(string *out){char buffer[gsize];//读取文件ssize_t n read(_fd,buffer,sizeof(buffer)-1);//预留一个空间后面是期望读这么多n是实际读取的字节数if(n 0)//读取成功{buffer[n]0;//读取成功将buffer带出去*out buffer;}//返回read实际的字节数return n;}void ClosePipe(){ClosePipeHelper(_fd);}~Server(){}
private:int _fd;
};首先是创建管道文件我们封装一个类用于管理管道文件的创建和销毁声明一个全局变量构造函数用于创建管道析构函数用于销毁管道由于全局变量的生命周期是和程序一样的所以当程序结束的时候管道文件也跟着销毁也意味着通信结束。 Server是客户端用于接收请求意思是我们要将Client发送的信息输出到屏幕上。 这个函数就是用于读取管道内部客户端发送的请求。
Client.hpp
#pragma once
#include Comm.hpp//看到同一份资源class Client
{
public://初始化Client():_fd(gdefulted){}//打开管道bool OpenpipeForWrite(){_fd OpenPipe(gForWrite);if(_fd 0) return false;return true;}//发送管道int SendPipe(const string in){return write(_fd,in.c_str(),sizeof(in));}//关闭管道void ClosePipe(){ClosePipeHelper(_fd);}~Client(){}
private:int _fd;
};Client唯一和Server不一样的就是一个是读取一个是发送而Client就是发送向服务器发送信息。 所以这里直接向管道中写即可。
string *out:输出型参数 | const string 输入型参数 | string 输入输出型参数 上面是每个参数的意义
Server.cpp
#include Server.hppint main()
{Server server;server.OpenPipeForRead();string message;while(true){if(server.RecvPipe(message) 0){coutclient Say#messageendl;}else break;}coutclient quit,me tooendl;server.ClosePipe();return 0;
}Client.cpp
#include Client.hppint main()
{Client client;client.OpenpipeForWrite();string message;while(true){coutplease Enter#;getline(cin,message);client.SendPipe(message);//这里的message从标准输入来}client.ClosePipe();return 0;
}效果 当客户端关闭时服务器也会跟着关闭
总结
命名管道FIFO作为 Linux 进程间通信IPC的一种机制提供了一种基于文件系统的数据传输方式使得不相关进程之间也能进行数据交换。相比于无名管道它具有更高的灵活性不需要父子进程关系适用于生产者-消费者模式、日志收集、进程调试等场景。
通过 mkfifo 创建命名管道我们可以实现进程间的数据流动而不必使用共享内存或消息队列等复杂机制。命名管道不仅支持流式数据传输还能够跨终端、跨进程进行数据交互极大简化了进程间通信的实现。
总结来说命名管道是一种简单、高效、灵活的 IPC 机制适用于轻量级的数据传输需求在系统编程和日常应用中都有着广泛的应用。
通过实践我们也看到了命名管道的易用性与强大功能它使得开发者能够更加高效地实现进程间的数据交换促进了软件系统的模块化与解耦。