linux下的有名管道文件读写操作

前言说明:

宿主主机系统:window 7 旗舰版

虚拟机:VMware 10.0.4

操作平台:Ubuntu 12.04

本文代码在个人电脑可以运行,不确保在其他平台可以正确运行

这两天在学进程通信,学到管道通信,弄了有两天,今天才算能弄个明白,赶紧写来保存方便然后查看,如果能帮到大家就更好了。

1、介绍:管道文件是linux下的一种特殊缓存文件(linux的文件其实都是对应底层的一种设备,管道文件和普通文件,一般对应的是我们的磁盘设备)所谓缓存就是只会存在于进程执行的时候,进程关闭管道文件也关闭了,这与普通文件不一样,普通文件会一直存在的。管道文件是linux特有的,windows下是没有的,所以当你在linux的共享目录下建立管道文件是不可行的(特别注意,管道文件没有文件后缀,里面也没有保留东西,她就好像是自来水管,这一刻流进去的,下一秒就会流出来,不会停留在里面的

管道文件分为有名管道和无名管道。

无名管道:是指没有文件节点的管道文件,只能用于有亲缘关系的进程间的通信

有名管道:是指有文件节点的管道文件,能用于没有亲缘关系和有亲缘关系的进程间的通信

划重点:什么是文件节点(看的很多博客都没有说到这个问题提一下)

linux下的管道文件是一种树形结构的,树形结构我们都知道,要找到当前内容的下一个内容我们需要知道一个根节点,通过根节点来遍历找到下一个对象。而我们上面说的文件加点就是这个根节点。因为无名管道没有根节点,要想找到一个对象,必须直接找到她,或者通过其中的亲缘关系找到她,就好比如,你跟你父亲之间不需要第三个中间的人就可以直接认识通信一样。(关于无名管道会专门写一篇blog,这里主要介绍有名管道)

有名管道有文件节点的意思即是说,两个进程不需要任何关系也可以通信,因为他们有节点联系,可以通过节点遍历找到需要通信的两个进程,然后进行通信。这个关系就好比如,你跟我认识,但是我不认识你朋友,我跟你的朋友交流我需要通过你这个节点来找到你朋友,才能建立通信。

2、创建有名管道

mkfifo函数

头文件

#include<sys/types.h>

#include<sys/stat.h>

原型

int mkfifo(const char *pathname, mode_t mode)

成功返回0,失败返回-1

第一个参数是存放的有名管道的路径,包含了有名管道文件的名字,例如:"/tmp/myfifo"的意思就是,在tmp目录下创建了一个名为myfifo的管道文件。但是,如果你写成"/mnt/hgfs/share/myfifo"就会失败,这个是共享目录,链接到windows所以会失败。

第二个参数是读写权限,是个八进制的数,这个以后会花时间写,本文的读写权限为0777(可读可写可执行)

创建成功的管道文件,就可以把他当做是普通文件一样进行操作(但是本质上两者不同)可操作如下:

open()

read()

write()

close()

3、实例 有不少的文章说管道文件只能以读写的方式打开,这个说法是正确的

1、假设有名管道文件以读或写的方式打开。我们知道管道文件是有出入口的(有名管道和普通文件一样共用一个出入口,只要一个文件标识符,无名管道有两个标识符,一个入口一个出口,分开的。)读写的方式打开,就代表,一个进程自己把东西写进管道,再读出来给自己用,这个意义就不大了,管道是用来给两个进程的通信,不是这样用的。网上的说法说“只能以读或写的方式打开“是对的

2、但是有些人在代码实现上面就出现了错误 点击打开链接

写操作

#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 | O_NONBLOCK;
    int bytes_sent = 0;
    char buffer[PIPE_BUF + 1];
    int bytes_read = 0;

    if(access(fifo_name, F_OK) == -1)
    {
        printf ("Create the fifo pipe.\n");
        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());
    pipe_fd = open(fifo_name, open_mode);
    printf("Process %d result %d\n", getpid(), pipe_fd);
	printf("%d\n",pipe_fd);

    if(pipe_fd != -1)
    {
        bytes_read = 0;
        data_fd = open("Data.txt", O_RDONLY);
        if (data_fd == -1)
        {
            close(pipe_fd);
            fprintf (stderr, "Open file[Data.txt] failed\n");
            return -1;
        }

        bytes_read = read(data_fd, buffer, PIPE_BUF);
        buffer[bytes_read] = '\0';
        while(bytes_read > 0)
        {

            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);
}


pipe_fd 是定义的文件操作符,我们知道,open函数打开成功返回的是0,失败返回-1;上面返回-1说明文件打开失败

为什么会打开失败呢?因为程序没有阻塞在open()函数里面,程序一直执行到后面直接close文件了。有名管道文件关闭了就不存在内容了,所以返回失败。

说明:源代码没有该打印语句,个人加上去测试的,具体点击链接查看别人的源代码

怎么修改呢?如下:

 const int open_mode = O_WRONLY | O_NONBLOCK;

修改为

 const int open_mode = O_WRONLY

结果:


可以看见,程序暂停了,阻塞在open函数里面,这才算是成功打开的操作

读操作

#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
#include <string.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 | O_NONBLOCK;
    char buffer[PIPE_BUF + 1];
    int bytes_read = 0;
    int bytes_write = 0;

    memset(buffer, '\0', sizeof(buffer));

    printf("Process %d opening FIFO O_RDONLY\n", getpid());
    pipe_fd = open(fifo_name, open_mode);
    data_fd = open("DataFormFIFO.txt", O_WRONLY|O_CREAT, 0644);
    

    if (data_fd == -1)
    {
        fprintf(stderr, "Open file[DataFormFIFO.txt] failed\n");
        close(pipe_fd);
        return -1;
    }

    printf("Process %d result %d\n",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\n", getpid(), bytes_read);
     exit(EXIT_SUCCESS);
}

读操作同样是这个问题,

int open_mode = O_RDONLY | O_NONBLOCK;

修改为:

int open_mode = O_RDONLY;

最后再开一个终端,运行读操作(注意不要把之前写操作的终端关闭),最后结过如下:



当然他的最后结果对不对不是我谈论的范围,我只是告诉读者正确打开方式

总结一下:

管道文件的特殊性,要求管道文件要在读写的时候,先阻塞在写操作的open函数,当有读操作进来的时候,通信链路建立,写操作开始往管道里写东西,写完成之后,在读操作会把东西读出来,最后两个进程都正常结束退出。注意当你读完一次之后再去读管道文件的时候,是会失败的,因为我前面说过,管道就像是水管,流出去之后就没有了,read完成里面就没东西了。

声明:本文在参考他人blog情况下原创,转载请说明。如有侵权联系删除。

参考文献

1、https://www.cnblogs.com/fangshenghui/p/4039805.html

2、https://blog.csdn.net/best_fiends_zxh/article/details/52923560

3、https://blog.csdn.net/xqhrs232/article/details/53636364


猜你喜欢

转载自blog.csdn.net/tiramisu_l/article/details/80176350