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. SQLServer之创建分布式事务

    分布式事务创建注意事项 指定一个由 Transact-SQL 分布式事务处理协调器 (MS DTC) 管理的 Microsoft 分布式事务的起点. 执行 BEGIN DISTRIBUTED TRAN ...

  2. mysql安装和配置(windowns||centos)

    windows10版本安装 1.获取mysql压缩包 https://dev.mysql.com/downloads/mysql/ 2.解压并配置文件my.ini .解压的文件路径 D:\Progra ...

  3. git如何设置ssh密钥

    git设置ssh密钥 目前git支持https和git两种传输协议,github分享链接时会有两种协议可选: 1.Clone with SSH 2.Clone with HTTPS git在使用htt ...

  4. centos7下 svn的配置

    安装svn yum install subversion 查看安装版本 svnserve --version 创建svn版本库目录 mkdir -p /root/svn/test 创建svn版本库 s ...

  5. springboot中配置过滤器以及可能出现的问题

    在springboot添加过滤器有两种方式: 1.通过创建FilterRegistrationBean的方式(建议使用此种方式,统一管理,且通过注解的方式若不是本地调试,如果在filter中需要增加c ...

  6. centos7下kubernetes(14。kubernetes-DNS访问service)

    我们在部署kubernetes时,会自动部署dns组件,其作用是通过dns解析的方法访问service coredns是一个DNS服务器,每当有新的service被创建,kube-dns会添加该ser ...

  7. shell中的EOF用法

    重定位运算符 >> 是追加内容> 是覆盖原有内容 1.EOF Shell中通常将EOF与 << 结合使用,表示后续的输入作为子命令或子Shell的输入,直到遇到EOF为止 ...

  8. CentOS 7 minimal配置网络连接及net-tools安装

    在Virtual Box中安装好CentOS 7的minimal后,第一件事就是设置网络访问. 首先需要用 nmtui 命令进入 Network Manager,如下: 选择 Edit a conne ...

  9. Linux实战教学笔记49:Zabbix监控平台3.2.4(一)搭建部署与概述

    https://www.cnblogs.com/chensiqiqi/p/9162986.html 一,Zabbix架构 zabbix 是一个基于 WEB 界面的提供分布式系统监视以及网络监视功能的企 ...

  10. stm32之不定长接收

    使用STM32CUBE_MAX配置工程,可以简化编程工作量,但是这样我们会遇到一些麻烦,比如利用串口接收不知道长度的数据的时候,我们可能会无从下手,前段时间看到他人程序中的串口不定长接收,此次特意总结 ...