目录
分类
功能区别
无名管道
命名管道
1、无名管道
2、命名管道
无名管道只能用于有父子关系的进程之间,通过公用进程描述符来实现连接在同一个管道。
命名管道可以实现任意两个进程之间的通信。
(此图从别处盗用过来的)
从这个图中可以看出来:
1、父进程首先创建了一个管道,管道的连个文件描述符分别保存在fd[0],fd[1]中,这两个也就分别是该管道文件的读端和写端。
2、父进程调用fork系统调用,创建一个子进程,子进程复制了所有父进程的资源。也就包括了fd[0],fd[1]。
3、然后父进程关闭f[0],也就是关闭了他关联管道的读端。子进程关闭f[1],也就是关闭了他关联管道的写端。
4、然后就可以对管道进行读取操作了。
这里贴一个小栗子:
#include
#include
#include
#define MAXLINE (2014)
int main(void)
{
int n,fd[2];//保存管道返回的两个文件描述符
pid_t pid;
char line[MAXLINE];
if(pipe(fd)<0)//创建管道,fd[0]是读端,fd[1]是写端。
printf("pipe error");
if((pid=fork())<0)//创建进程
printf("fock error");//创建进程失败
else if(pid>0)//pid大于零,为父进程,pid的值是子进程的
{
close(fd[0]);//关闭读端
printf("#the parent process pid %d\n",getpid());//返回当前进程的id
printf("#the children pid is %d\n",pid);
printf("#the process write to pipe: hello world\n");
write(fd[1],"hello world\n",12);//向写端写入12个字节数据
}
else
{
close(fd[1]);//关闭写端
printf("$the children process pid %d\n",getpid());
printf("$the parent process pid %d\n",getppid());
n = read(fd[0],line,MAXLINE);
write(STDOUT_FILENO,line,n);//把数据写入标准输出文件描述符
}
exit(0);
}
还有就是我们常用的”|“命令。他也是通过无名管道来实现的
他由shell执行。例如ps -aux | grep ssh
1、shell创建一个管道文件。然后获取文件描述符。有两个,一个指向管道文件的读端,一个指向管道文件的写端。
2、shell fork()出两个子进程,其中一个(1号进程)把管道的写端重定向到进程描述符1中,也就是标准输出中。另一个(2号进程)把读端重定向到进程描述符0中,也就是标准输入中。
3、一号进程调用ps -aux命令。因为重定向的原因,输出会写入到管道中。二号进程调用grep ssh命令,由于重定向的原因,标准输入从管道文件中获取。
由上面的介绍不难得出,无名管道实现进程之间的通信主要还是通过父子进程之间共享文件描述符来实现的。
如果想要实现任意两个进程之间的通信该怎么办呢?
学习linux,你或许听说过这么一句话 一切皆文件
也就是说我们之前创建的无名管道也是以文件的形式存在的。那么,我们是不是可以通过目录树来访问管道文件呢?
答案是不能的,因为管道文件是一种特殊的文件,它存在于特殊的文件系统(pipefs)中。他在目录树中没有安装点。
因为linux的目录树是VFS架构,所以将管道文件设置成符合其标准。使他可以通过目录树访问,就可以很方便的实现不同进程之间的通信了。
贴一个小程序:
读端
#include
#include
#include
#include
#include
#include
#define PATH "./fifo"
#define SIZE 128
int main()
{
umask(0);
if((access(PATH,F_OK))!=-1);//判断文件是否存在
else
{
if(mkfifo(PATH,0666|S_IFIFO)==-1)//0666设置了该文件各个用户的访问权限,并且为阻塞式
{//创建失败报错并退出
perror("mkfifo error");
exit(0);
}
}
int fd = open(PATH,O_RDONLY);//以只读的方式打开这个管道文件,阻塞等待写端打开这个管道文件。
if(fd<0)//打开失败
{
printf("open fd is error\n");
return 0;
}
char Buf[SIZE];
while(1)
{
ssize_t s = read(fd,Buf,sizeof(Buf));//阻塞式读取,若为空,阻塞等待其他进程写入。
if(s<0)
{
perror("read error");
exit(1);
}
else if(s==0)
{
printf("client quit! i shoud quit!\n");
break;
}
else
{
Buf[s] = '\0';
printf("client# %s ",Buf);
fflush(stdout);//刷新标准输出。
}
}
close(fd);
return 3;
}
写端:
#include
#include
#include
#include
#include
#include
#include
#include
#define PATH "./fifo"
#define SIZE 128
int main()
{
int fd = open(PATH,O_WRONLY);//以只写的方式打开文件。
if(fd<0)
{
perror("open error");
exit(0);
}
char Buf[SIZE];
while(1)
{
printf("please Enter#:");
fflush(stdout);
ssize_t s = read(0,Buf,sizeof(Buf));
if(s<0)
{
perror("read is failed");
exit(1);
}
else if(s==0)
{
printf("read is closed!");
return 1;
}
else
{
Buf[s]='\0';
write(fd,Buf,strlen(Buf));
}
}
return 0;
}
实现的原理和无名管道差不多。就是可以通过目录树来访问到管道文件。