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 不会在程序退出之前关闭该文件描述符本身;如果它关闭了,避免这种情况的一种方法是在作为标准输入传递的描述符上创建锁、标准输出或标准错误)。
我有一个 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.
然而,当我测试此 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= 之前的足够超时值]宁它。
我参考了
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 不会在程序退出之前关闭该文件描述符本身;如果它关闭了,避免这种情况的一种方法是在作为标准输入传递的描述符上创建锁、标准输出或标准错误)。