管道特点
- 只能用于具有具体祖先的进程之间的通信,通常,一个管道由一个进程创建,然后该进程调用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不用去关心\0ssize_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 failbreak;}}
}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;
}
实验结果:
最后的退出结果