• Linux进程间通信-命名管道


    前面我们讲了进程间通信的一种方式,匿名管道
    我们知道,匿名管道只能用于父子关系的进程之间。那么没有这种关系的进程之间该如何进行数据传递呢?

    1.什么是命名管道

    匿名管道是在缓存中开辟的输出和输入文件流的空间,只能用于父子关系的进程之间。因为父子进程的输入和输出文件描述符是一致的。
    命名管道是一种实际存在的FIFO文件,称作“管道文件”,用于不同进程之间,命名管道进程间打开同一个FIFO文件,进行数据传递。
    我们可以像普通文件一样操作FIFO文件。
    不同进程,引用同一个FIFO文件,进行数据传递。

    2.创建命名管道
    mkfifo函数:创建一个命名管道

    int mkfifo(const char *filename,mode_t mode);

    filename:指定FIFO文件的名称
    mode:指定文件的读写权限

    3.访问命名管道
    打开FIFO文件有四种方式:

    open(const char *filename,O_RDONLY);
    open(const char *filename,O_RDONLY|O_NONBLOCK);
    open(const char *filename,O_WRONLY);
    open(const char *filename,O_WRONLY|O_NONBLOCK);

    需要注意的是,不能以O_RDWR模式打开FIFO文件
    因为这样一个进程写入的数据会被该进程读取,FIFO一般只用做单向的数据传递。

    open函数的第二个参数,表示是读管道,还是写管道。
    O_NONBLOCK表示FIFO管道的读写是非阻塞的,默认的话,是阻塞的。
    那么何为阻塞呢?
    一个进程写模式打开管道的时候,必须有另一个进程以读模式打开;
    或读模式的时候,必须有另一个进程写写模式打开,否则该进程open函数阻塞,直到满足以上关系。

    非阻塞,意味着open函数会立即返回,若没有其他进程以只读方式打开,open返回-1,并且FIFO也不会被打开。

    4.FIFO管道使用示例
    下例有两个程序,fifowrite.c和fiforead.c分别写管道和读管道。
    fifowrite.c中将一个文本文件data.txt,写到管道。
    fiforead.c中从管道读取数据,并写到dataformfifo.txt文件中。
    程序使用了默认的阻塞模式。
    示例代码如下:

    fifowrite.c

    #include<sys/types.h>
    #include<stdlib.h>
    #include<stdio.h>
    #include<fcntl.h>
    #include<limits.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;
        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
    ");
                exit(EXIT_FAILURE);
            }
        }
        printf("process %d opening fifo O_WRONLY
    ",getpid());
        pipe_fd = open(fifo_name,open_mode);
        data_fd = open("data.txt",O_RDONLY);
        printf("process %d result %d
    ",getpid(),pipe_fd);
        if(pipe_fd!=-1)
        {
            int bytes_read = 0;
            bytes_read = read(data_fd,buffer,PIPE_BUF);
            while(bytes_read>0)
            {
                res = write(pipe_fd,buffer,bytes_read);
                if(res==-1)
                {
                    fprintf(stderr,"write error
    ");
                    exit(EXIT_FAILURE);
                }
                bytes_read = read(data_fd,buffer,PIPE_BUF);
                buffer[bytes_read]='';
            }
            close(pipe_fd);
            close(data_fd);
        }
        else{
            exit(EXIT_FAILURE);
        }
        printf("process %d finished.
    ",getpid());
        exit(EXIT_SUCCESS);
    }

    fiforead.c

    #include<stdlib.h>
    #include<stdio.h>
    #include<sys/types.h>
    #include<fcntl.h>
    #include<limits.h>
    int main()
    {
        const char *fifo_name = "/tmp/my_fifo";
        int pipe_fd = -1;
        int data_fd = -1;
        int res = 0;
        int open_mode = O_RDONLY;
        char buffer[PIPE_BUF+1];
        int bytes_read = 0;
        int bytes_write = 0;
        memset(buffer,'',sizeof(buffer));
    
        printf("process %d opening FIFO O_RDONLY
    ",getpid());
        pipe_fd = open(fifo_name,open_mode);
        data_fd = open("dataformfifo.txt",O_WRONLY|O_CREAT,0644);
        printf("process %d result %d
    ",getpid(),pipe_fd);
        if(pipe_fd!=-1)
        {
            do{
                res = read(pipe_fd,buffer,PIPE_BUF);
                bytes_write = write(data_fd,buffer,res);
                bytes_read +=res;
            }while(res>0);
            close(pipe_fd);
            close(data_fd);
        }
        else{
            exit(EXIT_FAILURE);
        }
        printf("process %d finished,%d bytes read
    ",getpid(),bytes_read);
        exit(EXIT_SUCCESS);
    }

    输出结果:

    我们在shell中输入命令 ls -l /tmp/my_fifo查看FIFO管道文件的属性

    可以看到,FIFO文件生成了,第一个字符‘p’,表示该文件是一个管道文件

    5.多个进程同时写管道
    当多个进程同时写管道时,读管道取得的数据是杂乱的。
    此时,我们可以控制每个进程,当要写入的数据超过某个大小时,才写管道,另外要以阻塞的方式打开FIFO。确保写操作的原子性。

  • 相关阅读:
    如何用vue组件做个机器人?有趣味的代码
    vue--监听属性完成大小写字母间的转换
    C语言基础:进制转换,变量,常量,表达式,基本数据类型,输出函数,输入函数,运算符. 分类: iOS学习 c语言基础 2015-06-10 21:39 25人阅读 评论(0) 收藏
    2019年最新50道java基础部分面试题
    28道java基础面试题-下
    最新28道java基础面试题-上
    后台管理UI模板
    Bellman-Ford 算法及其优化
    【枚举Day1】20170529-2枚举算法专题练习 题解
    【枚举Day1】20170529-2枚举算法专题练习 题目
  • 原文地址:https://www.cnblogs.com/shijingjing07/p/5621256.html
Copyright © 2020-2023  润新知