Python 多处理和文件查找

sho*_*hok 2 python python-multiprocessing

我正在尝试使用该multiprocessing包来同时读取文件并在进行一些数据转换后覆盖它(部分)。我知道这似乎有点抽象,但我可以使用这种并发性来加速我自己的blocksyncfork

您可以在下面找到我的代码片段:

#!/usr/bin/python2
import multiprocessing
import sys
import time
blocksize=1024

def do_open(f, mode):
    f = open(f, mode)
    f.seek(0, 2)
    size = f.tell()
    f.seek(0)
    return f, size

def pipe_getblocks(f, pipe, side):
    print "Child file object ID: "+str(id(f))
    while True:
        print "getblocks_seek_prev: "+str(f.tell())
        block = f.read(blocksize)
        if not block:
            break
        print "getblocks_seek_next: "+str(f.tell())
        pipe.send(block)

def pipe_server(dev):
    f, size = do_open(dev, 'r+')
    parent,child = multiprocessing.Pipe(False)
    reader = multiprocessing.Process(target=pipe_getblocks, args=(f,child,"R"))
    reader.daemon = True
    reader.start()
    child.close()
    i = 0
    print "Parent file object ID:"+str(id(f))
    while True:
        try:
            block = parent.recv()
        except:
            break
        else:
            print str(i)+":pseek: "+str(f.tell()/1024/1024)
            f.seek(0,0) # This seek should not be see in the child subprocess...
            i = i+1

pipe_server("/root/random.img")
Run Code Online (Sandbox Code Playgroud)

基本上,父进程应该等待子进程填充管道,然后从中读取。请注意这一f.seek(0,0)行:我将其放在这里是为了验证父级和子级对于在文件中的何处查找都有自己的想法。换句话说,作为两个完全不同的进程,我希望对f.seek父进程执行的操作不会对其子进程产生影响。

然而,这个假设似乎是错误的,因为上面的程序产生以下输出:

Child file object ID: 140374094691616
getblocks_seek_prev: 0
getblocks_seek_next: 1024
...
getblocks_seek_next: 15360
getblocks_seek_prev: 15360
getblocks_seek_next: 16384
getblocks_seek_prev: 16384
getblocks_seek_next: 17408 <-- past EOF!
getblocks_seek_prev: 17408 <-- past EOF!
getblocks_seek_next: 18432 <-- past EOF!
getblocks_seek_prev: 18432 <-- past EOF!
...
Parent file object ID:140374094691616
0:pseek: 0
1:pseek: 0
2:pseek: 0
3:pseek: 0
4:pseek: 0
5:pseek: 0
6:pseek: 0
7:pseek: 0
8:pseek: 0
9:pseek: 0
10:pseek: 0
...
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,子进程读取了其 EOF,或者,嗯,它是这么认为的,因为它实际上是从文件的开头读取。简而言之,父进程似乎f.seek(0,0)对子进程产生了影响,但它却没有意识到这一点。

我的假设是文件对象存储在共享内存上,因此两个进程都在修改相同的数据/对象。这个想法似乎得到了id(f)父进程和子进程的证实,它们报告了相同的数据。但是,我没有发现任何参考说明使用该包时文件对象保存在共享内存中multiprocessing

所以,我的问题是:这是预期的行为,还是我遗漏了一些明显的东西?

Hal*_*own 5

Python 使用 启动子进程fork(),这会导致子进程从其父进程继承文件描述符。由于它们共享文件描述符,因此它们也共享相同的查找偏移量。从手册页fork(2)

子进程继承父进程的打开文件描述符集的副本。子级中的每个文件描述符都引用与父级中相应的文件描述符相同的打开文件描述(请参阅 open(2))。这意味着两个文件描述符共享打开文件状态标志、文件偏移量和信号驱动的 I/O 属性(请参阅 fcntl(2) 中 F_SETOWN 和 F_SETSIG 的描述)。

unix 上的Pythonfile对象是文件描述符的非常薄的包装器(Python 中的实现目前可以归结为一个 fdno 和一些有关路径的元数据;seek()方法只是调用lseek(2)),因此将对象克隆到子进程中基本上只是发送一个文件描述符跨越。

我能想到的最简单的解决方案是将路径传递给子进程并分别在每个进程中打开文件。您也许可以使用 做一些棘手的事情os.dup,但我不确定除了在生成新进程时节省一些字节之外还有什么好处。