catalogue

. pipe匿名管道
. named pipe(FIFO)有名管道

1. pipe匿名管道

管道是Linux中很重要的一种通信方式,是把一个程序的输出直接连接到另一个程序的输入,常说的管道多是指无名管道,无名管道只能用于具有亲缘关系的进程之间,这是它与有名管道的最大区别。管道是Linux支持的最初Unix IPC形式之一,具有以下特点

. 管道是半双工的,数据只能向一个方向流动; 需要双方通信时,需要建立起两个管道
. 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)
. 单独构成一种独立的文件系统: 管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中
. 数据的读出和写入: 一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据

0x1: 管道的读写规则

管道两端可分别用描述字fd[0]、fd[1]来描述,需要注意的是,管道的两端是固定了任务的

. 即一端只能用于读,由描述字fd[]表示,称其为管道读端
. 另一端则只能用于写,由描述字fd[]来表示,称其为管道写端

如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于管道,如close、read、write等等

1. 从管道中读取数据

. 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0
. 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数

pycode

import os, sys

if __name__ == '__main__':
print "The child will write text to a pipe and "
print "the parent will read the text written by child..." # file descriptors r, w for reading and writing
r, w = os.pipe() processid = os.fork()
if processid:
# This is the parent process
# Closes file descriptor w
os.close(w)
r = os.fdopen(r)
print "Parent reading"
str = r.read()
print "text =", str
sys.exit()
else:
# This is the child process
os.close(r)
w = os.fdopen(w, 'w')
print "Child writing"
w.write("Text written by child...")
w.close()
print "Child closing"
sys.exit()

2. 向管道中写入数据

. 向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞
. 只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)

pycode: 写端对读端存在的依赖性

import os, sys

if __name__ == '__main__':
print "The child will write text to a pipe and "
print "the parent will read the text written by child..." # file descriptors r, w for reading and writing
r, w = os.pipe() processid = os.fork()
if processid:
# This is the parent process
os.close(w)
os.close(r)
sys.exit()
else:
# This is the child process
os.close(r)
w = os.fdopen(w, 'w')
print "Child writing"
w.write("Text written by child...")
w.close()
print "Child closing"
sys.exit()

则输出结果为: Broken pipe,原因就是该管道以及它的所有fork()产物的读端都已经被关闭。因此,在向管道写入数据时,至少应该存在某一个进程,其中管道读端没有被关闭,否则就会出现上述错误(管道断裂,进程收到了SIGPIPE信号,默认动作是进程终止)

从原理上,管道利用fork机制建立,从而让两个进程可以连接到同一个PIPE上。最开始的时候,上面的两个箭头都连接在同一个进程Process 1上(连接在Process 1上的两个箭头)。当fork复制进程的时候,会将这两个连接也复制到新的进程(Process 2)。随后,每个进程关闭自己不需要的一个连接 (两个黑色的箭头被关闭; Process 1关闭从PIPE来的输入连接,Process 2关闭输出到PIPE的连接),这样,剩下的红色连接就构成了如上图的PIPE

0x2: 管道应用实例

1. 用于shell

管道可用于输入输出重定向,它将一个命令的输出直接定向到另一个命令的输入,当在某个shell程序(Bourne shell或C shell等)键入who │ wc -l后,相应shell程序将创建who以及wc两个进程和这两个进程间的管道。考虑下面的命令行

kill -l | grep SIGRTMIN
) SIGPWR ) SIGSYS ) SIGRTMIN ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMIN+
) SIGRTMIN+ ) SIGRTMIN+ ) SIGRTMAX- ) SIGRTMAX-

2. 用于具有亲缘关系的进程间通信

0x3: 管道实现细节

在 Linux 中,管道的实现并没有使用专门的数据结构,而是借助了文件系统的file结构和VFS的索引节点inode。通过将两个 file 结构指向同一个临时的 VFS 索引节点,而这个 VFS 索引节点又指向一个物理页面而实现的

有两个 file 数据结构,但它们定义文件操作例程地址是不同的,其中一个是向管道中写入数据的例程地址,而另一个是从管道中读出数据的例程地址。这样,用户程序的系统调用仍然是通常的文件操作,而内核却利用这种抽象机制实现了管道这一特殊操作

0x4: 管道的局限性

管道的主要局限性正体现在它的特点上

. 只支持单向数据流
. 只能用于具有亲缘关系的进程之间
. 没有名字
. 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小)
. 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式,比如多少字节算作一个消息(或命令、或记录)等等

Relevant Link:

https://linux.die.net/man/2/pipe
https://www.cnblogs.com/chengmo/archive/2010/10/21/1856577.html
http://ryanstutorials.net/linuxtutorial/piping.php
http://hwchiu.logdown.com/posts/1733-c-pipe
https://www.tutorialspoint.com/python/os_pipe.htm

2. named pipe(FIFO)有名管道

为了解决飞亲属进程间通信这一问题,Linux提供了FIFO方式连接进程。FIFO又叫做命名管道(named PIPE)

FIFO (First in, First out)为一种特殊的文件类型,它在文件系统中有对应的路径。当一个进程以读(r)的方式打开该文件,而另一个进程以写(w)的方式打开该文件,那么内核就会在这两个进程之间建立管道,所以FIFO实际上也由内核管理,不与硬盘打交道

。之所以叫FIFO,是因为管道本质上是一个先进先出的队列数据结构,最早放入的数据被最先读出来,从而保证信息交流的顺序。FIFO只是借用了文件系统(file system,命名管道是一种特殊类型的文件,因为Linux中所有事物都是文件,它在文件系统中以文件名的形式存在)来为管道命名。写模式的进程向FIFO文件中写入,而读模式的进程从FIFO文件中读出。当删除FIFO文件时,管道连接也随之消失

0x1: 有名管道的操作规则

1. 有名管道的打开规则

. 如果当前打开操作是为读而打开FIFO时
) 若已经有相应进程为写而打开该FIFO,则当前打开操作将成功返回
) 否则,可能阻塞直到有相应进程为写而打开该FIFO(当前打开操作设置了阻塞标志)
) 或者,成功返回(当前打开操作没有设置阻塞标志)
. 如果当前打开操作是为写而打开FIFO时
) 如果已经有相应进程为读而打开该FIFO,则当前打开操作将成功返回
) 否则,可能阻塞直到有相应进程为读而打开该FIFO(当前打开操作设置了阻塞标志)
) 或者,返回ENXIO错误(当前打开操作没有设置阻塞标志)

2. 有名管道的读写规则

. 从FIFO中读取数据: 如果一个进程为了从FIFO中读取数据而阻塞打开FIFO,那么称该进程内的读操作为设置了阻塞标志的读操作。
) 如果有进程写打开FIFO,且当前FIFO内没有数据,则对于设置了阻塞标志的读操作来说,将一直阻塞。对于没有设置阻塞标志读操作来说则返回-,当前errno值为EAGAIN,提醒以后再试
) 对于设置了阻塞标志的读操作说,造成阻塞的原因有两种,解阻塞的原因则是FIFO中有新的数据写入,不论信写入数据量的大小,也不论读操作请求多少数据量
2.1) 当前FIFO内有数据,但有其它进程在读这些数据
2.2) 另外就是FIFO内没有数据
) 读打开的阻塞标志只对本进程第一个读操作施加作用,如果本进程内有多个读操作序列,则在第一个读操作被唤醒并完成读操作后,其它将要执行的读操作将不再阻塞,即使在执行读操作时,FIFO中没有数据也一样(此时,读操作返回0)
) 如果没有进程写打开FIFO,则设置了阻塞标志的读操作会阻塞 . 向FIFO中写入数据: 如果一个进程为了向FIFO中写入数据而阻塞打开FIFO,那么称该进程内的写操作为设置了阻塞标志的写操作
) 对于设置了阻塞标志的写操作
1.1) 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作
1.2) 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回
) 对于没有设置阻塞标志的写操作
2.1) 当要写入的数据量大于PIPE_BUF时,linux将不再保证写入的原子性。在写满所有FIFO空闲缓冲区后,写操作返回
2.2) 当要写入的数据量不大于PIPE_BUF时,linux将保证写入的原子性。如果当前FIFO空闲缓冲区能够容纳请求写入的字节数,写完后成功返回;如果当前FIFO空闲缓冲区不能够容纳请求写入的字节数,则返回EAGAIN错误,提醒以后再写

pycode1

from subprocess import *
import os if __name__ == '__main__':
FIFO_PATH = '/tmp/my_fifo' if os.path.exists(FIFO_PATH):
os.unlink(FIFO_PATH) if not os.path.exists(FIFO_PATH):
os.mkfifo(FIFO_PATH)
my_fifo = open(FIFO_PATH, 'w+')
print "my_fifo:", my_fifo pipe = Popen('/bin/date', shell=False, stdin=PIPE, stdout=my_fifo, stderr=PIPE) print open(FIFO_PATH, 'r').readline() os.unlink(FIFO_PATH)

pycode2

# -*- coding: utf- -*-

import io,win32file,win32pipe, win32api
import msvcrt as ms # for fd magic class pipe(io.IOBase):
def __init__(self, name, pipetype = 'server', openmode = win32pipe.PIPE_ACCESS_DUPLEX|win32file.FILE_FLAG_OVERLAPPED,
pipemode = win32pipe.PIPE_TYPE_BYTE|win32pipe.PIPE_NOWAIT,maxinstances=,outbuffersize=,inbuffersize=,
defaulttimeout=, securityattrib = None):
""" An implementation of a file-like python object pipe. Documentation can be found at https://msdn.microsoft.com/en-us/library/windows/desktop/aa365150(v=vs.85).aspx"""
self.pipetype = pipetype
self.name = name
self.openmode = openmode
self.pipemode = pipemode
self.__enter__ = self.connect
if pipetype == 'server':
self.handle = win32pipe.CreateNamedPipe(r"\\.\pipe\%s" % name,
openmode, # default PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED
pipemode, # default PIPE_TYPE_BYTE|PIPE_NOWAIT
maxinstances, # default
outbuffersize, # default
inbuffersize, # default
defaulttimeout,# default
securityattrib)# default None
elif pipetype == 'client':
# it doesn't matter what type of pipe the server is so long as we know the name
self.handle = win32file.CreateFile(r"\\.\pipe\%s" % name,
win32file.GENERIC_READ | win32file.GENERIC_WRITE,
, None,
win32file.OPEN_EXISTING,
, None)
self.fd = ms.open_osfhandle(self.handle,)
self.is_connected = False
self.flags,self.outbuffersize,self.inbuffersize,self.maxinstances = win32pipe.GetNamedPipeInfo(self.handle)
def connect(self): # TODO: WaitNamedPipe ?
win32pipe.ConnectNamedPipe(self.handle,None)
self.is_connected = True
def __del__(self):
print("del initiated")
try:
self.write(b'') # try to clear up anyone waiting
except win32pipe.error: # no one's listening
pass
self.close()
def __exit__(self):
print("exit started")
self.__del__()
def isatty(self): #Return True if the stream is interactive (i.e., connected to a terminal/tty device).
return False
def seekable(self):
return False
def fileno(self):
return self.fd
def seek(self): # seek family
raise IOError("Not supported")
def tell(self): # Part of the seek family. Not supported
raise IOError("Not supported")
def write(self,data): # WriteFileEx impossible due to callback issues.
if not self.is_connected and self.pipetype == 'server':
self.connect()
if type(data).__name__ != 'bytes': # if we don't get bytes, make it bytes
data = bytes(data)
win32file.WriteFile(self.handle,data)
return len(data)
def close(self):
print("closure started")
win32pipe.DisconnectNamedPipe(self.handle)
def read(self,length=None):
if length == None:
length=self.inbuffersize
resp = win32file.ReadFile(self.handle,length)
if resp[] != :
raise __builtins__.BrokenPipeError(win32api.FormatMessage(resp[]))
else:
return resp[] if __name__ == '__main__':
server = pipe("mypipename")
client = pipe("mypipename", "client")
client.write("hello")
print server.read() server.write("words")
print client.read()

Relevant Link:

http://quickies.seriot.ch/?id=241
https://www.ibm.com/developerworks/cn/linux/l-ipc/part1/
https://codereview.stackexchange.com/questions/88672/python-wrapper-for-windows-pipes
https://bytes.com/topic/python/answers/28069-sharing-pipes-win32
https://msdn.microsoft.com/en-us/library/windows/desktop/aa365783(v=vs.85).aspx
https://kodedevil.wordpress.com/2015/11/11/2-linux-fifo-in-python-autonomous-robot/
http://blog.csdn.net/gexueyuan/article/details/6428584
http://liwei.life/2016/07/18/pipe/
http://www.cnblogs.com/biyeymyhjob/archive/2012/11/03/2751593.html

Copyright (c) 2017 LittleHann All rights reserved

Linux中的pipe(管道)与named pipe(FIFO 命名管道)的更多相关文章

  1. 本地方法中printf如何传给java--java系统级命名管道

    本地方法中printf如何传给java--java系统级命名管道 摘自:https://blog.csdn.net/dog250/article/details/6007301 2010年11月13日 ...

  2. linux 进程学习笔记-named pipe (FIFO)命名管道

    与“无名管道”不同的是,FIFO拥有一个名称来标志它,所谓的名称实际上就是一个路径,比如“/tmp/my_fifo”,其对应到磁盘上的一个管道文件,如果我们用file命令来查看其文件类型的话,会得到如 ...

  3. mkfifo - 创建FIFO(命名管道)

    SYNOPSIS(总览) mkfifo [options] file... POSIX options(选项): [-m mode] GNU options(选项)(最短格式): [-m mode] ...

  4. 《Linux程序设计》--读书笔记---第十三章进程间通信:管道

    管道:进程可以通过它交换更有用的数据. 我们通常是把一个进程的输出通过管道连接到另一个进程的输入: 对shell命令来说,命令的连接是通过管道字符来完成的: cmd1    |     cmd2 sh ...

  5. 诠释Linux中『一切都是文件』概念和相应的文件类型

    导读 在 Unix 和它衍生的比如 Linux 系统中,一切都可以看做文件.虽然它仅仅只是一个泛泛的概念,但这是事实.如果有不是文件的,那它一定是正运行的进程. 要理解这点,可以举个例子,您的根目录( ...

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

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

  7. Linux环境进程间通信(一):管道及命名管道

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  8. linux系统编程之管道(三):命令管道(FIFO)

    一,匿名管道PIPE局限性 管道的主要局限性正体现在它的特点上: 只支持单向数据流: 只能用于具有亲缘关系的进程之间: 没有名字: 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配 ...

  9. Linux 命名管道

    前文中笔者介绍了管道,本文接着介绍命名管道.文中演示所用环境为 Ubuntu 18.04 desktop. 命名管道(named pipe)又被称为先进先出队列(FIFO),是一种特殊的管道,存在于文 ...

随机推荐

  1. Unity重置Animator到初始状态和重复播放同一个Animation

    遇到问题 特效同事给的Animation更改了物体的很多属性,如Active,Alpha, Scale,Position等等,物体本身需要重复利用,因此使用对象池技术不直接销毁而是隐藏等需要时再显示, ...

  2. javascript Json和String互转

      var jsonText = "{\"id\":\"123\",\"name\":\"tom\",\&qu ...

  3. 6.1Python数据处理篇之pandas学习系列(一)认识pandas

    目录 目录 (一)介绍与测试 2.作用: 3.导入的格式 4.小测试 (二)数据类型 1.两种重要的数据类型 2.pandas与numpy的比较 目录 (一)介绍与测试 号称处理数据与分析数据最好的第 ...

  4. 3星|《给产品经理讲技术》:APP开发技术介绍,没有技术背景的话恐怕只能看懂书中的比喻和结论

    基本是APP开发涉及到的相关技术的入门级介绍.涉及到的知识点与技术细节比较多,不少技术相关的内容并没有像标题暗示的那样没有技术背景也可以看懂,而是涉及到许多专业的术语.原理.也有一些内容是用比喻的方法 ...

  5. 使用docker swarm集群心得

    本片关于使用docker swarm 集群心得,也是一些经验吧!过程描述可能简单! 根据一些公司使用经历接收一下问题并针对问题作出应对策略 1.docker swarm集群 主节点数必须是单数,也就是 ...

  6. 修改 TeamViewer ID 的方法

    TeamViewer 使用频繁后会被判定为商业用途,不可用.此软件的账号和设备mac地址绑定. 修改TeamViewer ID后可以重新开始使用.下述方法可以成功修改TeamViewer ID. 关闭 ...

  7. Eclipse链接数据库

    在eclipse中找到数据库 选择数据库 然后点击next 填写数据库信息 选择需要执行的SQL语句 ALT+X 或者 右键点击execute selected text: 执行结果: 有问题请在评论 ...

  8. ENABLE_DDL_LOGGING 参数使用 监控对象的DDL(在alter 日志记录DDL语句)

    启用 DDL 日志记录 功能--支持动态调整 alter system set enable_ddl_logging=true; alter system set enable_ddl_logging ...

  9. wps for linux显示系统缺失字体解决办法

    1.下载字体库 链接: https://pan.baidu.com/s/1xil5_i9M53fM7EQNIt3Mcw 密码: jqnu 2.解压 sudo unzip wps_symbol_font ...

  10. App遍历探讨(含源代码)

    好像好久没有更新博客了,之前写的几篇博客关于自动化的框架的居多,其中好多博友向我提了好多问题,我没有回复.这里给博友道个歉~ ~ 总结几点原因如下: 1.我一般很少上博客,看到了都是好几天之前的问题 ...