进程间的通讯方式有很多种:管道通信,共享内存,消息队列,信号量、远程过程调用,以及网络部分的通过套接字(socket)来通讯,首先我们来了解一下管道通信。
一、管道
引入:在现实中的一些管道,比如水管、气管都起着运输作用,在进程通信中管道也起着对信息数据的承载运输作用,所谓管道通信就是开辟一块空间,进程在里面进行读写(如图一所示),这一块空间就是我们所说的管道文件,那管道文件和我们普通的文件有什么不同呢?为什么我们不能开辟一个普通文件进行读写呢?
基本信息:通信分为半双工通信、全双工通信。半双工通信就是指通信双方不能同时进行通信,只能一段发送一端接收,全双工通信是指通信双方即可发送消息也可接收消息。我们现在所说的管道通信就是半双工通信。之所以把管道设为半双工通信原因:A进程既可写可读,B可读的情况,当A写入一个数据时,读数据所读取的数据也可能是自己写入的数据,没有意义。
类别:管道分为有名管道和无名管道
1、有名管道:应用于任意两个进程之间的单向传递,文件目录树(在磁盘中存储)中有一个标识,仅仅占用了一个结点,没有占用磁盘上的任何内存,在使用中数据缓存在内存里,只在使用时开辟内存,读取一次后自动清空。
注:Linux文件系统中,每一个存放在磁盘上的文件由两部分组成:数据块:实际存放文件数据的磁盘块;inode:Linux内部用于描述文件特性的数据结构。inode含有关于文件的大部分信息,包括文件数据块。
不使用普通文件的原因:普通文件从磁盘进行io操作,耗费时间;管道是在内存上重新开辟一块空间,读写数据时,直接在内存上读取
2、无名管道:
定义:相对于有名管道,使用时产生,不使用时释放,不在系统上有任何痕迹,无名管道因其没有任何标识,所以只能应用于父子进程之间,子进程会拷贝父进程的文件表数组,其拷贝是浅拷贝,只是定义了一个指针,其共享一个文件描述符
使用:
1、有名管道
创建:(以命令方式)mkfifo +文件名 函数方式 谁调用命令
打开:open以只写打开管道文件时,没有文件以读/读写方式大开时,函数不返回
写入:write(fd,buff,size);
读取:read(fd,buff,size);读取数据并把数据清空??memset
关闭:close(fd);
2、无名管道
创建打开:(以函数方式)int pipe(int fd[2])形参加中括号相当于指针
fd[0] ,读 fd[1]写,实质上是一个文件描述符指向文件
写write(fd[0],buff,size)
读read(fd[1],buff,len)
关close(fd[1]);close(fd[0]);
代码:有名管道
写端代码:
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
void main()
{
int fd=open("FIFO",O_WRONLY);
assert(fd!=-1);
printf("open success\n");
char buff[128]={0};
while(1)
{
printf("please input:");
fgets(buff,128,stdin);
write(fd,buff,strlen(buff)-1);
if(strncmp(buff,"end",3)==0)
{
exit(0);
}
}
close(fd);
}
读端:
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<assert.h>
#include<stdlib.h>
#include<unistd.h>
void main()
{
int fd=open("FIFO",O_RDONLY);
assert(fd!=-1);
printf("open success\n");
char buff[128]={0};
int count=0;
while(1)
{
read(fd,buff,127);
printf("read: %s\n",buff);
count++;
if(strncmp(buff,"end",3)==0)
{
printf("word number:\n",count);
break;
}
}
close(fd);
}
无名管道:
#include<stdio.h>
#include<fcntl.h>
#include<string.h>
#include<unistd.h>
#include<stdlib.h>
#include<assert.h>
void main()
{
int fd[2]={0};
int pipe1=pipe(fd);
pid_t pid=fork();
if(pid==0)
{
close(fd[1]);
//printf("this is child\n");
char buff[128]={0};
while(1)
{
read(fd[0],buff,127);
printf("read:%s\n",buff);
if(strncmp(buff,"end",3)==0)
{
break;
}
}
}
else
{
// printf("this is parent\n")
close(fd[0]);
while(1)
{ char buff[128]={0};
printf("please input:\n"); fgets(buff,128,stdin);
write(fd[1],buff,strlen(buff)-1);
if(strncmp(buff,"end",3)==0)
{ break; }
sleep(3);
}
}
close(fd[1]);
close(fd[2]);
}
未解决问题:把管道文件的标识符放在磁盘上?为什么
磁盘,内存存储方式
管道的默认值是多少?能不能修改管道大小?
管道操作的内核实现(空间的大小,读写偏移量的变化 )