• 命名管道FIFO和mkfifo函数


    进程间通信必须通过内核提供的通道,而且必须有一种办法在进程中标识内核提供的某个通道,前面讲过的匿名管道是用打开的文件描述符来标识的。如果要互相通信的几个进程没有从公共祖先那里继承文件描述符,它们怎么通信呢?内核提供一条通道不成问题,问题是如何标识这条通道才能使各进程都可以访问它?文件系统中的路径名是全局的,各进程都可以访问,因此可以用文件系统中的路径名来标识一个IPC通道。


    FIFO和UNIX Domain Socket这两种IPC机制都是利用文件系统中的特殊文件来标识的。

    FIFO文件在磁盘上没有数据块,仅用来标识内核中的一条通道,如 prw-rw-r-- 1 simba simba      0 May 21 10:13 p2,文件类型标识为p表示FIFO,文件大小为0。各进程可以打开这个文件进行read/write,实际上是在读写内核通道(根本原因在于这个file结构体所指向的read、write函数和常规文件不一样),这样就实现了进程间通信。UNIX Domain Socket和FIFO的原理类似,也需要一个特殊的socket文件来标识内核中的通道,例如/run目录下有很多系统服务的socket文件:

    srw-rw-rw- 1 root       root          0 May 21 09:59 acpid.socket

    ....................

    文件类型s表示socket,这些文件在磁盘上也没有数据块。


    一、命名管道(FIFO)

    匿名管道应用的一个限制就是只能在具有共同祖先(具有亲缘关系)的进程间通信。
    如果我们想在不相关的进程之间交换数据,可以使用FIFO文件来做这项工作,它经常被称为命名管道。

    命名管道可以从命令行上创建,命令行方法是使用下面这个命令:
    $ mkfifo filename
    命名管道也可以从程序里创建,相关函数有:
    int mkfifo(const char *filename,mode_t mode);


    二、命名管道和匿名管道

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

    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.


    三、命名管道的打开规则

    如果当前打开操作是为读而打开FIFO时
    O_NONBLOCK disable:阻塞直到有相应进程为写而打开该FIFO
    O_NONBLOCK enable:立刻返回成功
    如果当前打开操作是为写而打开FIFO时
    O_NONBLOCK disable:阻塞直到有相应进程为读而打开该FIFO
    O_NONBLOCK enable:立刻返回失败,错误码为ENXIO


    需要注意的是打开的文件描述符默认是阻塞的,大家可以写两个很简单的小程序测试一下,主要也就一条语句

    int fd = open("p2", O_WRONLY); 假设p2是命名管道文件,把打开标志换成 O_RDONLY 就是另一个程序了,可以先运行RD程序,此时会阻塞,再在另一个窗口运行WR程序,此时两个程序都会从open返回成功。非阻塞时也不难测试,open时增加标志位就可以了。


    需要注意的是 命令管道与匿名管道的读写规则是一样的,参见这里


    下面示例命名管道完成拷贝文件的功能:

     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    /*************************************************************************
        > File Name: process_.c
        > Author: Simba
        > Mail: dameng34@163.com
        > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
     ************************************************************************/
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<signal.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)

    int main(int argc, char *argv[])
    {
        mkfifo("tp", 0644);
        int infd = open("Makefile", O_RDONLY);
        if (infd == -1)
            ERR_EXIT("open error");

        int outfd;
        outfd = open("tp", O_WRONLY);
        if (outfd == -1)
            ERR_EXIT("open error");

        char buf[1024];
        int n;
        while ((n = read(infd, buf, 1024)) > 0)
            write(outfd, buf, n);

        close(infd);
        close(outfd);

        return 0;
    }


    程序使用mkfifo函数创建一个命名管道文件tp,将Makefile 的文件都读取到tp文件中。


     C++ Code 
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
     
    /*************************************************************************
        > File Name: process_.c
        > Author: Simba
        > Mail: dameng34@163.com
        > Created Time: Sat 23 Feb 2013 02:34:02 PM CST
     ************************************************************************/
    #include<sys/types.h>
    #include<sys/stat.h>
    #include<unistd.h>
    #include<fcntl.h>
    #include<stdio.h>
    #include<stdlib.h>
    #include<errno.h>
    #include<string.h>
    #include<signal.h>
    #define ERR_EXIT(m) 
        do { 
            perror(m); 
            exit(EXIT_FAILURE); 
        } while(0)

    int main(int argc, char *argv[])
    {

        int outfd = open("Makefile2", O_WRONLY | O_CREAT | O_TRUNC, 0644);
        if (outfd == -1)
            ERR_EXIT("open error");

        int infd;
        infd = open("tp", O_RDONLY);
        if (infd == -1)
            ERR_EXIT("open error");

        char buf[1024];
        int n;
        while ((n = read(infd, buf, 1024)) > 0)
            write(outfd, buf, n);

        close(infd);
        close(outfd);
        unlink("tp"); // delete a name and possibly the file it refers to
        return 0;
    }

    可以看到跟上面的程序是相反的,即从tp读取到Makefile2,完成拷贝文件的功能,这里用到了一个unlink函数,属于inode_operations系列的一个函数,即inode引用计数减1,当引用计数为0且进程已经关闭文件描述符时,文件将被删除。


    参考:《APUE》

  • 相关阅读:
    如何设置项目encoding为utf-8
    如何使用Navicat恢复数据库脚本
    如何使用Navicat备份数据库脚本
    表单编辑组件使用指南
    Maven如何打包本地依赖包
    怎么替换jar包里面的文件?
    如何开始创建第一个基于Spring MVC的Controller
    怎么将数据库从Oracle迁移到SQL Server,或从Oracle迁移到MySQL
    如何使用Eclipse调试framework
    windows下安装oracle,sqlplus连接启动oracle(oracle 主机字符串输入是什么)
  • 原文地址:https://www.cnblogs.com/alantu2018/p/8477351.html
Copyright © 2020-2023  润新知