Linux下的鏈接文件詳解

幾個(gè)基本概念
Linux下的鏈接文件可以分為硬鏈接(hard link)與軟鏈接(soft link)。要理解它們,必須先要理解幾個(gè)基本概念。
inode
文件除了純數(shù)據(jù)本身之外,還必須包含有對(duì)這些純數(shù)據(jù)的管理信息,如文件名、訪問(wèn)權(quán)限、文件的屬主以及該文件的數(shù)據(jù)所對(duì)應(yīng)的磁盤(pán)塊等等,這些管理信息稱(chēng)之為元數(shù)據(jù)(mata data),保存在文件的inode節(jié)點(diǎn)之中。我們可以通過(guò)stat命令查看一個(gè)文件的inode信息:
$ stat /etc/passwd File: "/etc/passwd" Size: 936 Blocks: 8 IO Block: 4096 普通文件 Device: fd00h/64768d Inode: 137143 Links: 1 Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root) Access: 2016-08-05 23:01:39.905999995 +0800 Modify: 2016-07-15 16:36:12.802999997 +0800 Change: 2016-07-15 16:36:12.809000014 +0800 $ ls -l /etc/passwd -rw-r--r-- 1 root root 936 7月 15 16:36 /etc/passwd
這里我們查看了/etc/passwd文件的元數(shù)據(jù)信息。ls -l命令也會(huì)列出一些文件的元數(shù)據(jù)信息(由左至右分別為:權(quán)限、硬鏈接數(shù)、屬主、屬組、文件大小、最近更改時(shí)間、文件名),但相比之下,stat命令輸出的信息更加完整。我們注意到,stat輸出的信息中,文件有三個(gè)時(shí)間戳:最近訪問(wèn)、最近更改和最近改動(dòng),對(duì)應(yīng)于英文分別為Access、Modify和Change。 Access time比較好理解,當(dāng)每次訪問(wèn)這個(gè)文件的數(shù)據(jù)(注意,不是元數(shù)據(jù)),這個(gè)時(shí)間就會(huì)更新。比如用cat或者more命令讀取文件內(nèi)容時(shí),會(huì)更新access time,而用ls或者stat命令,由于只是訪問(wèn)了文件的inode,所以不會(huì)更新access time值。Modify time是文件數(shù)據(jù)最后一次被修改時(shí)間,比如用vim編輯文件后保存文件,此時(shí)就會(huì)更新該文件modify time。Change time是文件元數(shù)據(jù)(即inode)最后一次被修改的時(shí)間,比如用chown命令修改文件的屬主,此時(shí)就會(huì)更新文件的change time。
其實(shí)最初當(dāng)我們創(chuàng)建分區(qū)并用mkfs.ext4等命令創(chuàng)建文系統(tǒng)的時(shí)候,就已經(jīng)在文件系統(tǒng)的固定區(qū)域保留了inode節(jié)點(diǎn)區(qū)。我們可以通過(guò)df -i命令查看某文件系統(tǒng)inode節(jié)點(diǎn)區(qū)域的大小及使用情況:
# df -ih /dev/mapper/pdc_bcfaffjfaj2 文件系統(tǒng) Inode 已用(I) 可用(I) 已用(I)% 掛載點(diǎn) /dev/mapper/pdc_bcfaffjfaj2 18M 127K 18M 1% /home
可以看到,在筆者的Linux Mint17.3系統(tǒng)中,分區(qū)/dev/mapper/pdc_bcfaffjfaj2共保留了18M的inode區(qū)域,這個(gè)區(qū)域目前已經(jīng)使用了127K。有沒(méi)有可能出現(xiàn)某分區(qū)尚有空間而inode區(qū)域已用完的情況呢?有的。當(dāng)小文件特別多的時(shí)候就會(huì)出現(xiàn)這種情況!這個(gè)時(shí)候即使文件系統(tǒng)還有空間可用,但我們?nèi)匀粺o(wú)法繼續(xù)在這個(gè)文件系統(tǒng)內(nèi)創(chuàng)建新的文件了。那假如在我的應(yīng)用環(huán)境中真的小文件非常多該怎么辦?其實(shí)我們?cè)诮xt4文件系統(tǒng)時(shí)候是可以手動(dòng)指定inode區(qū)域所占的比例大小的,可以man mkfs.ext4查看相關(guān)的參數(shù)和選項(xiàng),這里不再詳述。
剛才用stat查看文件的inode信息時(shí),我們看到輸出的信息中有一行Inode: 137143,這個(gè)是/etc/passwd文件的inode號(hào)。每個(gè)inode都有一個(gè)全文件系統(tǒng)唯一的inode號(hào),操作系統(tǒng)內(nèi)核正是通過(guò)inode號(hào)而非文件名來(lái)識(shí)別不同的文件。文件名僅僅是為了方便用戶使用而已,內(nèi)核是通過(guò)文件名找到inode,然后通過(guò)inode訪問(wèn)實(shí)際文件數(shù)據(jù)的。有沒(méi)有可能有多個(gè)文件名對(duì)應(yīng)于同一個(gè)inode呢?有的,這樣就產(chǎn)生了所謂硬鏈接文件。
dentry
雖然每個(gè)文件對(duì)應(yīng)了唯一的inode號(hào),但inode號(hào)是雜亂而毫無(wú)意義的,不方面用戶記憶和使用,我們希望對(duì)每個(gè)文件取一個(gè)有意義的文件名。現(xiàn)代文件系統(tǒng)提供的一個(gè)基本功能是按名存取,所以我們還需要建立文件名到inode號(hào)的對(duì)應(yīng),這就引出了目錄項(xiàng)(directory entry即dentry)的概念。在Linux文件系統(tǒng)中有一類(lèi)特殊的文件稱(chēng)為“目錄”,目錄就保存了該目錄下所有文件的文件名到inode號(hào)的對(duì)應(yīng)關(guān)系,這里的每個(gè)對(duì)應(yīng)關(guān)系就稱(chēng)為一個(gè)dentry。而Linux把所有的文件和目錄構(gòu)建成了一個(gè)倒立的樹(shù)狀結(jié)構(gòu),這樣,我們只要確定了根目錄的inode號(hào),就可以對(duì)整個(gè)文件系統(tǒng)進(jìn)行按名存取了。
hard link
硬鏈接的實(shí)質(zhì)是現(xiàn)有文件在目錄樹(shù)中的另一個(gè)入口。也就是說(shuō),硬鏈接與原文件是分居于不同或相同目錄下的的dentry而已,它們指向同一個(gè)inode,對(duì)應(yīng)于相同的磁盤(pán)數(shù)據(jù)塊(data block),具有相同的訪問(wèn)權(quán)限、屬性等。簡(jiǎn)而言之,硬鏈接其實(shí)就是給現(xiàn)有的文件起了一個(gè)別名。如果把文件系統(tǒng)比喻成一本書(shū)的話,硬鏈接就是在書(shū)本的目錄中,有兩個(gè)目錄項(xiàng)指向了同一頁(yè)碼的同一章節(jié)。
硬鏈接的優(yōu)點(diǎn)是幾乎不占磁盤(pán)空間(因?yàn)閮H僅是增加了一個(gè)目錄項(xiàng)而已),但是這一優(yōu)點(diǎn)相對(duì)于軟鏈接其實(shí)并不明顯(因?yàn)檐涙溄诱加玫拇疟P(pán)空間也很少)。另外,硬鏈接有以下一些局限:1、不能跨文件系統(tǒng)創(chuàng)建硬鏈接。原因很簡(jiǎn)單,inode號(hào)只有在一個(gè)文件系統(tǒng)內(nèi)才能保證是唯一的,如果跨越文件系統(tǒng)則inode號(hào)就可能重復(fù)。2、不能對(duì)目錄創(chuàng)建硬鏈接。原因我在稍后解釋。正因?yàn)橛叉溄拥倪@些局限,加之軟鏈接更加易于管理,所以軟鏈接更加常用。這一點(diǎn)在本文中舉的例子也可以看出,幾乎都是軟鏈接的例子。
soft link
軟鏈接又稱(chēng)為符號(hào)鏈接(symbolic link),簡(jiǎn)寫(xiě)為“symlink”。與硬鏈接僅僅是一個(gè)目錄項(xiàng)不同,軟連接本身也是文件,不過(guò)這個(gè)文件的內(nèi)容是另一個(gè)文件名的指針。當(dāng)Linux訪問(wèn)軟鏈接時(shí),它會(huì)循著指針找出含有實(shí)際數(shù)據(jù)的目標(biāo)文件。我們還以書(shū)本來(lái)打比方,軟鏈接是書(shū)本里的某一章節(jié),不過(guò)這一章節(jié)什么內(nèi)容都沒(méi)有,只有一行字“轉(zhuǎn)某某章某某頁(yè)”。
軟鏈接可以跨越文件系統(tǒng)指向另一個(gè)分區(qū)的文件,甚至可以跨越主機(jī)指向遠(yuǎn)程主機(jī)的一個(gè)文件,也可以指向目錄。在ls -l輸出的文件列表中,第一個(gè)字段有“l”字樣者表示該文件是符號(hào)鏈接。
$ ls -l total 0 lrwxrwxrwx 1 wjm wjm 11 Aug 10 00:51 hh -> /etc/passwd
我們看到,軟鏈接的權(quán)限為777,即所有權(quán)限都是開(kāi)放的,實(shí)際上你也無(wú)法使用chmod命令修改其權(quán)限,但是實(shí)際文件的保護(hù)權(quán)限仍然起作用。
另外,符號(hào)鏈接可以指向不存在的文件(可能是原來(lái)指向的文件被刪除了,或者指向的文件系統(tǒng)尚未掛載,或者最初建立該符號(hào)鏈接的時(shí)候就指向了一個(gè)不存在的文件等等),我們稱(chēng)這種狀態(tài)為“斷裂”(broken)。與之相對(duì)的是,硬鏈接是不能指向一個(gè)不存在的文件的。
使用鏈接有何好處?
我們?cè)诖丝偨Y(jié)使用鏈接文件的以下幾個(gè)的好處:
保持軟件的兼容性
例如,在RHEL6中我們看下面這條命令的輸出:
$ ls -l /bin/sh lrwxrwxrwx. 1 root root 4 Jul 15 11:41 /bin/sh -> bash
我們看到,/bin/sh文件其實(shí)是一個(gè)指向/bin/bash的符號(hào)鏈接。為什么要這樣設(shè)計(jì)?因?yàn)閹缀跛械膕hell script的第一行都是下面這樣:
#!/bin/sh
“#!”符號(hào)表示該行指定該腳本所用的解釋器。#!/bin/sh表示使用Bourne Shell作為解釋器,這是一個(gè)早期的Shell。在現(xiàn)代的Linux發(fā)行版中通常采用Bourne Again Shell即bash,bash是對(duì)sh的改進(jìn)和增強(qiáng),而早期的Bourne Shell在系統(tǒng)的中根本不存在。為了能夠順利的運(yùn)行腳本而不必修改shell script,我們只需要?jiǎng)?chuàng)建一個(gè)軟鏈接/bin/sh讓其指向/bin/bash。如此一來(lái),就可以讓bash來(lái)解釋原本針對(duì)Bourne Shell編寫(xiě)的腳本了。
方便軟件的使用
比如我們安裝了一個(gè)大型軟件Matlab,它可能默認(rèn)安裝在/usr/opt/Matlab目錄下,它的可執(zhí)行文件位置在/usr/opt/Matlab/bin目錄下,除非你在這個(gè)路徑加入到PATH環(huán)境變量里,否則每次運(yùn)行這個(gè)軟件你都需要輸入一長(zhǎng)串的路徑很不方便。你還可以這樣做:
$ ln -s /usr/opt/Matlab/bin/matlab ~/bin/matlab
通過(guò)在你的~/bin下創(chuàng)建一個(gè)符號(hào)鏈接(~/bin系統(tǒng)默認(rèn)已經(jīng)包含在PATH環(huán)境變量里的),今后在命令行下無(wú)需輸入完整路徑,只需輸入matlab即可。
維持舊的操作習(xí)慣
比如在SuSE中,啟動(dòng)腳本的位置是放在/etc/init.d目錄下,而在RedHat的發(fā)行版中,是放在/etc/init.d/rc.d目錄下。為了避免因?yàn)閺腟uSE轉(zhuǎn)換到RedHat系統(tǒng)而導(dǎo)致管理員找不到位置的情況,我們可以創(chuàng)建一個(gè)符號(hào)鏈接/etc/init.d使其指向/etc/init.d/rc.d即可。事實(shí)上,RedHat發(fā)行版也正是這樣做的:
$ ls -ld /etc/init.d/ lrwxrwxrwx. 1 root root 11 Jul 15 11:41 init.d -> rc.d/init.d
方便系統(tǒng)管理
最讓人印象深刻的一個(gè)例子應(yīng)該是/etc/rc.d/rcX.d目錄下的符號(hào)鏈接了(X為0~7數(shù)字)。
$ ls -l /etc/rc.d/ total 60 drwxr-xr-x. 2 root root 4096 Jul 15 16:36 init.d -rwxr-xr-x. 1 root root 2617 Nov 23 2013 rc drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc0.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc1.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc2.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc3.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc4.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc5.d drwxr-xr-x. 2 root root 4096 Jul 15 16:36 rc6.d -rwxr-xr-x. 1 root root 220 Nov 23 2013 rc.local -rwxr-xr-x. 1 root root 19688 Nov 23 2013 rc.sysinit
在init.d/目錄下有許多用于啟動(dòng)、停止系統(tǒng)服務(wù)的腳本,如sshd、crond等。這些腳本可以接受一個(gè)參數(shù),代表要啟動(dòng)(start)或停止(stop)服務(wù)。為了決定在某個(gè)運(yùn)行級(jí)別運(yùn)行哪些腳本及傳遞給這些腳本哪些參數(shù),RedHat設(shè)計(jì)了一個(gè)額外的目錄機(jī)制,即rc0.d到rc6.d的7個(gè)目錄,每個(gè)目錄對(duì)應(yīng)一個(gè)運(yùn)行級(jí)別。如果在某運(yùn)行級(jí)別下需要啟動(dòng)某服務(wù)或者需要停止某服務(wù),就在對(duì)應(yīng)的rcX.d目錄下建立一個(gè)符號(hào)鏈接,指向init.d/目錄下的腳本。如:
$ ls -l /etc/rc.d/rc3.d total 0 lrwxrwxrwx. 1 root root 19 Jul 15 11:42 K10saslauthd -> ../init.d/saslauthd lrwxrwxrwx. 1 root root 20 Jul 15 11:42 K50netconsole -> ../init.d/netconsole lrwxrwxrwx. 1 root root 21 Jul 15 11:42 K87restorecond -> ../init.d/restorecond lrwxrwxrwx. 1 root root 15 Jul 15 11:42 K89rdisc -> ../init.d/rdisc lrwxrwxrwx. 1 root root 22 Jul 15 11:44 S02lvm2-monitor -> ../init.d/lvm2-monitor lrwxrwxrwx. 1 root root 19 Jul 15 11:42 S08ip6tables -> ../init.d/ip6tables lrwxrwxrwx. 1 root root 18 Jul 15 11:42 S08iptables -> ../init.d/iptables lrwxrwxrwx. 1 root root 17 Jul 15 11:42 S10network -> ../init.d/network lrwxrwxrwx. 1 root root 16 Jul 15 11:44 S11auditd -> ../init.d/auditd lrwxrwxrwx. 1 root root 17 Jul 15 11:42 S12rsyslog -> ../init.d/rsyslog ... ....
這里列出了在運(yùn)行級(jí)3下需要運(yùn)行的服務(wù)腳本及對(duì)應(yīng)的參數(shù),其中符號(hào)鏈接的第一個(gè)字母S和K分別表示傳遞參數(shù)start和stop,后面跟著的兩位數(shù)字表示腳本運(yùn)行的先后順序。這樣一來(lái),只要在rcX.d目錄下新增或者移除鏈接,就可以控制各個(gè)runlevel需要運(yùn)行哪些服務(wù)腳本;而如果需要修改某個(gè)服務(wù)腳本,只需要編輯init.d/目錄下的文件(“本尊”),而它可以影響所有rcX.d目錄下的軟鏈接(“分身”)。這是多么簡(jiǎn)潔而巧妙的設(shè)計(jì)!
ln命令
我們用ln命令創(chuàng)建硬鏈接或者軟鏈接。其語(yǔ)法為:
ln [options] file link
此命令的第一種形式會(huì)創(chuàng)建一個(gè)指向file的新的鏈接,其中options選項(xiàng),我們只記住一個(gè)就行,-s表示創(chuàng)建軟鏈接,而默認(rèn)會(huì)創(chuàng)建硬鏈接。例如:
# ln -s /usr/src/linux-2.6.32 /usr/src/linux
這里,我們創(chuàng)建了一個(gè)符號(hào)鏈接/usr/src/linux,指向真實(shí)的Linux源代碼目錄/usr/src/linux-2.6.32。
我們?cè)倥e一個(gè)例子,演示一下軟鏈接與硬鏈接的區(qū)別,我們創(chuàng)建一個(gè)myfile文件,然后再創(chuàng)建一個(gè)指向該文件的軟鏈接myslink和硬鏈接myhlink:
$ $ echo "an example." > myfile $ ln -s myfile myslink $ ls myfile myhlink
使用stat檢查前述文件:
$ stat my* File: `myfile' Size: 12 Blocks: 8 IO Block: 4096 regular file Device: fd00h/64768d Inode: 11552 Links: 2 Access: (0664/-rw-rw-r--) Uid: ( 500/ wjm) Gid: ( 500/ wjm) Access: 2016-08-10 03:59:54.421017669 +0800 Modify: 2016-08-10 03:59:54.421017669 +0800 Change: 2016-08-10 04:00:08.689000105 +0800 File: `myhlink' Size: 12 Blocks: 8 IO Block: 4096 regular file Device: fd00h/64768d Inode: 11552 Links: 2 Access: (0664/-rw-rw-r--) Uid: ( 500/ wjm) Gid: ( 500/ wjm) Access: 2016-08-10 03:59:54.421017669 +0800 Modify: 2016-08-10 03:59:54.421017669 +0800 Change: 2016-08-10 04:00:08.689000105 +0800 File: `myslink' -> `myfile' Size: 6 Blocks: 0 IO Block: 4096 symbolic link Device: fd00h/64768d Inode: 11553 Links: 1 Access: (0777/lrwxrwxrwx) Uid: ( 500/ wjm) Gid: ( 500/ wjm) Access: 2016-08-10 04:00:03.784997923 +0800 Modify: 2016-08-10 04:00:03.784997923 +0800 Change: 2016-08-10 04:00:03.784997923 +0800
仔細(xì)觀察myfile和myhlink,發(fā)現(xiàn)它們指向同一個(gè)inode(inode號(hào)同為11552)。硬鏈接數(shù)(Links字段)同為2,這表示有兩個(gè)目錄項(xiàng)指向該inode,每增加一個(gè)硬鏈接Links字段值就會(huì)增加1。而myslink文件,我們發(fā)現(xiàn)它的inode號(hào)與前兩個(gè)不同,其訪問(wèn)權(quán)限為0777。我們刪除myhlink這個(gè)硬鏈接,看看會(huì)出現(xiàn)什么變化?這次我們用ls -il命令來(lái)查看:
$ rm myfile $ ll -li total 4 11552 -rw-rw-r-- 1 wjm wjm 12 Aug 10 03:59 myhlink 11553 lrwxrwxrwx 1 wjm wjm 6 Aug 10 04:00 myslink -> myfile $ cat myhlink an example. $ cat myslink cat: myslink: No such file or directory
ls命令的-i選項(xiàng)也可以輸出文件的inode號(hào)。輸出信息的第三列為硬鏈接數(shù),我們發(fā)現(xiàn)刪除了myfile文件后,myhlink的硬鏈接數(shù)已經(jīng)由2變?yōu)?了,但是原myfile文件的數(shù)據(jù)依然可以通過(guò)myhlink這個(gè)硬鏈接訪問(wèn),因?yàn)橛叉溄邮峭ㄟ^(guò)文件的inode號(hào)來(lái)訪問(wèn)文件數(shù)據(jù)的。然而通過(guò)myslink軟鏈接卻無(wú)法再訪問(wèn)原myfile文件的數(shù)據(jù)了,因?yàn)檐涙溄訉?shí)質(zhì)上是一個(gè)指向目標(biāo)文件的全路徑,這個(gè)路徑中任何一個(gè)環(huán)節(jié)斷裂,都會(huì)使這個(gè)軟鏈接失效。
追隨鏈接
自從了軟連接,當(dāng)你要備份、復(fù)制或者移動(dòng)目錄或者文件時(shí)候,會(huì)出現(xiàn)是否要“追隨鏈接”的問(wèn)題。如果是,則會(huì)復(fù)制鏈接所指向的對(duì)象;如果不是,則僅僅操作鏈接本身。
通常如tar或cp之類(lèi)的命令工具會(huì)給出是否追隨鏈接的選項(xiàng)。如cp,你可以使用-L選項(xiàng)表示追隨鏈接(復(fù)制鏈接所指向的目標(biāo)),或者用-P表示不追隨鏈接(復(fù)制鏈接本身)。如下例:
$ mkdir dir1 $ ln -s /tmp/a.txt dir1/slink $ cp -rL dir1 dir2 $ ls -l dir2 total 0 -rw-rw-r-- 1 wjm wjm 0 Aug 6 17:02 slink
這里我們?cè)赿ir1目錄下創(chuàng)建了一個(gè)軟鏈接,當(dāng)用-L選項(xiàng)將其復(fù)制到dir2目錄下時(shí),我們看到dir2目錄下的slink現(xiàn)在成為一個(gè)普通文件。如果使用-P選項(xiàng)(保存鏈接)復(fù)制,則復(fù)制后的文件依然是一個(gè)軟鏈接:
$ cp -rP dir1 dir3 $ ls -l dir3 total 0 lrwxrwxrwx 1 wjm wjm 10 Aug 6 17:07 slink -> /tmp/a.txt
假如沒(méi)有明確指定-L或者-P選項(xiàng),則cp的默認(rèn)行為將隨版本而定。
目錄的硬鏈接
前文提到過(guò),無(wú)法對(duì)一個(gè)目錄創(chuàng)建硬鏈接。但其實(shí)目錄是存在硬鏈接的,只是這個(gè)硬鏈接是系統(tǒng)自動(dòng)創(chuàng)建的,而我們不能手動(dòng)創(chuàng)建。當(dāng)我們用mkdir創(chuàng)建一個(gè)空目錄時(shí),會(huì)發(fā)現(xiàn)這個(gè)目錄的硬鏈接數(shù)為2,例如:
$ ls -dl ~ drwx------. 6 wjm wjm 4096 Aug 10 04:25 /home/wjm $ cd ~ $ mkdir mydir $ ls -dli ~ 8605 drwx------. 7 wjm wjm 4096 Aug 10 04:25 /home/wjm $ ls -dli mydir 11556 drwxrwxr-x 2 wjm wjm 4096 Aug 10 04:25 mydir
原先/home/wjm目錄的硬鏈接數(shù)量為6,當(dāng)在/home/wjm下創(chuàng)建了一個(gè)空目錄mydir后,它的硬鏈接數(shù)量變成了7,而這個(gè)空目錄mydir的硬鏈接數(shù)為2。這是為什么呢?原因是任何一個(gè)目錄下,都有兩個(gè)隱藏的硬鏈接:
ls -ali mydir total 8 11556 drwxrwxr-x 2 wjm wjm 4096 Aug 10 04:25 . 8605 drwx------. 7 wjm wjm 4096 Aug 10 04:25 ..
我們看到mydir目錄下有兩個(gè)隱藏的硬鏈接,使用ls的-a選項(xiàng)才能使其列出來(lái)。其中一個(gè)硬鏈接是“.”,指向的inode號(hào)為11556,就是mydir這個(gè)目錄本身的inode號(hào);另一個(gè)是“..”,通過(guò)inode號(hào)我們發(fā)現(xiàn)它指向了其父目錄/home/wjm。因此,當(dāng)創(chuàng)建了空目錄mydir后,mydir的硬鏈接數(shù)為2,而其父目錄的硬鏈接數(shù)加1。所以一個(gè)目錄的硬鏈接數(shù)=其子目錄數(shù)+2。
這種硬鏈接是系統(tǒng)自動(dòng)為我們創(chuàng)建的,而當(dāng)你試圖手動(dòng)創(chuàng)建一個(gè)指向目錄的硬鏈接時(shí),系統(tǒng)一定會(huì)報(bào)錯(cuò)阻止你這樣做。為什么呢?
其實(shí)在UNIX操作系統(tǒng)的歷史上,對(duì)目錄創(chuàng)建硬鏈接曾經(jīng)是允許的。但人們發(fā)現(xiàn),這樣做會(huì)出現(xiàn)很多問(wèn)題,尤其是一些對(duì)目錄樹(shù)進(jìn)行遍歷操作的如fsck、find等命令無(wú)法正確執(zhí)行。在《Unix高級(jí)環(huán)境編程》中提到作者Steven在自己的系統(tǒng)上做過(guò)實(shí)驗(yàn),結(jié)果是:創(chuàng)建目錄硬鏈接后,文件系統(tǒng)變得錯(cuò)誤百出。因?yàn)檫@樣做會(huì)破壞文件系統(tǒng)的樹(shù)形結(jié)構(gòu),可能會(huì)使目錄之間出現(xiàn)環(huán)。例如:
$ ln ~ ~/mydir/myhdir_link ln: `/home/wjm': hard link not allowed for directory $ ln -s ~ ~/mydir/myhdir_link
這里第一條命令我們?cè)噲D在mydir目錄下創(chuàng)建一個(gè)硬鏈接指向其父目錄,然而失敗了。因?yàn)檫@使得/home/wjm和/home/wjm/mydir兩個(gè)目錄之間形成一個(gè)環(huán),我們無(wú)法再區(qū)分這兩者誰(shuí)是父目錄誰(shuí)是子目錄了。然而第二條命令創(chuàng)建一個(gè)指向其父目錄的軟鏈接卻可以成功,難道這樣不是同樣形成了一個(gè)環(huán)嗎?
為什么軟鏈接可以指向目錄而硬鏈接不行呢?根本原因在于軟鏈接實(shí)質(zhì)上是一個(gè)文件,而硬鏈接實(shí)質(zhì)上是一個(gè)目錄項(xiàng)(dentry)。在linux系統(tǒng)中,每個(gè)文件(目錄也是文件,軟鏈接也是文件)都對(duì)應(yīng)著一個(gè)inode結(jié)構(gòu),其中inode數(shù)據(jù)結(jié)構(gòu)中包含了文件類(lèi)型(目錄,普通文件,符號(hào)連接文件等等)的信息,也就是說(shuō)操作系統(tǒng)在遍歷目錄時(shí)可以判斷出符號(hào)連接。既然可以判斷出符號(hào)連接當(dāng)然就可以采取一些措施來(lái)防范進(jìn)入死循環(huán)了,系統(tǒng)在連續(xù)遇到8個(gè)符號(hào)連接后就停止遍歷,這就是為什么對(duì)目錄符號(hào)連接不會(huì)進(jìn)入死循環(huán)的原因了。而“硬鏈接”本質(zhì)上是“目錄項(xiàng)”的同義詞。當(dāng)一個(gè)目標(biāo)第一次被創(chuàng)建,就會(huì)為它創(chuàng)建一個(gè)目錄項(xiàng),這其實(shí)就是硬鏈接。大多數(shù)人常常把“硬鏈接”聯(lián)想成為一個(gè)已有的對(duì)象創(chuàng)建一個(gè)額外的目錄項(xiàng),但其實(shí)是原來(lái)的目錄項(xiàng)沒(méi)有任何特殊,所有的硬鏈接都是平等的,所以Linux內(nèi)核沒(méi)有方法能識(shí)別出哪個(gè)是“原文件”哪個(gè)是“硬鏈接”。這樣對(duì)于由于目錄硬鏈接而形成的環(huán)就無(wú)法進(jìn)行合適的處理。
但是根目錄是一個(gè)特例。我們觀察:
$ ls -dli / 2 dr-xr-xr-x. 22 root root 4096 Aug 10 00:50 / $ ls -ali / total 102 2 dr-xr-xr-x. 22 root root 4096 Aug 10 00:50 . 2 dr-xr-xr-x. 22 root root 4096 Aug 10 00:50 .. ... ...
可見(jiàn)這里根目錄的inode號(hào)為2,而且根目錄下的指向其父目錄的隱藏硬鏈接(..)也指向了自身。
相關(guān)文章
linux centos 系統(tǒng)中文模式設(shè)置方法
怎么設(shè)置Linux系統(tǒng)中文語(yǔ)言?在我們使用CentOS的使用都喜歡使用中文語(yǔ)言,本文小編將為大家具體的介紹centos 系統(tǒng)中文默認(rèn)語(yǔ)言修改的方法2016-11-23- Ping是Windows、Unix和Linux系統(tǒng)下的一個(gè)命令,也是通信協(xié)議,是TCP/IP協(xié)議的一部分,還可以檢查網(wǎng)絡(luò)是否連通,更好的幫助我們分析和判定網(wǎng)絡(luò)故障等問(wèn)題2016-11-22
linux系統(tǒng)下Centos中"vim配置"到底有多強(qiáng)大
當(dāng)我們?cè)趌inux系統(tǒng)中使用vim編輯器時(shí),總感覺(jué)vim編輯器的界面不是太美觀,不能讓用戶使用的舒服,不僅僅頁(yè)面設(shè)置不習(xí)慣,沒(méi)有顯示的行號(hào),也沒(méi)有自動(dòng)的縮進(jìn),頁(yè)面的背景也2016-11-22- 這篇文章主要介紹了Linux刪除重復(fù)行的代碼,需要的朋友可以參考下2016-11-25
Linux 統(tǒng)計(jì)代碼行數(shù)的代碼
這篇文章主要介紹了Linux 統(tǒng)計(jì)代碼行數(shù)的代碼,需要的朋友可以參考下2016-11-21- 在Linux下的磁盤(pán)內(nèi)容作為子目錄形式出現(xiàn)的,可移動(dòng)介質(zhì)的內(nèi)容不會(huì)自動(dòng)出現(xiàn)在這些子目錄中,因此需要我們通過(guò)掛載驅(qū)動(dòng)器來(lái)實(shí)現(xiàn)2016-11-21
- 這篇文章主要介紹了Linux date日期格式及加減運(yùn)算,需要的朋友可以參考下2016-11-21
- 這篇文章主要介紹了Linux 刪除文本中的重復(fù)行的方法,需要的朋友可以參考下2016-11-21
Linux下which、whereis、locate、find 區(qū)別
這篇文章主要介紹了Linux下which、whereis、locate、find 區(qū)別,需要的朋友可以參考下2016-11-21- Linux中編譯CoreCLR,很多用戶不知道如何在CentOS上編譯并安裝Clang,本文將詳細(xì)介紹具體的步驟,希望可以幫助到大家2016-11-18