Python subprocess.check_call(["wine"]..) 有同步问题

Python subprocess.check_call(["wine"]..) has a synchronization issue

我有一个 python 脚本,它使用 subprocess.check_call 启动 Wine(Windows Emulator on Linux),然后 wine 启动 Z:\Program Files (x86)\PeaZip\peazip.exe

首先,当我在调试模式 python3 -u -m ipdb unpack_archive.py 下测试这个 python 脚本,并逐步围绕 wine 启动和 运行 语句设置断点时,Wine 运行 s peazip.exe 成功。也就是说,peazip 在 Linux.

上成功提取了 PEA 存档

然而,当我测试此 python 脚本时未处于调试模式 python3 unpack_archive.py,然后我发现 peazip.exe 未成功提取 PEA 存档。所以我怀疑wine或者python subprocess.check_call().

有同步问题

现在我的解决方法是,在启动 wine 后插入 time.sleep(1.0)

elif 'PEA archive' in ftype:
    if splitext(arcname)[1] != '.pea':
        tmpfile = os.path.join(tmpdir, basename(arcname))+'.pea'
    else:
        tmpfile = os.path.join(tmpdir, basename(arcname))
    shutil.copy(arcname, tmpfile)
    subprocess.check_call(["wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
        "-ext2here", to_wine_path(tmpfile)])
    import time
    time.sleep(1.0) # if we don't sleep, then peazip.exe won't extract file successfully 
    os.remove(tmpfile)
    copy_without_symlink(tmpdir, outdir)

我检查了 wine manual, it doesn't mention anything about synchronization. I also checked subprocess.check_call()。该文件明确表示 check_call() 将等待命令完成。

我不想要这个解决方法,因为如果 PEA 存档文件非常大,那么 sleep() 的超时值必须更大,我们无法预测 [=53= 之前的足够超时值]宁它。


我参考了的建议。使用 subprocess.check_output() 而不是 check_call()

    elif 'PEA archive' in ftype:
        if splitext(arcname)[1] != '.pea':
            tmpfile = os.path.join(tmpdir, basename(arcname))+'.pea'
        else:
            tmpfile = os.path.join(tmpdir, basename(arcname))
        shutil.copy(arcname, tmpfile)
        subprocess.check_output(["wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
            "-ext2here", to_wine_path(tmpfile)])
        os.remove(tmpfile)
        copy_without_symlink(splitext(tmpfile)[0], outdir)

我用 python3 unpack_archive.py Kevin.pea 测试了它,这是一个 2.0GB 的 PEA 存档。提取过程耗时 4 分 16 秒。三个子文件解压成功

我的理解是 wine 可执行文件不是真正的模拟器 - 它只是启动一个名为 wineserver 的后台进程,如果它还没有 运行ning,告诉它 运行 Windows 程序,然后立即自行退出 - 很可能在 Windows 程序启动之前 运行ning.

this question 的一个答案表明,将 wine 的输出通过管道传输到另一个程序将延迟事情,直到 Windows 程序实际退出。在 Python 术语中,这相当于使用 check_output() 而不是 check_call(),尽管我自己还没有尝试过。

考虑使用建议锁定来阻塞,直到进程退出:

lockfile=open(tmpfile, 'a')
subprocess.check_call([
         "wine", "/home/acteam/.wine/drive_c/Program Files (x86)/PeaZip/peazip.exe",
        "-ext2here", to_wine_path(tmpfile)],
    preexec_fn=lambda: fcntl.flock(lockfile, fcntl.LOCK_EX),
    close_fds=False)
fcntl.flock(lockfile, fcntl.LOCK_EX)

在这里,我们的 preexec_fn(运行 在我们 fork() 关闭子进程之后但在 wine 启动之前)获取一个锁,然后 check_call() 已返回,然后我们尝试自己获取该锁——如果尚未释放,它将阻塞。

(请注意,您需要确保 wine 不会在程序退出之前关闭该文件描述符本身;如果它关闭了,避免这种情况的一种方法是在作为标准输入传递的描述符上创建锁、标准输出或标准错误)。