有名管道:FIFO
到目前为止,我们只是可以在两个相关的程序之间传递数据,也就是说,由一个共同的祖先进程启动的程序。通常这并不是十分方便,因为我们希望不相关的进程也可以交换数据。
我们可以使用FIFO来实现这个操作,通常称之为有名管道。一个有名管道是在文件系统中作为名字存在的一个特殊的文件类型(记住,在Linux一切皆文件),但是行为类似于我们已经了解的无名管道。
我们可以通过命令行或是在一个程序内部创建有名管道。由于历史原因,用于创建有名管道的命令为mknod:
$ mknod filename p
然而,mknod命令并不在X/Open命令列表中,所以他并不是在所有的类Unix系统上都可用。更好的命令行方法是使用
$ mkfifo filename
在程序内部,我们可以使用两个不同的命令:
#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);
类似于mknod命令,我们可以使用mknod函数创建许多特殊的文件类型。使用值为dev_t类型的0值以及使用S_IFIFO的或文件访问模型是创建有名管道函数的唯一可移植用法。在我们的例子中我们会使用简化的mkfifo。
试验--创建有名管道
作为fifo1.c,我们只需要输入下面的代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int res = mkfifo("/tmp/my_fifo",0777);
if (res == 0)
printf("FIFO created\n");
exit(EXIT_SUCCESS);
}
我们可以用下面的命令来查看这个管道:
$ ls -lF /tmp/my_fifo
prwxr-xr-x 1 rick users 0 July 10 14:55 /tmp/my_fifo|
注意,输出的第一个字符是p,表明是一个管道。后面的|是ls命令的-F选项添加上的,也表明是一个管道。
工作原理
程序使用mkfifo函数创建一个特殊的文件。尽管我们请求一个0777的模式,但是这个模式被用户掩码设置进行了修改,就如同在普通的文件创建中一样,所以最终的文件模式为755。如果我们的umask设置不同,例如为0002,我们就会看到不同的权限。
我们可以使用rm命令或是在一个程序中通过使用unlink系统调用就如同移除一个普通文件一样删除FIFO。
访问FIFO
有名管道的一个非常有用的特性就在于,因为他们出现在文件系统中,我们可以像平时使用文件名一样来使用他们。在我们使用我们创建的FIFO进行更多的编程之前,我们可以使用通常的文件命令来探索FIFO文件的行为。
试验--访问FIFO文件
1 首先,让我们试着读取(空)FIFO文件:
$ cat < /tmp/my_fifo
2 现在试着向FIFO写入数据。我们需要使用另一个不同的终端,因为第一个终端现在正被挂起,等待数据出现在FIFO中。
$ echo "sdsdfasdf" > /tmp/my_fifo
我们可以看到由cat命令输出的结果。如果我们没有向FIFO发送任何数据,cat命令就会一直挂起直到我们中断他,通常是使用Ctrl+C。
3 我们可以通过将第一个命令放入后端而同时完成两个操作:
$ cat < /tmp/my_fifo &
[1] 1316
$ echo “sdsdfasdf” > /tmp/my_fifo
sdsdfasdf
[1]+ Done cat </tmp/my_fifo
$
工作原理
因为在FIFO中没有数据,cat与echo程序就会阻塞,分别等待数据到达与某个进程读取数据。
看一下第三步,cat进程初始在后台被阻塞。当echo命令使得一些数据可用时,cat命令就会读取数据并将其输出到标准输出。注意到cat命令会立即退出而不会等待更多的数据。他并没有阻塞,因为第二个命令将数据完全放入FIFO中之后,管道将会被关闭,所以在cat程序中的read调用会返回0,表明文件的结束。
现在我们已经看到当我们使用命令行程序FIFO是如何动作的,现在让我们更详细的了解一下程序接口,这是当我们访问FIFO时允许我们在读取与写入的动作上执行更多控制的地方。
注意:与pipe调用所创建的管道不同,FIFO是作为一个命名文件而存在的,而不是一个打开的文件描述符,而且他在可以读取与写入之后必须打开。我们可以使用我们在前面对文件进行操作时的open与close函数来打开与关闭一个FIFO。open调用需要传递一个FIFO的路径名,而不是一个常规文件。
使用open打开FIFO
打开FIFO的主要限制在于使用O_RDWR模型的程序也许会并不会打开一个FIFO进行读取与写入。如果一个程序这样做,结果是未定义的。这是一个合理的限制,因为通常我们使用FIFO只是在一个方向上传递数据,所以我们并不需要O_RDWR模型。进程将会读取其自己的输出端。
如果我们希望在程序之间进行双向的数据传递,最好是使用一对FIFO,或是管道,每个用于一个方向,或是通过显示的关闭与重新打开FIFO来改变数据流的方向。我们将会在本章的稍后部分来探讨双向的数据交换。
打开一个FIFO与打开一个常规文件的另一个不同就在于带有O_NONBLOCK选项的打开标记(open的第二个参数)。使用这个打开模式不仅会改变open调用是如何处理的,而且会改变在返回的文件描述符上read与write请求是如何处理的。
O_RDONLY,O_WRONLY与O_NONBLOCK标记有四种合法的组合。我们将会依次考虑每一个。
open(const char *path, O_RDONLY);
在这种情况下,open调用会阻塞;直到有一个进程打开同一个FIFO用于写入时这个调用才会返回。这种情况类似于前面的cat例子。
open(const char *path, O_RDONLY | O_NONBLOCK);
open调用将会成功而且会立即返回,尽管此时的FIFO还没有被任何进程打开用于写入数据。
open(const char *path, O_WRONLY);
在这种情况下,open调用会阻塞,直到有一个进程打开同一个FIFO用于读取。
open(const char *path, O_WRONLY | O_NONBLOCK);
这个调用会立即返回,但是如果没有进程打开FIFO用于读取,open就会返回错误-1,而FIFO也不会被打开。如果有进程使得FIFO打开用于读取,返回的文件描述符可以用于写入FIFO。
注意:O_NONBLOCK与O_RDONLY和O_WRONLY的使用之间的区别在于,在非阻塞的情况下,如果没有进程使得管道打开用于读取,用于写入的open调用就会失败,但是非阻塞的读取调用不会失败。O_NONBLOCK标记不会影响close调用的行为。
试验--打开FIFO文件
下面我们来看一下如何使用带有O_NONBLOCK标记的open来同步两个进程。我们不会使用多个例子程序,相反我们会编写一个测试程序,fifo2.c,这个程序会通过传递不同的参数使得我们来探讨FIFO的行为。
1 我们由头文件与#define开始,并且检测所提供的命令行参数的正确个数:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#define FIFO_NAME "/tmp/my_fifo"
int main(int argc, char **argv)
{
int res;
int open_mode = 0;
if (argc < 2)
{
fprintf(stderr, "Usage: %s <some comination of O_RDONLY O_WRONLY O_NONBLOCK> \n", *argv);
exit(EXIT_FAILURE);
}
2 假设程序传递了正确的参数,我们现在由这些参数设置open_mode的值:
argv++;
if (strncmp(*argv, "O_RDONLY", 8) == 0) open_mode |= O_RDONLY;
if (strncmp(*argv, "O_WRONLY", 8) == 0) open_mode |= O_WRONLY;
if (strncmp(*argv, "O_NONBLOCK", 10) == 0) open_mode |= O_NONBLOCK;
argv++;
if(*argv)
{
if(strncmp(*argv, "O_RDONLY", 8) == 0) open_mode |= O_RDONLY;
if(strncmp(*argv, "O_WRONLY", 8) == 0) open_mode |= O_WRONLY;
if(strncmp(*argv, "O_NONBLOCK", 10) == 0) open_mode |= O_NONBLOCK;
}
3 我们现在检测FIFO是否存在,如果需要我们要进行创建。然后FIFO会被打开并产生输出。最后FIFO会被关闭。
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 open FIFO\n", getpid());
res = open(FIFO_NAME, open_mode);
printf("Process %d result %d\n", getpid(), res);
sleep(5);
if(res != -1) (void)close(res);
printf("Process %d finished\n", getpid());
exit(EXIT_SUCCESS);
}
工作原理
这个程序允许我们在命令行上指定我们希望使用的O_RDONLY,O_WRONLY与O_NONBLOCK的组合。通过将已知的字符串与命令行参数进行比较,如果字符串匹配就会设置相应的标记。这个程序使用access函数来检测FIFO是否已经存在,如果需要则会创建这个文件。
我们并没有销毁FIFO,因为我们并没有办法来区分是否还有其他的程序正在使用FIFO。
带有O_NONBLOCK的O_RDONLY与O_WRONLY
现在我们有了测试程序,让我们来试验一些组合;注意我们将文件程序放入后台:
$ ./fifo2 O_RDONLY &
[1] 152
Process 152 opening FIFO
$ ./fifo2 O_WRONLY
Process 153 opening FIFO
Process 152 result 3
Process 153 result 3
Process 152 finished
Process 153 finished
这是有名管道的最通常用法。他会使用读取进程启动并且在open调用中等待,然后当第二个程序打开FIFO时,两个程序都会继续。注意在open调用时,读取进程与写入进程都进行了同步。
注意:当一个Linux进程被阻塞时,他并不消耗CPU资源,所以这种进程同步方法是很有效的。
带有O_NONBLOCK的O_RDONLY与O_WRONLY
这一次,读取进程会执行open调用并立即继续,尽管并不存在写入进程。写入进程也会在open调用之后立即继续,因为FIFO已经打开用于读取。
$ ./fifo2 O_RDONLY O_NONBLOCK &
[1] 160
Process 160 opening FIFO
$ ./fifo2 O_WRONLY
Process 161 opening FIFO
Process 160 result 3
Process 161 result 3
Process 160 finished
Process 161 finished
[1]+ Done fifo2 O_RDONLY O_NONBLOCK
这两个例子是open模式的最通常组合。我们可以使用其他的一些组合来试验这个程序。
分享到:
相关推荐
本文将详细介绍五种主要的进程间通信方式:管道通信、消息队列通信、共享内存通信、信号量通信以及套接字通信。 1. **管道通信**: 管道是一种半双工通信机制,数据只能单向流动,通常用于父子进程或兄弟进程之间...
七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四.信号 ( sinal ) 五.消息队列 ( message queue ) 六.信号量 ( semophore ) 七.套接字 ( socket ) 进程间通信...
Linux环境进程间通信(五) 共享内存(上)
进程间通信之信号 sinal ) 唯一的异步通信方式 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) 六 信号量 ...
在操作系统领域,进程间通信(IPC,Inter-Process Communication)是一项至关重要的技术,它使得不同进程之间能够交换数据,协同工作。本实验“进程间通信--操作系统实验”旨在帮助学生深入理解和熟练掌握这一核心...
进程间通信之套接字( socket ) 网络间通信 七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四.信号 ( sinal ) 五.消息队列 ( message queue ) 六.信号量 ( ...
### Linux下的进程间通信详解 #### 一、引言 在多进程的环境中,进程间通信(IPC,Inter-Process Communication)是操作系统中一个至关重要的功能。Linux作为一款优秀的开源操作系统,提供了多种进程间通信机制,...
在编程领域,进程间通信(IPC,Inter-Process Communication)是一项关键的技术,它允许不同的进程之间交换数据,协同工作。Delphi,作为一个强大的面向对象的编程环境,提供了多种方式进行进程间通信。本篇文章将...
进程间通信之消息队列 ( message queue ) 消息队列是消息的链表,具有特定的格式,并由消息队列标识符标识. 七种进程间通信方式: 一.无名管道( pipe ) 二.有名管道( fifo ) 三.共享内存 ( shared memory ) 四....
在Linux操作系统中,进程间通信(IPC,Inter-Process Communication)是多个进程之间共享数据、交换信息的关键技术。本文将深入探讨Linux进程间通信的多种方法、原理以及实际应用。 一、管道(Pipe) 管道是一种...
在Windows操作系统中,进程间通信(IPC,Interprocess Communication)是一种技术,允许不同的进程之间共享数据、协调工作或交换信息。这种技术对于多线程和多进程应用的开发至关重要,尤其是在分布式系统和并发编程...
### Binder与进程间通信 #### 一、Binder机制概述 Binder机制是Android系统中实现进程间通信(Inter-Process Communication, IPC)的核心架构之一。它采用客户端/服务器(Client/Server, C/S)架构,允许不同进程...
### 进程间通信之管道通信 #### 一、引言 在现代操作系统中,进程间的通信(IPC)是一项至关重要的技术,它使得不同进程能够相互通信与协作,完成复杂的任务。管道作为一种简单而有效的IPC机制,在Unix及类Unix...
在Windows操作系统中,进程间通信(Inter-Process Communication, IPC)是一种允许不同进程之间交换数据和协调工作的技术。它是多任务环境下程序设计的关键部分,使得应用程序可以共享资源、协同工作,甚至实现...
进程间通信之无名管道(pipe) 注意: 1 只能用于具有亲缘关系的进程之间的通信 2 SIGPIPE信号的处理 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 ...
《UNIX网络编程卷2:进程间通信》是网络编程领域中的经典著作,深入探讨了在UNIX操作系统环境下如何实现进程间的高效通信。这部分源代码包含了书中多个章节的关键示例,可以帮助读者更好地理解和应用书中的理论知识...
《Linux环境进程间通信(五):_共享内存(上).doc》则介绍了共享内存,这是一种效率极高的通信方式,但需要额外的同步机制来避免数据竞争。 最后,《5_Linux环境进程间通信(上).ppt》可能涵盖了多种IPC方式的概述,...
进程间通信之有名管道(fifo) 注意: 如果只打开有名管道的一端 则系统将暂时阻塞打开进程 直到有另一个进程打开该管道的另一端 当前进程才会继续执行 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道...
进程间通信之共享内存 shared memory ) 1 效率最高 2 存在竞态 七种进程间通信方式: 一 无名管道( pipe ) 二 有名管道( fifo ) 三 共享内存 shared memory 四 信号 sinal 五 消息队列 message queue ) ...