有名管道(如何创建,基本读写,进程间通信)

1.1 有名管道概述

有名管道,也被称为FIFO(First In First Out),是一种在文件系统中创建的特殊文件。它允许不相关的进程通过文件系统进行通信,而不需要这些进程之间存在亲缘关系或共享内存。

命名管道(FIFO)和管道(pipe)基本相同,但也有一些显著的不同, 其特点是:

1、半双工,数据在同一时刻只能在一个方向上流动。

2、写入FIFO中的数据遵循先入先出的规则。

3、FIFO所传送的数据是无格式的,这要求FIFO的读出方与写入方必须事先约定好数据的格 式,如多少字节算一个消息等。

4FIFO在文件系统中作为一个特殊的文件而存在并且在文件系统中可见,所以有名管道可以实现不相关进程间通信,但FIFO中的内容却存放在内存中

5、管道在内存中对应一个缓冲区。不同的系统其大小不一定相同。

6、从FIFO读数据是一次性操作,数据一旦被读,它就从FIFO中被抛弃,释放空间以便写更多的数据。

7、当使用FIFO的进程退出后,FIFO文件将继续保存在文件系统中以便以后使用

8、FIFO有名字,不相关的进程可以通过打开命名管道进行通信

那么再具体说明一下和管道的异同

共同特点

  1. 半双工通信:数据在同一时刻只能在一个方向上流动,即只能单向传输数据。
  2. 先进先出(FIFO)原则:写入的数据按照先入先出的规则进行读取,确保数据的有序性。
  3. 无格式数据传输:传输的数据是无格式的字节流,读写双方需要事先约定数据格式。
  4. 基于内存的缓冲区:数据在内存中进行传输和缓冲,提高了数据传输效率。
  5. 一次性读取操作:从管道或FIFO中读取数据是一次性操作,数据一旦被读取就会被抛弃,为新数据腾出空间。

不同之处

  1. 可见性与持久性:命名管道(FIFO)在文件系统中可见,并作为特殊文件存在,即使创建进程退出后仍然保留,以便后续使用。而管道(pipe)则不可见,且在使用它的进程退出后自动消失。
  2. 通信范围:命名管道(FIFO)允许不相关的进程通过打开管道文件进行通信,而管道(pipe)通常用于具有亲缘关系的进程之间的通信,如父子进程。
  3. 缓冲区大小:管道(pipe)的缓冲区大小在不同系统中可能有所不同,而命名管道(FIFO)作为文件系统中的特殊文件,其缓冲区大小可能受到文件系统的限制。
  4. 有名与无名:命名管道(FIFO)有具体的名称,可以通过名称来引用和访问;而管道(pipe)通常是匿名的,没有具体的名称标识。

1.2 有名管道的创建 

方法1:用过shell命令mkfifo创建有名管道

 mkfifo 文件名

 

ls -l命令用于显示文件和目录的详细信息。 

方法2:使用函数mkfifo


  #include <sys/types.h>
  #include <sys/stat.h>
  int mkfifo(const char *pathname, mode_t mode);
  功能:
     创建一个有名管道,产生一个本地文件系统可见的文件pathname
  参数:
     pathname:有名管道创建后生成的文件,可以带路径
     mode:管道文件的权限,一般通过八进制数设置即可,例如0664
  返回值:
     成功:0
     失败:‐1

写一个案例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>

int main()
{
    //通过mkfifo函数创建有名管道
    if(mkfifo("fifo_file", 0664) == -1)
    {
        //printf("errno = %d\n", errno);
        //如果管道文件已经存在,不需要报错退出,直接使用即可,所以需要在错误输出之前把
        //因为文件存在的错误排除
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            // exit(1);
        }
    }
    return 0;
}

执行结果

1.3 有名管道的基本读写操作 

由于有名管道在本地创建了一个管道文件,所以系统调用的IO函数基本都可以对有名管道进行操作, 但是不能使用lseek修改管道文件的偏移量

注意有名管道创建的本地的文件只是起到标识作用,真正有名管道实现进程间通信还是在内核空间开辟内存,所以本地产生的文件只是一个标识,没有其他作用,对本地管道文件的操作实质就是对内核空间的操作 

案例

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#define FIFONAME "fifo_file"

int main(int argc, char const *argv[])
{
    //通过mkfifo函数创建有名管道
    if(mkfifo(FIFONAME, 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //对有名管道进行操作
    //管道后写入的数据会保存在之前写入数据的后面,不会替换
    //如果管道中没有数据了,读操作会阻塞

    //通过open函数打开管道文件并得到文件描述符
    int fd;
    fd = open(FIFONAME, O_RDWR);
    if(fd == -1)
    {
        perror("fail to open");
        exit(1);
    }

    //通过write函数向管道中写入数据
    if(write(fd, "hello world", strlen("hello world")) == -1)
    {
        perror("fail to write");
        exit(1);
    }

    write(fd, "nihao beijing", strlen("nihao beijing"));

    //通过read函数读取管道中的数据
    char buf[32] = "";
    if(read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = [%s]\n", buf);

    if(read(fd, buf, sizeof(buf)) == -1)
    {
        perror("fail to read");
        exit(1);
    }
    printf("buf = [%s]\n", buf);

    //使用close函数关闭文件描述符
    close(fd);
    return 0;
}

运行结果

 1.4 有名管道实现进程间通信

由于有名管道在本地创建了一个管道文件,所以不相关的进程间也可以实现通信

1.4.1 send

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    //如果没有创建有名管道,则创建有名管道
    //为了实现两个进程都可以收发数据,所以需要创建两个有名管道
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }
    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    //打开两个有名管道并得到文件描述符
    int fd_w, fd_r;
    if((fd_w = open("myfifo1", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    if((fd_r = open("myfifo2", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    ssize_t bytes;
    while(1)
    {
        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';

        //send进程负责将数据写入myfifo1,接着从myfifo2中读取数据
        if((bytes = write(fd_w, buf, sizeof(buf))) == -1)
        {
            perror("fail to write");
            exit(1);
        }

        if((bytes = read(fd_r, buf, sizeof(buf))) == -1)
        {
            perror("fail to read");
            exit(1);
        }
        printf("from recv: %s\n", buf);
    }
    return 0;
}

这段代码实现了一个简单的双向通信机制,其中"myfifo1"管道用于发送数据,"myfifo2"管道用于接收数据。通过这种方式,两个不相关的进程可以互相发送和接收数据,而不需要直接引用对方。

1.4.2  recv

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int main(int argc, char const *argv[])
{
    if(mkfifo("myfifo1", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    if(mkfifo("myfifo2", 0664) == -1)
    {
        if(errno != EEXIST)
        {
            perror("fail to mkfifo");
            exit(1);
        }
    }

    int fd_w, fd_r;
    if((fd_r = open("myfifo1", O_RDONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }
    if((fd_w = open("myfifo2", O_WRONLY)) == -1)
    {
        perror("fail to open");
        exit(1);
    }

    char buf[128] = "";
    ssize_t bytes;
    while(1)
    {
        if((bytes = read(fd_r, buf, sizeof(buf))) == -1)
        {
            perror("fail to read");
            exit(1);
        }
        printf("from send: %s\n", buf);

        fgets(buf, sizeof(buf), stdin);
        buf[strlen(buf) - 1] = '\0';
        write(fd_w, buf, sizeof(buf));
    }
    return 0;
}

我创建了两个有名管道"myfifo1"和"myfifo2",并分别以只读和只写的方式打开它们。在无限循环中,代码首先从"myfifo1"中读取数据,并将其打印出来。然后,它从标准输入读取一行数据,并将其写入到"myfifo2"中。通过这种方式,两个进程可以互相发送和接收数据。

运行结果

  • 8
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值