从 0 开始学习 Linux 系列之「19.无名管道 Pipe」

无名管道 Pipe

版权声明:本文为 cdeveloper 原创文章,可以随意转载,但必须在明确位置注明出处!

Linux 进程间通信

当系统中有了多个进程时,进程之间的通信就显得格外必要了,进程就相当于现实世界中的人,人跟人之间的交流就相当与进程之间的通信了。Linux 的进程间通信Inter Process Communication,IPC)主要有 7 种:

  1. 无名管道 Pipe
  2. 有名管道 Fifo
  3. 信号 Signal
  4. 消息队列 Message Queue
  5. 共享内存 Share Memory
  6. 信号量 Semphone
  7. 套接字 Socket

这 7 种方式有各自的适用场合。在早期管道和信号是用于单机 IPC 的主要方式,在后来 AT&T 的贝尔实验室在那之上又拓展了一个 System V IPC,其中包含了共享内存,消息队列,信号量这 3 种方法。

之后 BSD(加州大学伯克利分校软件研发中心)开发了套接字用来进行网络通信,从这也可以得出网络通信其实就是不同机器之间的进程相互通信,本质上还是属于进程间的通信,只不过多了一个网络的桥梁而已。这就是整个 IPC 的发展过程,IPC 是 Linux 中的一个非常重要的模块,必须掌握这 7 种方式,这也是面试必问的东西。

这篇文章主要介绍第一种 IPC 的机制:无名管道 Pipe,并且会分析它在 Linux 内核的实现机制,废话不多说,赶紧上车...

什么是无名管道 Pipe?

shell 管道

管道是 UNIX 系统 IPC 的最古老的形式,所有的 UNIX 系统都提供管道机制,如果你使用过 shell 中的管道,应该不会默认,例如:

ps -aux | grep "xxx"

这个意思是将 ps -aux 的输出作为 grep xxx 的输入,通过管道可以将两个进程连接起来,功能非常强大,但是有名管道与 shell 的管道有些区别。

无名管道

有名管道具有下面 3 个特点:

  1. 只能用于有亲缘关系(父子进程)的进程间通信
  2. 半双工通信方式,具有固定的读写端
  3. Pipe 被当作特殊文件来对待(Linux 下一切都是文件)

需要了解下半双工和全双工的区别:

  1. 半双工:同一时刻,数据只能往一个方向传输
  2. 全双工:同一时刻,数据可以往两个方向传输

有名管道是半双工的,每个时刻一个进程只能读取或者写入,即只能打开读端口或者写端口,不可同时打开。下面的图可以更好地解释在父子进程之间使用管道的模型:

管道模型

这个模型中内核有一个管道的缓冲区,父进程将数据写入管道写端(fd[1])子进程从管道读端(fd[0])中读取数据

例子:test_pipe.c

了解了有名管道的基本原理,下面我们使用 pipe 来创建一个管道,这是 pipe 函数定义:

#include <unistd.h>

/*
 * fd[0]:用于读取
 * fd[1]:用于写入
 * return:成功返回 0, 失败返回 -1,并设置 erron
 */
int pipe(int pipefd[2]);

这个例子中我们在父进程中 fork 了一个子进程,在 fork 之后要做什么取决与我们想要的数据流的方向,这里设置子进程从父进程读取数据,所以需要关闭子进程的写端 fd[1] 和父进程的读端 fd[0],注意无名管道不能同时读写

// test_pipe.c
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>


int main() {
    int pfd[2];
    int pid;
    int status = 0;

    char w_cont[] = "Hello child, I'm parent!";
    char r_cont[255] = { 0 }; 
    int write_len = strlen(w_cont);
    
    // 创建管道
    if(pipe(pfd) < 0) {
        perror("create pipe failed"); 
        exit(1); 
    } else {
        // 创建子进程
        if((pid = fork()) < 0) {
            perror("create process failed");
    } else if(pid > 0) {
        // 关闭父进程读端
        close(pfd[0]); 
        // 父进程像写端写入数据
        write(pfd[1], w_cont, write_len); 
        close(pfd[1]);
        // 等待子进程结束
        wait(&status);
    } else { 
        sleep(2); 
        // 关闭子进程写端
        close(pfd[1]);
        // 子进程从读端读取数据
        read(pfd[0], r_cont, write_len);
        // 子进程输出读取的数据
        printf("child process read: %s\n", r_cont); 
    }

    return 0 ; 
}

编译运行看看:

gcc test_pipe.c -o test_pipe
./test_pipe
child process read: Hello child, I'm parent!

可以看到子进程成功读取了父进程写入的数据,整个过程一共分为 6 个步骤:

  1. 创建管道
  2. 创建子进程
  3. 父进程关闭读端,向写端写入数据
  4. 子进程等待 2s,等父进程写入完毕
  5. 子进程关闭写端,从读端读取数据并输出
  6. 父进程用 wait 等待子进程结束

这个例子可以很好的解释管道的使用方法:父进程写入,子进程读取,当然你也可以设置子进程写,父进程读,只要改变进程的读写端口和代码逻辑即可,代码参考:test_pipe.ctest_pipe2.c

Pipe 的内核实现

管道的操作比较的简单,为了更好的理解它的原理,我们看看 Linux 内核中的管道是如何实现的,因为不同版本的 Linux 内核中的修改比较大,这里以 Linux-3.4 版本来分析。

Pipe 注册过程

内核的 Pipe 的实现原理大体上如下:Pipe 将内存中一片区域映射到虚拟文件系统 VFS,使得上层应用可以像操作文件那样来操作 Pipe,从而实现 IPC,也就是说 Pipe 是以管道文件系统为基础的,我们来看看 fs/pipe.c 中的 pipe 文件系统的注册过程,实际上就是一个驱动程序:

内核管道机制

这个过程向内核注册了 pipe 的文件系统,这个文件系统也受 VFS 的控制。

Pipe 的调用过程

再来看看管道的调用过程,上层的 pipe 调用一般都对应底层的 sys_pipe 调用,但是随着内核的修改,有些名称会改变,比如 sys_pipe 在 3.4 中就是用宏定义来表示的:

/*
 * fs/pipe.c
 * sys_pipe() is the normal C calling standard for creating
 * a pipe. It's not the way Unix traditionally does this, though.  
 **/
SYSCALL_DEFINE2(pipe2, int __user *, fildes, int, flags)
{
    int fd[2];
    int error;
    
    error = do_pipe_flags(fd, flags);
    if (!error) {
        if (copy_to_user(fildes, fd, sizeof(fd))) {
            sys_close(fd[0]);
            sys_close(fd[1]);
            error = -EFAULT;
        }
    }
    
    return error;
}

这是具体的执行过程:

管道的系统调用过程

这个过程所做的事情主要是向内核申请内存,创建读写描述符,以此建立 pipe 文件。其中比较重要的是 create_write_pipe,这个函数创建一个写管道,在最后调用 kzalloc 向内核申请内存空间:

管道的创建过程

这也印证了 pipe 将内存中一片区域映射成虚拟文件系统以及 Linux 的进程间通信实质上就是 IO 操作这两个概念。

结语

本次,我们了解了 Linux 下进程间通信(IPC)的 7 种方式,并着重学习了第一种方式:无名管道 Pipe。管道是最古老的 IPC 方式,使用起来也比较简单,并且我们也简单分析了内核中对 pipe 的实现过程,知道了 pipe 其实也是以文件 IO 的方式来实现 IPC 的,了解些内核的机制可以让我们对 IPC 有一个更好的理解。

感谢你的阅读,我们下次再见 :)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 157,298评论 4 360
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 66,701评论 1 290
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 107,078评论 0 237
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,687评论 0 202
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,018评论 3 286
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,410评论 1 211
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,729评论 2 310
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,412评论 0 194
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,124评论 1 239
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,379评论 2 242
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 31,903评论 1 257
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,268评论 2 251
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 32,894评论 3 233
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,014评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,770评论 0 192
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,435评论 2 269
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,312评论 2 260

推荐阅读更多精彩内容

  • Android跨进程通信IPC整体内容如下 1、Android跨进程通信IPC之1——Linux基础2、Andro...
    隔壁老李头阅读 15,193评论 19 112
  • 1 进程介绍 1.1 进程和程序 所谓进程是由正文段(text)、用户数据段(user segment)以及系统数...
    疯狂小王子阅读 1,191评论 0 7
  • 前言 管道是UNIX环境中历史最悠久的进程间通信方式,也是最简单的进程间通信方式,一般用来作为IPC的入门,最合适...
    GeekerLou阅读 1,111评论 0 6
  • 一.管道机制(pipe) 1.Linux的fork操作 在计算机领域中,尤其是Unix及类Unix系统操作系统中,...
    Geeks_Liu阅读 3,617评论 1 9
  • 项目崩溃,crashLog可是你的救命稻草,能让你快速分析出bug的问题所在,不会收集可不行。当然项目在开发时,可...
    怪小喵阅读 6,772评论 1 15