這裡它說你可以重寫一個可執行文件,並且該進程將運行得很好 - 當進程重新啟動時它將被重新讀取。
但是,當我嘗試在進程運行時替換二進位(使用 scp,從開發伺服器到測試伺服器)時,它會顯示「檔案忙」。如果我替換共享庫檔案 (*.so),則連結它的所有進程都會崩潰。
為什麼這樣?我錯過了什麼嗎?如何在不停止/崩潰進程的情況下替換二進位檔案?
答案1
如中所提到的為什麼軟體包升級後仍能正常運作?,鎖被放置在 inode 上而不是檔案名稱上。當您載入並執行二進位檔案時,該檔案被標記為繁忙 - 這就是為什麼當您嘗試寫入該檔案時會收到 ETXTBSY(檔案繁忙)錯誤。
現在,對於共享庫,情況略有不同:庫將記憶體映射到進程的位址空間mmap()
。儘管MAP_DENYWRITE
可能已指定,但至少 Linux 上的 Glibc 會默默地忽略它(根據手冊頁,請隨意檢查原始程式碼) - 檢查此線。因此,您實際上可以寫入該文件,並且由於它是內存映射的,因此任何更改幾乎立即可見 - 這意味著如果您足夠努力,您可以設法 磚透過覆蓋庫來您的機器。
因此正確的更新方法是:
刪除文件,這會從文件系統中刪除對數據的引用,以便任何可能想要使用它的新生成的應用程序都無法訪問該文件,同時保持數據可供任何已打開(或映射)的人訪問;
建立一個包含更新內容的新檔案。
新建立的進程將使用更新的內容,正在運行的應用程式將存取舊版本。這就是任何健全的套件管理實用程式所做的事情。請注意,這並不是完全沒有任何危險 - 例如,dlsym()
如果庫的 API 默默更改,則動態加載程式碼(使用和朋友)的應用程式將遇到麻煩。
如果你想成為真正的人,真的為了安全起見,關閉系統,從另一個作業系統執行個體掛載檔案系統,更新並再次啟動更新的系統。
答案2
rpm 升級的作用是相同的 - 運行二進位和庫而不會崩潰。
那麼差別是什麼呢:
- 取消連結文件
- 寫入同名的新文件
這不會就地替換文件:引用正在使用的二進位檔案的 inode 仍然“忙”,直到最後一個保持其打開的物件完成為止。新檔案將使用新的索引節點號建立。
現在scp
或cp
將嘗試就地替換文件 - 這將更改 inode 引用的內容。正如您所描述的,這不起作用。