Linux——命名管道
- 命名管道
- 命名管道和匿名管道的区别
- 创建命名管道
- 利用命名管道实现简单通信
我们之前学习了匿名管道,这种管道有一个缺点就是只有两个有血缘关系的进程才能够使用匿名管道,这个非常不方便。所以我们又在匿名管道的基础之上引入了命名管道,它允许两个没有血缘关系的进程进行通信。
如果有小伙伴对匿名管道还不是很了解的话,可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/136371517
命名管道
命名管道(Named Pipes)是一种进程间通信(IPC,Inter-Process Communication)机制,允许在操作系统中的不同进程之间进行可靠的数据传输。它在Unix-like系统(如Linux)和Windows系统中均有实现。
在命名管道中,不同于匿名管道,通信双方不需要有直接的父子进程关系,任何一个进程可以通过管道的名字(即“命名”)来访问管道进行通信,这使得不相关的进程之间也能进行数据交换。
命名管道在操作系统中表现为一种特殊类型的文件,它存在于系统的命名空间中,可以像打开文件那样被打开和读写。一旦创建,命名管道就可以在不同的进程中被打开多次,允许单向或双向的数据流传输。
在Windows系统中,命名管道可通过服务器/客户端模式进行通信,一个进程可以作为管道服务器监听并接受连接请求,而其他进程作为管道客户端连接到已命名的管道并进行通信。
在Linux系统中,命名管道也称为FIFO(First-In-First-Out),是文件系统中的一个特殊文件,具有先进先出的特性,通过文件操作接口进行读写。
无论是Windows还是Linux系统,命名管道都支持跨进程甚至是跨计算机(在网络环境下)的通信,为分布式系统中的进程间协作提供了便利。
命名管道和匿名管道的区别
命名管道(Named Pipe)和匿名管道(Anonymous Pipe)在操作系统中都是用于进程间通信(IPC)的重要机制,它们之间的主要区别如下:
- 名称和持久性:
- 匿名管道:没有名字,由创建它的进程自动创建并立即可用,生命周期通常与创建它的进程相关联,进程结束时管道消失。
- 命名管道:有全局唯一的名称,在文件系统中作为一个特殊类型的文件存在,即使创建它的进程终止,只要管道文件未被删除,其他进程依然可以通过名称打开并使用这个管道进行通信。
- 范围和用途:
- 匿名管道:主要用于同一台计算机上的具有亲缘关系(通常是父子进程)的进程间通信,不适合跨机器或跨越长时间周期的通信。
- 命名管道:不仅可用于同一台计算机上的进程间通信,而且支持非亲缘关系进程间的通信,甚至可以在网络环境中的不同计算机之间使用,实现了跨计算机的IPC功能。
- 创建和使用方式:
- 匿名管道:由操作系统在创建新进程时自动创建(如fork调用后),或者通过特定API函数(如Unix/Linux中的
pipe()
函数)手动创建。- 命名管道:需要通过系统调用如
mkfifo()
在文件系统中显式创建,随后其他进程通过文件路径名打开。
- 访问权限和安全性:
- 匿名管道:创建后只能由创建它的进程及其后代进程访问,权限控制相对简单。
- 命名管道:可以设置权限,允许不同用户和组访问,具有更高的安全性,因为它可以根据文件系统权限进行控制。
- 方向性:
- 早期的匿名管道在某些实现中可能是单向的,而命名管道通常可以实现双向通信。
- 复用性:
- 匿名管道:一般是一对一通信,不能被多个读写进程共享(除了父子进程)。
- 命名管道:可以被多个读写进程同时打开,实现一对多或多对多的通信。
综上所述,命名管道相比于匿名管道提供了更大的灵活性和扩展性,但也带来了额外的管理和安全性考量。
创建命名管道
在Linux下,我们可以直接敲命令行直接创建命名管道:mkfifo:
比如我想创建一个myfifo的命名管道:
这个时候我们就创建了一个myfifo的命名管道。那么这个命名管道有什么用呢?
利用命名管道实现简单通信
我们可以就利用我们的这个管道实现一个简单的通信,首先我们先创建两个.cc文件:
我们在client.cc中写下这么一段代码:
#include<iostream>
#include<stdlib.h>
#include<fcntl.h>
#include<cstring>
#include<unistd.h>
int main()
{
//以写的方式打开管道
int wfd = open("../myfifo",O_WRONLY);
if(wfd == -1)
{
perror("open fail");
exit(EXIT_FAILURE);
}
else
{
while(true)
{
//准备输入
std::string my_information;
std::cout<<"Please enter:";
std::getline(std::cin, my_information); // 使用 getline 获取整行输入,包括换行符
// 移除末尾的换行符
if (!my_information.empty() && my_information.back() == '\n')
{
my_information.pop_back(); // 删除最后一个字符(如果它是换行符)
}
size_t length_without_newline = my_information.length();
if(write(wfd, my_information.c_str(), length_without_newline) == -1)
{
perror("write fail");
exit(EXIT_FAILURE);
}
std::cout << "Client say: " << my_information;
}
}
return 0;
}
在server.cc中写下:
#include<iostream>
#include<stdlib.h>
#include<fcntl.h>
#include<cstring>
#include<unistd.h>
int main()
{
//以写的方式打开管道
int wfd = open("../myfifo",O_WRONLY);
if(wfd == -1)
{
perror("open fail");
exit(EXIT_FAILURE);
}
else
{
while(true)
{
//准备输入
std::string my_information;
std::cout<<"Please enter:"<<std::endl;
std::getline(std::cin, my_information); // 使用 getline 获取整行输入,包括换行符
// 移除末尾的换行符
if (!my_information.empty() && my_information.back() == '\n')
{
my_information.pop_back(); // 删除最后一个字符(如果它是换行符)
}
size_t length_without_newline = my_information.length();
if(write(wfd, my_information.c_str(), length_without_newline) == -1)
{
perror("write fail");
exit(EXIT_FAILURE);
}
}
}
return 0;
}
我们打开多个窗口:
通过命名管道我们实现了两个程序的交流。
我们也可以通过mkfifo函数实现对管道的创建:
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h> // 包含mkfifo函数声明
int main() {
const char *fifo_name = "/tmp/my_fifo"; // 命名管道的路径
// 创建命名管道
if (mkfifo(fifo_name, 0666) == -1) { // 第二个参数是权限模式
perror("mkfifo");
exit(EXIT_FAILURE);
}
printf("命名管道 '%s' 已成功创建\n", fifo_name);
return 0;
}
在这段代码中,mkfifo(“/tmp/my_fifo”, 0666)用于在"/tmp"目录下创建一个名为"my_fifo"的命名管道,权限设置为所有用户均可读写(0666)。创建成功后,其他进程可以通过文件路径名打开并使用此命名管道进行通信。
命名管道的知识不是特别多,但是我们可以利用命名管道来创建进程池。这个应该还蛮有意思的。