命名管道
命名管道,就是创建一个命名的管道文件,供进程间通信。
命名管道与匿名管道的区别就在于使用命名管道可以在文件夹中看到创建出来的管道文件。
匿名管道只能用于具有血缘关系的通信,而命名管道可以用于没有血缘关系的进程间通信。
1、创建命名管道
使用mkfifo创建命名的管道文件。
参数:pathname表示管道文件的路径名,mode表示创建管道文件的权限。
返回值:成功返回 0;失败返回 -1,并设置错误码。
实验示例:
#include <iostream>
#include <string>
#include <error.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
int main()
{
// 在当前路径下创建管道文件,名字为fifo,权限为0666
int ret = mkfifo("./fifo", 0666);
return 0;
}
2、使用管道文件
创建管道文件成功后,进程间通信就转化为了向管道写数据、向管道读数据。
我们模拟客户端和服务端,客户端发数据,服务端读数据,测试命名管道。
服务端:a、负责创建管道文件以及删除管道文件。
b、负责读取数据。
#include <iostream>
#include <string>
#include <error.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
int main()
{
// 创建命名管道
int ret = mkfifo("./fifo", 0666);
if(ret < 0)
{
printf("open fifo fail, errno is %d, errinfo is %s\n", errno, strerror(errno));
return errno;
}
// 打开命名管道
int rfd = open(Path, O_RDONLY);
if (rfd < 0)
{
printf("open fifo fail, errno is %d, errinfo is %s\n", errno, strerror(errno));
}
printf("open success\n");
// 读
char buffer[1024];
while (1)
{
// 从文件读取数据
ssize_t n = read(rfd, buffer, sizeof(buffer) - 1);
if (n > 0)
{
buffer[n] = '\0';
cout << "server get a mseeage : " << buffer << endl;
}
else if (n == 0)
{
cout << "server quit..." << endl;
break;
}
else
{
printf("read fifo fail, errno is %d, errinfo is %s\n", errno, strerror(errno));
}
}
// 关闭管道
close(rfd);
// 删除命名管道
unlink("./fifo");
return 0;
}
客户端:负责输入数据,输入为 "quit" 时,关闭写端。
当客户端关闭写端时,服务端就会读到0表示结尾,服务端就会关闭写端并删除文件。
#include <iostream>
#include <string>
#include <error.h>
#include <cstring>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
using namespace std;
int main()
{
// 打开命名管道
int rfd = open("./fifo", 0666);
if (rfd < 0)
{
printf("open fifo fail, errno is %d, errinfo is %s\n", errno, strerror(errno));
}
// 写
string str;
while (1)
{
cout << "Please Enter Your Message:> ";
getline(cin, str);
// 如果用户输入quit,那就退出,然后关闭写端。
if(str == "quit")
{
cout << "client quit..." << endl;
break;
}
ssize_t n = write(rfd, str.c_str(), str.size());
if (n > 0)
{
cout << "Send Your Message Successfully" << endl;
}
else if(n == 0)
{
continue;
}
else
{
printf("write fifo fail, errno is %d, errinfo is %s\n", errno, strerror(errno));
}
}
// 关闭管道
close(rfd);
return 0;
}
3、总结
管道都是基于文件的。
1、当写端未关闭,读快写慢,当文件内没数据时,读端就会在read阻塞等待,写端写了再读
2、当读端未关闭,写快读慢,写端就会一直写,直到写满,阻塞等读端读,然后再写。
3、当写端关闭,读端就会读到0,表示读到结束。
4、当读端关闭,如果写端还在写,会通过13信号杀死进程。
5、在使用open时,先打开读端或写端中的任意一个,open()函数都会阻塞,只有读端和写端都打开了,才能正常运行。