添加链接
link管理
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
  • 没有名字;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等;
  • 如果我们想在不相关的进程之间交换数据,可以使用 FIFO 文件来做这项工作,它经常被称为命名管道,是一种特殊类型的文件。

    二,命名管道 FIFO

    2.1 有名管道相关的关键概念

    管 道应用的一个重大限制是它没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道( named pipe FIFO )提出后,该限制得到了克服。 FIFO 不同于管道之处在于它提供一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中。这样,即 使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信(能够访问该路径的进程以及 FIFO 的创建进程之 间),因此,通过 FIFO 不相关的进程也能交换数据。值得注意的是, FIFO 严格遵循先进先出( first in first out ),对管道及 FIFO 的读总是从开始处返回数据,对它们的写则把数据添加到末尾。它们不支持诸如 lseek() 等文件定位操作。

    2.2 有名管道的创建

    命名管道可以从命令行上创建,命令行方法是使用下面这个命令:

    $ mkfifo filename

    命名管道也可以从程序里创建,相关函数有:

    #include <sys/types.h>
    #include <sys/stat.h>
    int mkfifo(const char * pathname, mode_t mode)

    该函数的第一个参数是一个普通的路径名,也就是创建 后 FIFO 的名字。第二个参数与打开普通文件的 open() 函数中的 mode 参数相同。 如果 mkfifo 的第一个参数是一个已经存在的路径名时,会返回 EEXIST 错误,所以一般典型的调用代码首先会检查是否返回该错误,如果确实返回该错 误,那么只要调用打开 FIFO 的函数就可以了。一般文件的 I/O 函数都可以用于 FIFO ,如 close read write 等等。

    2.3 有名管道的打开规则 (与匿名管道一样)

    FIFO (命名管道)与 pipe (匿名管道)之间唯一的区别在它们创建与打开的方式不同,一量这些工作完成之后,它们具有相同的语义。

    man 帮助说明: The only difference between pipes and FIFOs is the manner in which they are created and opened. Once these tasks have been accomplished, I/O on pipes and FIFOs has exactly the same semantics

    有名管道比管道多了一个打开操作: open

    FIFO 的打开规则:

    如果当前打开操作是为读而打开 FIFO 时,若已经有相应进程为写而打开该 FIFO ,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为写而打开该 FIFO (当前打开操作设置了阻塞标志);或者,成功返回(当前打开操作没有设置阻塞标志)。

    如果当前打开操作是为写而打开 FIFO 时,如果已经有相应进程为读而打开该 FIFO ,则当前打开操作将成功返回;否则,可能阻塞直到有相应进程为读而打开该 FIFO (当前打开操作设置了阻塞标志);或者,返回 ENXIO 错误(当前打开操作没有设置阻塞标志)。

    2.4 有名管道的读写规则

    FIFO 中读取数据:

    约定:如果一个进程为了从 FIFO 中读取数据而阻塞打开 FIFO ,那么称该进程内的读操作为设置了阻塞标志的读操作。

  • 如果有进程写打开 FIFO ,且当前 FIFO 内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回 -1 ,当前 errno 值为 EAGAIN ,提醒以后再试。
  • 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种:当前 FIFO 内有数据,但有其它进程在读这些数据;另外就是 FIFO 内没有数据。解阻塞的原因则是 FIFO 中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量。
  • 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时, FIFO 中没有数据也一样(此时,读操作返回 0 )。
  • 如果没有进程写打开 FIFO ,则设置了阻塞标志的读操作会阻塞。
  • 注:如果 FIFO 中有数据,则设置了阻塞标志的读操作不会因为 FIFO 中的字节数小于请求读的字节数而阻塞,此时,读操作会返回 FIFO 中现有的数据量。

    FIFO 中写入数据:

    约定:如果一个进程为了向 FIFO 中写入数据而阻塞打开 FIFO ,那么称该进程内的写操作为设置了阻塞标志的写操作。

    对于设置了阻塞标志的写操作:

  • 当要写入的数据量不大于 PIPE_BUF 时, linux 将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。
  • 当要写入的数据量大于 PIPE_BUF 时, linux 将不再保证写入的原子性。 FIFO 缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
  • 对于没有设置阻塞标志的写操作:

  • 当要写入的数据量大于 PIPE_BUF 时, linux 将不再保证写入的原子性。在写满所有 FIFO 空闲缓冲区后,写操作返回。
  • 当要写入的数据量不大于 PIPE_BUF 时, linux 将保证写入的原子性。如果当前 FIFO 空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前 FIFO 空闲缓冲区不能够容纳请求写入的字节数,则返回 EAGAIN 错误,提醒以后再写;
  • 来看一个具体的实现:

    write 的函数:

    #include <stdio.h>

    #include <unistd.h>

    #include <stdlib.h>

    #include <sys/stat.h>

    #include <sys/types.h>

    #include <fcntl.h>

    int main(int argc, char **argv)

    int infd;

    int fd;

    char buf[1024*4];

    int n = 0;

    char *path="/home/zhf/c_prj/test.c";

    char *path1="/home/zhf/c_prj/tmpfifo";

    infd = open(path,O_RDONLY);

    if(infd == -1){

    perror("open error");

    exit(EXIT_FAILURE);

    if(mkfifo(path1,0644) == -1){

    perror("mkfifo error");

    exit(EXIT_FAILURE);

    fd = open(path1,O_WRONLY);

    if(fd == -1){

    perror("open fifo error");

    exit(EXIT_FAILURE);

    while((n = read(infd,buf,1024*4))){

    write(fd,buf,n);

    close(infd);

    close(fd);

    printf("write success\n");

    return 0;

    read 的函数:

    #include <stdio.h>

    #include <unistd.h>

    #include <stdlib.h>

    #include <sys/stat.h>

    #include <sys/types.h>

    #include <fcntl.h>

    int main(int argc, char **argv)

    int outfd;

    int fd;

    char buf[1024*4];

    int n = 0;

    char *path="/home/zhf/c_prj/tmp.txt";

    char *path1="/home/zhf/c_prj/tmpfifo";

    outfd = open(path,O_WRONLY | O_CREAT | O_TRUNC);

    if(outfd == -1){

    perror("open error");

    exit(EXIT_FAILURE);

    fd = open(path1,O_RDONLY);

    if(fd == -1){

    perror("open fifo error");

    exit(EXIT_FAILURE);

    while((n = read(fd,buf,1024*4))){

    write(outfd,buf,n);

    close(outfd);

    close(fd);

    printf("read success\n");

    return 0;

    运行步骤:

    1 首先在 write 函数中打开 /home/zhf/c_prj/test.c文件并且建立 tmpfifo 文件。以写的方式打开 FIFO 文件

    2 test.c 中读取数据存入 buf 数组中,并继续写入 tmpfifo 文件中

    3 read 函数中打开 /home/zhf/c_prj/tmp.txt 以及 tmpfifo 文件,以读的方式打开 FIFO 文件。并将 FIFO 文件的数据写入 tmp.txt