进程间通信——管道
1.进程为什么要通信
进程之间需要摸某种协同,协同的条件是通信,而通信也是有类别的,有通知就绪的,传递数据的,控制相关信息等等,
2.进程之间如何通信
事实上,进程是具有独立性的,那么进程通信的前提是让不同的进程看到操作系统中同一块资源
进程通信的常见方式 system V,是一种商业常用通信,而我们今天学习的是管道通信,
3.管道
当我们打开一个文件时,会获得这个文件的fd,当我们以不同的方式打开同一个文件,我们会获得两个fd,但是都指向同一个文件
我们可以使用pipe()系统来调用管道,一旦管道被创建,一个进程就可以在fd[1]上写数据,另一个进程在fd[0]上读数据,
#include<iostream>
#include<string>
#include<cerrno>
#include<cstring>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
const int size=1024;
std::string GetOtherMmessage()
{
static int cnt=0;
std::string messageid=std::to_string(cnt);
cnt++;
pid_t self_id=getpid();
std::string stringid=std::to_string(self_id);
std::string message="messiageid:";
message+=messageid;
message+=" my pid is:";
message+=stringid;
return message;
}
//子进程进行写入
void SubProceeeWrite(int wfd)
{
int pipesize=0;
std::string message=" father,I am Process ! ";
while(true)
{
std::string info=message+GetOtherMmessage();
write(wfd,info.c_str(),info.size());
}
std::cout<<"child quit . . . "<<std::endl;
}
void FatherProcessRead(int rfd)
{
char inbuffer[size];
while(true)
{
ssize_t n=read(rfd,inbuffer,sizeof(inbuffer)-1);
if(n>0)
{
inbuffer[n]=0;//在数组的最后一个元素的在一个设置为\0
std::cout<<inbuffer<<std::endl;
}
else if(n==0)
{
//read返回的是0,表示写端直接关闭了,我们读到了文件的结尾
std::cout<<"client quit ,father get return val :"<<n<<"father quit too !"<<std::endl;
}
else if(n<0)
{
std::cerr<<"read error"<<std::endl;
break;
}
}
}
int main()
{
//1.创建管道
int pipefd[2];
int n=pipe(pipefd);//输出型参数 rfd wfd
if(n!=0)
{
//创建失败
std::cout<<"errno: "<<errno<<" "<<"errstring: "<<strerror(errno) <<std::endl;
}
std::cout<<"pipefd[0]: "<<pipefd[0]<<" pipefd[1]: "<<pipefd[1]<<std::endl;
//2.创建子进程
pid_t id=fork();
if(id==0)
{
//子进程
close(pipefd[0]);//关闭读进程
SubProceeeWrite(pipefd[1]);
close(pipefd[1]);
exit(0);
}
close(pipefd[1]);//关闭写进程
FatherProcessRead(pipefd[0]);
close(pipefd[0]);
int status=0;
pid_t rid=waitpid(id,&status,0);
if(rid>0)
{
std::cout << "wait child process done, exit sig: " << (status&0x7f) << std::endl;
std::cout << "wait child process done, exit code(ign): " << ((status>>8)&0xFF) << std::endl;
}
return 0;
}
这是一个简单的子进程写数据,父进程读数据,
对于父进程,我们要关闭他的写进程,因为父进程只能读取数据,对于子进程,我们要关闭读进程,因为子进程只能写入数据
管道的四种情况
- 如果管道内部是空的&&wfd没有关闭,读取条件不具备,读进程会被阻塞,---wait ->读取条件具备<-写入数据
- 管道被写满&&rfd不读且没有关闭,写进程会被阻塞(管道被写满,写进程不具备)---wait ->写进程具备<-读取数据
- 管道一直在读&&写端关闭了wfd,读端read返回值会读到0,表示读到了文件结尾
- rfd直接关闭,写端wfd一直在写入?不合理,操作系统会直接使用13号信号关掉wfd,相当于进程出现了异常
管道的5种特征
- 匿名管道:只用来进行具有血缘关系的进程之间进行通信吗,通常在父子进程之间
- 管道内部:自带进程之间的同步机制,多执行流执行代码时具有顺序性
- 管道文件的生命周期是随进程的
- 管道文件在通信时,是面向字节流的,比如说,write和read的次数不一定是一一匹配的
- 管道的通信模式是半双工模式,半双工模式允许数据在两个方向上传输,但是不允许同时进行双向通信,在这种模式下,一次只能向一个方向传输数据