进程间通信 命名管道 ─── Linux第24课
匿名管道利用文件的内核级缓冲区,实现了父子进程之间的通信
而非父子进程的通信是什么样的? 命名管道
其实匿名管道与命名管道的本质是一样的(文件)
目录
命名管道
创建⼀个命名管道
关闭创建的命名管道
匿名管道与命名管道的相同点
匿名管道与命名管道的区别
命名管道的打开规则
实现两个非父子进程之间的命名管道通信
Common.hpp
Sever.hpp
Sever.cc
Client.hpp
Client.cc
makefile
效果:
命名管道
- 匿名管道应用的⼀个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
- 如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项⼯作,它经常被称为命名管道。
- 命名管道是⼀种特殊类型的文件,有名字(与匿名管道不同),不与磁盘(外设)进行数据来往,只让进程互相通信。
- 命名管道本质: 两个非父子进程基于命名管道的内核级缓冲区进行数据交换。
创建⼀个命名管道
命名管道可以从命令行上创建,命令行方法是使用下⾯这个命令:
mkfifo filename
示例:
命名管道也可以从程序里创建,相关函数有:
int mkfifo(const char *pathname , mode_t mode);
int mkfifo_ret =mkfifo( "./MyFifo" , 0600); if(mkfifo_ret < 0) { std::cerr<<"mkfile error"<<std::endl; } 命名文件本质是文件 ,进程中创建可以不带路径只带filename (就是在当前路径下创建),mode_t 就是创建后文件的权限
命名文件本质是文件 ,进程中创建可以不带路径只带filename (就是在当前路径下创建),mode_t 就是创建后文件的权限
关闭创建的命名管道
unlink命令
示例:
匿名管道与命名管道的相同点
- 在匿名管道和命名管道中 ,管道文件的inode和管道内核级缓冲区只存在一份 (Linux文件系统保证文件的数据和属性的唯一性)
- 他们都只使用文件的内核级缓冲区,不向磁盘(外设)刷新
匿名管道与命名管道的区别
- 匿名管道由pipe函数创建并打开。
- 命名管道由mkfifo函数创建,打开用open(命名管道是文件,用open打开)
- FIFO(命名管道)与pipe(匿名管道)之间唯⼀的区别在它们创建与打开的方式不同,⼀但这些 工作完成之后,它们具有相同的意义。
命名管道的打开规则
1.如果当前打开操作是为读⽽打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为写⽽打开该FIFO(在open处阻塞)
- O_NONBLOCK enable:⽴刻返回成功
2.如果当前打开操作是为写⽽打开FIFO时
- O_NONBLOCK disable:阻塞直到有相应进程为读⽽打开该FIFO (在open处阻塞)
- O_NONBLOCK enable:⽴刻返回失败,错误码为ENXIO
实现两个非父子进程之间的命名管道通信
思路:程序server创建命名管道 ,使用命名管道(以读的方式打开命名管道 ,读取命名管道的数据)
程序client 使用命名管道(以写的方式打开管道 ,向命名管道中写入数据)
两个程序都知道的信息包含在头文件common.hpp中
Common.hpp
#pragma once
#include<iostream>
#include<string>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
const std::string gPipeFile ="./fifo";
const int gmode =0600;
const int gDefaultfd =-1;//初始是-1 成功打开管道后改变
const int gsize =1024;
Sever.hpp
#pragma once
#include"Common.hpp"
class CreatInit
{
public:
CreatInit()
{
umask(0);
int mkfifo_ret =mkfifo(gPipeFile.c_str() ,gmode);
if(mkfifo_ret < 0)
{
std::cerr<<"mkfile error"<<std::endl;
}
}
~CreatInit()
{
int unlink_ret =unlink(gPipeFile.c_str());
if(unlink_ret < 0)
{
std::cerr<<"unlink_ret error"<<std::endl;
}
}
private:
};
//建立命名管道
CreatInit CI;
class server
{
public:
server():_fd(gDefaultfd)
{}
~server()
{}
bool OpenPipeForRead()
{
// 如果读端打开文件时,写端还没打开,读端对用的open就会阻塞
_fd =open(gPipeFile.c_str() , O_RDONLY);
if(_fd < 0) return false;
return true;
}
// std::string *: 输出型参数
// const std::string &: 输入型参数
// std::string &: 输入输出型参数
int ReceviePipe(std::string *out)
{
char buf[gsize];
while(true)
{
ssize_t n =read(_fd ,buf ,sizeof(buf)-1);
if(n > 0)
{
buf[n]=0;
*out =buf;
}
return n;
}
}
bool ClosePipe()
{
int n =::close(_fd);
if(n< 0) return false;
return true;
}
private:
int _fd;
};
Sever.cc
#include<iostream>
#include"Server.hpp"
//建立命名管道,以读的方式打开
int main()
{
server s;
if(!s.OpenPipeForRead())
{
std::cerr<<"server openPipe false"<<std::endl;
}
std::string message;
while(true)
{
int n = s.ReceviePipe(&message);
if(n == 0)
{
break;//管道特性返回值为零(读到0)就退出
}
else if(n > 0)
{
std::cout<<"client Say# "<< message <<std::endl;
}
else
{
std::cerr<<"sever receice false"<<std::endl;
return 1;
}
}
std::cout << "client quit, me too!" << std::endl;
s.ClosePipe();
return 0;
}
Client.hpp
#pragma once
#include"Common.hpp"
class client
{
public:
client():_fd(gDefaultfd)
{}
~client()
{}
bool OpenPipeForWrite()
{
_fd =open(gPipeFile.c_str() , O_WRONLY);
if(_fd < 0) return false;
return true;
}
// std::string *: 输出型参数
// const std::string &: 输入型参数
// std::string &: 输入输出型参数
int SendPipe(const std::string& in)
{
ssize_t n =::write(_fd ,in.c_str(),in.size());
//&in sizeof(in)
if(n > 0) return n;
else
{
std::cerr<<"write error"<<std::endl;
return -1;
}
}
bool ClosePipe()
{
int n =::close(_fd);
if(n < 0) return false;
return true;
}
private:
int _fd;
};
Client.cc
#include<iostream>
#include"Client.hpp"
int main()
{
client c;
if(!c.OpenPipeForWrite())
{
std::cerr<<"client openPipe error"<<std::endl;
}
std::string message;
while(true)
{
std::getline(std::cin,message);
c.SendPipe(message);
}
c.ClosePipe();
return 0;
}
makefile
SERVER=server
CLIENT=client
cc=g++
SERVER_SRC=Server.cc
CLIENT_SRC=Client.cc
.PHONY:all
all:$(SERVER) $(CLIENT)
$(SERVER):$(SERVER_SRC)
$(cc) -o $@ $^ -std=c++11
$(CLIENT):$(CLIENT_SRC)
$(cc) -o $@ $^ -std=c++11
.PHONY:clean
clean:
rm -f $(SERVER) $(CLIENT)