在前一篇文章中,我们看到了如何使用匿名管道来在进程之间传递数据,这个方式有一个缺陷,就是这些进程必须由一个共同的祖先进程启动,这在不相关的的进程之间交换数据带来了不便。而另一种通信方式——命名管道,可以解决不相关进程间的通信问题。
1.什么是命名管道?
命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。
由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。
2.如何创建命名管道?
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *filename, mode_t mode);
若成功则返回0,否则返回-1,错误原因存于errno中。
这个函数创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限。
3.访问命名管道
3.1 打开FIFO文件
与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还是将其打开。
但是有两点要注意,1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。
打开FIFO文件通常有四种方式,
[cpp] view plain copy print?
open(const char *path, O_RDONLY);//1
open(const char *path, O_RDONLY | O_NONBLOCK);//2
open(const char *path, O_WRONLY);//3
open(const char *path, O_WRONLY | O_NONBLOCK);//4
在open函数的调用的第二个参数中,你看到一个陌生的选项O_NONBLOCK,选项O_NONBLOCK表示非阻塞,加上这个选项后,表示open调用是非阻塞的,如果没有这个选项,则表示open调用是阻塞的。
open调用的阻塞是什么一回事呢?很简单,对于以只读方式(O_RDONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_RDONLY),除非有一个进程以写方式打开同一个FIFO,否则它不会返回;如果open调用是非阻塞的的(即第二个参数为O_RDONLY | O_NONBLOCK),则即使没有其他进程以写方式打开同一个FIFO文件,open调用将成功并立即返回。
对于以只写方式(O_WRONLY)打开的FIFO文件,如果open调用是阻塞的(即第二个参数为O_WRONLY),open调用将被阻塞,直到有一个进程以只读方式打开同一个FIFO文件为止;如果open调用是非阻塞的(即第二个参数为O_WRONLY | O_NONBLOCK),open总会立即返回,但如果没有其他进程以只读方式打开同一个FIFO文件,open调用将返回-1,并且FIFO也不会被打开。
4. 实例
//fifo_read.c
include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO "/tmp/myfifo"
main(int argc,char** argv)
{
char buf_r[100];
int fd;
int nread;
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
printf("cannot create fifoserver\n");
printf("Preparing for reading bytes....\n");
memset(buf_r,0,sizeof(buf_r));
fd=open(FIFO,O_RDONLY|O_NONBLOCK,0);
if(fd==-1)
{
perror("open");
exit(1);
}
while(1)
{
memset(buf_r,0,sizeof(buf_r));
if((nread=read(fd,buf_r,100))==-1)
{
if(errno==EAGAIN)
printf("no data yet\n");
}
printf("read %s from FIFO\n",buf_r);
sleep(1);
}
pause();
unlink(FIFO);
}
//fifo_write.c
#include<sys/types.h>
#include<sys/stat.h>
#include<errno.h>
#include<fcntl.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define FIFO_SERVER "/tmp/myfifo"
main(int argc,char** argv)
{
int fd;
int nwrite;
char w_buf[100];
for(;;)
{
memset(&w_buf, 0, sizeof(w_buf));
fd=open(FIFO_SERVER,O_WRONLY|O_NONBLOCK,0);
printf("please input your data:\n");
scanf("%s", w_buf);
if((nwrite=write(fd,w_buf,100))==-1)
{
if(errno==EAGAIN)
printf("The FIFO has not been read yet. Please try later\n");
}
else
printf("write %s to the FIFO\n",w_buf);
}
}
4.1程序的编译
[[email protected] fifo]$ls
fifo_read.c fifo_write.c
[[email protected] fifo]$gcc fifo_read.c -o read
[[email protected] fifo]$gcc fifo_write.c -o write
[[email protected] fifo]$ls
fifo_read.c fifo_write.c read write
[[email protected] fifo]$
程序的运行
在当前console 1中运行read,在console2中运行write
[[email protected] fifo]$sudo ./read
Preparing for reading bytes....
read from FIFO
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
read Hello from FIFO
read FIFO from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
no data yet
read from FIFO
^C[[email protected] fifo]$
console2
[[email protected] fifo]$./write
please input your data:
Hello fifo!
write Hello to the FIFO
please input your data:
write fifo! to the FIFO
please input your data:
^C
[[email protected] fifo]$./write
please input your data:
Hello FIFO
write Hello to the FIFO
please input your data:
write FIFO to the FIFO
please input your data:
^C
[[email protected] fifo]$