一、无名管道
管道,通常指无名管道(之所以叫无名管道是因为,没有文件名),是 UNIX 系统IPC最古老的形式。
特点
(1)它是半双工的(即数据只能在一个方向上流动),具有固定的读端和写端。
(2)它只能用于具有亲缘关系的进程之间的通信(也是父子进程或者兄弟进程之间)。
(3)它可以看成是一种特殊的文件,对于它的读写也可以使用普通的read、write 等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中。
(4)管道中的数据被读走就没了。
原理
当一个管道建立时,它会创建两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。管道的建立存在与内核之中,如下图:
要关闭管道只需将这两个文件描述符关闭即可。
创建
单个进程中的管道几乎没有任何用处。所以,通常调用 pipe 的进程接着调用 fork,这样就创建了父进程与子进程之间的 IPC 通道。如下图所示:
1 #include <unistd.h>
2 int pipe(int fd[2]); // 返回值:若成功返回0,失败返回-1
实例
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
int main()
{
int pid=0;
int fd[2]={
0};
char buf[128]={
0};
if(pipe(fd) == -1){
//建立管道
printf("creat pipe failed\n");
}
pid=fork();//建立子进程
if(pid<0){
//建立子进程失败
printf("creat failed\n");
}
else if(pid >0){
//父进程运行空间,pid>0时
sleep(3);
printf("father\n");
close(fd[0]);//父进程关闭读端文件描述符
write(fd[1],"hello from father",strlen("hello from father"));//父进程对管道内写入
}else{
//子进程运行空间,pid=0时
printf("child\n");
close(fd[1]);//子进程关闭写端文件描述符
read(fd[0],buf,128);//子进程从管道内读出数据到buf
printf("read form father:%s\n",buf);
}
return 0;
}
实验结果:
二、命名管道(FIFO)
特点
(1)FIFO可以在无关的进程之间交换数据,与无名管道不同。
(2)FIFO 有路径名(无名管道没有)与之相关联,它以一种特殊设备文件形式存在于文件系统中。
(3)管道中的数据被读走就没了,同时保持==FIFO(先进先出)==的特点。
原理
与无名管道类似。
创建
1 #include <sys/stat.h>
2 int mkfifo(const char *pathname, mode_t mode);// 返回值:成功返回0,出错返回-1
其中的 mode 参数与open函数中的 mode 相同(0600-读写)(open函数)。一旦创建了一个 FIFO,就可以用一般的文件I/O函数操作它。
当 open 一个FIFO时,==是否设置非阻塞标志(O_NONBLOCK)==的区别:
若没有指定O_NONBLOCK(默认)
只读open 要阻塞到某个其他进程为写而打开此 FIFO。
只写open 要阻塞到某个其他进程为读而打开它。
若指定了O_NONBLOCK,则只读 open 立即返回。而只写 open 将出错返回 -1 如果没有进程已经为读而打开该 FIFO,其errno置ENXIO
实例
read.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
char buf[128]={
0};
if((mkfifo("./file",0600)==-1) && errno!=EEXIST){
//建立管道
printf("mkfifo failure\n");
perror("why");
}else{
if(errno==EEXIST){
printf("file eexist\n");
}else{
printf("mkfifo successed\n");
}
}
int fd = open("./file",O_RDONLY);//打开管道,O_RDINLY-只读模式打开,如果没有另一个进程以只写模式打开管道,程序会阻塞此处
printf("open file success\n");
int nread=read(fd,buf,20);//从管道内读数据
printf("read %d byte from fifo,context:%s\n",nread,buf);
close(fd);//关闭管道
return 0;
}
write.c
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <errno.h>
#include <fcntl.h>
int main()
{
// ssize_t read(int fd, void *buf, size_t count);
char *buf="aioplo!!!!!!!!!!!!!!!!!";
int fd = open("./file",O_WRONLY);//打开管道,O_WRONLY-只写模式打开,如果没有另一个进程读它,程序会阻塞此处
printf("write file success\n");
write(fd,buf,strlen(buf));//写入数据
return 0;
}
实验结果:
上述例子可以扩展成 客户进程—服务器进程 通信的实例,
write的作用类似于客户端,可以打开多个客户端向一个服务器发送请求信息,
read类似于服务器,它适时监控着FIFO的读端,当有数据时,读出并进行处理,
每一个客户端必须预先知道服务器提供的FIFO接口,如下图: