inode定義
inode 是 linux 系統中用作數據索引的標識符。
簡單來說,inode 指示了一個文件的基本信息,如inode編號、修改時間、文件的位置等,就如同一本書的目錄,會直接告訴你想看的章節是在第幾頁。不同的是,書是以頁爲單位的,而 linux 文件存取是以“塊”爲單位的。
操作系統在讀取硬盤的時候,會一次性讀取一個“塊”(一個“塊”的大小往往是4kb,包含了連續8個扇區,每個扇區存儲512個字節)。而inode就告訴了文件位於哪個“塊”,於是系統就會從這個“塊”開始讀取內容,我們就可以看到這個文件的內容。
每個文件都有對應的inode,存儲着關於這個文件的基本信息。linux 系統不使用文件名,而使用 inode 號來識別文件。對於使用者,我們是通過文件名打開的文件;但是對於系統內部,是分爲三步的:
- 系統找到這個文件名對應的 inode 號
- 通過 inode 號,獲取 inode 信息
- 根據 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 命令
系統內部會執行以下操作:
分配一個未被使用的 inode 號,在 inode 表中新添一個項目。
如果是覆蓋複製,則 inode 號不變,沿用之前同名文件的 inode 號。
在目錄中新建一個目錄項,並指向步驟 1 中的 inode。
- 把數據複製到 block 中。
rm 命令
系統內部會執行以下操作:
- 減少待刪除文件名所對應的 inode 的鏈接數量,如果鏈接數變爲0,則釋放 inode,同時數據塊放到可用空間中(對外表現爲數據已刪除,因爲隨時可以覆蓋。如果沒有覆蓋,數據還可以恢復;一旦覆蓋了,那麼刪除的數據無法恢復。)。
- 刪除目錄中的目錄項。
mv 命令
一、如果目標文件和源文件屬於同一個文件系統:
- 在目標文件的目錄中新建目錄項
- 刪除源文件的目錄中的目錄項
- 目標文件名會指向源文件名的 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,創建軟鏈接連接數不變
- 是否可以跨分區:硬鏈接不可以跨分區,軟鏈接可以跨分區
- 目錄是否可以創建鏈接:硬鏈接不可以對目錄創建,軟鏈接可以對目錄創建
- 硬鏈接的inode號相同,軟鏈接inode號不同
四、硬鏈接和軟鏈接的佔用空間分析
大家可能注意到了,對於同一個 test_file(大小爲 479M
),建立硬鏈接後,目錄的整體空間佔用爲 total 957M
,而建立軟鏈接後,目錄的整體佔用空間仍爲 479M
。既然硬鏈接指向的是同一塊數據,那麼就不會開闢新的空間去複製一份,應該是不佔用空間的啊?爲什麼空間佔用會加倍呢?這個問題,可以參考關於硬鏈接與軟連接佔用磁盤空間問題的分析研究。