2017-04-07


管道通信在linux中使用较为频繁的进程通信机制。基于unix一切皆文件的传统,管道也是一种文件。所以可以使用一般的VFS接口对管道进行读写操作,如read、write。具体管道分为有名管道和无名管道。无名管道的使用场景较为局限,仅仅限制在有亲缘关系的进程之间通信,多由于父子进程。而有名管道使用就广泛一些,可以在任何有权限的进程之间进行通讯。而这正是有其本质的实现机制所导致的。

一、无名管道

在linux中,管道的实现没有具体的数据结构,而是借助了文件系统的file结构和VFS的inode节点,在之前的文件系统章节介绍过,file结构代表进程打开的一个文件,记录着关于本次打开这个文件的动态信息,是局部于进程的。通过将两个file结构指向同一个inode节点,inode节点又指向一个物理页面,实现两个通过两个文件描述符操作同一文件。关于具体文件系统的知识这里不赘述,感兴趣可以参考之前的博文。正如前面所述,文件描述符不是通过open系统调用打开具体的文件获得,而是通过pipe直接通过内核创建的,所以其他进程无法获取文件描述符也就无法使用其进行通信。这在一定程度上保证了通信的安全,但是也限制了其应用场景。

  管道的读写函数是pipe_read和pipe_write函数,管道写函数通过将字节复制到inode节点指向的物理内存而写入数据,而管道读函数通过复制物理内存中的字节而读出数据。既然涉及到两个进程操作同一资源,就避免不了使用同步机制,为此,内核使用了锁,等待队列和信号机制。当写进程向管道写入数据时,利用标准库函数write(),系统根据库函数传递的文件描述符,找到文件的file结构,(文件描述符其实是file数组中的索引),根据file结构找到inode节点,在正式写入数据时,必须检查节点中的信息,在满足如下条件时,才能进行实际的复制工作。

  • 内存中有足够的空间可容纳所要写入的数据。
  • 内存中没有被读程序锁定。

  如果满足条件,写函数要先锁定内存,防止读进程干扰,然后从写进程地址空间复制数据到内存。如果不满足,写进程就休眠在节点的等待队列中,而休眠函数会触发内核调度。当条件满足时,读取进程会唤醒写入进程,写入进程收到信号,重新挂入就绪队列,待再次被调度就可以执行写操作。写入完成后,释放锁,此时所有休眠在该节点的读取进程会被唤醒,因为读操作并不是排他的。

  管道的读取过程和写入过程类似,这里就不在重复。默认情况下,当管道为空时,读进程会阻塞;而在管道空间不足时,写进程会阻塞。当然,这是可以设置的,具体见fcntl()函数的介绍。

二、有名管道

  前面已经介绍到无名管道的局限性,在看无名管道的时候我也在想,为何不直接通过一个磁盘文件来通信,这样还可以任意进程之间的通信,难道当初就是为了避免这样,才设计的无名管道??

  和无名管道相反,有名管道真真实实的有名字,即它在磁盘上有自己对应的文件,所以不同的进程也可以通过打开该文件,对其进行读写。这正是克服了无名管道仅用于亲缘进程之间通信的缺点。有名管道的操作方式基于先进先出的原理,所以有称有名管道为FIFO。通过mknod函数可以创建FIFO文件,该文件一旦创建成功,任何具备权限的进程都可以对其执行打开操作,进而发生读写。只是当多个进程操作文件时,也需要同步机制的保障。

三、管道通信面临的问题

1、缓冲区的限制

2、读写速度不一致

由于不管是有名管道和无名管道都不能像普通文件那样任意扩展空间,其通信的数据量就受到限制,linux下缓冲区的大小为1页面即4kb,所以管道比较适合数据量不大的进程通信。而面对读写速度不一致的问题,读进程只能通过阻塞自己来等待,一定程度上也会影响管道通信的效率。

参考资料:

1、《深入分析linux内核源码》

Linux IPC之管道通信的更多相关文章

  1. linux进程的管道通信

    linux进程的管道通信 要求 编程实现进程的管道通信,掌握管道通信的同步和互斥机制. 相关函数 pipe管道 指用于连接一个读进程和一个写进程以实现他们之间通信的一个共享文件,又名pipe文件.向管 ...

  2. linux IPC总结——管道

    管道 管道是unix ipc的最古老形式,是一种在内存中的特殊文件,只能在具有公共祖先的进程之间使用(即父子进程,兄弟进程). 管道由pipe函数创建 #include <unistd.h> ...

  3. linux 管道通信

    下面举linux下有名管道通信的代码. ----------------------------------------- fifo_read.c =========== #include<er ...

  4. windows10使用VS(VC++)创建c++多进程命名管道通信

    代码可以在 这里 下载 代码主要涉及到: 管道通信 多线程(含临界区) 多进程通信 创建的子进程独立运行 更新日志: 04-12-2020 1. 去除自定义函数返回值,改为int作为函数返回值并增加相 ...

  5. Linux下进程间管道通信小作业

    在进行这次作业之前,我们先来看看什么是管道吧! 管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常说的管道多是指无名管道,无名管道只能用于具有亲缘关系的进程之间, ...

  6. Linux学习记录--命名管道通信

    命名管道通信 什么是命名管道 一个主要的限制是,它是匿名管道的应用还没有名字,因此,只有它可以用于进程间通信的方式与亲缘关系.在命名管道(named pipe或FIFO)提出后,该限制得到了克服.FI ...

  7. linux进程篇 (三) 进程间的通信1 管道通信

    通信方式分4大类: 管道通信:无名管道 有名管道 信号通信:发送 接收 和 处理 IPC通信:共享内存 消息队列 信号灯 socke 网络通信 用户空间 进程A <----无法通信----> ...

  8. linux下的进程通信之管道与FIFO

    概念:管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条.管道的一端连接一个进程的输出.这个进程会向管道中放入信息.管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息. 优点:不需 ...

  9. Linux无名管道通信介绍

    Linux下无名管道一般仅用于父子进程间的通信: 测试代码如下 //file name: fifo_test.c #include <sys/prctl.h> #include " ...

随机推荐

  1. scala中计算的的一个小问题,超出Int.maxValue时不会报错

    如果小于Int.max时他不会报错 def sumcount(str:String): Int ={ val ints = for(c <- str)yield { println(c.asIn ...

  2. [vundle]利用vundle工具来管理vim插件

    转自:http://os.51cto.com/art/201507/484174.htm Vim是Linux上一款用途广泛的轻量级文本编辑工具.虽然对普通的Linux用户来说开始学用起来难度相当大,但 ...

  3. fmpeg

  4. android之SQLite数据库insert操作

    原型: long Android.database.sqlite.SQLiteDatabase.insert(String table, String nullColumnHack, ContentV ...

  5. Eclipse & Visual Studio

    VS中的解决方案 vs Eclipse中的workspace Maven包管理 vs  Nuget类库管理 build path  vs

  6. Struts2_day01--课程安排_Struts2概述_入门

    Struts2_day01 Struts2课程安排 今天内容 Struts2概述 Struts2框架入门 导入源文件 Struts2执行过程 查看源代码 Struts2的核心配置文件 标签packag ...

  7. 链接href的多重使用

    1. 拨打电话 在电话号码前面可以加上 + (加号)表示国际号码. <a href="tel:10086">10086</a> 使用wtai协议进行拨打电话 ...

  8. 使用async/await——Nodejs+ExpressJs+Babel

    在使用诸如restify/expressjs等Nodejs Web Framework时,我们最头疼的问题就是回调黑洞. 虽然后又Koa/Koa2号称“The Next Framework”使用co解 ...

  9. JZOJ.5305【NOIP2017模拟8.18】C

    Description

  10. 【BZOJ2049,2631,3282,1180】LCT模板四连A

    好吧我并不想讲LCT 只是贴4个代码~ [BZOJ2049][Sdoi2008]Cave 洞穴勘测 #include <cstdio> #include <cstring> # ...