深入講解 linux 中 inode、硬鏈接、軟鏈接的原理

inode定義

inode 是 linux 系統中用作數據索引的標識符。
簡單來說,inode 指示了一個文件的基本信息,如inode編號、修改時間、文件的位置等,就如同一本書的目錄,會直接告訴你想看的章節是在第幾頁。不同的是,書是以頁爲單位的,而 linux 文件存取是以“塊”爲單位的。
操作系統在讀取硬盤的時候,會一次性讀取一個“塊”(一個“塊”的大小往往是4kb,包含了連續8個扇區,每個扇區存儲512個字節)。而inode就告訴了文件位於哪個“塊”,於是系統就會從這個“塊”開始讀取內容,我們就可以看到這個文件的內容。
每個文件都有對應的inode,存儲着關於這個文件的基本信息。linux 系統不使用文件名,而使用 inode 號來識別文件。對於使用者,我們是通過文件名打開的文件;但是對於系統內部,是分爲三步的:

  1. 系統找到這個文件名對應的 inode 號
  2. 通過 inode 號,獲取 inode 信息
  3. 根據 inode 信息,找到文件數據所在的 block,讀取內容

inode內容

inode 包含了文件的以下基本信息:

  • 文件的字節數
  • inode 編號
  • 文件擁有者的 Uid
  • 文件所屬group的 Gid
  • 文件的讀、寫、執行權限
  • 文件的時間戳,共有三個:
    • change:inode 上一次變動的時間
    • modify:文件內容上一次變動的時間
    • access:文件上一次打開的時間
  • 鏈接數,即有多少文件名指向這個 inode
  • 文件數據 block 的位置

我們可以使用 stat 命令來查看文件的 inode 信息,如:

$ stat v0.1.0.zip 
  File: ‘v0.1.0.zip’
  Size: 94267       Blocks: 192        IO Block: 4096   regular file
Device: 811h/2065d  Inode: 5659765     Links: 1
Access: (0640/-rw-r-----)  Uid: ( 3457/mart_bda)   Gid: ( 3457/mart_bda)
Access: 2018-06-12 14:22:18.434027485 +0800
Modify: 2018-06-12 14:18:00.840994081 +0800
Change: 2018-06-12 14:18:00.840994081 +0800
 Birth: -

也可以在 ls 後加上 -i 直接獲取 incode 編號:

$ ls -i v0.1.0.zip 
5659765 v0.1.0.zip

inode大小

inode存儲了文件的基本信息,雖然信息很少,但是也會佔用空間。
硬盤格式化的時候,操作系統自動將硬盤分爲兩個區域:

  • 數據區:存放文件內容
  • inode 區:存放 inode 包含的信息,也叫作 inode table

每個 inode 節點的大小,一般是 128 字節或 256 字節。inode 節點的總數,在硬盤格式化時就固定了。一般,數據區每1KB 或 2KB,inode區就會增加一個 inode。假如在一塊 1GB 的硬盤中,每個 inode 節點的大小爲 128 字節,那麼 inode 表的大小就會達到 128 MB,佔整塊硬盤的 12.8%。
既然 inode 節點總數是有限的,那麼分區的節點數就有用完的時候,一旦 inode 用完了,即使磁盤空間還有剩餘,也不能再存放任何數據,因爲需要保證每個文件必須有一個 inode。
查看每個硬盤分區的 inode 或者磁盤容量的使用情況,可以使用 df 命令加上參數 -i 或者 -h,如:

$ df -i
Filesystem        Inodes   IUsed     IFree IUse% Mounted on
/dev/sda5      275436544  801853 274634691    1% /
devtmpfs         8192960     524   8192436    1% /dev
tmpfs            8195307       4   8195303    1% /dev/shm
tmpfs            8195307     765   8194542    1% /run
tmpfs            8195307      13   8195294    1% /sys/fs/cgroup
/dev/sda2         204800     342    204458    1% /boot
/dev/sdb1       11443200 3329257   8113943   30% /data0
tmpfs            8195307       1   8195306    1% /run/user/0
tmpfs            8195307       1   8195306    1% /run/user/3457
$ df -h
Filesystem      Size  Used Avail Use% Mounted on
/dev/sda5       263G  134G  130G  51% /
devtmpfs         32G     0   32G   0% /dev
tmpfs            32G   12K   32G   1% /dev/shm
tmpfs            32G  394M   31G   2% /run
tmpfs            32G     0   32G   0% /sys/fs/cgroup
/dev/sda2       197M  139M   58M  71% /boot
/dev/sdb1        11T  5.2T  5.2T  51% /data0
tmpfs           6.3G     0  6.3G   0% /run/user/0
tmpfs           6.3G     0  6.3G   0% /run/user/3457

關於 df -h -i 的區別,可以參考 Linux df命令

文件操作對 inode 的影響

要理解文件的操作對 inode 的影響,先要理解目錄的原理。目錄對外表現是一個容器,存放着子文件和子目錄,實際上在系統內部,目錄本身也是一個文件,目錄文件的內容即是該目錄下的文件名與 inode 號的映射表(即一個個的目錄項)。因此,linux 訪問一個文件時,要先查詢到上一級目錄,根據目錄內容查找到文件對應的 inode 號,然後讀取對應的 block。

cp 命令

系統內部會執行以下操作:

  1. 分配一個未被使用的 inode 號,在 inode 表中新添一個項目。

    如果是覆蓋複製,則 inode 號不變,沿用之前同名文件的 inode 號。

  2. 在目錄中新建一個目錄項,並指向步驟 1 中的 inode。

  3. 把數據複製到 block 中。

rm 命令

系統內部會執行以下操作:

  1. 減少待刪除文件名所對應的 inode 的鏈接數量,如果鏈接數變爲0,則釋放 inode,同時數據塊放到可用空間中(對外表現爲數據已刪除,因爲隨時可以覆蓋。如果沒有覆蓋,數據還可以恢復;一旦覆蓋了,那麼刪除的數據無法恢復。)。
  2. 刪除目錄中的目錄項。

mv 命令

一、如果目標文件和源文件屬於同一個文件系統:

  1. 在目標文件的目錄中新建目錄項
  2. 刪除源文件的目錄中的目錄項
  3. 目標文件名會指向源文件名的 inode。因此該操作對 inode 沒有影響(除了時間戳),對數據的位置也沒有影響,不移動任何數據。

二、如果目標文件和源文件屬於不同文件系統,則相當於 cp + rm。

ln 命令

一、硬鏈接
一般情況下,文件名和 inode 號是一一對應,但是也有可能多個文件名指向同一個 inode 號,即硬鏈接。硬鏈接可以實現用不同的文件名訪問同一個文件;對文件內容修改,會影響到所有的文件名;但是,刪除一個文件名,不影響其他文件名的訪問。
創建硬鏈接的命令:

ln [source file] [new file]

如:

$ ll -h -i
total 479M
5659849 -rw-r----- 1 mart_bda mart_bda 479M Jun 13 10:57 test_file
$ ln test_file test_file_hardlink
$ ll -i -h
total 957M
5659849 -rw-r----- 2 mart_bda mart_bda 479M Jun 13 10:57 test_file
5659849 -rw-r----- 2 mart_bda mart_bda 479M Jun 13 10:57 test_file_hardlink

這樣,兩個文件的 inode 號均爲 5659849。具體查看兩個文件的 inode 內容:

$ stat test_file
  File: ‘test_file’
  Size: 501577774   Blocks: 979656     IO Block: 4096   regular file
Device: 811h/2065d  Inode: 5659849     Links: 2
Access: (0640/-rw-r-----)  Uid: ( 3457/mart_bda)   Gid: ( 3457/mart_bda)
Access: 2018-06-13 10:57:13.961409755 +0800
Modify: 2018-06-13 10:57:14.931383436 +0800
Change: 2018-06-13 10:58:11.382851699 +0800
 Birth: -
$ stat test_file_hardlink 
  File: ‘test_file_hardlink’
  Size: 501577774   Blocks: 979656     IO Block: 4096   regular file
Device: 811h/2065d  Inode: 5659849     Links: 2
Access: (0640/-rw-r-----)  Uid: ( 3457/mart_bda)   Gid: ( 3457/mart_bda)
Access: 2018-06-13 10:57:13.961409755 +0800
Modify: 2018-06-13 10:57:14.931383436 +0800
Change: 2018-06-13 10:58:11.382851699 +0800
 Birth: -

可以看到,兩個文件的 inode 內容完全相同,且 Links 變成了 2。修改任何一個文件名的內容,另一個文件名的內容也會同時改變,因爲訪問的就是硬盤中的同一塊數據。
如果再將 test_file_hardlink 刪掉,會使得 Links 變回 1。當這個值減到 0 時,說明沒有文件名指向這個 inode,系統就會回收這個號碼,以及所對應的 block 區域。
另外,對於目錄的鏈接數,創建一個目錄時,默認會生成兩個目錄項:...。前者的 inode 號就是當前目錄的 inode 號,等同於當前目錄的硬鏈接;後者的 inode 號是父目錄的 inode 號,等同於父目錄的硬鏈接。因此,任何一個目錄的硬鏈接總數,總是等於 2 加上它的子目錄總數(含隱藏目錄,且除去...)。

二、軟鏈接(符號鏈接)
軟鏈接也可以通過不同的文件名訪問同一塊數據,但是與硬鏈接不同的是,兩個文件名的 inode 是不一樣的。那如何訪問同一塊區域呢?比如文件 A 是文件 B 的軟連接,那麼文件 A 的內容存放的是文件 B 的路徑名(可以通過這個找到文件 B 的目錄項)。因此訪問 A 時,會讀取文件 B 的路徑,進而讀取文件 B 的內容。這樣,對外表現來看,文件 A 和文件 B 的內容就相同了。類似於 windows 系統下的快捷方式。
建立軟鏈接的命令:

ln -s [source file] [new file]

如:

$ ll
total 489824
-rw-r----- 1 mart_bda mart_bda 501577774 Jun 13 11:21 test_file
$ ln -s test_file test_file_soft
$ ll -h -i
total 479M
5659853 -rw-r----- 1 mart_bda mart_bda 479M Jun 13 11:21 test_file
5659854 lrwxrwxrwx 1 mart_bda mart_bda    9 Jun 13 11:22 test_file_soft -> test_file

如果是對文件夾建立軟鏈接,則爲

ln -s /tmp/test_directory ./

會自動地在當前目錄建立一個文件夾 test_directory ,並指向 /tmp/test_directory

可以看到,兩個文件的 inode 號是不同的。
既然文件 A 是依賴文件 B 存在的,那麼如果刪除了文件 B,打開文件 A 就會報錯:No such file or directory;如果刪除了文件 A,則對文件 B 的打開無影響,因爲只是刪除了“快捷方式”而已。
軟連接的建立,不會影響到文件 B 的 inode 的任何信息,包括 Links

三、硬鏈接和軟鏈接的不同

  1. 本質不同:硬鏈接是指向同一個文件,軟鏈接指向的不是同一個文件。
  2. 刪除時:硬鏈接不受影響,軟鏈接失效
  3. 創建鏈接時:創建硬鏈接鏈接數加1,創建軟鏈接連接數不變
  4. 是否可以跨分區:硬鏈接不可以跨分區,軟鏈接可以跨分區
  5. 目錄是否可以創建鏈接:硬鏈接不可以對目錄創建,軟鏈接可以對目錄創建
  6. 硬鏈接的inode號相同,軟鏈接inode號不同

四、硬鏈接和軟鏈接的佔用空間分析

大家可能注意到了,對於同一個 test_file(大小爲 479M),建立硬鏈接後,目錄的整體空間佔用爲 total 957M,而建立軟鏈接後,目錄的整體佔用空間仍爲 479M。既然硬鏈接指向的是同一塊數據,那麼就不會開闢新的空間去複製一份,應該是不佔用空間的啊?爲什麼空間佔用會加倍呢?這個問題,可以參考關於硬鏈接與軟連接佔用磁盤空間問題的分析研究

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章