Linux替換動態庫導致正在運行的程序崩潰


在替換so文件時,如果在不停程序的情況下,直接用 cp new.so old.so 的方式替換程序使用的動態庫文件會導致正在運行中的程序崩潰。解決的辦法是采用“rm+cp” 或“mv+cp” 來替代直接“cp” 的操作方法。

linux系統的動態庫有兩種使用方法:運行時動態鏈接庫,動態加載庫並在程序控制之下使用。 

 

1、在不停程序的情況下,直接用 cp 命令替換程序使用的 so 文件,導致程序崩潰:

這與 cp 命令的實現有關,cp 並不改變目標文件的 inode,cp 的目標文件會繼承被覆蓋文件的屬性而非源文件。實際上它是這樣實現的: 

strace cp libnew.so libold.so 2>&1 |grep open.*lib.*.so 

open("libnew.so", O_RDONLY|O_LARGEFILE) = 3 

open("libold.so", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4 

在 cp 使用“O_WRONLY|O_TRUNC” 打開目標文件時,原 so 文件的鏡像被意外的破壞了。這樣動態鏈接器 ld.so 不能訪問到 so 文件中的函數入口。從而導致 Segmentation fault,程序崩潰。 

2、怎樣在不停止程序的情況下替換so文件,並且保證程序不會崩潰? 

答案是采用“rm+cp” 或“mv+cp” 來替代直接“cp” 的操作方法。 

在用新的so文件 libnew.so 替換舊的so文件 libold.so 時,如果采用如下方法: 

rm libold.so 

cp libnew.so libold.so 

采用這種方法,目標文件 libold.so 的 inode 其實已經改變了,原來的 libold.so 文件雖然不能用 ”ls”查看到,但其 inode 並沒有被真正刪除,直到內核釋放對它的引用。同理,mv只是改變了文件名,其 inode 不變,新文件使用了新的 inode。這樣動態鏈接器 ld.so 仍然使用原來文件的 inode 訪問舊的 so 文件。因而程序依然能正常運行。 

到這里,我們回想在上線操作中在替換可執行程序時,為什么直接使用“cp new old”這樣的命令時,系統會禁止這樣的操作,並且給出這樣的提示“cp: cannot create regular file `old': Text file busy”。這時,我們采用的辦法仍然是用“rm+cp”或者“mv+cp”來替代直接“cp”,這跟以上提到的so文件的替換有同樣的道理。 

但是,為什么系統會阻止 cp 覆蓋可執行程序,而不阻止覆蓋 so 文件呢?這是因為 Linux 有個 Demand Paging 機制,所謂“Demand Paging”,簡單的說,就是系統為了節約物理內存開銷,並不會程序運行時就將所有頁(page)都加載到內存中,而只有在系統有訪問需求時才將其加載。 

“Demand Paging”要求正在運行中的程序鏡像(注意,並非文件本身)不被意外修改,因此內核在啟動程序后會鎖定這個程序鏡像的 inode。對於 so 文件,它是靠 ld.so 加載的,而ld.so畢竟也是用戶態程序,沒有權利去鎖定inode,也不應與內核的文件系統底層實現耦合。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM