在前一篇文章——Linux进程间通信——使用匿名管道中,我们看到了如何使用匿名管道来在进程之间传递数据,同时也看到了这个方式的一个缺陷,就是这些进程都由一个共同的祖先进程启动,这给我们在不相关的的进程之间交换数据带来了不方便。这里将会介绍进程的另一种通信方式——命名管道,来解决不相关进程间的通信问题。
一、什么是命名管道
命名管道也被称为FIFO文件,它是一种特殊类型的文件,它在文件系统中以文件名的形式存在,但是它的行为却和之前所讲的没有名字的管道(匿名管道)类似。
由于Linux中所有的事物都可被视为文件,所以对命名管道的使用也就变得与文件操作非常的统一,也使它的使用非常方便,同时我们也可以像平常的文件名一样在命令中使用。
二、创建命名管道
我们可以使用两下函数之一来创建一个命名管道,他们的原型如下:
#include <sys/types.h> #include <sys/stat.h> int mkfifo(const char *filename, mode_t mode); int mknod(const char *filename, mode_t mode | S_IFIFO, (dev_t)0);
这两个函数都能创建一个FIFO文件,注意是创建一个真实存在于文件系统中的文件,filename指定了文件名,而mode则指定了文件的读写权限。
mknod是比较老的函数,而使用mkfifo函数更加简单和规范,所以建议在可能的情况下,尽量使用mkfifo而不是mknod。
三、访问命名管道
1、打开FIFO文件
与打开其他文件一样,FIFO文件也可以使用open调用来打开。注意,mkfifo函数只是创建一个FIFO文件,要使用命名管道还是将其打开。
但是有两点要注意,1、就是程序不能以O_RDWR模式打开FIFO文件进行读写操作,而其行为也未明确定义,因为如一个管道以读/写方式打开,进程就会读回自己的输出,同时我们通常使用FIFO只是为了单向的数据传递。2、就是传递给open调用的是FIFO的路径名,而不是正常的文件。
打开FIFO文件通常有四种方式,
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也不会被打开。
四、使用FIFO实现进程间的通信
说了这么多,下面就用一个例子程序来说明一下,两个进程如何通过FIFO实现通信吧。这里有两个源文件,一个fifowrite.c,它在需要时创建管道,然后向管道写入数据,数据由文件Data.txt提供,大小为10M,内容全是字符‘0’。另一个源文件为fiforead.c,它从FIFO中读取数据,并把读到的数据保存到另一个文件DataFormFIFO.txt中。为了让程序更加简洁,忽略了有些函数调用是否成功的检查。
fifowrite.c的源代码如下:
#include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <limits.h> #include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <string.h> int main() { const char *fifo_name = "/tmp/my_fifo"; int pipe_fd = -1; int data_fd = -1; int res = 0; const int open_mode = O_WRONLY; int bytes_sent = 0; char buffer[PIPE_BUF + 1]; if(access(fifo_name, F_OK) == -1) { //管道文件不存在 //创建命名管道 res = mkfifo(fifo_name, 0777); if(res != 0) { fprintf(stderr, "Could not create fifo %s\n", fifo_name); exit(EXIT_FAILURE); } } printf("Process %d opening FIFO O_WRONLY\n", getpid()); //以只写阻塞方式打开FIFO文件,以只读方式打开数据文件 pipe_fd = open(fifo_name, open_mode); data_fd = open("Data.txt", O_RDONLY); printf("Process %d result %d\n", getpid(), pipe_fd); if(pipe_fd != -1) { int bytes_read = 0; //向数据文件读取数据 bytes_read = read(data_fd, buffer, PIPE_BUF); buffer[bytes_read] = '\0'; while(bytes_read > 0) { //向FIFO文件写数据 res = write(pipe_fd, buffer, bytes_read); if(res == -1) { fprintf(stderr, "Write error on pipe\n"); exit(EXIT_FAILURE); } //累加写的字节数,并继续读取数据 bytes_sent += res; bytes_read = read(data_fd, buffer, PIPE_BUF); buffer[bytes_read] = '\0'; } close(pipe_fd); close(data_fd); } else exit(EXIT_FAILURE); printf("Process %d finished\n", getpid()); exit(EXIT_SUCCESS); }
查看本栏目更多精彩内容:http://www.bianceng.cnhttp://www.bianceng.cn/OS/Linux/
以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索文件
, 阻塞
, 进程
, open()
, read阻塞
, 匿名管道 readfile
, open
, nonblock
, 管道
, open创建文件ntfslinuxwrite
, 命名管道
, 命名 管道 C
, 非阻塞通信
FIFO
,以便于您获取更多的相关知识。