1. 程式人生 > >深入講解 linux 中 inode、硬連結、軟連結的原理

深入講解 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。既然硬連結指向的是同一塊資料,那麼就不會開闢新的空間去複製一份,應該是不佔用空間的啊?為什麼空間佔用會加倍呢?這個問題,可以參考關於硬連結與軟連線佔用磁碟空間問題的分析研究