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. [网络]Linux一些网络知识

    今天刚搬到新家,ubuntu一启动,无线网络又连不上了,之前就是大费周折才搞好的,于是又花了两小时才搞好. 下面就先来了解一些基础知识: 1. ifconfig输出的eth0/lo/wlan0分别代表 ...

  2. Unix domain socket IPC

    UNIX Domain socket 虽然网络socket也可用于同一台主机的进程间通讯(通过lo地址127.0.0.1),但是unix domain socket用于IPC更有效率:不需要经过网络协 ...

  3. js学习笔记22----BOM属性和方法

    BOM基本概念 : Browser Object Model 浏览器对象模型. BOM属性: window.navigator.userAgent : 浏览器信息 判断是否是某个浏览器,可以用 ind ...

  4. 【BZOJ】1635: [Usaco2007 Jan]Tallest Cow 最高的牛(差分序列)

    http://www.lydsy.com/JudgeOnline/problem.php?id=1635 差分序列是个好东西啊....很多地方都用了啊,,, 线性的进行区间操作orz 有题可知 h[a ...

  5. 开源 java CMS - FreeCMS2.3 移动app生成栏目数据

    原文地址:http://javaz.cn/site/javaz/site_study/info/2015/28230.html​ 项目地址:http://www.freeteam.cn/ 生成栏目数据 ...

  6. C#引用传递[转]

    学过C#的人都知道,通过值或通过引用,值类型和引用类型都可以作为方法参数传递.在C#中,不管是值类型或者是引用类型,所有方法参数在默认情况下是通过值传递的. 1)通过值传递值类型 在通过值传递作为方法 ...

  7. when case group by 的用法集合

    1.用那个以前大家都熟悉的例子,要求是依旧下面的表格求每个大洲的人口总和 国家(countrcoungry) 人口(population) 中国 600 美国 100 加拿大 100 英国 200 法 ...

  8. Linxu内核参数详解

    #表示SYN队列的长度,默认为1024,加大队列长度,可以容纳更多等待连接的网络连接数. net.ipv4.tcp_max_syn_backlog = 65536 #每个网络接口接收数据包的速率比内核 ...

  9. HashMap实现原理、核心概念、关键问题的总结

    简单罗列一下较为重要的点: 同步的问题 碰撞处理问题 rehash的过程 put和get的处理过程 HashMap基础: HashMap的理论基础:维基百科哈希表 JDK中HashMap的描述:Has ...

  10. Qt slot中获取sender

    调用sender();函数 例如获取一个QRadioButton QRadioButton *rb = qobject_cast<QRadioButton *>(sender());