用戶指南:Linux文件系統(tǒng)的鏈接
學(xué)習(xí)如何使用鏈接,通過從 Linux 文件系統(tǒng)多個(gè)位置來訪問文件,可以讓日常工作變得輕松。
在我為 opensource.com 寫過的關(guān)于 Linux 文件系統(tǒng)方方面面的文章中,包括 Linux 的 EXT4 文件系統(tǒng)的歷史、特性以及***實(shí)踐; 在 Linux 中管理設(shè)備;Linux 文件系統(tǒng)概覽 和 用戶指南:邏輯卷管理,我曾簡(jiǎn)要的提到過 Linux 文件系統(tǒng)一個(gè)有趣的特性,它允許用戶從多個(gè)位置來訪問 Linux 文件目錄樹中的文件來簡(jiǎn)化一些任務(wù)。
Linux 文件系統(tǒng)中有兩種鏈接:硬鏈接和軟鏈接。雖然二者差別顯著,但都用來解決相似的問題。它們都提供了對(duì)單個(gè)文件的多個(gè)目錄項(xiàng)(引用)的訪問,但實(shí)現(xiàn)卻大為不同。鏈接的強(qiáng)大功能賦予了 Linux 文件系統(tǒng)靈活性,因?yàn)橐磺薪允俏募?/p>
舉個(gè)例子,我曾發(fā)現(xiàn)一些程序要求特定的版本庫方可運(yùn)行。 當(dāng)用升級(jí)后的庫替代舊庫后,程序會(huì)崩潰,提示舊版本庫缺失。通常,庫名的唯一變化就是版本號(hào)。出于直覺,我僅僅給程序添加了一個(gè)新的庫鏈接,并以舊庫名稱命名。我試著再次啟動(dòng)程序,運(yùn)行良好。程序就是一個(gè)游戲,人人都明白,每個(gè)玩家都會(huì)盡力使游戲進(jìn)行下去。
事實(shí)上,幾乎所有的應(yīng)用程序鏈接庫都使用通用的命名規(guī)則,鏈接名稱中包含了主版本號(hào),鏈接所指向的文件的文件名中同樣包含了小版本號(hào)。再比如,程序的一些必需文件為了迎合 Linux 文件系統(tǒng)規(guī)范,從一個(gè)目錄移動(dòng)到另一個(gè)目錄中,系統(tǒng)為了向后兼容那些不能獲取這些文件新位置的程序在舊的目錄中存放了這些文件的鏈接。如果你對(duì) /lib64 目錄做一個(gè)長(zhǎng)清單列表,你會(huì)發(fā)現(xiàn)很多這樣的例子。
- 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 目錄清單列表中,文件模式***個(gè)字母 l (小寫字母 l)表示這是一個(gè)軟鏈接(又稱符號(hào)鏈接)。
硬鏈接
在 Linux 的 EXT4 文件系統(tǒng)的歷史、特性以及***實(shí)踐一文中,我曾探討過這樣一個(gè)事實(shí),每個(gè)文件都有一個(gè)包含該文件信息的 inode,包含了該文件的位置信息。上述文章中的圖2展示了一個(gè)指向 inode 的單一目錄項(xiàng)。每個(gè)文件都至少有一個(gè)目錄項(xiàng)指向描述該文件信息的 inode ,目錄項(xiàng)是一個(gè)硬鏈接,因此每個(gè)文件至少都有一個(gè)硬鏈接。
如下圖 1 所示,多個(gè)目錄項(xiàng)指向了同一 inode 。這些目錄項(xiàng)都是硬鏈接。我曾在三個(gè)目錄項(xiàng)中使用波浪線 (~) 的縮寫,這是用戶目錄的慣例表示,因此在該例中波浪線等同于 /home/user 。值得注意的是,第四個(gè)目錄項(xiàng)是一個(gè)完全不同的目錄,/home/shared,可能是該計(jì)算機(jī)上用戶的共享文件目錄。
圖 1
硬鏈接被限制在一個(gè)單一的文件系統(tǒng)中。此處的“文件系統(tǒng)” 是指掛載在特定掛載點(diǎn)上的分區(qū)或邏輯卷,此例中是 /home。這是因?yàn)樵诿總€(gè)文件系統(tǒng)中的 inode 號(hào)都是唯一的。而在不同的文件系統(tǒng)中,如 /var 或 /opt,會(huì)有和 /home 中相同的 inode 號(hào)。
因?yàn)樗械挠叉溄佣贾赶蛄税募畔⒌膯我?inode ,這些屬性都是文件的一部分,像所屬關(guān)系、權(quán)限、到該 inode 的硬鏈接數(shù)目,對(duì)每個(gè)硬鏈接來說這些特性沒有什么不同的。這是一個(gè)文件所具有的一組屬性。唯一能區(qū)分這些文件的是包含在 inode 信息中的文件名。鏈接到同一目錄中的單一文件/ inode 的硬鏈接必須擁有不同的文件名,這是基于同一目錄下不能存在重復(fù)的文件名的事實(shí)的。
文件的硬鏈接數(shù)目可通過 ls -l 來查看,如果你想查看實(shí)際節(jié)點(diǎn)號(hào),可使用 ls -li 命令。
符號(hào)(軟)鏈接
硬鏈接和軟鏈接(也稱為符號(hào)鏈接)的區(qū)別在于,硬鏈接直接指向?qū)儆谠撐募?inode ,而軟鏈接直接指向一個(gè)目錄項(xiàng),即指向一個(gè)硬鏈接。因?yàn)檐涙溄又赶虻氖且粋€(gè)文件的硬鏈接而非該文件的 inode ,所以它們并不依賴于 inode 號(hào),這使得它們能跨越不同的文件系統(tǒng)、分區(qū)和邏輯卷起作用。
軟鏈接的缺點(diǎn)是,一旦它所指向的硬鏈接被刪除或重命名后,該軟鏈接就失效了。軟鏈接雖然還在,但所指向的硬鏈接已不存在。所幸的是,ls 命令能以紅底白字的方式在其列表中高亮顯示失效的軟鏈接。
實(shí)驗(yàn)項(xiàng)目: 鏈接實(shí)驗(yàn)
我認(rèn)為最容易理解鏈接用法及其差異的方法是動(dòng)手搭建一個(gè)項(xiàng)目。這個(gè)項(xiàng)目應(yīng)以非超級(jí)用戶的身份在一個(gè)空目錄下進(jìn)行。我創(chuàng)建了 ~/temp 目錄做這個(gè)實(shí)驗(yàn),你也可以這么做。這么做可為項(xiàng)目創(chuàng)建一個(gè)安全的環(huán)境且提供一個(gè)新的空目錄讓程序運(yùn)作,如此以來這兒僅存放和程序有關(guān)的文件。
初始工作
首先,在你要進(jìn)行實(shí)驗(yàn)的目錄下為該項(xiàng)目中的任務(wù)創(chuàng)建一個(gè)臨時(shí)目錄,確保當(dāng)前工作目錄(PWD)是你的主目錄,然后鍵入下列命令。
- mkdir temp
使用這個(gè)命令將當(dāng)前工作目錄切換到 ~/temp。
- cd temp
實(shí)驗(yàn)開始,我們需要?jiǎng)?chuàng)建一個(gè)能夠鏈接到的文件,下列命令可完成該工作并向其填充內(nèi)容。
- du -h > main.file.txt
使用 ls -l 長(zhǎng)列表命名確認(rèn)文件正確地創(chuàng)建了。運(yùn)行結(jié)果應(yīng)類似于我的。注意文件大小只有 7 字節(jié),但你的可能會(huì)有 1~2 字節(jié)的變動(dòng)。
- [dboth@david temp]$ ls -l
- total 4
- -rw-rw-r-- 1 dboth dboth 7 Jun 13 07:34 main.file.txt
在列表中,文件模式串后的數(shù)字 1 代表存在于該文件上的硬鏈接數(shù)。現(xiàn)在應(yīng)該是 1 ,因?yàn)槲覀冞€沒有為這個(gè)測(cè)試文件建立任何硬鏈接。
對(duì)硬鏈接進(jìn)行實(shí)驗(yàn)
硬鏈接創(chuàng)建一個(gè)指向同一 inode 的新目錄項(xiàng),當(dāng)為文件添加一個(gè)硬鏈接時(shí),你會(huì)看到鏈接數(shù)目的增加。確保當(dāng)前工作目錄仍為 ~/temp。創(chuàng)建一個(gè)指向 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
目錄中兩個(gè)文件都有兩個(gè)鏈接且大小相同,時(shí)間戳也一樣。這就是有一個(gè) inode 和兩個(gè)硬鏈接(即該文件的目錄項(xiàng))的一個(gè)文件。再建立一個(gè)該文件的硬鏈接,并列出目錄清單內(nèi)容。你可以建立硬鏈接: 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
注意,該目錄下的每個(gè)硬鏈接必須使用不同的名稱,因?yàn)橥荒夸浵碌膬蓚€(gè)文件不能擁有相同的文件名。試著創(chuàng)建一個(gè)和現(xiàn)存鏈接名稱相同的硬鏈接。
- [dboth@david temp]$ ln main.file.txt link2.file.txt
- ln: failed to create hard link 'link2.file.txt': File exists
顯然不行,因?yàn)?link2.file.txt 已經(jīng)存在。目前為止我們只在同一目錄下創(chuàng)建硬鏈接,接著在臨時(shí)目錄的父目錄(你的主目錄)中創(chuàng)建一個(gè)鏈接。
- [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 文件確實(shí)存在于主目錄中,且與該文件在 temp 目錄中的名稱一致。當(dāng)然它們不是不同的文件,它們是同一文件的兩個(gè)鏈接,指向了同一文件的目錄項(xiàng)。為了幫助說明下一點(diǎn),在 temp 目錄中添加一個(gè)非鏈接文件。
- [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 選項(xiàng)查看 inode 的硬鏈接號(hào)和新創(chuàng)建文件的硬鏈接號(hào)。
- [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
注意上面文件模式左邊的數(shù)字 657024 ,這是三個(gè)硬鏈接文件所指的同一文件的 inode 號(hào),你也可以使用 i 選項(xiàng)查看主目錄中所創(chuàng)建的鏈接的節(jié)點(diǎn)號(hào),和該值相同。而那個(gè)只有一個(gè)鏈接的 inode 號(hào)和其他的不同,在你的系統(tǒng)上看到的 inode 號(hào)或許不同于本文中的。
接著改變其中一個(gè)硬鏈接文件的大小。
- [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
現(xiàn)在所有的硬鏈接文件大小都比原來大了,因?yàn)槎鄠€(gè)目錄項(xiàng)都鏈接著同一文件。
下個(gè)實(shí)驗(yàn)在我的電腦上會(huì)出現(xiàn)這樣的結(jié)果,是因?yàn)槲业?/tmp 目錄在一個(gè)獨(dú)立的邏輯卷上。如果你有單獨(dú)的邏輯卷或文件系統(tǒng)在不同的分區(qū)上(如果未使用邏輯卷),確定你是否能訪問那個(gè)分區(qū)或邏輯卷,如果不能,你可以在電腦上掛載一個(gè) U 盤,如果上述方式適合你,你可以進(jìn)行這個(gè)實(shí)驗(yàn)。
試著在 /tmp 目錄中建立一個(gè) ~/temp 目錄下文件的鏈接(或你的文件系統(tǒng)所在的位置)。
- [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
為什么會(huì)出現(xiàn)這個(gè)錯(cuò)誤呢? 原因是每一個(gè)單獨(dú)的可掛載文件系統(tǒng)都有一套自己的 inode 號(hào)。簡(jiǎn)單的通過 inode 號(hào)來跨越整個(gè) Linux 文件系統(tǒng)結(jié)構(gòu)引用一個(gè)文件會(huì)使系統(tǒng)困惑,因?yàn)橄嗤墓?jié)點(diǎn)號(hào)會(huì)存在于每個(gè)已掛載的文件系統(tǒng)中。
有時(shí)你可能會(huì)想找到一個(gè) inode 的所有硬鏈接。你可以使用 ls -li 命令。然后使用 find 命令找到所有硬鏈接的節(jié)點(diǎn)號(hào)。
- [dboth@david temp]$ find . -inum 657024
- ./main.file.txt
- ./link1.file.txt
- ./link2.file.txt
注意 find 命令不能找到所屬該節(jié)點(diǎn)的四個(gè)硬鏈接,因?yàn)槲覀冊(cè)?~/temp 目錄中查找。 find 命令僅在當(dāng)前工作目錄及其子目錄中查找文件。要找到所有的硬鏈接,我們可以使用下列命令,指定你的主目錄作為起始查找條件。
- [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
如果你是非超級(jí)用戶,沒有權(quán)限,可能會(huì)看到錯(cuò)誤信息。這個(gè)命令也使用了 -samefile 選項(xiàng)而不是指定文件的節(jié)點(diǎn)號(hào)。這個(gè)效果和使用 inode 號(hào)一樣且更容易,如果你知道其中一個(gè)硬鏈接名稱的話。
對(duì)軟鏈接進(jìn)行實(shí)驗(yàn)
如你剛才看到的,不能跨越文件系統(tǒng)邊界創(chuàng)建硬鏈接,即在邏輯卷或文件系統(tǒng)中從一個(gè)文件系統(tǒng)到另一個(gè)文件系統(tǒng)。軟鏈接給出了這個(gè)問題的解決方案。雖然它們可以達(dá)到相同的目的,但它們是非常不同的,知道這些差異是很重要的。
讓我們?cè)?~/temp 目錄中創(chuàng)建一個(gè)符號(hào)鏈接來開始我們的探索。
- [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
擁有節(jié)點(diǎn)號(hào) 657024 的那些硬鏈接沒有變化,且硬鏈接的數(shù)目也沒有變化。新創(chuàng)建的符號(hào)鏈接有不同的 inode 號(hào) 658270。 名為 link3.file.txt 的軟鏈接指向了 link2.file.txt 文件。使用 cat 命令查看 link3.file.txt 文件的內(nèi)容。符號(hào)鏈接的 inode 信息以字母 l (小寫字母 l)開頭,意味著這個(gè)文件實(shí)際是個(gè)符號(hào)鏈接。
上例中軟鏈接文件 link3.file.txt 的大小只有 14 字節(jié)。這是文本內(nèi)容 link3.file.txt 的大小,即該目錄項(xiàng)的實(shí)際內(nèi)容。目錄項(xiàng) link3.file.txt 并不指向一個(gè) inode ;它指向了另一個(gè)目錄項(xiàng),這在跨越文件系統(tǒng)建立鏈接時(shí)很有幫助。現(xiàn)在試著創(chuàng)建一個(gè)軟鏈接,之前在 /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
刪除鏈接
當(dāng)你刪除硬鏈接或硬鏈接所指的文件時(shí),需要考慮一些問題。
首先,讓我們刪除硬鏈接文件 main.file.txt。注意指向 inode 的每個(gè)目錄項(xiàng)就是一個(gè)硬鏈接。
- [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 是該文件被創(chuàng)建時(shí)所創(chuàng)建的***個(gè)硬鏈接。現(xiàn)在刪除它,仍然保留著原始文件和硬盤上的數(shù)據(jù)以及所有剩余的硬鏈接。要?jiǎng)h除原始文件,你必須刪除它的所有硬鏈接。
現(xiàn)在刪除 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
注意軟鏈接的變化。刪除軟鏈接所指的硬鏈接會(huì)使該軟鏈接失效。在我的系統(tǒng)中,斷開的鏈接用顏色高亮顯示,目標(biāo)的硬鏈接會(huì)閃爍顯示。如果需要修復(fù)這個(gè)損壞的軟鏈接,你需要在同一目錄下建立一個(gè)和舊鏈接相同名字的硬鏈接,只要不是所有硬鏈接都已刪除就行。您還可以重新創(chuàng)建鏈接本身,鏈接保持相同的名稱,但指向剩余的硬鏈接中的一個(gè)。當(dāng)然如果軟鏈接不再需要,可以使用 rm 命令刪除它們。
unlink 命令在刪除文件和鏈接時(shí)也有用。它非常簡(jiǎn)單且沒有選項(xiàng),就像 rm 命令一樣。然而,它更準(zhǔn)確地反映了刪除的基本過程,因?yàn)樗鼊h除了目錄項(xiàng)與被刪除文件的鏈接。
寫在***
我用過這兩種類型的鏈接很長(zhǎng)一段時(shí)間后,我開始了解它們的能力和特質(zhì)。我為我所教的 Linux 課程編寫了一個(gè)實(shí)驗(yàn)室項(xiàng)目,以充分理解鏈接是如何工作的,并且我希望增進(jìn)你的理解。
(題圖: Paul Lewin,Opensource.com 修改。 CC BY-SA 2.0)






