引言
在項目上線過程中,總會遇到更新可執行程序的操作。而我們知道cp和mv都可以實現更新文件的操作,如果使用不當可能造成源文件丟失等一系列問題,那么怎樣正確使用cp和mv呢?
linux文件系統基本概念
Block:
文件數據存儲在硬盤上,硬盤的最小存儲單位叫做"扇區"(Sector)。OS讀取硬盤的時候,為了提高效率會一次性讀取一個"塊"(Block)。
inode節點:
下面用思維導圖來對文件系統進行介紹:
如圖,inode是儲存文件元信息的區域,除文件名以外的所有文件元信息,都存在inode之中。
可以通過Linux的stat命令查看文件對應的inode:stat <filname>.<filetype>
文件系統如何存取文件:
根據Filename,通過Dircetory的映射關系找到對應的
Inode number
,例如:Inode:10307391
。根據
Inode number
讀取到文件的Inode table
再根據
Inode table
中的Pointer
讀取到對應的Blocks
inode存儲也需要消耗對應的空間,所以操作系統會將硬盤分成兩個區域:數據區和inode區,這里的inode區就是指Inode table。
可以通過df -i
查看硬盤分區inode總數和存余的情況。一般情況下文件和inode是一一對應的關系,如果inode已經用光,即使硬盤還有空間也無法創建文件。
文件名只是inode的一個alias,Unix/Linux系統內部使用inode來識別文件。使用ls -i test.js
可以查看一個文件的Inode number
。若通過filename刪除失敗,可以直接刪除inode節點。
硬鏈接和軟鏈接:
Linux和Unix系統也允許多個文件名指向同一個inode number,但增刪改互不影響,這種被稱為硬鏈接。
使用ln <sourcefile> <targetfile>
可以創建硬鏈接,此時鏈接數+1。
軟鏈接的inode number雖然不同,但是文件A的內容是文件B的路徑,A依賴于B。
mv和cp命令底層依賴
inode是識別一個文件的核心,移動文件或重命名文件,只是改變文件名,不影響inode number。一個文件打開或執行后,系統就以inode號碼來識別,所以項目可以在不關閉軟件的的情況下進行更新,不需要重啟。因為系統通過inode號碼,識別運行中的文件,不通過文件名。更新的時候,新版文件以同樣的文件名,生成一個新的inode,不會影響到運行中的文件。等到下一次運行這個軟件的時候,文件名就自動指向新版文件,舊版文件的inode則被GC回收。
在MacOS操作系統中,我們可以使用dtruss
命令查看系統調用SYSCALL。系統調用是操作系統提供給用戶程序調用的接口。通過添加調用參數,在內核態中執行,內核態返回數據復制到用戶態,最終用戶態得到結果。
另外,大部分Unix系統支持strace調用kernel和ptrace接口實現系統調用。
以查看rm命令的系統調用為例:
輸出的每一行都顯示了一個系統調用、參數及返回值。
這里,我們重點關注unlinkat
,該調用是解除文件的鏈接,即刪除文件名。使源目錄不再含有此文件名。當該文件的鏈接數為1且沒有進程打開此文件時,才會真正刪除文件內容。所以,用該方法直接刪除打開的文件是安全的。
查看mv和cp命令系統調用
節選部分跟蹤mv命令的系統調用指令:mv test.js zhuyue/test.js
綜上,mv命令首先檢查初始文件和目標文件是否存在訪問權限,然后是通過rename
指令實現系統調用。目標文件存在時,mv的行為類似于rename的行為,該行為會導致inode節點發生變化,所以mv更新文件相當于刪除文件后在新建一個同名文件。
節選部分跟蹤cp命令的系統調用指令:cp test.js zhuyue/test.js
執行cp命令后文件inode number沒有改變,cp 使用了 open 及O_TRUNC 參數打開了目標文件,因此cp更新操作是將目標文件內容清空,然后把新的內容寫入目標文件。
mv命令與cp命令更新文件對比:
對比 | mv | cp |
---|---|---|
更新方式 | 刪除->替換 | 清空->寫入 |
屬組和屬主 | 屬組和屬主不變 | 屬組或屬主改變 |
inode節點 | 改變 | 不改變 |
總結:項目上線最優雅的方式
正在運行的可執行文件得到了操作系統的保護,被打開的文件及正在使用的動態鏈接庫文件都是可以被寫入的。對使用中的動態鏈接庫的寫入,通常是不需要的,并且很可能導致程序崩潰。因而要避免對動態庫文件的寫入。
綜上,在上線需要更新可執行程序或動態鏈接庫時,不要使用 cp 命令覆蓋,而是要使用 rm 刪除舊有文件,然后再把新的文件移動到原文件的位置。 install 命令和rpm包安裝時使用的機制都是先刪除舊文件,再建立新文件。這種操作能安全的更新文件,并且不影響當前進程的運行。當然,如果要想讓新文件生效,重啟使用它的程序或者動態加載新的庫。