使用者指南:Linux 檔案系統的連結

David Both發表於2017-12-03

學習如何使用連結,通過從 Linux 檔案系統多個位置來訪問檔案,可以讓日常工作變得輕鬆。

在我為 opensource.com 寫過的關於 Linux 檔案系統方方面面的文章中,包括 Linux 的 EXT4 檔案系統的歷史、特性以及最佳實踐; 在 Linux 中管理裝置Linux 檔案系統概覽 和 使用者指南:邏輯卷管理,我曾簡要的提到過 Linux 檔案系統一個有趣的特性,它允許使用者從多個位置來訪問 Linux 檔案目錄樹中的檔案來簡化一些任務。

Linux 檔案系統中有兩種連結link硬連結hard link軟連結soft link。雖然二者差別顯著,但都用來解決相似的問題。它們都提供了對單個檔案的多個目錄項(引用)的訪問,但實現卻大為不同。連結的強大功能賦予了 Linux 檔案系統靈活性,因為一切皆是檔案

舉個例子,我曾發現一些程式要求特定的版本庫方可執行。 當用升級後的庫替代舊庫後,程式會崩潰,提示舊版本庫缺失。通常,庫名的唯一變化就是版本號。出於直覺,我僅僅給程式新增了一個新的庫連結,並以舊庫名稱命名。我試著再次啟動程式,執行良好。程式就是一個遊戲,人人都明白,每個玩家都會盡力使遊戲進行下去。

事實上,幾乎所有的應用程式連結庫都使用通用的命名規則,連結名稱中包含了主版本號,連結所指向的檔案的檔名中同樣包含了小版本號。再比如,程式的一些必需檔案為了迎合 Linux 檔案系統規範,從一個目錄移動到另一個目錄中,系統為了向後相容那些不能獲取這些檔案新位置的程式在舊的目錄中存放了這些檔案的連結。如果你對 /lib64 目錄做一個長清單列表,你會發現很多這樣的例子。

lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.hwm -> ../../usr/share/cracklib/pw_dict.hwm 
lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.pwd -> ../../usr/share/cracklib/pw_dict.pwd 
lrwxrwxrwx.  1 root root       36 Dec  8  2016 cracklib_dict.pwi -> ../../usr/share/cracklib/pw_dict.pwi
lrwxrwxrwx.  1 root root       27 Jun  9  2016 libaccountsservice.so.0 -> libaccountsservice.so.0.0.0 
-rwxr-xr-x.  1 root root   288456 Jun  9  2016 libaccountsservice.so.0.0.0 
lrwxrwxrwx   1 root root       15 May 17 11:47 libacl.so.1 -> libacl.so.1.1.0 
-rwxr-xr-x   1 root root    36472 May 17 11:47 libacl.so.1.1.0 
lrwxrwxrwx.  1 root root       15 Feb  4  2016 libaio.so.1 -> libaio.so.1.0.1 
-rwxr-xr-x.  1 root root     6224 Feb  4  2016 libaio.so.1.0.0 
-rwxr-xr-x.  1 root root     6224 Feb  4  2016 libaio.so.1.0.1 
lrwxrwxrwx.  1 root root       30 Jan 16 16:39 libakonadi-calendar.so.4 -> libakonadi-calendar.so.4.14.26 
-rwxr-xr-x.  1 root root   816160 Jan 16 16:39 libakonadi-calendar.so.4.14.26 
lrwxrwxrwx.  1 root root       29 Jan 16 16:39 libakonadi-contact.so.4 -> libakonadi-contact.so.4.14.26 

/lib64 目錄下的一些連結

在上面展示的 /lib64 目錄清單列表中,檔案模式第一個字母 l (小寫字母 l)表示這是一個軟連結(又稱符號連結)。

硬連結

在 Linux 的 EXT4 檔案系統的歷史、特性以及最佳實踐一文中,我曾探討過這樣一個事實,每個檔案都有一個包含該檔案資訊的 inode,包含了該檔案的位置資訊。上述文章中的圖2展示了一個指向 inode 的單一目錄項。每個檔案都至少有一個目錄項指向描述該檔案資訊的 inode ,目錄項是一個硬連結,因此每個檔案至少都有一個硬連結。

如下圖 1 所示,多個目錄項指向了同一 inode 。這些目錄項都是硬連結。我曾在三個目錄項中使用波浪線 (~) 的縮寫,這是使用者目錄的慣例表示,因此在該例中波浪線等同於 /home/user 。值得注意的是,第四個目錄項是一個完全不同的目錄,/home/shared,可能是該計算機上使用者的共享檔案目錄。

fig1directory_entries.png

圖 1

硬連結被限制在一個單一的檔案系統中。此處的“檔案系統” 是指掛載在特定掛載點上的分割槽或邏輯卷,此例中是 /home。這是因為在每個檔案系統中的 inode 號都是唯一的。而在不同的檔案系統中,如 /var/opt,會有和 /home 中相同的 inode 號。

因為所有的硬連結都指向了包含檔案元資訊的單一 inode ,這些屬性都是檔案的一部分,像所屬關係、許可權、到該 inode 的硬連結數目,對每個硬連結來說這些特性沒有什麼不同的。這是一個檔案所具有的一組屬性。唯一能區分這些檔案的是包含在 inode 資訊中的檔名。連結到同一目錄中的單一檔案/ inode 的硬連結必須擁有不同的檔名,這是基於同一目錄下不能存在重複的檔名的事實的。

檔案的硬連結數目可通過 ls -l 來檢視,如果你想檢視實際節點號,可使用 ls -li 命令。

符號(軟)連結

硬連結和軟連結(也稱為符號連結symlink)的區別在於,硬連結直接指向屬於該檔案的 inode ,而軟連結直接指向一個目錄項,即指向一個硬連結。因為軟連結指向的是一個檔案的硬連結而非該檔案的 inode ,所以它們並不依賴於 inode 號,這使得它們能跨越不同的檔案系統、分割槽和邏輯捲起作用。

軟連結的缺點是,一旦它所指向的硬連結被刪除或重新命名後,該軟連結就失效了。軟連結雖然還在,但所指向的硬連結已不存在。所幸的是,ls 命令能以紅底白字的方式在其列表中高亮顯示失效的軟連結。

實驗專案: 連結實驗

我認為最容易理解連結用法及其差異的方法是動手搭建一個專案。這個專案應以非超級使用者的身份在一個空目錄下進行。我建立了 ~/temp 目錄做這個實驗,你也可以這麼做。這麼做可為專案建立一個安全的環境且提供一個新的空目錄讓程式運作,如此以來這兒僅存放和程式有關的檔案。

初始工作

首先,在你要進行實驗的目錄下為該專案中的任務建立一個臨時目錄,確保當前工作目錄(PWD)是你的主目錄,然後鍵入下列命令。

mkdir temp

使用這個命令將當前工作目錄切換到 ~/temp

cd temp

實驗開始,我們需要建立一個能夠連結到的檔案,下列命令可完成該工作並向其填充內容。

du -h > main.file.txt

使用 ls -l 長列表命名確認檔案正確地建立了。執行結果應類似於我的。注意檔案大小隻有 7 位元組,但你的可能會有 1~2 位元組的變動。

[dboth@david temp]$ ls -l 
total 4 
-rw-rw-r-- 1 dboth dboth 7 Jun 13 07:34 main.file.txt

在列表中,檔案模式串後的數字 1 代表存在於該檔案上的硬連結數。現在應該是 1 ,因為我們還沒有為這個測試檔案建立任何硬連結。

對硬連結進行實驗

硬連結建立一個指向同一 inode 的新目錄項,當為檔案新增一個硬連結時,你會看到連結數目的增加。確保當前工作目錄仍為 ~/temp。建立一個指向 main.file.txt 的硬連結,然後檢視該目錄下檔案列表。

[dboth@david temp]$ ln main.file.txt link1.file.txt 
[dboth@david temp]$ ls -l 
total 8 
-rw-rw-r-- 2 dboth dboth 7 Jun 13 07:34 link1.file.txt 
-rw-rw-r-- 2 dboth dboth 7 Jun 13 07:34 main.file.txt

目錄中兩個檔案都有兩個連結且大小相同,時間戳也一樣。這就是有一個 inode 和兩個硬連結(即該檔案的目錄項)的一個檔案。再建立一個該檔案的硬連結,並列出目錄清單內容。你可以建立硬連結: link1.file.txtmain.file.txt

[dboth@david temp]$ ln link1.file.txt link2.file.txt ; ls -l
total 16 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 link1.file.txt 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 link2.file.txt 
-rw-rw-r-- 3 dboth dboth 7 Jun 13 07:34 main.file.txt

注意,該目錄下的每個硬連結必須使用不同的名稱,因為同一目錄下的兩個檔案不能擁有相同的檔名。試著建立一個和現存連結名稱相同的硬連結。

[dboth@david temp]$ ln main.file.txt link2.file.txt 
ln: failed to create hard link 'link2.file.txt': File exists

顯然不行,因為 link2.file.txt 已經存在。目前為止我們只在同一目錄下建立硬連結,接著在臨時目錄的父目錄(你的主目錄)中建立一個連結。

[dboth@david temp]$ ln main.file.txt ../main.file.txt ; ls -l ../main*
-rw-rw-r--    4 dboth dboth     7 Jun 13 07:34 main.file.txt

上面的 ls 命令顯示 main.file.txt 檔案確實存在於主目錄中,且與該檔案在 temp 目錄中的名稱一致。當然它們不是不同的檔案,它們是同一檔案的兩個連結,指向了同一檔案的目錄項。為了幫助說明下一點,在 temp 目錄中新增一個非連結檔案。

[dboth@david temp]$ touch unlinked.file ; ls -l
total 12
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link1.file.txt
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link2.file.txt
-rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 main.file.txt
-rw-rw-r-- 1 dboth dboth 0 Jun 14 08:18 unlinked.file

使用 ls 命令的 i 選項檢視 inode 的硬連結號和新建立檔案的硬連結號。

[dboth@david temp]$ ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 7 Jun 13 07:34 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth 0 Jun 14 08:18 unlinked.file

注意上面檔案模式左邊的數字 657024 ,這是三個硬連結檔案所指的同一檔案的 inode 號,你也可以使用 i 選項檢視主目錄中所建立的連結的節點號,和該值相同。而那個只有一個連結的 inode 號和其他的不同,在你的系統上看到的 inode 號或許不同於本文中的。

接著改變其中一個硬連結檔案的大小。

[dboth@david temp]$ df -h > link2.file.txt ; ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

現在所有的硬連結檔案大小都比原來大了,因為多個目錄項都連結著同一檔案。

下個實驗在我的電腦上會出現這樣的結果,是因為我的 /tmp 目錄在一個獨立的邏輯捲上。如果你有單獨的邏輯卷或檔案系統在不同的分割槽上(如果未使用邏輯卷),確定你是否能訪問那個分割槽或邏輯卷,如果不能,你可以在電腦上掛載一個 U 盤,如果上述方式適合你,你可以進行這個實驗。

試著在 /tmp 目錄中建立一個 ~/temp 目錄下檔案的連結(或你的檔案系統所在的位置)。

[dboth@david temp]$ ln link2.file.txt /tmp/link3.file.txt
ln: failed to create hard link '/tmp/link3.file.txt' => 'link2.file.txt': 
Invalid cross-device link

為什麼會出現這個錯誤呢? 原因是每一個單獨的可掛載檔案系統都有一套自己的 inode 號。簡單的通過 inode 號來跨越整個 Linux 檔案系統結構引用一個檔案會使系統困惑,因為相同的節點號會存在於每個已掛載的檔案系統中。

有時你可能會想找到一個 inode 的所有硬連結。你可以使用 ls -li 命令。然後使用 find 命令找到所有硬連結的節點號。

[dboth@david temp]$ find . -inum 657024 
./main.file.txt
./link1.file.txt
./link2.file.txt

注意 find 命令不能找到所屬該節點的四個硬連結,因為我們在 ~/temp 目錄中查詢。 find 命令僅在當前工作目錄及其子目錄中查詢檔案。要找到所有的硬連結,我們可以使用下列命令,指定你的主目錄作為起始查詢條件。

[dboth@david temp]$ find ~ -samefile main.file.txt 
/home/dboth/temp/main.file.txt
/home/dboth/temp/link1.file.txt
/home/dboth/temp/link2.file.txt
/home/dboth/main.file.txt

如果你是非超級使用者,沒有許可權,可能會看到錯誤資訊。這個命令也使用了 -samefile 選項而不是指定檔案的節點號。這個效果和使用 inode 號一樣且更容易,如果你知道其中一個硬連結名稱的話。

對軟連結進行實驗

如你剛才看到的,不能跨越檔案系統邊界建立硬連結,即在邏輯卷或檔案系統中從一個檔案系統到另一個檔案系統。軟連結給出了這個問題的解決方案。雖然它們可以達到相同的目的,但它們是非常不同的,知道這些差異是很重要的。

讓我們在 ~/temp 目錄中建立一個符號連結來開始我們的探索。

[dboth@david temp]$ ln -s link2.file.txt link3.file.txt ; ls -li
total 12
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 link2.file.txt
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt
657024 -rw-rw-r-- 4 dboth dboth 1157 Jun 14 14:14 main.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

擁有節點號 657024 的那些硬連結沒有變化,且硬連結的數目也沒有變化。新建立的符號連結有不同的 inode 號 658270。 名為 link3.file.txt 的軟連結指向了 link2.file.txt 檔案。使用 cat 命令檢視 link3.file.txt 檔案的內容。符號連結的 inode 資訊以字母 l (小寫字母 l)開頭,意味著這個檔案實際是個符號連結。

上例中軟連結檔案 link3.file.txt 的大小隻有 14 位元組。這是文字內容 link3.file.txt 的大小,即該目錄項的實際內容。目錄項 link3.file.txt 並不指向一個 inode ;它指向了另一個目錄項,這在跨越檔案系統建立連結時很有幫助。現在試著建立一個軟連結,之前在 /tmp 目錄中嘗試過的。

[dboth@david temp]$ ln -s /home/dboth/temp/link2.file.txt 
/tmp/link3.file.txt ; ls -l /tmp/link*
lrwxrwxrwx 1 dboth dboth 31 Jun 14 21:53 /tmp/link3.file.txt -> 
/home/dboth/temp/link2.file.txt

刪除連結

當你刪除硬連結或硬連結所指的檔案時,需要考慮一些問題。

首先,讓我們刪除硬連結檔案 main.file.txt。注意指向 inode 的每個目錄項就是一個硬連結。

[dboth@david temp]$ rm main.file.txt ; ls -li
total 8
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link1.file.txt
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link2.file.txt
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

main.file.txt 是該檔案被建立時所建立的第一個硬連結。現在刪除它,仍然保留著原始檔案和硬碟上的資料以及所有剩餘的硬連結。要刪除原始檔案,你必須刪除它的所有硬連結。

現在刪除 link2.file.txt 硬連結檔案。

[dboth@david temp]$ rm link2.file.txt ; ls -li 
total 8 
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 link1.file.txt 
658270 lrwxrwxrwx 1 dboth dboth   14 Jun 14 15:21 link3.file.txt -> 
link2.file.txt 
657024 -rw-rw-r-- 3 dboth dboth 1157 Jun 14 14:14 main.file.txt 
657863 -rw-rw-r-- 1 dboth dboth    0 Jun 14 08:18 unlinked.file

注意軟連結的變化。刪除軟連結所指的硬連結會使該軟連結失效。在我的系統中,斷開的連結用顏色高亮顯示,目標的硬連結會閃爍顯示。如果需要修復這個損壞的軟連結,你需要在同一目錄下建立一個和舊連結相同名字的硬連結,只要不是所有硬連結都已刪除就行。您還可以重新建立連結本身,連結保持相同的名稱,但指向剩餘的硬連結中的一個。當然如果軟連結不再需要,可以使用 rm 命令刪除它們。

unlink 命令在刪除檔案和連結時也有用。它非常簡單且沒有選項,就像 rm 命令一樣。然而,它更準確地反映了刪除的基本過程,因為它刪除了目錄項與被刪除檔案的連結。

寫在最後

我用過這兩種型別的連結很長一段時間後,我開始瞭解它們的能力和特質。我為我所教的 Linux 課程編寫了一個實驗室專案,以充分理解連結是如何工作的,並且我希望增進你的理解。

(題圖: Paul LewinOpensource.com 修改。 CC BY-SA 2.0


作者簡介:

戴維.布斯 - 戴維.布斯是 Linux 和開源倡導者,居住在北卡羅萊納的羅列 。他在 IT 行業工作了四十年,為 IBM 工作了 20 多年的 OS/2。在 IBM 時,他在 1981 年編寫了最初的 IBM PC 的第一個培訓課程。他為 RedHat 教授過 RHCE 班,並曾在 MCI Worldcom、思科和北卡羅萊納州工作。他已經用 Linux 和開源軟體工作將近 20 年了。


via: https://opensource.com/article/17/6/linking-linux-filesystem

作者:David Both 譯者:yongshouzhang 校對:wxy

本文由 LCTT 原創編譯,Linux中國 榮譽推出

相關文章