两个进程通过打开同一个”文件“来实现进程间通信。
其中一个进程向文件里写入数据,另一个进程通过文件读取数据。
或者一个进程向文件里读取数据,另一个进程通过文件写入数据。
把从一个进程连接到另一个进程的一个这个“文件”称为一个管道
根据上面的分析可知,管道只能单向通信
在我们创建子进程的时候,子进程会共享父进程的数据,当修改进程数据时发生写时拷贝。当父进程打开文件后再创建子进程,文件描述符信息也会被子进程接受到,两个进程通过文件描述符就可以找到同一个文件,就可以实现数据通信
函数原型:
int pipe(int fd[2]);
参数意义:
fd为输出型参数,传入fd数组后,当函数调用结束后fd[0]表示读端,fd[1]表示写端。
fd数组中保存的是文件描述符
返回值:
成功返回0,失败返回-1
因为管道只能单向通信,所以当我们调用pipe后创建子进程后会出现下图状况
这时要确认读写进程,并且关闭对应的读写段。
eg:父进程要读取管道数据,此时关闭父进程的fd[1]。子进程要向管道写入数据,关闭子进程的fd[0].
#include
#include
#include
#include
#include
#include
int main()
{
int fd[2]={
0};
if(pipe(fd)<0)
{
perror("pipe error\n");
}
pid_t id=fork();
if(id==0)
{
//child
close(fd[0]);//子进程关闭读端
const char* Str="Hello Linux";
for(int i=0;i<10;i++)
{
write(fd[1],Str,strlen(Str));//向管道写入数据
sleep(1);
}
close(fd[1]);
exit(0);
}
//father
close(fd[1]);
char buff[32]={
0};
while(1)
{
ssize_t ReadSize=read(fd[0],buff,sizeof(buff));
if(ReadSize>0)//读取到数据
{
buff[ReadSize]='\0';//补充字符串接受的标准
printf("receive:%s\n",buff);
}
else if(ReadSize==0)//读取结束
{
printf("read end!\n");
break;
}
else //读取失败
{
perror("read error!\n");
break;
}
}
waitpid(id,NULL,0);//父进程阻塞式等待
return 0;
}
注意:
1.实现父子进程不可以通过设置全局变量的方式来实现,因为在创建子进程后,为了保持进程独立性,子进程在修改数据时会发生写时拷贝。此时父子进程的全局变量不是同一份了。
2.如果写端关闭,此时读端read会返回0代表文件结束
3.当管道文件为空时,此时读取进程挂起等待写入。当管道文件写满时,此时写入进程挂起等待读取。
4.当管道读取进程被关闭,此时管道写入进程会被操作系统终止,因为写入的数据无意义。
在多执行流下,父子进程看到的同一份资源称为临界资源。
eg:
当子进程正在向管道中写入数据,父进程此时不能进入管道中读取数据。
理由:
管道内部提供了互斥与同步机制,管道文件为空时,此时父进程的read函数会被阻塞不再读取。
命名管道有文件名,多个进程可以通过打开同一个文件,来让不同进程访问同一份资源。
函数原型
int mkfifo(const char*Pathname,mode_t mode);
参数解释:
Pathname:创建命名管道的路径和名称。
mode:创建命名管道的文件权限
返回值:创建成功返回0,失败返回-1。
因为我们要让不同进程找到相同的管道文件,所以将两个进程所需要的文件名以及头文件放到Common.c中
Common.c
#pragma once
#include
#include
#include
#include
#include
#include
#include
#define FIFE_NAME "MyFiFo"
server是创建管道文件,并且读取管道文件的数据,将这些数据打印到显示器上
#include"Commmen.c"
int main()
{
if(mkfifo(FIFE_NAME,0664)<0)//创建管道文件
{
perror("make fifo error\n");
return 1;
}
int fd=open(FIFE_NAME,O_RDONLY);//打开管道文件,只读取
if(fd<0)
{
perror("open error\n");
return 2;
}
char buff[32]={
0};
while(1)
{
buff[0]='\0';//每次情况字符串
ssize_t size=read(fd,buff,sizeof(buff)-1);//读取字符串放到buff缓冲区中,空余一个空间存放'\0'
if(size>0)//读取成功
{
buff[size]='\0';//为字符串添加'\0'
printf("client:%s\n",buff);
}
else if(size==0)//管道关闭
{
printf("End Client\n");
break;
}
else //读取失败
{
perror("read error\n");
break;
}
}
close(fd);
return 0;
}
client是读取键盘上的字符,并且将这些字符写到管道文件中。
client.c
#include"Commmen.c"
int main()
{
int fd=open(FIFE_NAME,O_WRONLY);//通过写的方式打开管道文件
if(fd<0)
{
perror("open error\n");
return 1;
}
char buff[32]={
0};
while(1)
{
printf("Enter String\n");
buff[0]='\0';//清空字符缓冲区
ssize_t size=read(0,buff,sizeof(buff));//0为标准输入的文件描述符,读取键盘输入的字符
if(size>0)
{
buff[size]='\0';
write(fd,buff,strlen(buff)-1);//向管道文件中写入buff数组的数据
}
else
{
printf("error wirte\n");
}
}
close(fd);
return 0;
}
注意:当我们向管道文件写入而不读取时,管道文件的大小并不会发生变化,这些数据保存在系统中,没有刷新到磁盘中。双方通信在内存中通信