管道
管道是⼀一种最基本的 IPC机制,由pipe函数创建。
#include<stdio.h>
int pipe(int fileds[2])
调用pipe函数时在内存中开辟一块缓冲区(就称为管道),用于通信,它有一个读端和一个写端,通过函数参数传给用户程序两个文件描述符,fileds[0]指向管道的读端,fileds[1]指向管道的写端。(方便记忆就可以理解为,标准输入0,标准输出1)看起来管道就像是一个打开的文件, 通过read(fileds[0]),和write(fileds[1])往里面写和读, 从上面的图我们可以看出,也就是在读写内核缓冲区。 pipe函数创建管道成功返回0,失败返回-1。
1.父进程创建管道
2.父进程fork出子进程
3.父进程关闭fd[0],子进程关闭fd[1]
代码:
#include<stdio.h>
#include<unistd.h>
#include<errno.h>
#include<string.h>
int main()
{
int _pipe[2];
int ret = pipe(_pipe);
if(ret==-1)//创建管道失败
{
printf("creat pipe error!errno code is :%d\n",errno);//错误码
return 1;//返回值
}
pid_t id = fork();
if(id<0)//创建子进程失败
{
printf("fork error!");
return 2;
}
else if(id==0)
{
//child
close(_pipe[0]);//关闭读端
int i = 0;
char *_mesg = NULL;
while(i<100)
{
_mesg = "I am child!";
write(_pipe[1],_mesg,strlen(_mesg)+1);//xie
sleep(1);
i++;
}
}
else
{
//father
close(_pipe[1]);//关闭写端
char _mesg_c[100];
int j = 0;
while(j<100)
{
memset(_mesg_c,'\0',sizeof(_mesg_c));
read(_pipe[0],_mesg_c,sizeof(_mesg_c));
printf("%s\n",_mesg_c);
j++;
}
}
}
运行结果:
管道的特点:
1.匿名管道支持单方向通信。
2.管道通信依赖于文件系统。
所以管道的生命周期是随进程。(进程一旦结束,管道也就随之而被释放)
3.匿名管道只能用于两个具有亲缘关系的两个进程。(例如父子进程)
4.匿名管道是按照字节流的方式来进行读写的。(没有格式要求)
5.管道自带同步机制。(读写的顺序一致)
匿名管道存在着如下两个缺点:
1. 匿名管道只能用于连接具有共同祖先的进程。
2. 匿名管道是依附进程而临时存在的。
但是 命名管道可以在任意的文件之间通信。
命名管道(FIFO)
命名管道(NamedPipe)是服务器进程和一个或多个客户进程之间通信的单向或双向管道。不同于匿名管道的是:命名管道可以在不相关的进程之间和不同计算机之间使用,服务器建立命名管道时给它指定一个名字,任何进程都可以通过该名字打开管道的另一端,根据给定的权限和服务器进程通信。而且,FIFO总是按照先进先出的原则工作,第一个被写入的数据首先从管道中读出。
创建命名管道的系统函数有两个:mknod和mkfifo
函数原型:
#include<sys/types.h>
#include<sys/stat.h>
int mknod(const char*path,mode_t mod,dev_t dev);
int mkfifo(const char*path,mode_t mode);
函数参数中的path为创建的命名管道的路径名,mod为创建命名管道的模式,指明其存取权限,dev为设备值,该值文件创建的种类,它只在创建设备文件时才会用到。这两个函数带哦用成功返回0,失败都返回-1.线面用mknod函数创建一个命名管道
umask(0);//重置管道的存取权限
if(mknod("/tmp/fifo",S_IFIFO|0666)==-1)
{
perror("mknod error");
exit(1);
}
//函数mkfifo的使用代码
umask(0);
if(mkfifo("/tmp/fifo",S_IFIFO|0666)==-1 )
{
perror("mkfifo error");
exit(1);
}
//"S_IFIFO|0666"致命创建一个管道的存取权限为0666
命名管道的使用和匿名管道基本相同,只是在使用命名管道之前首先要使用open函数打开,因为命名管道是存在于硬盘上的文件,而管道是存在于内存中的特殊文件。
需要注意,使用open的几点:
1. 调用open()打开命名管道可能会被阻塞,但是如果同时用读写方式(O_RDWR)打开,则一定不会造成阻塞。
2. 如果以制度方式(O_RDONLY)打开,则调用open()函数的进程将会被阻塞直到有写才能打开管道。
3. 同样,以写方式(O_WRONLY)打开也会阻塞直到有读方式打开管道。
命名管道可以实现进程间的通信:
//client.c 管道的写端
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
umask(0);
if(mkfifo("./mypipe",0666 | S_IFIFO)<0)
{
perror("mkfifo error");
return 1;
}
int fd = open("./mypipe",O_RDONLY);
if(fd<0)
{
printf("open file error!\n");
return 2;
}
char buf[1024];
while(1)
{
ssize_t ret = read(fd,buf,sizeof(buf)-1);
if(ret>0)//error or end of file
{
buf[ret] = 0;
printf("client say# %s\n",buf);
}
else if(ret==0)
{
printf("client quit !server begin quit!\n");
break;
}
}
close(fd);
return 0;
}
//server.c 管道的读端
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<string.h>
int main()
{
int fd = open("./mypipe",O_WRONLY);
if(fd<0)
{
printf("open file error!\n");
return 2;
}
char buf[1024];
while(1)
{
printf("please enter # ");
fflush(stdout);
ssize_t ret = read(0,buf,sizeof(buf)-1);
if(ret>0)
{
buf[ret-1] = 0;
write(fd,buf,strlen(buf));
}
}
close(fd);
return 0;
}
运行结果:
匿名管道和命名管道:
首先,FIFO是一种永久性的机构,它具有普通的UNIX系统文件名。在系统下可利用MKNOD命令建立永久的管道,除非刻意删除它,否则它将一直保持在系统中。
其次,正是由于有名管道以“文件名”来标识,所以只要事先约定某一特定文件名,那样所有知道该约定的服务进程,不论它们之间是否有亲属关系,都可以便利地利用管道进行通信。
命名管道文件被创建后,一些进程就可以不断地将信息写入命名管道文件里,而另一些进程也可以不断地从命名管道文件中读取信息。对命名管道文件的读写操作是可以同时进行的。