管道特点
- 只能用于具有具体祖先的进程之间的通信,通常,一个管道由一个进程创建,然后该进程调用fork,创建子进程,关闭相应的读写端,然后父子进程就可以通信了
- 管道提供流式服务
- 一般而言,进程退出,管道释放,所以管道的声明周期随进程
- 一般而言,内核会对管道操作进行同步和互斥
创建命名管道
mkfifo :第一个参数是命名管道的路劲名,第二个参数是权限,mode_t 其实是对unsigned int 的封装
返回值:成功返回0,失败返回-1,错误码被设置
文件分类
- -:普通文件
- d:目录文件
- b:块设备文件
- c:字符文件
- p:管道文件,上述图中所示
- s:网络(socket)文件
- l:链接文件
这时有一个问题,就是如何进行通信的。通信的前提就是看到同一份资源,只有看到同一份资源,才能进行通信。
而mkfifo一个库函数,就是通过第一个参数看到同一份资源的,我们都知道,每一个进程都有属于自己的PCB结构体,其中有一个指针指向了文件描述符表,文件描述符表的内容又指向了file结构体,其中file结构体中存放着文件的inode,属性信息等等
当另一个进程创建的时候,去访问同一份资源,最后的访问的文件内容其实只有一份,进程最后一访问的就是同一块缓冲区。
如保证两个不同的进程打开的是同一个文件??
在linux中 文件路径 + 文件名就可以保证打开的是同一个文件
实验:
管道是具有同步和互斥的,当我们读取一个管道文件的时候,如果没有文件就会被阻塞住,当有文件就会被读出来
这里我用客户端和服务端来实验, 当我们的服务端受到了客户端发来的消息,它会去读取,通过调用read函数,其中同步由read系统调用函数来解决。
ssize_t read(int fd,void buf,size_t count)*
功能:将fd指向的文件中的count个传送到buf中
如果返回0,表示已经到文件末尾或是无可读取的数据。如果返回-1,代表读取不成功,错误码存入errno中。
fd:文件描述符
buf:所要读的内容
count:要读取的大小
客户端打开管道文件是通过open系统调用函数,open函数中,flags可以是O_RDONLY、O_WRONLY、O_NONBLOCK,但是不能是O_RDWR,因为管道是单向的
成功打开则返回文件描述符,失败返回-1
其中flags 是以什么方式打开。
O_RDONLY 只读方式
O_WDONLY只写方式
O_RDWR读写方式
O_APPEND在文件的末尾上追加
O_CREAT如果文件不存在就创建
mode是权限
server.cc
#include "comm.hpp"
#include "log.hpp"
#include <sys/wait.h>
//这是从fd中拿到数据的函数
static void getMessage(int fd)
{
char buffer[SIZE];
while (true)
{
memset(buffer, '\0', sizeof(buffer));
// 这里sizeof-1 是因为系统接口,文件有自己的管理机制,OS不用去关心\0
ssize_t s = read(fd, buffer, sizeof(buffer) - 1);
if (s > 0)
{
cout <<"[" << "pid:" << getpid() << "]" << " " << "client say:"<< " " << buffer << endl;
}
else if (s == 0)
{
cout << "[" << "pid:" << getpid() << "]" << " "<< "read end of file, client quit,server quit too" << endl;
break;
}
else
{
// read fail
break;
}
}
}
int main()
{
//1.创建管道文件
if(mkfifo(ipcPath.c_str(),MODE) < 0)
{
perror("mkfifo fail");
exit(1);
}
log("创建管道成功",Debug) << " | " << "Step 1" << endl;
//2.正常的文件操作
int fd = open(ipcPath.c_str(),O_RDONLY);
if(fd < 0)
{
perror("open");
exit(2);
}
log("打开管道成功",Debug)<< " | " << "Step 2" << endl;
int nums = 5;
for(int i =0; i<nums; i++)
{
pid_t id = fork();
if(id == 0)
{
//3.编写正常代码
//child
//从fd中拿到数据
getMessage(fd);
exit(1);
}
}
for(int i =0; i<nums; i++)
{
//阻塞式等待
waitpid(-1,nullptr,0);
}
//4.关闭文件
close(fd);
log("关闭管道成功",Debug)<< " | " << "Step 3" << endl;
//通信结束,关闭管道文件
unlink(ipcPath.c_str());
log("删除管道成功",Debug) << " | "<< "Step 4" << endl;
return 0;
}
client.cc
#include "comm.hpp"
#include "log.hpp"
int main()
{
//1.获取管道文件
int fd = open(ipcPath.c_str(),O_WRONLY);
if(fd < 0)
{
perror("open");
exit(1);
}
// 2.ipc过程
string buffer;
while(true)
{
cout << "Please Enter Message Line ->";
getline(cin,buffer);
//向文件里面去写
write(fd,buffer.c_str(),buffer.size());
}
//3.关闭文件
close(fd);
return 0;
}
实验结果:
最后的退出结果