如果要想說清楚 ln 命令,則必須先解釋下 ext 文件系統(Linux 文件系統)是如何工作的。我們在前面講解了分區的格式化就是寫入文件系統,而我們的 Linux 目前使用的是 ext4 文件系統。如果用一張示意圖來描述 ext4 文件系統,則可以參考圖 1。

圖 1 ext4 文件系統示意圖
ext4 文件系統會把分區主要分為兩大部分(暫時不提超級塊):小部分用於保存文件的 inode (i 節點)信息;剩余的大部分用於保存 block 信息。
inode 的默認大小為 128 Byte,用來記錄文件的權限(r、w、x)、文件的所有者和屬組、文件的大小、文件的狀態改變時間(ctime)、文件的最近一次讀取時間(atime)、文件的最近一次修改時間(mtime)、文件的數據真正保存的 block 編號。每個文件需要占用一個 inode。大家如果仔細查看,就會發現 inode 中是不記錄文件名的,那是因為文件名記錄在文件所在目錄的 block 中。
block 的大小可以是 1KB、2KB、4KB,默認為 4KB。block 用於實際的數據存儲,如果一個 block 放不下數據,則可以占用多個 block。例如,有一個 10KB 的文件需要存儲,則會占用 3 個 block,雖然最后一個 block 不能占滿,但也不能再放入其他文件的數據。這 3 個 block 有可能是連續的,也有可能是分散的。
由此,我們可以知道以下 2 個重要的信息:
- 每個文件都獨自占用一個 inode,文件內容由 inode 的記錄來指向;
- 如果想要讀取文件內容,就必須借助目錄中記錄的文件名找到該文件的 inode,才能成功找到文件內容所在的 block 塊;
了解了 Linux 系統底層文件的存儲狀態后,接下來學習 ln 命令。
ln 命令用於給文件創建鏈接,根據 Linux 系統存儲文件的特點,鏈接的方式分為以下 2 種:
- 軟鏈接:類似於 Windows 系統中給文件創建快捷方式,即產生一個特殊的文件,該文件用來指向另一個文件,此鏈接方式同樣適用於目錄。
- 硬鏈接:我們知道,文件的基本信息都存儲在 inode 中,而硬鏈接指的就是給一個文件的 inode 分配多個文件名,通過任何一個文件名,都可以找到此文件的 inode,從而讀取該文件的數據信息。
ln 命令的基本格式如下:
[root@localhost ~]# ln [選項] 源文件 目標文件
選項:
- -s:建立軟鏈接文件。如果不加 "-s" 選項,則建立硬鏈接文件;
- -f:強制。如果目標文件已經存在,則刪除目標文件后再建立鏈接文件;
【例 1】創建硬鏈接:
[root@localhost ~]# touch cangls
[root@localhost ~]# ln /root/cangls /tmp
#建立硬鏈接文件,目標文件沒有寫文件名,會和原名一致
#也就是/tmp/cangls 是硬鏈接文件
【例 2】創建軟鏈接:
[root@localhost ~]# touch bols
[root@localhost ~]# In -s /root/bols /tmp
#建立軟鏈接文件
這里需要注意,軟鏈接文件的源文件必須寫成絕對路徑,而不能寫成相對路徑(硬鏈接沒有這樣的要求);否則軟鏈接文件會報錯。這是初學者非常容易犯的錯誤。
ln創建鏈接的深度剖析
建立硬鏈接和軟鏈接非常簡單,那這兩種鏈接有什么區別?它們都有什么作用?這才是鏈接文件最不容易理解的地方,我們分別來講講。
ln創建硬鏈接
我們再來建立一個硬鏈接文件,然后看看這兩個文件的特點。
[root@localhost ~]# touch test
#建立源文件
[root@localhost ~]# ln /root/test /tmp/test-hard
#給源文件建立硬鏈接文件 /tmp/test-hard
[root@localhost ~]# ll -i /root/test /tmp/test-hard
262147 -rw-r--r-- 2 root root 0 6月 19 10:06 /root/test
hard
262147 -rw-r--r-- 2 root root 0 6月 19 10:06 /tmp/test-hard
#查看兩個文件的詳細信息,可以發現這兩個文件的 inode 號是一樣的,"ll"等同於"ls -l"。
這里有一件很奇怪的事情,我們之前在講 inode 號的時候說過,每個文件的 inode 號都應該是不一樣的。inode 號就相當於文件 ID,我們在查找文件的時候,要先查找 inode 號,才能讀取到文件的內容。
但是這里源文件和硬鏈接文件的 inode 號居然是一樣的,那我們在查找文件的時候,到底找到的是哪一個文件呢?我們來畫一張示意圖,如圖 2 所示。

圖 2 硬鏈接示意圖
在 inode 信息中,是不會記錄文件名稱的,而是把文件名記錄在上級目錄的 block 中。也就是說,目錄的 block 中記錄的是這個目錄下所有一級子文件和子目錄的文件名及 inode 的對應;而文件的 block 中記錄的才是文件實際的數據。
當我們查找一個文件,比如 /root/test 時,要經過以下步驟:
- 首先找到根目錄的 inode(根目錄的 inode 是系統已知的,inode 號是 2),然后判斷用戶是否有權限訪問根目錄的 block。
- 如果有權限,則可以在根目錄的 block 中訪問到 /root 的文件名及對應的 inode 號。
- 通過 /root/ 目錄的 inode 號,可以查找到 /root/ 目錄的 inode 信息,接着判斷用戶是否有權限訪問 /root/ 目錄的 block。
- 如果有權限,則可以從 /root/ 目錄的 block 中讀取到 test 文件的文件名及對應的 inode 號。
- 通過 test 文件的 inode 號,就可以找到 test 文件的 inode 信息,接着判斷用戶是否有權限訪問 test 文件的 block。
- 如果有權限,則可以讀取 block 中的數據,這樣就完成了 /root/test 文件的讀取與訪問。
按照這個步驟,在給源文件 /root/test 建立了硬鏈接文件 /tmp/test-hard 之后,在 /root/ 目錄和 /tmp/ 目錄的 block 中就會建立 test 和 test-hard 的信息,這個信息主要就是文件名和對應的 inode 號。但是我們會發現 test 和 test-hard 的 inode 信息居然是一樣的,那么,我們無論訪問哪個文件,最終都會訪問 inode 號是 262147 的文件信息。
這就是硬鏈接的原理。硬鏈接的特點如下:
- 不論是修改源文件(test 文件),還是修改硬鏈接文件(test-hard 文件),另一個文件中的數據都會發生改變。
- 不論是刪除源文件,還是刪除硬鏈接文件,只要還有一個文件存在,這個文件(inode 號是 262147 的文件)都可以被訪問。
- 硬鏈接不會建立新的 inode 信息,也不會更改 inode 的總數。
- 硬鏈接不能跨文件系統(分區)建立,因為在不同的文件系統中,inode 號是重新計算的。
- 硬鏈接不能鏈接目錄,因為如果給目錄建立硬鏈接,那么不僅目錄本身需要重新建立,目錄下所有的子文件,包括子目錄中的所有子文件都需要建立硬鏈接,這對當前的 Linux 來講過於復雜。
硬鏈接的限制比較多,既不能跨文件系統,也不能鏈接目錄,而且源文件和硬鏈接文件之間除 inode 號是一樣的之外,沒有其他明顯的特征。這些特征都使得硬鏈接並不常用,大家有所了解就好。
我們通過實驗來測試一下。
[root@localhost ~]# echo 1111 >>/root/test
#向源文件中寫入數據
[root@localhost ~]# cat /root/test
1111
[root@localhost ~]# cat /tmp/test-hard
1111
#源文件和硬鏈接文件都會發生改變
[root@localhost ~]# echo 2222 >> /tmp/test-hard
#向硬鏈接文件中寫入數據
[root@localhost ~]# cat /root/test
1111
2222
[root@localhost ~】# cat /tmp/test-hard
1111
2222
#源文件和硬鏈接文件也都會發生改變
[root@localhost ~]# rm -rf/root/test
#刪除源文件
[root@localhost ~]# cat /tmp/test-hard
1111 2222
#硬鏈接文件依然可常讀取
ln創建軟鏈接
軟鏈接也稱作符號鏈接,相比硬鏈接來講,軟鏈接就要常用多了。我們先建立一個軟鏈接,再來看看軟鏈接的特點。
[root@localhost ~]# touch check
#建立源文件
[root@localhost ~]# ln -s /root/check /tmp/check-soft
#建立軟鏈接文件
[root@localhost ~]# ll -id /root/check /tmp/check-soft
262154 -rw-r--r-- 1 root root 0 6月 19 11:30 /root/check
917507 lrwxrwxrwx 1 root root 11 6月 19 11:31 /tmp/ check-soft -> /root/check
#軟鏈接和源文件的 inode 號不一致,軟鏈接通過 -> 明顯地標識出源文件的位置
#在軟鏈接的權限位 lrwxrwxrwx 中,l 就代表軟鏈接文件
再強調一下,軟鏈接的源文件必須寫絕對路徑,否則建立的軟鏈接文件就會報錯,無法正常使用。
軟鏈接的標志非常明顯,首先,權限位中"l"表示這是一個軟鏈接文件;其次,在文件的后面通過 "->" 顯示出源文件的完整名字。所以軟鏈接比硬鏈接的標志要明顯得多,而且軟鏈接也不像硬鏈接的限制那樣多,比如軟鏈接可以鏈接目錄,也可以跨分區來建立軟鏈接。
軟鏈接完全可以當作 Windows 的快捷方式來對待,它的特點和快捷方式一樣,我們更推薦大家使用軟鏈接,而不是硬鏈接。
大家在學習軟鏈接的時候會有一些疑問:Windows 的快捷方式是由於源文件放置的位置過深,不容易找到,建立一個快捷方式放在桌面,方便查找,那 Linux 的軟鏈接的作用是什么呢?
筆者個人覺得,軟鏈接主要是為了照顧管理員的使用習慣。比如,有些系統的自啟動文件 /etc/rc.local 放置在 /etc 目錄中,而有些系統卻將其放置在 /etc/rc.d/rc.local 中,那么干脆對這兩個文件建立軟鏈接,不論你習慣操作哪一個文件,結果都是一樣的。
如果你比較細心,則應該已經發現軟鏈接和源文件的 inode 號是不一致的,我們也畫一張示意圖來看看軟鏈接的原理,如圖 3 所示。

圖 3 軟鏈接示意圖
軟鏈接和硬鏈接在原理上最主要的不同在於:硬鏈接不會建立自己的 inode 索引和 block(數據塊),而是直接指向源文件的 inode 信息和 block,所以硬鏈接和源文件的 inode 號是一致的;而軟鏈接會真正建立自己的 inode 索引和 block,所以軟鏈接和源文件的 inode 號是不一致的,而且在軟鏈接的 block 中,寫的不是真正的數據,而僅僅是源文件的文件名及 inode 號。
我們來看看訪問軟鏈接的步驟和訪問硬鏈接的步驟有什么不同。
- 首先找到根目錄的 inode 索引信息,然后判斷用戶是否有權限訪問根目錄的 block。
- 如果有權限訪問根目錄的 block,就會在 block 中查找到 /tmp/ 目錄的 inode 號。
- 接着訪問 /tmp/ 目錄的 inode 信息,判斷用戶是否有權限訪問 /tmp/ 目錄的 block。
- 如果有權限,就會在 block 中讀取到軟鏈接文件 check-soft 的 inode 號。因為軟鏈接文件會真正建立自己的 inode 索引和 block,所以軟鏈接文件和源文件的 inode 號是不一樣的。
- 通過軟鏈接文件的 inode 號,找到了 check-soft 文件 inode 信息,判斷用戶是否有權限訪問 block。
- 如果有權限,就會發現 check-soft 文件的 block 中沒有實際數據,僅有源文件 check 的 inode 號。
- 接着通過源文件的 inode 號,訪問到源文件 check 的 inode 信息,判斷用戶是否有權限訪問 block。
- 如果有權限,就會在 check 文件的 block 中讀取到真正的數據,從而完成數據訪問。
通過這個過程,我們就可以總結出軟鏈接的特點(軟鏈接的特點和 Windows 中的快捷方式完全一致)。
- 不論是修改源文件(check),還是修改硬鏈接文件(check-soft),另一個文件中的數據都會發生改變。
- 刪除軟鏈接文件,源文件不受影響。而刪除原文件,軟鏈接文件將找不到實際的數據,從而顯示文件不存在。
- 軟鏈接會新建自己的 inode 信息和 block,只是在 block 中不存儲實際文件數據,而存儲的是源文件的文件名及 inode 號。
- 軟鏈接可以鏈接目錄。
- 軟鏈接可以跨分區。
我們測試一下軟鏈接的特性。
[root@localhost ~]# echo 111>>/root/check
#修改源文件
[root@localhost ~]# cat /root/check
111
[root@localhost ~]# cat /tmp/check-soft
111
#不論是源文件還是軟鏈接文件,數據都發生改變
[root@localhost ~]# echo 2222 >>/tmp/check-soft
#修改軟鏈接文件
[root@localhost ~]# cat /tmp/check-soft
111
2222
[root@localhost ~]# cat /root/check
111
2222
#不論是源文件還是軟鏈接文件,數據也都會發生改變
[root@localhost ~]# rm -rf/root/check
#刪除源文件
[root@localhost ~]# cat /tmp/check-soft
cat: /tmp/check-soft:沒有那個文件或目錄
#軟鏈接無法正常使用
軟鏈接是可以鏈接目錄的,例如:
[root@localhost ~]# mkdir test
#建立源目錄
[root@localhost ~]# ln -s /root/test/ /tmp/
[root@localhost ~]# ll -d /tmp/test
lrwxrwxrwx 1 root root 11 6月 19 12:43 /tmp/test->/root/test/
#軟鏈接可以鏈接目錄