2017-08-11 102 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