最近,看到很多文章都在介紹 Linux 中的文件系統(tǒng),其中就包括:inode 節(jié)點(diǎn)、軟鏈接、硬鏈接等重要的概念。
于是就有小伙伴私信問(wèn)我:這些概念我都懂,但是我能利用他們來(lái)完成什么工作呢?
或者說(shuō),在哪些情況下,軟鏈接和硬鏈接能夠提供提供更好的解決方案呢?
這篇文章我們就來(lái)簡(jiǎn)單梳理一下,軟鏈接和硬鏈接的幾個(gè)使用場(chǎng)景。
什么是索引節(jié)點(diǎn)
什么是硬鏈接
什么是軟鏈接
軟鏈接應(yīng)用之:靈活切換不同版本的目標(biāo)程序
軟鏈接應(yīng)用之:動(dòng)態(tài)庫(kù)版本管理
軟鏈接應(yīng)用之:快捷方式
硬鏈接之應(yīng)用:從不同角度對(duì)文件進(jìn)行分類
硬鏈接應(yīng)用之:文件多人共享
硬鏈接之應(yīng)用:文件備份
文件和索引節(jié)點(diǎn) inode
在 Linux 系統(tǒng)中,我們可以把一個(gè)文件看做 3 個(gè)組成部分:
文件名:從用戶角度來(lái)描述一個(gè)文件;
文件內(nèi)容:也就是文件中存儲(chǔ)的那些數(shù)據(jù);
文件的描述信息:文件的類型、所有者、創(chuàng)建時(shí)間等等,可以稱之為元信息;
可以簡(jiǎn)單的做一個(gè)類比:
文件本身的內(nèi)容,可以看做一個(gè)實(shí)實(shí)在在的人。
文件的描述信息,可以看做是派出所里的戶籍卡。
戶籍卡上記錄了一個(gè)人的姓名、年齡、住址等信息,警察叔叔通過(guò)這個(gè)戶籍卡,就知道這個(gè)人的一切描述信息,除了你腦袋里的知識(shí)。
回到計(jì)算機(jī)中,文件的所有信息都需要存儲(chǔ)在硬盤(pán)上,因此就要對(duì)硬盤(pán)進(jìn)行區(qū)域劃分:不同的區(qū)域存儲(chǔ)不同類型的數(shù)據(jù),這就是文件系統(tǒng)的重要作用。
在 Linux 系統(tǒng)使用的 ext2/ext3 文件系統(tǒng)中,從硬盤(pán)上劃分一塊區(qū)域,用來(lái)存放文件本身的內(nèi)容(數(shù)據(jù)),這塊區(qū)域按照一個(gè)最小單位:塊(block)來(lái)進(jìn)行劃分。
然后從硬盤(pán)上劃分出另一塊區(qū)域,專門(mén)用來(lái)存放所有文件的描述信息。
每一個(gè)文件的描述信息,都用一個(gè)名為索引節(jié)點(diǎn)(inode)的數(shù)據(jù)結(jié)構(gòu)來(lái)表示,所有文件的 inode 就統(tǒng)一放在這塊硬盤(pán)區(qū)域中。
就像戶籍卡上記錄了一個(gè)人的住址一樣,一個(gè)文件的索引節(jié)點(diǎn)(inode)中,也記錄了這個(gè)文件的所有描述信息,包括:文件類型、所有者、創(chuàng)建時(shí)間等待,當(dāng)然也包括文件內(nèi)容存儲(chǔ)在硬盤(pán)的哪些塊(block)中。
當(dāng)我們調(diào)用打開(kāi)文件 API 函數(shù)的時(shí)候,操作系統(tǒng)首先根據(jù)傳入的文件路徑,找到這個(gè)文件的 inode,然后進(jìn)行一系列的權(quán)限檢查操作,最后從 inode 中獲得這個(gè)文件的內(nèi)容存儲(chǔ)在哪些塊(block)中,從而可以對(duì)文件的內(nèi)容進(jìn)行讀取、寫(xiě)入操作。
文件名稱只是給我們用戶來(lái)使用的,操作系統(tǒng)只是通過(guò) inode 節(jié)點(diǎn),來(lái)對(duì)文件進(jìn)行管理的。
當(dāng)我們創(chuàng)建一個(gè)新文件的時(shí)候,就同時(shí)創(chuàng)建了這個(gè)文件對(duì)應(yīng)的 inode 節(jié)點(diǎn)。
當(dāng)我們刪除一個(gè)文件的時(shí)候,就同時(shí)刪除了這個(gè)文件對(duì)應(yīng)的 inode 節(jié)點(diǎn)。
此時(shí),文件本身內(nèi)容所在的那個(gè)塊中,數(shù)據(jù)并不會(huì)被抹除掉,因此有些數(shù)據(jù)恢復(fù)軟件就是利用這個(gè)特點(diǎn)來(lái)進(jìn)行數(shù)據(jù)找回。
一句話總結(jié):索引節(jié)點(diǎn)(inode)就像戶籍卡,操作系統(tǒng)通過(guò) inode 來(lái)管理所有的文件。
硬鏈接
剛才已經(jīng)說(shuō)到,每一個(gè)文件都對(duì)應(yīng)一個(gè) inode 節(jié)點(diǎn)。
例如有一個(gè)文件 a.txt,文件內(nèi)容長(zhǎng)度是 1024 個(gè)字節(jié),存放在硬盤(pán)上的某個(gè)塊(block)中,假設(shè)就是第 10000 個(gè)塊吧。
那么這個(gè)文件對(duì)應(yīng)的 inode 節(jié)點(diǎn)中,就會(huì)把 10000 這個(gè)塊記錄下來(lái)。
同時(shí),它還有一個(gè) links 字段,表示:當(dāng)前這個(gè) inode 對(duì)應(yīng)一個(gè)文件,此時(shí) inode.links 的值為 1。
此時(shí),如果我們用另一個(gè)文件名 a_h(yuǎn)ard_link.txt,也來(lái)表示 a.txt 這個(gè)文件。
也就是說(shuō):雖然我們用了 2 個(gè)文件名稱,但是本質(zhì)上指向同一個(gè)文件,內(nèi)容都指向第 10000 個(gè)塊中存儲(chǔ)的文件內(nèi)容。
Linux 系統(tǒng)中提供了硬鏈接來(lái)支持這樣的目的,它僅僅是把 inode 節(jié)點(diǎn)中的 links 字段的值 加1 即可,也就是 inode.links 的值變成了 2。
硬鏈接的操作指令是:
$ ln a.txt b.txt
基于硬鏈接,用戶就可以用不同的文件名來(lái)訪問(wèn)同一個(gè)文件,所有的操作最終修改的都是同一個(gè)文件。
如果僅僅從用戶的角度來(lái)看,好像我們是在操作不同的文件,但是這些文件具有自動(dòng)同步的功能。
這個(gè)行為有點(diǎn)類似于網(wǎng)盤(pán):
在云存儲(chǔ)中有一個(gè)文件 hello.txt,然后我有兩臺(tái)電腦 A 和 B,這兩臺(tái)電腦會(huì)把云端的文件 hello.txt 都創(chuàng)建一個(gè)鏡像文件在本地,就好像這個(gè)文件就在自己的硬盤(pán)上一樣。
當(dāng)我在電腦 A 上操作 hello.txt 時(shí),電腦 B 中的同名文件會(huì)自動(dòng)更新。
因此,從行為上來(lái)看,硬鏈接就相當(dāng)于是:文件拷貝 + 自動(dòng)同步。
再來(lái)看一下硬鏈接文件的刪除操作。
在執(zhí)行 $ ln a.txt a_h(yuǎn)ard_link.txt 指令之后,該文件對(duì)應(yīng)的 inode 節(jié)點(diǎn)中,links 的值為 2。
如果我們刪除 a.txt,操作系統(tǒng)會(huì)把該文件對(duì)應(yīng)的 inode 中的 links 值減1,結(jié)果為 1,操作系統(tǒng)發(fā)現(xiàn)不為 0,因此并不會(huì)刪掉這個(gè) inode。
如果我們?cè)賱h除 a_h(yuǎn)ard_link.txt,操作系統(tǒng)再次執(zhí)行 inode.links 減1 動(dòng)作,發(fā)現(xiàn)值變成了 0,于是就把這個(gè) inode 刪除了,于是這個(gè)文件就徹底不存在了。
這就相當(dāng)于把一個(gè)人的戶籍卡給注銷掉了,從戶籍管理角度看,這個(gè)人就不存在了。即使存在,也是一個(gè)黑戶。
硬鏈接存在 2 個(gè)限制:
不允許用戶給目錄創(chuàng)建硬鏈接,即:用戶不可以,操作系統(tǒng)可以(想一下每個(gè)目錄下的 . 和 ..);
只有在同一個(gè)文件系統(tǒng)中的文件,才能創(chuàng)建硬鏈接,也就是說(shuō):不能跨文件系統(tǒng);
軟鏈接
為了克服硬鏈接的 2 個(gè)限制,軟鏈接被引入進(jìn)來(lái)了。
軟鏈接也叫符號(hào)鏈接,它是一個(gè)獨(dú)立的文件。
軟鏈接文件的內(nèi)容是一個(gè)文本字符串,存儲(chǔ)的是目標(biāo)文件(即:鏈接到的文件)的路徑名。
這個(gè)路徑名可以指向任意一個(gè)文件系統(tǒng)的任意文件或者目錄,甚至可以指向一個(gè)不存在的文件。
與創(chuàng)建硬鏈接不同的是:當(dāng)我們創(chuàng)建了一個(gè)軟鏈接之后,操作系統(tǒng)會(huì)創(chuàng)建一個(gè)新的 inode 來(lái)表示這個(gè)軟鏈接文件。
例如有一個(gè)文件 a.txt,我們創(chuàng)建一個(gè)軟鏈接 a_soft_link.txt 來(lái)指向它:
$ ln -s a.txt a_soft_link.txt
此時(shí),a.txt 和 a_soft_link.txt 各自都有自己的 inode 節(jié)點(diǎn)。
圖中的綠色虛線,就表示軟鏈接文件中的文件路徑。
正因?yàn)檐涙溄游募写鎯?chǔ)的僅僅是目標(biāo)文件的路徑字符串,所以可以表示任意一個(gè)文件系統(tǒng)中的文件,或者是目錄。
當(dāng)我們打開(kāi)文件軟鏈接 a_soft_link.txt 時(shí),操作系統(tǒng)從 a_soft_link.txt 對(duì)應(yīng)的 inode 數(shù)據(jù)結(jié)構(gòu)中發(fā)現(xiàn):這是一個(gè)軟鏈接文件。
于是操作系統(tǒng)就根據(jù)其中的路徑信息,找到 a.txt 的 inode 節(jié)點(diǎn),從而對(duì)最終的目標(biāo)文件進(jìn)行操作。
再來(lái)看一下軟鏈接文件的刪除操作。
如果我們把目標(biāo)文件 a.txt 刪除掉之后,inode 節(jié)點(diǎn)會(huì)被刪除掉,就相當(dāng)于它的戶籍卡被注銷掉了。
此時(shí)再次打開(kāi)軟鏈接 a_soft_link.txt 時(shí),雖然其中的路徑信息仍然存在,但是系統(tǒng)此時(shí)卻找不到 a.txt 對(duì)應(yīng)的 inode 節(jié)點(diǎn)了。
因此,軟鏈接就類似于與 Windows 系統(tǒng)中的快捷方式。
當(dāng)真正的目標(biāo)文件被刪除之后,快捷方式也就沒(méi)有存在的意義了。
軟鏈接應(yīng)用之:靈活切換不同版本的目標(biāo)程序
在開(kāi)發(fā)的過(guò)程中,對(duì)于同一個(gè)工具軟件,可能要安裝多個(gè)不同的版本,例如:Python2 和 Python3, JDK8 和 JDK9 等等。
此時(shí)就可以通過(guò)軟鏈接來(lái)指定當(dāng)前使用哪個(gè)版本。例如在我的電腦中:
$ ll -l /usr/bin/python*
lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python -> python2.7*
lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python2 -> python2.7*
-rwxr-xr-x 1 root root 3492624 3月 2 04:47 /usr/bin/python2.7*
lrwxrwxrwx 1 root root 9 12月 31 08:19 /usr/bin/python3 -> python3.5*
-rwxr-xr-x 2 root root 4456208 1月 27 02:48 /usr/bin/python3.5*
當(dāng)在終端窗口中輸入:python 時(shí),啟動(dòng)的是 python2.7 版本。
如果有一天我需要使用 python3.5 版本,只需要把軟鏈接 python 指向 python3.5 即可。
軟鏈接應(yīng)用之:動(dòng)態(tài)庫(kù)版本管理
在 Linux 系統(tǒng)的動(dòng)態(tài)庫(kù)版本管理中,有一個(gè) SONAME 的概念。
我們?cè)诰幾g一個(gè)動(dòng)態(tài)鏈接庫(kù)時(shí),一般使用如下編譯命令:
$ gcc -fPIC -shared -o libhello.so hello.c
在使用這個(gè)動(dòng)態(tài)庫(kù)時(shí),需要鏈接這個(gè)庫(kù):-llibhello。
簡(jiǎn)單的 demo 可以這么來(lái)寫(xiě),但是如果遇到一些比較大的項(xiàng)目,需要執(zhí)行嚴(yán)格的版本管理,那應(yīng)該怎么來(lái)操作呢?
Linux 系統(tǒng)已經(jīng)為我們想到了問(wèn)題的解決方案,利用 SO-NAME。
首先,在編譯動(dòng)態(tài)鏈接庫(kù)文件時(shí),就指定產(chǎn)生 SO-NAME,它會(huì)被存儲(chǔ)在動(dòng)態(tài)鏈接庫(kù) ELF 文件中。
我們來(lái)直接看一個(gè)優(yōu)秀的開(kāi)源工具 libevent 的例子:
$ ll /usr/lib/libevent-2.1.so*
lrwxrwxrwx 1 root root 17 Jul 27 2020 /usr/lib/libevent-2.1.so -> libevent-2.1.so.7
lrwxrwxrwx 1 root root 21 Jul 27 2020 /usr/lib/libevent-2.1.so.7 -> libevent-2.1.so.7.0.1
-rw-r--r-- 1 root root 412016 Jul 27 2020 /usr/lib/libevent-2.1.so.7.0.1
此時(shí)使用 readelf 命令來(lái)查看生成的動(dòng)態(tài)庫(kù)文件 libevent-2.1.so.7.0.1:
$ readelf -a libevent-2.1.so.7.0.1 | grep SONAME
0x000000000000000e (SONAME) Library soname: [libevent-2.1.so.7]
它這么做有什么好處呢?
Linux 系統(tǒng)在查找動(dòng)態(tài)鏈接庫(kù)文件時(shí),會(huì)到下面這 3 個(gè)默認(rèn)目錄下查找(當(dāng)然然還有其他目錄,比如:當(dāng)前目錄,LD_LIBRARY_PATH 指定的目錄)
/lib: 存放操作系統(tǒng)最關(guān)鍵和基礎(chǔ)的庫(kù)文件;
/usr/lib: 存放一些非系統(tǒng)運(yùn)行時(shí)所需要的關(guān)鍵庫(kù)文件;
/usr/local/lib: 存放用戶自己安裝的一些第三方庫(kù)文件;
系統(tǒng)中安裝的所有動(dòng)態(tài)鏈接庫(kù),借助 ldconfig 這個(gè)程序,會(huì)自動(dòng)的創(chuàng)建、更新或者刪除對(duì)應(yīng)的 SONAME(它是一個(gè)軟鏈接,鏈接到 實(shí)際的庫(kù)文件),并把這些 SONAME 匯總到一個(gè)文件 /etc/ld.so.cache 中緩存起來(lái)。
這樣,當(dāng)動(dòng)態(tài)庫(kù)加載器查找動(dòng)態(tài)庫(kù)文件時(shí),就可以直接在這個(gè)緩存文件中進(jìn)行查找,加快了動(dòng)態(tài)庫(kù)的查找速度。
軟鏈接應(yīng)用之:快捷方式
利用軟鏈接的快捷方式功能就比較好理解了,想一想:我們?yōu)槭裁丛?Windows 的桌面上創(chuàng)建很多軟件的快捷方式啊?
在 Linux 中同樣如此!
比如:最近一段時(shí)間的工作,每次都要打開(kāi)一個(gè)路徑很深的文件。
如果在資源管理器中,一層一層的點(diǎn)擊鼠標(biāo),是不是比較浪費(fèi)時(shí)間。
此時(shí),就可以在桌面上創(chuàng)建一個(gè)軟鏈接,每次直接雙擊就打開(kāi)所鏈接的目標(biāo)文件了。
硬鏈接之應(yīng)用:從不同角度對(duì)文件進(jìn)行分類
比如我有一個(gè)文件夾,存儲(chǔ)了10 個(gè)G的照片。
這些照片中的人物、拍照地點(diǎn)、拍照時(shí)間都是不一樣的。
現(xiàn)在,我既想根據(jù)照片中的人物進(jìn)行分類,也想根據(jù)拍照地點(diǎn)進(jìn)行分類,還想根據(jù)拍照時(shí)間進(jìn)行分類,那該怎么辦?
因?yàn)橐粡堈掌赡芡瑫r(shí)屬于多個(gè)不同的分類,難道每個(gè)分類中都復(fù)制一張照片?這樣也太浪費(fèi)硬盤(pán)空間了!
解決方案是:
所有的照片仍舊放在一個(gè)總的文件夾中,然后創(chuàng)建不同的分類文件夾,在每個(gè)分類文件夾中,創(chuàng)建硬鏈接到目標(biāo)照片文件。
這樣的話,不僅對(duì)照片進(jìn)行了分類,而且一點(diǎn)都不占用硬盤(pán)空間。
硬鏈接應(yīng)用之:文件多人共享
當(dāng)很多人同時(shí)對(duì)同一個(gè)文件進(jìn)行維護(hù)的時(shí)候,如果大家都直接操作這個(gè)文件,萬(wàn)一不小心把文件刪除了,大家就都玩完了!
此時(shí),可以在每個(gè)人自己的私人目錄中,創(chuàng)建一個(gè)硬鏈接。
每次只需要對(duì)這個(gè)硬鏈接文件進(jìn)行操作,所有的改動(dòng)會(huì)自動(dòng)同步到目標(biāo)文件中。
由于每個(gè)人都是操作硬鏈接文件,即使不小心刪除了,也不會(huì)導(dǎo)致文件的丟失。
因?yàn)閯h除硬鏈接文件,僅僅是把該文件的 inode 節(jié)點(diǎn)中的 links 值減 1 而已,只要不為 0,就不會(huì)真正的刪除文件。
硬鏈接之應(yīng)用:文件備份
一些小伙伴有定期備份文件、清理文件的好習(xí)慣。
在備份的時(shí)候,如果是實(shí)實(shí)在在的拷貝一份,那真的是太浪費(fèi)磁盤(pán)空間,特別是對(duì)于我這種只有 256G 硬盤(pán)空間的筆記本。
此時(shí),就可以利用硬鏈接功能,既實(shí)現(xiàn)文件備份的目的,又節(jié)省了大量的硬盤(pán)空間,一舉兩得!
很多備份工具利用的就是硬鏈接的功能,包括 git 工具,當(dāng)克隆本地的一個(gè)倉(cāng)庫(kù)時(shí),執(zhí)行 clone 指令:
git clone --reference <repository>
git 并不會(huì)把倉(cāng)庫(kù)中的所有文件拷貝到本地,而僅僅是創(chuàng)建文件的硬鏈接,幾乎是零拷貝!
-
Linux
+關(guān)注
關(guān)注
87文章
11312瀏覽量
209711 -
索引
+關(guān)注
關(guān)注
0文章
59瀏覽量
10482 -
軟鏈接
+關(guān)注
關(guān)注
0文章
8瀏覽量
2230
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論