引言
在項目上線過程中,總會遇到更新可執行程式的操作。而我們知道cp和mv都可以實作更新檔案的操作,如果使用不當可能造成源檔案丢失等一系列問題,那麼怎樣正确使用cp和mv呢?
linux檔案系統基本概念
Block:
檔案資料存儲在硬碟上,硬碟的最小存儲機關叫做"扇區"(Sector)。OS讀取硬碟的時候,為了提高效率會一次性讀取一個"塊"(Block)。
block和sector的關系
inode節點:
下面用思維導圖來對檔案系統進行介紹:
檔案系統概覽
如圖,inode是儲存檔案元資訊的區域,除檔案名以外的所有檔案元資訊,都存在inode之中。
可以通過Linux的stat指令檢視檔案對應的inode:stat .
stat-inode
檔案系統如何存取檔案:
根據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 可以建立硬連結,此時連結數+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指令的系統調用為例:
rm-dtruss
輸出的每一行都顯示了一個系統調用、參數及傳回值。
這裡,我們重點關注unlinkat,該調用是解除檔案的連結,即删除檔案名。使源目錄不再含有此檔案名。當該檔案的連結數為1且沒有程序打開此檔案時,才會真正删除檔案内容。是以,用該方法直接删除打開的檔案是安全的。
檢視mv和cp指令系統調用
節選部分跟蹤mv指令的系統調用指令:mv test.js zhuyue/test.js
mv-dtruss
綜上,mv指令首先檢查初始檔案和目标檔案是否存在通路權限,然後是通過rename指令實作系統調用。目标檔案存在時,mv的行為類似于rename的行為,該行為會導緻inode節點發生變化,是以mv更新檔案相當于删除檔案後在建立一個同名檔案。
節選部分跟蹤cp指令的系統調用指令:cp test.js zhuyue/test.js
cp-dtruss
執行cp指令後檔案inode number沒有改變,cp 使用了 open 及O_TRUNC 參數打開了目标檔案,是以cp更新操作是将目标檔案内容清空,然後把新的内容寫入目标檔案。
mv指令與cp指令更新檔案對比:
對比
mv
cp
更新方式
删除->替換
清空->寫入
屬組和屬主
屬組和屬主不變
屬組或屬主改變
inode節點
改變
不改變
總結:項目上線最優雅的方式
正在運作的可執行檔案得到了作業系統的保護,被打開的檔案及正在使用的動态連結庫檔案都是可以被寫入的。對使用中的動态連結庫的寫入,通常是不需要的,并且很可能導緻程式崩潰。因而要避免對動态庫檔案的寫入。
綜上,在上線需要更新可執行程式或動态連結庫時,不要使用 cp 指令覆寫,而是要使用 rm 删除舊有檔案,然後再把新的檔案移動到原檔案的位置。 install 指令和rpm包安裝時使用的機制都是先删除舊檔案,再建立新檔案。這種操作能安全的更新檔案,并且不影響目前程序的運作。當然,如果要想讓新檔案生效,重新開機使用它的程式或者動态加載新的庫。