
于此止?:个人主页
❄️个人专栏传送门:《Linux篇》 《QT百日筑基篇》 《算法篇》
《C++与STL剖析篇》 《数据结构篇》 《Python基础篇》
⭐️纵有狂风拔地起,我亦乘风破万里⭐️

命名管道的深度解析
前言
相对于我们的匿名管道(匿名管道只能让我们有血缘关系的进程进行通信),而我们也想让我们没有血缘关系的进程进行通信,因此我们的命名管道就来了。
目录/索引
目录
命名管道的深度解析
前言
目录/索引
一、命名管道的特点
二、创建命名管道。
1.使用命令行命令的方法进行创建
2.使用函数来进行创建我们的命名管道
三、删除管道文件
1.命令行的形式删除
rm
unlink
2.在程序中使用完进行删除
四、匿名管道和命名管道的区别
五、命名管道的打开方式
六、命名管道的进阶
1.使用命名管道制作用命名管道拷贝对应的文件内容
2.用命名管道实现两个进程之间的通信
3.面向过程之间的封装:(封装第二个)
commit.hpp
select.cc
click:
一、命名管道的特点
1.匿名管道的的限制是只能有血缘关系的进程可以进行通信。而我们的命名管道打破了这一观点。
2.命名管道是一种特殊的文件类型(通常是一个以p开头的文件)。
3.操作简单,我们创建好了管道之后就是基于我们文件进行操作了。
4.在一定情况下节省空间,os不会做浪费空间和时间的事情。
5.命名管道通过打开同一路径下的同一个文件,文件有路径和名字。路径具有唯一性。名字代表我们的命名管道。
二、创建命名管道。
1.使用命令行命令的方法进行创建
mkfifo 文件名
我们发现果然我们创建好了对应的命名管道, 他的名字我们设置为namepipo,并且该文件是以p来进行开头的。
0字节应该只是带有某种的属性。

2.使用函数来进行创建我们的命名管道
我们先开一下官方文档:
man 3 mkfifo
int mkfifo(const char *pathname, mode_t mode);
我们的第一个参数是我们的要创建文件的文件名, 也就是我们管道的名字。
我们的第二个参数,我们发现很眼熟,我们在使用open的时候使用过,是我们创建这个文件的权限。所以这个位置是我们的权限位。


返回值:
成功返回0,失败返回-1

那我们就创建一个管道来看一下
我们发现我们果然创建好了对应的管道文件。

命名管道不能重复进行创建,否则可能会报错,注意管道的先后顺序, mkfifo是创建管道,不像open那样没有就直接新建,有了也可以打开。
三、删除管道文件
我们不想使用对应的管道文件,我们可以进行删除。
1.命令行的形式删除
rm
rm -rf 管道名

unlink
unlink 管道名

2.在程序中使用完进行删除
我们使用unlink的系调用

#include<iostream>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main()
{
umask(0);
int retfifo = mkfifo("test1", 0666);
//一系列操作
unlink("test1");
return 0;
}
我们在使用完的时候直接的进行了对应的销毁,所以我们后续查看文件目录的时候看不到对应的目录文件。

返回值:
成功返回0,失败返回-1 错误码被设置。

四、匿名管道和命名管道的区别
1.匿名管道有pipe进行创建。
2.命名管道有我们的mkfifo进行创建。然后由open打开。
3.Fifo命名管道和pipe匿名管道之间的唯一区别是他们创建不同,我们的fifo是由我们的mkfifo进行创建后直接使用open进行打开, 而我们的pipe是每次都要申请管道用对应的管道进行通信
4.命名管道的特性与匿名管道相似,只是我们的命名管道可以让不同的进程进行交流,而我们的匿名管道只能让和自己有血缘关系的进程进行交流。
五、命名管道的打开方式
- 如果当前打开操作是为读而打开 FIFO 时
- O_NONBLOCK disable:阻塞直到有相应进程为写而打开该 FIFO
- O_NONBLOCK enable:立刻返回成功
- 如果当前打开操作是为写而打开 FIFO 时
- O_NONBLOCK disable:阻塞直到有相应进程为读而打开该 FIFO
- O_NONBLOCK enable:立刻返回失败,错误码为 ENXIO
只有我们读写均打开才能进行对应的写入。
一句话总结: 我们的w方没有进行我们的open的时候,我们的read方主要在open方进行阻塞, 只有双方都进行了对应的open后我们才能双方开始通信。
六、命名管道的进阶
1.使用命名管道制作用命名管道拷贝对应的文件内容
-首先我们要进行相一下我们要怎么进行相对的文件之间进行拷贝呢?
我们一定要有一个文件我们这里叫作"test.txt" 拿这个文件的数据去拷贝到其他的文件中去, 我们要创建两个进程创建两个不同的进程, 我们要其中的一个进程创建管道,一个进程进行读文件的内容,写道管道中,另一个进程读管道,并将管道的内容写到我们想拷贝到的文件中,我们这里选择创建两个文件,一个是"file.cc"和“copy_file.cc”这两个文件主要是干什么呢???
我们规定我们的copy_file.cc用来创建我们的管道我们这里命名为:NamePipe并且这个文件用来进行读文件写管道。
我们对应的实现:
#include"comm.hpp"
int main()
{
umask(0);
//创建namepipe
int ret_fifo = mkfifo("NamePipe",0666);
if(ret_fifo < 0) EXIT("ret_fifo fail!");
//读
int fd1 = open("test.txt", O_RDONLY);
if(fd1 < 0) EXIT("open fail!");
char buffer[RD_MAX];
int fd2 = open("NamePipe", O_WRONLY| O_TRUNC);
while(1)
{
buffer[0]=0;
int ret_rd = read(fd1, buffer, sizeof(buffer));
if(ret_rd < 0) EXIT("read fail!");
else if(ret_rd == 0) break;
else
{
//写入管道
buffer[ret_rd] = 0;
write(fd2,buffer,strlen(buffer));
}
}
return 0;
}
我们再次创建一个file.cc文件这个文件用来读管道,写被拷贝的文件。我们要注意我们的这个文件要进行创建对应的文件我们一定要设置对应的权限掩码。
我们对应的实现:
#include "comm.hpp"
int main()
{
//文件操作,
int fd1 = open("NamePipe", O_RDONLY);
if(fd1<0) EXIT("1open fail!");
int fd2 = open("copy.txt", O_WRONLY | O_TRUNC | O_CREAT,0666);
if(fd2<0) EXIT("2open fail!");
char buffer[RD_MAX];
while(1)
{
buffer[0]=0;
int ret_rd = read(fd1,buffer, sizeof(buffer));
if(ret_rd < 0) EXIT("read fail!");
else if(ret_rd == 0) break;//读完了
else
{
//写道另一个文件中
buffer[ret_rd] =0;
write(fd2, buffer,strlen(buffer));
}
}
return 0;
}
这里我们将用到的头文件一块包含到我们的comm.hpp文件统一进行管理:
#pragma once
#include<iostream>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
#include<cstring>
#define RD_MAX 1024
#define EXIT(m) do{perror(m);exit(EXIT_FAILURE);}while(0)
查看我们的test.txt文件随便写一部分内容
只要将我们的对应的内容完整的进行拷贝就可以了。

我们编译make一下:
这里的makefile
.PHONY:all
all:
g++ -o copy_file copy_file.cc
g++ -o file file.cc
.PHONY:clean
clean:
rm -f copy_file
rm -f copy.txt file
unlink NamePipe
make编译,先运行创建管道的copy_file 在运行另一个file,果然是一样的。
我们查看对应的文件内容:

运行:
需要两个能运行的我这里一个是vscode一个是xshell,也可以2个xshell同时执行。

2.用命名管道实现两个进程之间的通信
我们要实现的效果:
现在我们要进行创建两个进程来进行两个进程之间的相互交流。我们在一个进程上发信息让另一个进程也进行收到。
首先我们先将我们对应的头文件进行创建在一个文件中:
#pragma once
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define PATH "mypipe"
#define COM_MAX 1024
我们创建对应的select.cc,我们在这个文件进行在管道中进行读取文件, 读取我们的前面的进程发送过来的信息。
#include "commit.hpp"
int main() {
// 服务端读
int fd = open(PATH, O_RDONLY);
if (fd < 0) {
perror("open fail");
exit(1);
}
char buffer[COM_MAX];
printf("select start!!!\\n");
fflush(stdout);
while (1) {
buffer[0] = 0;
ssize_t re = read(fd, buffer, sizeof(buffer));
buffer[re – 1] = 0;
if (re < 0) {
perror("read fail!!!");
exit(1);
}
std::cout << buffer << std::endl;
}
close(fd);
return 0;
}
我们的客户端click用来创建我们的管道,并进行我们对应文件的写入。
#include "commit.hpp"
int main() {
unlink(PATH);
int ret = mkfifo(PATH, 0666);
if (ret < 0) {
perror("mkfifo fail");
exit(1);
}
int fd = open(PATH, O_WRONLY | O_TRUNC);
if (fd < 0) {
perror("open fail!!!");
exit(1);
}
printf("client start!!!\\n");
fflush(stdout);
while (1) {
char buffer[COM_MAX];
buffer[0] = 0;
fgets(buffer, sizeof(buffer), stdin);
write(fd, buffer, strlen(buffer));
}
close(fd);
return 0;
}
运行发现我们果然能进行对应的交流。

3.面向过程之间的封装:(封装第二个)
封装后的代码:
我们充分的进行利用cpp的特性,这样让我们的代码更加的美观。
commit.hpp
#pragma once
#include <cstring>
#include <fcntl.h>
#include <iostream>
#include <stdio.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#define PATH "mypipe"
#define COM_MAX 1024
#define SEV "serve"
#define CLI "click"
class Pipe {
public:
Pipe(std::string user, std::string path=PATH) : _user(user), _path(path) {
if (user == CLI) {
int ret = mkfifo(path.c_str(), 0666);
if (ret < 0) {
perror("mkfifo fail");
exit(1);
}
}
}
void Open(int type) {
_fd = open(_path.c_str(), type);
if (_fd < 0) {
perror("open fail!!!");
exit(1);
}
}
void Write() {
char buffer[COM_MAX];
printf("client start!!!\\n");
fflush(stdout);
while (1) {
buffer[0] = 0;
fgets(buffer, sizeof(buffer), stdin);
write(_fd, buffer, strlen(buffer));
}
}
void Read() {
char buffer[COM_MAX];
printf("select start!!!\\n");
fflush(stdout);
while (1) {
buffer[0] = 0;
ssize_t re = read(_fd, buffer, sizeof(buffer));
buffer[re – 1] = 0;
if (re < 0) {
perror("read fail!!!");
exit(1);
}
std::cout << buffer << std::endl;
}
}
~Pipe() {
if (_user == CLI) {
unlink(PATH);
}
close(_fd);
}
private:
int _fd;
std::string _user;
std::string _path;
};
select.cc
#include "commit.hpp"
int main() {
Pipe pe("serve");
pe.Open(O_RDONLY);
pe.Read();
return 0;
}
click:
#include "commit.hpp"
int main() {
Pipe pe("click");
pe.Open( O_WRONLY | O_TRUNC);
pe.Write();
return 0;
}
效果展示:




