python進程通信之匿名管道實例講解

字號:


    匿名管道
    管道是一個單向通道,有點類似共享內(nèi)存緩存.管道有兩端,包括輸入端和輸出端.對于一個進程的而言,它只能看到管道一端,即要么是輸入端要么是輸出端.
    os.pipe()返回2個文件描述符(r, w),表示可讀的和可寫的.示例代碼如下:
    代碼如下:
    #!/usr/bin/python
    import time
    import os
    def child(wpipe):
    print('hello from child', os.getpid())
    while true:
    msg = 'how are you\n'.encode()
    os.write(wpipe, msg)
    time.sleep(1)
    def parent():
    rpipe, wpipe = os.pipe()
    pid = os.fork()
    if pid == 0:
    child(wpipe)
    assert false, 'fork child process error!'
    else:
    os.close(wpipe)
    print('hello from parent', os.getpid(), pid)
    fobj = os.fdopen(rpipe, 'r')
    while true:
    recv = os.read(rpipe, 32)
    print recv
    parent()
    輸出如下:
    代碼如下:
    ('hello from parent', 5053, 5054)
    ('hello from child', 5054)
    how are you
    how are you
    how are you
    how are you
    我們也可以改進代碼,不用os.read()從管道中讀取二進制字節(jié),而是從文件對象中讀取字符串.這時需要用到os.fdopen()把底層的文件描述符(管道)包裝成文件對象,然后再用文件對象中的readline()方法讀取.這里請注意文件對象的readline()方法總是讀取有換行符'\n'的一行,而且連換行符也讀取出來.還有一點要改進的地方是,把父進程和子進程的管道中不用的一端關閉掉.
    代碼如下:
    #!/usr/bin/python
    import time
    import os
    def child(wpipe):
    print('hello from child', os.getpid())
    while true:
    msg = 'how are you\n'.encode()
    os.write(wpipe, msg)
    time.sleep(1)
    def parent():
    rpipe, wpipe = os.pipe()
    pid = os.fork()
    if pid == 0:
    os.close(rpipe)
    child(wpipe)
    assert false, 'fork child process error!'
    else:
    os.close(wpipe)
    print('hello from parent', os.getpid(), pid)
    fobj = os.fdopen(rpipe, 'r')
    while true:
    recv = fobj.readline()[:-1]
    print recv
    parent()
    輸出如下:
    代碼如下:
    ('hello from parent', 5108, 5109)
    ('hello from child', 5109)
    how are you
    how are you
    how are you
    如果要與子進程進行雙向通信,只有一個pipe管道是不夠的,需要2個pipe管道才行.以下示例在父進程新建了2個管道,然后再fork子進程.os.dup2()實現(xiàn)輸出和輸入的重定向.spawn功能類似于subprocess.popen(),既能發(fā)送消息給子進程,由能從子子進程獲取返回數(shù)據(jù).
    代碼如下:
    #!/usr/bin/python
    #coding=utf-8
    import os, sys
    def spawn(prog, *args):
    stdinfd = sys.stdin.fileno()
    stdoutfd = sys.stdout.fileno()
    parentstdin, childstdout = os.pipe()
    childstdin, parentstdout= os.pipe()
    pid = os.fork()
    if pid:
    os.close(childstdin)
    os.close(childstdout)
    os.dup2(parentstdin, stdinfd)#輸入流綁定到管道,將輸入重定向到管道一端parentstdin
    os.dup2(parentstdout, stdoutfd)#輸出流綁定到管道,發(fā)送到子進程childstdin
    else:
    os.close(parentstdin)
    os.close(parentstdout)
    os.dup2(childstdin, stdinfd)#輸入流綁定到管道
    os.dup2(childstdout, stdoutfd)
    args = (prog, ) + args
    os.execvp(prog, args)
    assert false, 'execvp failed!'
    if __name__ == '__main__':
    mypid = os.getpid()
    spawn('python', 'pipetest.py', 'spam')
    print 'hello 1 from parent', mypid #打印到輸出流parentstdout, 經(jīng)管道發(fā)送到子進程childstdin
    sys.stdout.flush()
    reply = raw_input()
    sys.stderr.write('parent got: %s\n' % reply)#stderr沒有綁定到管道上
    print 'hello 2 from parent', mypid
    sys.stdout.flush()
    reply = sys.stdin.readline()#另外一種方式獲得子進程返回信息
    sys.stderr.write('parent got: %s\n' % reply[:-1])
    pipetest.py代碼如下:
    代碼如下:
    #coding=utf-8
    import os, time, sys
    mypid = os.getpid()
    parentpid = os.getppid()
    sys.stderr.write('child %d of %d got arg: %s\n' %(mypid, parentpid, sys.argv[1]))
    for i in range(2):
    time.sleep(3)
    recv = raw_input()#從管道獲取數(shù)據(jù),來源于父經(jīng)常stdout
    time.sleep(3)
    send = 'child %d got: [%s]' % (mypid, recv)
    print(send)#stdout綁定到管道上,發(fā)送到父進程stdin
    sys.stdout.flush()
    輸出:
    代碼如下:
    child 7265 of 7264 got arg: spam
    parent got: child 7265 got: [hello 1 from parent 7264]
    parent got: child 7265 got: [hello 2 from parent 7264]