今天被朋友問及「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我原以為cp的實現是「rm + open(o_creat)」,不過現在想想上面的實現方式才是最可靠的(保證了時序安全和目標檔案的屬性)。這也可以解釋為什麼cp的目標檔案會繼承被覆蓋檔案的屬性而非原始檔。open("test2", o_rdonly|o_largefile) = 3
open("test", o_wronly|o_trunc|o_largefile) = 4
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()是另乙個測試動態庫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,也不應與核心的檔案系統底層實現耦合。
在思考這個問題的過程中,我意識到前面這個測試程式的乙個致命漏洞,稍作修改如下:
#include int main(int argc, char * argv)這次,再執行上面的三個用例後發現,「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下使用socket執行的服務程式
獲得本機ip位址 char getlocaladdr return addrip 守護程序的建立 守護程序 daemon 是指其他多工作業系統中在後台執行的電腦程式,並不會接受計算機使用者的直接控制,其好處是不占用終端。void daemonize setsockopt sockfd,sol soc...
Linux下vi的替換
s substitute 命令用來查詢和替換字串。語法如下 s 例如 s foo bar g會在全域性範圍 查詢foo並替換為bar,所有出現都會被替換 g 作用範圍分為當前行 全文 選區等等。當前行 s foo bar g全文 s foo bar g選區,在visual模式下選擇區域後輸入 vim...
linux 下的查詢與替換
以例子進行說明 在當前目錄下的.c檔案中查詢字串 password grep password c 在當前目錄及其多個子目錄中查詢檔案test.c find name test.c print 在當前目錄及其多個子目錄中查詢.vbs檔案並刪除之 find name vbs exec rm 在當前目錄...