命名管道(Named Pipe),也被称为FIFO(First In, First Out),是一种在Unix和Unix-like操作系统中用于进程间通信的特殊文件类型。它允许不相关的进程通过文件系统中的路径名进行通信。
命名管道(Named Pipe)是一种在Unix和Unix-like系统中用于进程间通信的特殊文件类型。它的作用主要体现在以下几个方面:
进程间通信: 命名管道提供了一种进程间通信的方式。不同的进程可以通过共享同一个命名管道文件进行通信,其中一个进程将数据写入管道,而另一个进程从管道中读取数据。这样,进程之间可以实现数据的交换和共享。
无关进程通信: 与匿名管道不同,命名管道可以用于无关的进程之间通信。因为命名管道在文件系统中有一个路径名,进程可以通过路径名来打开和访问它,而不需要共享相同的地址空间或具有亲缘关系。
独立于进程生命周期: 命名管道是持久的,它存在于文件系统中,不受创建它的进程的生命周期限制。这意味着即使创建命名管道的进程结束,其他进程仍然可以使用相同的管道进行通信。
阻塞式通信: 命名管道是阻塞的,即写入端会等待读取端准备好接收数据,反之亦然。这样可以协调进程之间的通信,确保数据的正确传输。
常用于Shell编程: 在Shell编程中,命名管道常用于将一个命令的输出传递给另一个命令,实现管道传输的同时也可以用于进程间通信。
总体而言,命名管道提供了一种方便、灵活且持久的进程间通信机制,适用于各种需要数据交换的场景。
文件系统中的路径名: 命名管道在文件系统中有一个路径名,类似于普通文件。进程可以通过打开这个路径名的方式来访问命名管道。
独立的进程通信: 不同进程可以通过共享同一个命名管道文件来进行通信。这使得命名管道成为不同进程之间进行进程间通信的有效手段。
创建和删除: 命名管道可以使用 mkfifo 函数来创建。创建后,它就成为文件系统中的一个特殊文件。当通信结束时,可以使用 unlink 函数删除命名管道。
读写操作: 进程可以像普通文件一样使用文件描述符对命名管道进行读取和写入操作,实现进程间的数据传输
命名管道(Named Pipe)和匿名管道(Anonymous Pipe)是两种不同类型的管道,它们在创建、使用和生命周期等方面有一些关键区别。
命名管道:
创建: 命名管道在文件系统中有一个路径名,可以通过 mkfifo 函数创建。它是一个具有持久性的文件,不依赖于创建它的进程的生命周期。
路径名: 命名管道有一个在文件系统中的路径名,因此可以被不同的进程通过路径名来访问。
无关进程通信: 不相关的进程可以通过共享同一个路径名的命名管道进行通信。
使用: 通过文件描述符进行读写操作,可以使用标准的文件I/O函数。
生命周期: 命名管道存在于文件系统中,直到显式删除。
匿名管道:
创建: 匿名管道是由 pipe 系统调用创建的,没有在文件系统中的路径名。它是一种临时的、仅存在于相关进程之间的通信机制。
路径名: 不存在路径名,只能在创建它的进程及其相关子进程之间使用。
有关进程通信: 主要用于有关系(亲缘关系)的父子进程之间通信。
使用: 通过文件描述符进行读写操作,可以使用标准的文件I/O函数。
生命周期: 随着创建它的进程或相关子进程的终止而结束。
综上所述,主要区别在于命名管道是具有路径名的、持久的文件,可以被不相关的进程使用;而匿名管道是临时的、只存在于相关进程之间的通信机制,主要用于父子进程之间通信。选择使用哪种管道取决于具体的应用场景和需求。
首先我们可以先看一下匿名管道的代码
#include <iostream>
#include <string>
#include <unistd.h>
#include <string.h>
#include <string>
#include <cstdio>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
using namespace std;
#define N 2
const int NUM = 1024;
void Write(int n)
{
string s = "hello,I am chile";
pid_t self = getpid();
char buffer[NUM]; // 缓冲区
int number = 0;
while (true)
{
buffer[0] = 0; // 字符串清空
snprintf(buffer, sizeof(buffer), "%s-%d-%d", s.c_str(), self, number++);
// cout << buffer << endl;
write(n, buffer, strlen(buffer));
sleep(1);
}
}
void Read(int rfd)
{
char buffer[NUM];
while (true)
{
buffer[0] = 0;
ssize_t n = read(rfd, buffer, sizeof(buffer));
if (n > 0)
{
buffer[n] = 0;
cout << "father get a message[" << getpid() << "]# " << buffer << endl;
}
}
}
int main()
{
int pipefd[N] = {0};
int n = pipe(pipefd);
if (n < 0)
return 1;
pid_t id = fork();
if (id < 0)
return 2;
if (id == 0)
{
close(pipefd[0]);
Write(pipefd[1]);
close(pipefd[1]);
exit(0);
}
close(pipefd[1]);
Read(pipefd[0]);
pid_t rid = waitpid(id, NULL, 0);
if (rid < 0)
{
return 3;
}
close(pipefd[0]);
return 0;
}
从上面代码我们可以清晰的感知到匿名管道只适用与具有亲缘关系的进程之间的通信但是我们在实际开发环境中更多的进程是不具备亲缘关系的因此匿名管道就非常的局限了。我们看一下匿名管道的最终实现的效果图
我们来看一下命名管道的使用效果图
在这张图我们可以清楚的看到右边输入左边输出达到了我们想要的无亲缘关系的进程的通信
1.comm.hpp文件
#pragma once
#include <iostream>
#include <string>
#include <cerrno>
#include <cstring>
#include <cstdlib>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#define FIFO_FILE "./myfifo"
#define MODE 0664
enum
{
FIFO_CREATE_ERR = 1,
FIFO_DELETE_ERR,
FIFO_OPEN_ERR
};
class Init
{
public:
Init()
{
int n = mkfifo(FIFO_FILE, MODE);
if (n == -1)
{
perror("mkfifo");
exit(FIFO_CREATE_ERR);
}
}
~Init()
{
int m = unlink(FIFO_FILE);
if (m == -1)
{
perror("unlink");
exit(FIFO_DELETE_ERR);
}
}
};
#include "comm.hpp"
#include <iostream>
using namespace std;
int main()
{
Init init;
int fd = open(FIFO_FILE, O_RDONLY);
if (fd < 0)
{
exit(FIFO_OPEN_ERR);
}
while (true)
{
char buffer[1024];
int len = read(fd, buffer, sizeof(buffer));
if (len < 0)
{
break;
}
else if (len > 0)
{
buffer[len] = 0;
cout << "client say#" << buffer << endl;
}
else if (len == 0)
{
break;
}
}
close(fd);
return 0;
}
#include "comm.hpp"
#include <iostream>
using namespace std;
int main()
{
int fd = open(FIFO_FILE, O_WRONLY);
if (fd == -1)
{
perror("open");
exit(FIFO_OPEN_ERR);
}
else
{
cout << "client open file done" << endl;
while (1)
{
cout << "Please Enter@ ";
string a;
getline(cin, a);
write(fd, a.c_str(), a.size());
}
}
close(fd);
return 0;
}