博客
关于我
Linux系统编程27:进程间通信之管道的基本概念和匿名管道与命名管道及管道特性
阅读量:208 次
发布时间:2019-02-28

本文共 3251 字,大约阅读时间需要 10 分钟。

管道与命名管道的深入理解

(1)管道是什么?

管道是UNIX系统中的一种古老的通信方式,实际上它本质上是一个文件。管道允许进程之间进行简单的数据传输,数据可以是文本或二进制数据。例如,命令行工具who的标准输出原本会显示在屏幕上,但可以通过管道将输出重定向到一个文件中。这种重定向称为匿名管道,常见的例子是who | wc -l,其中who将数据写入管道,wc -l则从管道读取数据并显示结果。


(2)匿名管道

2.1 读端与写端

who | wc -l的例子可以看出,who进程使用管道的写端将数据写入管道,而wc进程使用管道的读端读取数据。两个进程通过管道进行通信时,一个进程需要使用写端,另一个进程需要使用读端。因此,管道文件需要两个文件描述符来控制读写操作。

2.2 匿名管道的创建

在UNIX系统中,匿名管道可以通过pipe()函数创建。该函数的头文件是<unistd.h>,函数原型为int pipe(int fd[2])。传入的fd数组包含两个元素,fd[0]表示管道的读端文件描述符,fd[1]表示写端文件描述符。默认情况下,fd[0]fd[1]分别打开管道文件进行读写操作。


(3)最简单的进程间通信-演示

3.1 管道的读写流程

  • 首先调用pipe()函数创建管道。
  • 然后调用fork()创建子进程。
  • 父进程关闭写端fd[1],子进程关闭读端fd[0]
  • 子进程不断向写端写入数据,父进程从读端读取数据。
  • 以下是一个简单的示例:

    #include 
    #include
    #include
    #include
    int main() { int pipefd[2] = {0}; pipe(pipefd); pid_t child_id = fork(); if (child_id == 0) { close(pipefd[0]); const char *msg = "This is the data that the child process wrote"; while (1) { write(pipefd[1], msg, strlen(msg)); sleep(1); } } else { close(pipefd[1]); char buffer[64]; while (1) { ssize_t ret = read(pipefd[0], buffer, sizeof(buffer) - 1); if (ret > 0) { buffer[ret] = '\0'; printf("The father process got the information: %s\n", buffer); } } } return 0;}

    3.2 进程间通信的效果

    在上述代码中,子进程每秒写入一段数据,父进程则持续读取并输出数据。当子进程退出时,父进程也随之退出。这种简单的通信机制展示了管道的基本特性。


    (4)管道的四大特性

    4.1 特性一:读端快速,写端慢

    如果写端没有关闭文件描述符且不进行写入操作,读端可能会长时间阻塞(即读端慢)。例如,当子进程的睡眠时间从1秒增加到5秒时,父进程仍然可以及时读取数据。

    4.2 特性二:写端条件不就绪时会被阻塞

    当写端试图向已满的管道中写入数据时,写端会被阻塞,直到读端读取数据并释放管道空间。

    4.3 特性三:写端关闭后读端会读到数据结束

    如果写端关闭,读端在读到管道内容结束(返回值为0)时会停止读取。

    4.4 特性四:读端关闭后写端可能被操作系统终止

    如果读端关闭,操作系统会发送SIGPIPE信号终止写端进程。


    (5)命名管道

    5.1 命名管道与匿名管道的区别

    匿名管道只能在具有共同祖先的进程间通信,而命名管道可以在任何进程间通信。

    5.2 命名管道的创建

    命名管道可以通过mkfifo命令或函数mkfifo()创建。例如:

    mkfifo my_pipe

    函数原型为int mkfifo(const char *filename, mode_t mode)

    5.3 命名管道的实现

    以下是一个使用命名管道的简单示例:

    #include 
    #include
    #include
    #include
    int main() { const char *fifo_path = "my_fifo"; umask(0); if (mkfifo(fifo_path, 0666) == -1) { perror("创建管道失败"); return 1; } int fd = open(fifo_path, O_WRONLY); if (fd == -1) { perror("打开管道失败"); return 1; } char *msg = "Hello, world!"; while (1) { write(fd, msg, strlen(msg)); sleep(1); } return 0;}

    5.4 命名管道的通信

    在另一进程中打开同一管道文件并进行读写操作:

    #include 
    #include
    #include
    #include
    int main() { const char *fifo_path = "my_fifo"; int fd = open(fifo_path, O_RDONLY); if (fd == -1) { perror("打开管道失败"); return 1; } char *buffer = "Hello, parent!"; while (1) { ssize_t ret = read(fd, buffer, sizeof(buffer) - 1); if (ret > 0) { buffer[ret] = '\0'; printf("Parent received: %s\n", buffer); } } return 0;}

    (6)从内核角度理解管道

    在Linux内核中,所有文件都以文件结构体file表示。管道文件的file结构体的type成员为S_IFIFO,表明这是一个FIFO文件。文件的路径和目录结构体dentry关联,目录的inode存储文件的信息。


    (7)管道总结

    • 特性

      • 只能用于具有共同祖先的进程间通信。
      • 提供流式服务,读端可以任意读取数据。
      • 管道的生命周期随进程而终结。
      • 具有同步和互斥机制。
      • 半双工,数据只能单向流动。
    • 优势

      • 简单且高效,适合小量数据传输。
      • 不占用内存,通信成本低。

    通过对管道和命名管道的理解,我们可以更好地利用这些机制进行进程间通信。例如,who | wc -l的实现正是基于匿名管道的通信机制,而mkfifo命令则用于创建命名管道以支持跨进程通信。

    转载地址:http://yisi.baihongyu.com/

    你可能感兴趣的文章
    Node的Web应用框架Express的简介与搭建HelloWorld
    查看>>
    Node第一天
    查看>>
    node编译程序内存溢出
    查看>>
    Node读取并输出txt文件内容
    查看>>
    node防xss攻击插件
    查看>>
    noi 1996 登山
    查看>>
    noi 7827 质数的和与积
    查看>>
    NOI-1.3-11-计算浮点数相除的余数
    查看>>
    NOI2010 海拔(平面图最大流)
    查看>>
    NOIp2005 过河
    查看>>
    NOIP2011T1 数字反转
    查看>>
    NOIP2014 提高组 Day2——寻找道路
    查看>>
    noip借教室 题解
    查看>>
    NOIP模拟测试19
    查看>>
    NOIp模拟赛二十九
    查看>>
    Vue3+element plus+sortablejs实现table列表拖拽
    查看>>
    Nokia5233手机和我装的几个symbian V5手机软件
    查看>>
    non linear processor
    查看>>
    Non-final field ‘code‘ in enum StateEnum‘
    查看>>
    none 和 host 网络的适用场景 - 每天5分钟玩转 Docker 容器技术(31)
    查看>>