博客
关于我
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/

    你可能感兴趣的文章
    Nginx模块 ngx_http_limit_conn_module 限制连接数
    查看>>
    nginx添加模块与https支持
    查看>>
    Nginx用户认证
    查看>>
    Nginx的location匹配规则的关键问题详解
    查看>>
    Nginx的Rewrite正则表达式,匹配非某单词
    查看>>
    Nginx的使用总结(一)
    查看>>
    Nginx的使用总结(三)
    查看>>
    Nginx的使用总结(二)
    查看>>
    Nginx的可视化神器nginx-gui的下载配置和使用
    查看>>
    Nginx的是什么?干什么用的?
    查看>>
    Nginx访问控制_登陆权限的控制(http_auth_basic_module)
    查看>>
    nginx负载均衡和反相代理的配置
    查看>>
    nginx负载均衡器处理session共享的几种方法(转)
    查看>>
    nginx负载均衡的5种策略(转载)
    查看>>
    nginx负载均衡的五种算法
    查看>>
    nginx转发端口时与导致websocket不生效
    查看>>
    Nginx运维与实战(二)-Https配置
    查看>>
    Nginx配置Https证书
    查看>>
    Nginx配置ssl实现https
    查看>>
    Nginx配置TCP代理指南
    查看>>