2017-08-11 101 views
1

我有一个python脚本,它使用subprocess.check_call来启动Wine(Linux上的Windows模拟器),然后葡萄酒启动Z:\\Program Files (x86)\\PeaZip\\peazip.exePython subprocess.check_call([“wine”] ..)有一个同步问题

首先,当我在调试模式python3 -u -m ipdb unpack_archive.py中测试了这个python脚本,并且一步步设置了wine启动和运行语句的断点时,Wine成功运行peazip.exe。也就是说,peazip在Linux上成功提取PEA档案。

但是,当我测试此python脚本不在调试模式python3 unpack_archive.py,然后我发现peazip.exe不会成功提取PEA存档。所以我怀疑葡萄酒或python subprocess.check_call()中存在同步问题。现在

我的解决方法是,将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,它没有提及任何同步。我也检查了subprocess.check_call()。该文件明确指出check_call()将等待命令完成。

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


我参考了@jasonharper的建议。使用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秒。三个子文件已成功解压。

回答

1

我的理解是,wine可执行文件不是真正的模拟器 - 它只是启动一个名为wineserver的后台进程,如果它尚未运行,告诉它运行Windows程序,然后立即退出本身 - 很可能在Windows程序甚至开始运行。

this question的回答之一表明,将wine的输出管道输送到另一个程序会延迟一些事情,直到Windows程序实际退出。用Python术语来说,这相当于使用check_output()而不是check_call(),尽管我自己并没有尝试过。

+0

好。我试图使用'wineserver --foreground'。我发现'peazip.exe'完成提取后'wineserver'也会终止。 我在启动'wine'之前启动'wineserver --foreground',并等待'wineserver'子进程终止。 – MikimotoH

1

考虑使用咨询锁阻塞,直到该进程已退出:

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在程序退出前不会关闭该文件描述符;如果是这样,避免这种情况的一种方法是在作为标准输入传递的描述符上创建锁, stdout或stderr)。

+0

我想你的示例代码,但我遇到这样的例外: ' 回溯(最近通话最后一个): 文件 “unpack_archive.py”,线路258,在 的main() 文件 “unpack_archive.py”,行251,主 for f,sha1 in unpack_archive(arcname,outdir): 文件“unpack_archive.py”,第169行,在unpack_archive close_fds = False) 文件“/usr/lib/python3.4/subprocess.py “,第556行,check_call retcode = call(* popenargs,** kwargs) subprocess.SubprocessError:异常发生在preexec_fn中。 ' – MikimotoH

+0

这还不够详细 - 我需要实际的例外。你可能会考虑使用一个真正的函数替换lambda表达式,然后在提升之前打印一个堆栈跟踪。 –

+0

我用一个真实函数修改了lambda。结果是第二个'fcntl.flock(lockfile,fcntl.LOCK_EX)'不等到peazip执行完成。 它仍然无法解决竞争条件。 – MikimotoH