![](/img/trans.png)
[英]Why is standard output from subprocess (redirected to unbuffered file) being buffered?
[英]Redirected output from a subprocess call getting lost?
我有一些Python代碼大致使用了這樣的方式,其中使用了一些您可能會或可能不會擁有的庫:
# Open it for writing
vcf_file = open(local_filename, "w")
# Download the region to the file.
subprocess.check_call(["bcftools", "view",
options.truth_url.format(sample_name), "-r",
"{}:{}-{}".format(ref_name, ref_start, ref_end)], stdout=vcf_file)
# Close parent process's copy of the file object
vcf_file.close()
# Upload it
file_id = job.fileStore.writeGlobalFile(local_filename)
基本上,我正在啟動一個子進程,該子進程應該為我下載一些數據並將其打印為標准輸出。 我將數據重定向到文件,然后,子進程調用返回時,我將句柄關閉到文件,然后將文件復制到其他位置。
我觀察到的是,有時候,我期望的數據的尾端沒有將其放入副本中。 現在, bcftools
可能偶爾不寫入該數據,但是我擔心我可能正在做一些不安全的事情,並且在subprocess.check_call()
返回之后但在子進程的數據之前以某種方式無法訪問該文件寫入標准輸出后,將其寫入到可以看到它的磁盤上。
查看C標准(因為bcftools是在C / C ++中實現的),看起來程序正常退出時,所有打開的流(包括標准輸出)都將被刷新和關閉。 請參閱此處的[lib.support.start.term]
部分,描述exit()
的行為,當main()
返回時將隱式調用exit()
的行為:
-接下來,將清除所有具有未寫入的緩沖數據的打開的C流(由聲明的函數簽名介導),關閉所有打開的C流,並刪除通過調用tmp- file()創建的所有文件。30)
-最后,控制權返回到主機環境。 如果status為零或EXIT_SUCCESS,則返回狀態成功終止的實現定義形式。 如果status為EXIT_FAILURE,則返回狀態未成功終止的實現定義形式。 否則,返回的狀態是實施定義的。31)
因此,在子進程退出之前,它會關閉(並刷新)標准輸出。
但是,Linux close(2)
的手冊頁指出,關閉文件描述符並不一定保證寫入其中的任何數據實際上已將其寫入磁盤:
成功關閉並不能保證數據已成功保存到磁盤,因為內核延遲寫入操作。 關閉流時,文件系統刷新緩沖區並不常見。 如果需要確保物理存儲數據,請使用fsync(2)。 (這將取決於磁盤硬件。)
因此,看來,當進程退出時,將刷新其標准輸出流,但是如果該流實際上由指向磁盤上文件的文件描述符支持,則無法保證對磁盤的寫入已完成。 我懷疑這可能是這里正在發生的事情。
所以,我的實際問題是:
我對規格的閱讀正確嗎? 子進程在其父級看來可以在磁盤上獲得其重定向的標准輸出之前終止嗎?
是否可以以某種方式等待,直到子進程寫入文件的所有數據都已由OS實際同步到磁盤上?
我應該在父進程的文件對象副本上調用flush()
或某些Python版本的fsync()
嗎? 可以強制子進程將相同的文件描述符寫入磁盤嗎?
是的,可能要在幾分鍾后(物理地)將數據寫入磁盤。 但是您可以在此之前很長時間閱讀。
除非您擔心電源故障或內核崩潰,否則, 數據是否在磁盤上都沒有關系。 內核是否認為數據已寫入很重要。
check_call()
返回后,從文件中讀取是安全的。 如果您沒有看到所有數據; 它可能表明bcftools
存在錯誤,或者writeGlobalFile()
並未上傳文件中的所有數據。 您可以嘗試通過禁用bsftools
的stdout的塊緩沖模式來解決前者( 提供偽tty,使用unbuffer
命令行實用程序,等等 )。
問:我對規格的閱讀正確嗎? 子進程在其父級看來可以在磁盤上獲得其重定向的標准輸出之前終止嗎?
是。 是。
問:是否可以以某種方式等待,直到子進程寫入文件的所有數據都已由OS實際同步到磁盤上?
沒有。 在一般情況下, fsync()
是不夠的。 可能您仍然不需要它(回讀數據是另一個問題,與確保將數據寫入磁盤不同)。
問:我應該在父進程的文件對象副本上調用flush()或某些Python版本的fsync()嗎? 可以強制子進程將相同的文件描述符寫入磁盤嗎?
這將毫無意義。 .flush()
刷新父進程內部的緩沖區(您可以使用open(filename, 'wb', 0)
以避免在父進程中創建不必要的緩沖區)。
fsync()
文件描述符(子文件具有自己的文件描述符)。 我不知道內核是否為指向同一磁盤文件的不同文件描述符使用了不同的緩沖區。 同樣,這沒關系-如果您觀察到數據丟失(無崩潰); fsync()
在這里無濟於事。
問:為清楚起見,我看到您是在斷言其他進程確實應該讀取數據,因為相關的OS緩沖區在進程之間共享。 但是,您的斷言來源是什么? 您可以在規范或Linux文檔中找到一個可以確保共享這些緩沖區的地方嗎?
從該寫操作修改的文件中每個字節位置開始的任何成功
read()
都應返回該位置的write()
指定的數據,直到再次修改該字節位置為止。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.