Linux 进程通信 -- 管道

本文介绍了Linux中的管道通信机制,包括匿名管道和命名管道的使用。管道是一种单向通信方式,允许进程通过内核的环形缓存区传递数据。文章详细阐述了管道的实现机制,如文件描述符的设置、内核数据结构,并分析了读写操作的过程,强调了SIGPIPE信号的处理和数据原子性的保证。

1、管道简介

管道是Linux提供的进程间通信机制之一,允许通信进程之间通过文件读写的方式单向传递数据。内核实现的文件系统pipefs,会在内核为每个管道文件分配一个的环形缓存区,以支持读/写操作。进程可以使用两种类型的管道进行通信:

  • 匿名管道:只支持在父子进程、兄弟进程之间通信。一般使用方式为,父进程调用pipe()创建匿名管道,fork()的子进程默认继承父进程打开的管道
  • 命名管道:支持任意的进程通过管道通信。进程通信前,需要先创建fifo类型特殊的文件,然后读写进程分别打开该文件进行读写

2、 管道机制

2.1 匿名使用示例

举一个常见的场景,我们在shell(如bash)执行下面的命令行,该命令行的意思是,将cat 命令执行的标准输出流,重定向至grep命令的输入流。其中'|'字符就是让bash知道,这里有左右两边的命令,需要通过管道进行数据传输。

~ $ cat '/etc/passwd' | grep 'docker'

忽略bash对命令行的解析过程,可以用下面一段简单的代码,展示bash是怎么实现命令之间的管道传输的。

#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
#include <stdlib.h>
    
int main(int argc, char** argv) {
    int fd[2];
    // fd[0]为管道写端, fd[1]为管道文件读端
    if (pipe(fd) != 0) {
        exit(errno);
    }
        
    int pid = fork();
    if (pid == -1) {
        exit(errno);
    } else if (pid == 0) {
        // 关闭标准输出
        close(1);
        // 利用dup(fd[1]])从管道写端复制一个描述符,新文件新的文件为最小可用的文件描述符,即上面关闭的1,所以这里是将标准输出重定向至管道写端
        dup(fd[1]);
        
        // 关闭管道读端
        close(fd[0]);
        // 关闭已复制的文件描述符
        close(fd[1]);
        
        // 重定向完成,加载命令
        execlp("cat", "/etc/passwd", NULL);
    } else {
        close(0);
        dup(fd[0]);
        close(fd[1]);
        close(fd[0]);
        execlp("grep", "docker" , NULL);
    }

}

我们知道,Linux进程中文件标识符0、1、2分别代表进程的标准输入、标准输出、标准错误流文件,默认是已打开状态,上面的过程简要介绍如下:

  • 10 ~ 13行:进程1调用pipe()命令创建管道文件,文件标识符保存于fd[]
  • 15 ~ 17行:进程1调用fork(),创建子进程2,此时两个进程还是执行bash的代码
  • 20 ~ 31行:进程1关闭原来的标准输出流,然后将管道的写端,置为新的标准输出流。最后执行execlp函数,加载cat程序并执行(加载后会保留已打开的文件,此时标准输出已被重定向)
  • 33 ~ 37行:与上面
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值