深入理解文件系統(tǒng)inode | 比較Linux mv和cp命令

引言

在項(xiàng)目上線過程中,總會遇到更新可執(zhí)行程序的操作。而我們知道cp和mv都可以實(shí)現(xiàn)更新文件的操作,如果使用不當(dāng)可能造成源文件丟失等一系列問題,那么怎樣正確使用cp和mv呢?

linux文件系統(tǒng)基本概念

Block

文件數(shù)據(jù)存儲在硬盤上,硬盤的最小存儲單位叫做"扇區(qū)"(Sector)。OS讀取硬盤的時候,為了提高效率會一次性讀取一個"塊"(Block)。

block和sector的關(guān)系

inode節(jié)點(diǎn)

下面用思維導(dǎo)圖來對文件系統(tǒng)進(jìn)行介紹:


文件系統(tǒng)概覽

如圖,inode是儲存文件元信息的區(qū)域,除文件名以外的所有文件元信息,都存在inode之中。

可以通過Linux的stat命令查看文件對應(yīng)的inode:stat <filname>.<filetype>

stat-inode

文件系統(tǒng)如何存取文件

  1. 根據(jù)Filename,通過Dircetory的映射關(guān)系找到對應(yīng)的Inode number,例如:Inode:10307391

  2. 根據(jù)Inode number讀取到文件的Inode table

  3. 再根據(jù)Inode table中的Pointer讀取到對應(yīng)的Blocks

inode存儲也需要消耗對應(yīng)的空間,所以操作系統(tǒng)會將硬盤分成兩個區(qū)域:數(shù)據(jù)區(qū)和inode區(qū),這里的inode區(qū)就是指Inode table。

可以通過df -i查看硬盤分區(qū)inode總數(shù)和存余的情況。一般情況下文件和inode是一一對應(yīng)的關(guān)系,如果inode已經(jīng)用光,即使硬盤還有空間也無法創(chuàng)建文件。

文件名只是inode的一個alias,Unix/Linux系統(tǒng)內(nèi)部使用inode來識別文件。使用ls -i test.js可以查看一個文件的Inode number。若通過filename刪除失敗,可以直接刪除inode節(jié)點(diǎn)。

硬鏈接和軟鏈接

Linux和Unix系統(tǒng)也允許多個文件名指向同一個inode number,但增刪改互不影響,這種被稱為硬鏈接。

使用ln <sourcefile> <targetfile>可以創(chuàng)建硬鏈接,此時鏈接數(shù)+1。

軟鏈接的inode number雖然不同,但是文件A的內(nèi)容是文件B的路徑,A依賴于B。

mv和cp命令底層依賴

inode是識別一個文件的核心,移動文件或重命名文件,只是改變文件名,不影響inode number。一個文件打開或執(zhí)行后,系統(tǒng)就以inode號碼來識別,所以項(xiàng)目可以在不關(guān)閉軟件的的情況下進(jìn)行更新,不需要重啟。因?yàn)橄到y(tǒng)通過inode號碼,識別運(yùn)行中的文件,不通過文件名。更新的時候,新版文件以同樣的文件名,生成一個新的inode,不會影響到運(yùn)行中的文件。等到下一次運(yùn)行這個軟件的時候,文件名就自動指向新版文件,舊版文件的inode則被GC回收。

在MacOS操作系統(tǒng)中,我們可以使用dtruss命令查看系統(tǒng)調(diào)用SYSCALL。系統(tǒng)調(diào)用是操作系統(tǒng)提供給用戶程序調(diào)用的接口。通過添加調(diào)用參數(shù),在內(nèi)核態(tài)中執(zhí)行,內(nèi)核態(tài)返回?cái)?shù)據(jù)復(fù)制到用戶態(tài),最終用戶態(tài)得到結(jié)果。

另外,大部分Unix系統(tǒng)支持strace調(diào)用kernel和ptrace接口實(shí)現(xiàn)系統(tǒng)調(diào)用。

以查看rm命令的系統(tǒng)調(diào)用為例:

rm-dtruss

輸出的每一行都顯示了一個系統(tǒng)調(diào)用、參數(shù)及返回值。

這里,我們重點(diǎn)關(guān)注unlinkat,該調(diào)用是解除文件的鏈接,即刪除文件名。使源目錄不再含有此文件名。當(dāng)該文件的鏈接數(shù)為1且沒有進(jìn)程打開此文件時,才會真正刪除文件內(nèi)容。所以,用該方法直接刪除打開的文件是安全的。

查看mv和cp命令系統(tǒng)調(diào)用

節(jié)選部分跟蹤mv命令的系統(tǒng)調(diào)用指令:mv test.js zhuyue/test.js

mv-dtruss

綜上,mv命令首先檢查初始文件和目標(biāo)文件是否存在訪問權(quán)限,然后是通過rename指令實(shí)現(xiàn)系統(tǒng)調(diào)用。目標(biāo)文件存在時,mv的行為類似于rename的行為,該行為會導(dǎo)致inode節(jié)點(diǎn)發(fā)生變化,所以mv更新文件相當(dāng)于刪除文件后在新建一個同名文件。

節(jié)選部分跟蹤cp命令的系統(tǒng)調(diào)用指令:cp test.js zhuyue/test.js

cp-dtruss

執(zhí)行cp命令后文件inode number沒有改變,cp 使用了 open 及O_TRUNC 參數(shù)打開了目標(biāo)文件,因此cp更新操作是將目標(biāo)文件內(nèi)容清空,然后把新的內(nèi)容寫入目標(biāo)文件。

mv命令與cp命令更新文件對比

對比 mv cp
更新方式 刪除->替換 清空->寫入
屬組和屬主 屬組和屬主不變 屬組或?qū)僦鞲淖?/td>
inode節(jié)點(diǎn) 改變 不改變

總結(jié):項(xiàng)目上線最優(yōu)雅的方式

正在運(yùn)行的可執(zhí)行文件得到了操作系統(tǒng)的保護(hù),被打開的文件及正在使用的動態(tài)鏈接庫文件都是可以被寫入的。對使用中的動態(tài)鏈接庫的寫入,通常是不需要的,并且很可能導(dǎo)致程序崩潰。因而要避免對動態(tài)庫文件的寫入。

綜上,在上線需要更新可執(zhí)行程序或動態(tài)鏈接庫時,不要使用 cp 命令覆蓋,而是要使用 rm 刪除舊有文件,然后再把新的文件移動到原文件的位置。 install 命令和rpm包安裝時使用的機(jī)制都是先刪除舊文件,再建立新文件。這種操作能安全的更新文件,并且不影響當(dāng)前進(jìn)程的運(yùn)行。當(dāng)然,如果要想讓新文件生效,重啟使用它的程序或者動態(tài)加載新的庫。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容