Linux下可以替換執行中的程式麼?
今天被朋友問及“Linux下可以替換執行中的程式麼?”,以前依稀記得Linux下是可以的(而Windows就不讓),於是隨口答道“OK”。結果朋友發來一個執行結果:(test正在執行中)
# cp test2 test cp: cannot create regular file `test`: Text file busy
看起來是程式被佔用,無法覆蓋。於是自己又再做了幾個實驗:
(1)先rm刪除正在執行的test,然後cp test2 test就沒有錯誤了。
(2)先mv改名正在執行的test,然後cp test2 test也沒有問題。
查了查資料並動手分析了一下,找到了比較滿意的解釋。cp並不改變目標檔案的inode,事實上它的實現是這樣的:
# strace cp test2 test 2>&1 | grep open.*test open("test2", O_RDONLY|O_LARGEFILE) = 3 open("test", O_WRONLY|O_TRUNC|O_LARGEFILE) = 4
我原以為cp的實現是“rm + open(O_CREAT)”,不過現在想想上面的實現方式才是最可靠的(保證了時序安全和目標檔案的屬性)。這也可以解釋為什麼cp的目標檔案會繼承被覆蓋檔案的屬性而非原始檔。
Linux由於Demand Paging機制的關係,必須確保正在執行中的程式映象(注意,並非檔案本身)不被意外修改,因此核心在啟動程式後會鎖定這個程式映象的inode。這就是為什麼cp在用“O_WRONLY|O_TRUNC”模式open目標檔案時會失敗。而先rm再cp的話,新檔案的inode其實已經改變了,原inode並沒有被真正刪除,直到核心釋放對它的引用。同理,mv只是改變了檔名,其inode不變,新檔案使用了新的inode。
問題到這裡已經水落石出,不過刨根究底的個性驅使我再做了以下一組實驗,沒想到結果完全出乎我意料之外!
寫了一個簡單的測試程式:
#include int main(int argc, char * argv[]) { foo(); // An export function by libtest.so. sleep(1000); return 0; }
foo()是另一個測試動態庫libtest.so的匯出介面,只列印一行提示就返回。接下來我把上面對執行檔案的測試用例對動態庫又做了一遍:
(1)cp libtest2.so libtest.so可以直接覆蓋已載入的動態庫。
(2)先rm刪除已載入的libtest.so,然後cp libtest2.so libtest.so成功。
(3)先mv改名已載入的libtest.so,然後cp libtest2.so libtest.so成功。
除了第一個用例外,結果相同。這樣看來,動態庫被載入時難道ld並沒有鎖定inode?不過想想也可以寬恕,畢竟ld也是使用者態程式,沒有權利去鎖定inode,也不應與核心的檔案系統底層實現耦合。
到這裡都還算在情理之中,看起來Linux也都處理的很好。不過還剩下一個問題:動態庫被以cp的方式覆蓋後難道不會和Demand Paging機制產生衝突?
在思考這個問題的過程中,我意識到前面這個測試程式的一個致命漏洞,稍作修改如下:
#include int main(int argc, char * argv[]) { loop: foo(); // An export function by libtest.so. sleep(1); goto loop; return 0; }
這次,再執行上面的三個用例後發現,“cp libtest2.so libtest.so”雖然仍可直接覆蓋已載入的動態庫,但是測試程式馬上出現了“Segmentation fault”。而後兩個用例結果不變。由此可見,想要安全的替換已載入的動態庫,還是用“笨拙”的“rm + cp”吧,看似捷徑的“cp覆蓋”會直接葬送掉你的程式……
看來,我再一次低估了Linux的健壯性,看似符合邏輯的流程也可能會帶來災難性的後果;“rm & cp”與“cp覆蓋”背後所隱藏的底層差異卻可以成為你的救星。Linux用得越久越是讓人覺得這是一塊充滿了荊棘和陷阱的原始叢林,只有步步為營實踏前行才能走的更遠。
注:以上實驗基於SuSE Linux Enterprise Server 9 SP1(Linux 2.6.5 & glibc 2.3.3)。
相關文章
- linux中批量替換文字中字串Linux字串
- 程式執行緒篇——執行緒切換(下)執行緒
- linux文字替換Linux
- Linux——程式建立、程式終止、程式等待、程式程式替換Linux
- Linux vi替換字串Linux字串
- 讓 Markdown 中的程式碼可以實時執行
- 如何在word中進行查詢與替換 word文件中的替換與查詢功能
- linux系統中怎麼中執行jar包?linux系統中執行jar包的方法LinuxJAR
- js中字串的替換JS字串
- 如何優雅的替換掉程式碼中的ifelse
- 程式等待和程式替換
- Linux下程式與執行緒概念淺析Linux執行緒
- linux crontab下的指令碼不執行怎麼辦Linux指令碼
- Linux 下後臺執行和按照守護程式方式後臺執行的坑Linux
- Linux系統程式設計之程式替換:exec 函式族Linux程式設計函式
- Java 替換PDF中的字型Java
- Java替換PDF中的字型Java
- linux-如何快速替換IPLinux
- 如何在Linux中檢視所有正在執行的程式Linux
- 程式執行緒篇——執行緒切換(上)執行緒
- gohook 一個支援執行時替換 golang 函式的庫實現HookGolang函式
- 替換專案中的包名
- puppet替換檔案中的string
- Swift 中單例模式的替換Swift單例模式
- linux: awk 指定位置替換字元Linux字元
- Linux的VI (連線行,查詢和替換,多檔案編輯)Linux
- Linux系統程式設計之程式控制(程式建立、終止、等待及替換)Linux程式設計
- idea替換內容快捷鍵 idea怎麼替換掉所選的文字Idea
- Linux上執行記憶體中的指令碼和程式Linux記憶體指令碼
- php怎麼替換文件中手機號碼PHP
- js中字串全部替換JS字串
- 在Linux中,系統目前有許多正在執行的任務,在不重啟機器的條件下,有什麼方法可以把所有正在執行的程序移除呢?Linux
- 網站程式碼修改替換流程圖,輕鬆掌握程式碼修改替換流程網站流程圖
- Linux配置IP地址需要什麼許可權?可以執行哪些操作?Linux
- 執行緒與程式之間有什麼關係?Linux執行緒與程式有什麼區別?執行緒Linux
- Linux執行緒與程式的區別Linux執行緒
- Linux程式與執行緒的區別Linux執行緒
- 教你如何替換@PathVariable中的變數變數
- bash 引數替換中的模式匹配模式