古之成大事者,不唯有超世之才,亦唯有堅韌不拔之志也!

            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理 ::
            3 隨筆 :: 44 文章 :: 1 評論 :: 0 Trackbacks

          轉:

          http://blog.csdn.net/zhangxinrun/article/details/5591246

          今天被朋友問及“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 <stdio.h>

          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 <stdio.h>

          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)。


          posted on 2011-12-02 11:08 goto 閱讀(215) 評論(0)  編輯  收藏 所屬分類: LINUX & UNIX
          主站蜘蛛池模板: 新龙县| 水城县| 和林格尔县| 沙湾县| 陆河县| 奇台县| 临桂县| 广宗县| 曲阳县| 太康县| 越西县| 防城港市| 张掖市| 民勤县| 镇原县| 沙田区| 洛扎县| 日喀则市| 阿图什市| 福海县| 昌乐县| 桃园县| 隆昌县| 宿州市| 江孜县| 华安县| 壶关县| 廉江市| 河北区| 开平市| 甘孜| 高淳县| 云安县| 澎湖县| 泰州市| 郑州市| 惠东县| 安多县| 山阳县| 崇信县| 呼图壁县|