用戶指南:Linux 文件系統的鏈接
在我為 opensource.com 寫過的關於 Linux 文件系統方方面面的文章中,包括 Linux 的 EXT4 文件系統的歷史、特性以及最佳實踐; 在 Linux 中管理設備;Linux 文件系統概覽 和 用戶指南:邏輯卷管理,我曾簡要的提到過 Linux 文件系統一個有趣的特性,它允許用戶從多個位置來訪問 Linux 文件目錄樹中的文件來簡化一些任務。
Linux 文件系統中有兩種 鏈接 : 硬鏈接 和 軟鏈接 。雖然二者差別顯著,但都用來解決相似的問題。它們都提供了對單個文件的多個目錄項(引用)的訪問,但實現卻大為不同。鏈接的強大功能賦予了 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
,可能是該計算機上用戶的共享文件目錄。
圖 1
硬鏈接被限制在一個單一的文件系統中。此處的「文件系統」 是指掛載在特定掛載點上的分區或邏輯卷,此例中是 /home
。這是因為在每個文件系統中的 inode 號都是唯一的。而在不同的文件系統中,如 /var
或 /opt
,會有和 /home
中相同的 inode 號。
因為所有的硬鏈接都指向了包含文件元信息的單一 inode ,這些屬性都是文件的一部分,像所屬關係、許可權、到該 inode 的硬鏈接數目,對每個硬鏈接來說這些特性沒有什麼不同的。這是一個文件所具有的一組屬性。唯一能區分這些文件的是包含在 inode 信息中的文件名。鏈接到同一目錄中的單一文件/ inode 的硬鏈接必須擁有不同的文件名,這是基於同一目錄下不能存在重複的文件名的事實的。
文件的硬鏈接數目可通過 ls -l
來查看,如果你想查看實際節點號,可使用 ls -li
命令。
符號(軟)鏈接
硬鏈接和軟鏈接(也稱為 符號鏈接 )的區別在於,硬鏈接直接指向屬於該文件的 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.txt
或 main.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 Lewin,Opensource.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
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive