这两天在研究多进程之间通信的底层的事情,以前可能更多的是用语言本身的特性和封装好的模块。 现在直接用python 本源的os.fork派生进程,用pipe 做简单的数据的交换。如果你要传输对象的话,需要针对对象进行序列化,比如用pickle 、msgpack这类的。 

 

管道pipe

管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

python-fork-pipe-pipe-communication-01

 

实现机制:

管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条。管道的一端连接一个进程的输出。这个进程会向管道中放入信息。管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息。一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用。当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息。当管道被放满信息的时候,尝试放入信息的进程会等待,直到另一端的进程取出信息。当两个进程都终结的时候,管道也自动消失。

python-fork-pipe-pipe-communication-02

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

python-fork-pipe-pipe-communication-03

 

Python 示例

vim  f2.py

import os, time, sys
pipe_name = 'pipe_test'

def child( ):
    pipeout = os.open(pipe_name, os.O_WRONLY)
    counter =0
    while True:
        time.sleep(1)
        os.write(pipeout, 'Number %03d\n' % counter)
        counter = (counter+1) %5

def parent( ):
    pipein = open(pipe_name, 'r')
    while True:
        line = pipein.readline()[:-1]
        print 'Parent %d got "%s" at %s' % (os.getpid(), line, time.time( ))

if not os.path.exists(pipe_name):
    os.mkfifo(pipe_name)  
pid = os.fork()    
if pid !=0:
    parent()
else:       
    child()

运行结果:

[xiaorui@localhost ~ ]$ python f2.py
17355 got "Number 000" at1418177682.7
17355 got "Number 001" at1418177683.7
17355 got "Number 002" at1418177684.7
17355 got "Number 003" at1418177685.7
17355 got "Number 004" at1418177686.7
17355 got "Number 000" at1418177687.7
17355 got "Number 001" at1418177688.7
17355 got "Number 002" at1418177689.7
17355 got "Number 003" at1418177690.71
17355 got "Number 004" at1418177691.71
17355 got "Number 000" at1418177692.71
17355 got "Number 001" at1418177693.71
17355 got "Number 002" at1418177694.71
17355 got "Number 003" at1418177695.71