Watch at Ease看見世界,讀懂生活。

科技 · 實用指南

Linux 顯示「Text file busy」?覆寫或重試前先鎖定執行中的 inode

ETXTBSY 可能表示部署正在寫入執行中的可執行檔,也可能表示啟動器要執行仍以寫入模式開啟的檔案。終止任何程序前先保留失敗系統呼叫、程序、inode 與命名空間,再修正競態或原子替換路徑。

執行中 inode 與受阻就地覆寫示意圖
原創插畫:修改路徑前先證明執行中的 inode。 Watch at Ease

ETXTBSY 可能表示部署正在寫入執行中的可執行檔,也可能表示啟動器要執行仍以寫入模式開啟的檔案。終止任何程序前先保留失敗系統呼叫、程序、inode 與命名空間,再修正競態或原子替換路徑。

可執行檔仍以寫入模式開啟時,execve() 可能回傳 ETXTBSY。 同一 inode 正在執行時,寫入或截斷可能被拒絕。

1. 保留方向、路徑與 inode

記錄完整錯誤、時間、命令、PID、容器或掛載命名空間,以及真正失敗的動作。區分 exec、open、truncate、copy 與 rename;同一句錯誤不代表同一方向。重新啟動前用 stat 保存裝置號與 inode。

不要先重新開機、pkill -f 或無限重試。替換後相同路徑可能指向另一 inode,容器也可能看不到主機占用者。應在失敗命名空間比較裝置號與 inode,而非只比對文字路徑。

2. 在正確命名空間證明執行者或寫入者

懷疑執行中檔案時,檢查 /proc/<pid>/exe,並取得小範圍 fuser -v 或等價程序證據。可執行標記與寫入標記回答不同問題;注意權限,不要公開無關命令列或路徑。

執行失敗時,找出仍持有可寫描述符的程序並核對目標與旗標。也要檢查父子程序,因為未設 close-on-exec 的寫入描述符可能被並行產生的子程序繼承。只停止已證實的所有者。

3. 修正寫後執行競態,而不是掩蓋它

重現最小序列:建立同目錄新檔、完整寫入、設定模式、關閉後再執行。並行負載下應序列化快取解壓,或使用按版本隔離的暫存路徑,避免兩個 worker 同時填充與執行同一目標。

從來源縮短描述符生命週期,並在合適處設定 close-on-exec。只針對 ETXTBSY 的短暫有界重試可降低殘餘競態,但必須可觀測,不能取代關閉寫入者或移除共享路徑競爭。

4. 寫入同目錄新檔並原子替換路徑

不要就地截斷執行中的可執行檔。在同一檔案系統寫入新檔,設定擁有者與模式,驗證雜湊並直接測試,再以 rename 覆蓋目標。原子路徑交接後,舊程序仍透過舊 inode 執行。

若需要當機持久性,還要依應用需求同步檔案與目錄。重新命名後只重新啟動或重載受控服務,並保留舊製品供回復,不要立刻刪除證據。

5. 用程序與 inode 證據驗收復原

確認新 PID 的 /proc/<pid>/exe 對應預期路徑、裝置號、inode 與內容雜湊。若刻意保留舊 PID,確認它在排空前仍映射舊 inode。

執行過去會觸發 ETXTBSY 的並行寫入與啟動測試。驗收要求沒有 busy 錯誤、可執行檔上沒有殘留可寫描述符、權限與雜湊正確且回復已測試;任何門檻失敗就還原舊路徑。

已驗證新檔經原子重新命名切換的示意圖
原創插畫:驗證同目錄新檔,原子重新命名後排空舊 inode。 Watch at Ease