上次我们讲了进程间通过管道通信的方式,上次所说的管道也叫做匿名管道,匿名管道创建了一块缓冲区供进程来使用,但是这块缓冲区是没有名字的,因此除了具有亲缘关系的进程之外其他进程都找不到这块缓冲区。
今天我们要介绍的是命名管道,命名管道实际上是一种特殊文件,在同一台计算机的不同进程之间或在跨越一个网络的不同计算机的不同进程之间,支持可靠的、单向或双向的数据通信。在创建这个命名管道的时候给他起一个名字,任何进程都可以通过这个文件名来访问这个FIFO文件。因为匿名管道是我们内存中的特殊文件,但是命名管道是我们磁盘上的文件,命名管道和匿名管道只是他们的创建方式和打开方式,其他步骤都是相同的。
这里我们通过一个程序来实现一下我们的命名管道,命名管道本质就是一个特殊的文件,所以我们在创建这个文件的时候,我们可以选择提前创建好,在进程中直接使用同样我们也可以在程序内创建一个我们的FIFO文件。
我们可以通过mkfifo filename来创建我们的FIFO文件创建成功之后发现我们的test.fifo是特殊的黄色。
并且能看到一种新的文件类型p也就是我们的管道文件。
为了验证我们的fifo文件可以在不同进程间使用我们用两个程序,一个专门读一个专门写。
//我们首先实现向管道文件中写入数据的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
int main()
{
umask(0);
if(mkfifo("/test.fifo",0664)<0)
{
if(errno==EEXIST)
{
//如果我们去创建我们的文件,返回的错误值是EEXIST代表这个文件已经存在了,这时候正常继续就可以
}
else
{
perror("mkfifo error\n");
return -1;
}
}
int fd=open("./test.fifo",O_WRONLY);
if(fd<0)
{
perror("open error\n");
return -1;
}
printf("success: write!\n");
while(1)
{
char buff[1024];
scanf("%s",buff);
write(fd,buff,strlen(buff));
}
close(fd);
return 0;
}
这是我们的向fifo文件中写数据的程序。
//这是实现管道写的程序
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include<errno.h>
#include<fcntl.h>
int main()
{
umask(0);
if(mkfifo("./test.fifo",0664)<0)
{
if(errno==EEXIST)
{
}
else
{
perror("mkfifo error\n");
return -1;
}
}
int fd=open("./test.fifo",O_RDONLY);
if(fd<0)
{
perror("open error\n");
return -1;
}
printf("success read\n");
while(1)
{
char buff[1024]={0};
int ret=read(fd,buff,1023);
if(ret>0)
{
printf("client say:%s\n",buff);
}
else
{
if(ret==0)
{
printf("all write port close\n");
sleep(1);
}
}
}
close(fd);
return 0;
}
这是我们从fifo中读数据的文件。
这里我们要说几个需要注意的点,因为我们可能会在程序里创建某一个权限的文件,所以我们需要获取管理员权限才能去执行我们的程序。我们在打开文件的时候用的是open函数,open函数是会给我们提供选项,让我们选择是只读只写还是读写打开。如果是只读打开,那open函数会阻塞, 只到有另一个进程以只写的方式打开我们文件,同样如果是只写打开我们的文件也会阻塞,只到有一个进程以只读的方式打开这个命名管道,如果我们open以读写的方式打开就不会阻塞,所以在运行程序的时候你会发现执行程序之后什么反应都没有,只到读写两个程序都运行才会输出success。还有最后一个特性,就是我们在读端里边写的,当然我们也可以设置把默认阻塞的变成非阻塞的O_RDONLY|O_NONBLOCK 这样设置就好了。
int ret=read(fd,buff,1023);
if(ret>0)
{
printf("client say:%s\n",buff);
}
else
{
if(ret==0)
{
printf("all write port close\n");
sleep(1);
}
}
如果读取成功read函数会返回我们的实际上读取到的值,这里也是一样,到那时如果说我们的管道里边所有的写端全都关闭的时候,会返回ret0值,所以我们这里写了一个如果ret等于0我们就输出所有写端口已经关闭。
我们开两个终端来分别运行我们的两个读写程序
两边都启动之后我们的会出现打开成功的字样,并且我们在写入端输入一个程序之后,读取端就会输出对应的内容。
如果说我们把写入端关闭之后,我们的读取端会返回所有写入端都已经关闭的字样。
我们算是实现了通过命名管道文件实现服务端和客户端的简单通信
这么简单的也比我们写的复杂....毕竟我的是只有一个读一个写,但是我们可以通过fifo实现复杂一些的程序
像这样的