Linux Handle Book

檔案系統的結構

ext2 與 ext3

ext2 與 ext3 架構上完全相同,唯一的差別是 ext3 多出一個日誌檔案來記錄磁碟的狀態。

ext3 是一個日誌式檔案系統(Journal File System),其最大的優點在於提供了更好的安全性,ext3 會將整個磁碟所做過的更動像寫日記一樣完整的記錄下來。一旦發生非預期的當機狀況,會在下次啟動時,自動檢查已經記錄的日誌,然後依照日誌記錄的動作將系統恢復到當機前的正常狀態。

而同樣的狀況發生在 ext2,便需要使用 fsck 指令檢查與修復檔案系統,耗時且不保證資料不會流失。

ext3 與 ext4

ext4優點:

  • 支援更大的硬碟
  • 單一檔案的最大容量也擴大為16TB
  • 一個目錄下可建立的子目錄總數量也不再有限制
  • 大幅加快了檔案讀寫的速度
  • 減少檔案不連續存放的問題

將 ext3 轉換為 ext4

// 卸載 /dev/sda3
$ umount /dev/sda3

// 將檔案系統轉為 ext4
$ tune2fs -O extents,uninit_bg,dir_index/dev/sda3

// 檢查並修正檔案系統
$ e2fsck -y -fD/dev/sda3

// 修改 /etc/fstab 檔,開機自動掛載 ext4
// /etc/fstab
// ....
// ....
// /dev/sda3    /home1    ext4    defaults    0    0
$ sudo vi /etc/fstab

ps:

fsck is just the original name. When they came out with new file systems they would need a specific tool for each one, efsck for ext, e2fsck for ext2, dosfsck, fsckvfat. So they made fsck the front end that just calls whichever is the appropriate tool.

inode 是什們

硬碟的最小存儲單位叫做 Sector,每個Sector儲存512Bytes(相當於0.5KB)。 作業系統讀取硬碟的時候,會一次讀取多個Sector,即一次讀取一個 Block。

Block 是檔案存取的最小單位,一般大小是4KB,即連續 8 個 Sector 組成一個 Block。

檔案資料儲存在 Block 中,還必須找到一個地方儲存檔案的屬性,如檔案的建立者、建立日期、大小等等。

這種儲存檔案屬性的區域就叫做inode。

inode的內容

名稱 內容
檔案模式 inode會描述其所對應的資料類型,這些資料可以是一個檔案、目錄、符號連結(symbolic link)、周邊設備代號(包括儲存設備的分割區編號),以及權限設定的資訊
擁有者資訊 User ID 以及 Group ID,這些資訊和權限設定息息相關
檔案的大小 單位為 byte
時間戳記 ctime(inode上一次變動的時間)、mtime(指檔案內容上一次變動的時間)、atime(指檔案上一次打開的時間)
鏈接數 即有多少檔案名稱即有多少檔案名指向這個inode 指向這個inode
資料區塊位址(address of data block) 存放檔案必定會佔用資料區塊,且每個資料區塊都有其存在的位址。如果inode所對應的資料為實體檔案,而非虛擬檔案(如/proc目錄內的檔案),則inode會指出這些位址,讓系統得以順利找到檔案並使用它。一個inode能夠指向12個資料區塊,如果12個資料區塊還放不下這個檔案,它就會啟用間接指向指標(indirect pointer),透過另一個資料區塊指向更多的資料區塊,以便容納大型檔案。

ps: 除了檔案名以外的所有檔案資訊,都存在inode之中

可以用stat命令,查看某個檔案的inode資訊:

$ stat example.txt

inode的大小

inode也會消耗硬碟空間,所以硬碟格式化的時候,操作系統自動將硬碟分成兩個區域。一個是資料區,存放檔案資料;另一個是inode區(inode table),存放inode所包含的資訊。 每個inode節點的大小,一般是128字節或256字節。inode節點的總數,在格式化時就給定,一般是每1KB或每2KB就設置一個inode。假定在一塊1GB的硬碟中,每個inode節點的大小為128字節,每1KB就設置一個inode,那麼inode table的大小就會達到128MB,佔整塊硬碟的12.8%。

查詢硬碟使用狀況的相關指令

查看每個硬碟分區的inode總數和已經使用的數量,可以使用 df 指令。

$ df -i

查看每個inode節點的大小,可以用如下命令:

// -h: only display the superblock information and not any of the block group descriptor detail information.

$ sudo dumpe2fs -h /dev/hda | grep "Inode size"
// 查詢系統中每一個目錄所佔用的磁碟空間

$ du -ch /home

由於每個檔案都必須有一個inode,因此有可能發生inode已經用光,但是硬碟還未存滿的情況。這時,就無法在硬碟上創建新檔案。

inode號碼

每個inode都有一個號碼,操作系統用inode號碼來識別不同的檔案。 Unix/Linux系統內部不使用檔案名,而使用inode號碼來識別檔案。

對於系統來說,檔案名只是inode號碼便於識別的別稱。

表面上,用戶通過檔案名,打開檔案。實際上,系統內部這個過程分成三步:

  1. 系統找到這個檔案名對應的inode號碼
  2. 通過inode號碼,獲取inode資訊
  3. 根據inode資訊,找到檔案數據所在的Block,讀出資料。
// 使用ls -i命令,可以看到檔案名對應的inode號碼:

$ ls -i example.txt

目錄檔案

Unix/Linux系統中,目錄(directory)也是一種檔案。打開目錄,實際上就是打開目錄檔案。

目錄檔案的結構非常簡單,就是一系列目錄項(dirent)的列表。每個目錄項,由兩部分組成

  1. 所包含檔案的檔案名
  2. 對應的inode號碼
// ls -i命令列出整個目錄檔案,即檔案名和inode號碼:
$ ls -i /etc

目錄檔案的讀權限(r)和寫權限(w),都是針對目錄檔案本身。

由於目錄檔案內只有檔案名和inode號碼,所以如果只有讀權限,只能獲取檔案名,無法獲取其他資訊。

因為其他資訊都儲存在inode節點中,而讀取inode節點內的資訊需要目錄檔案的執行權限(x)。

一般情況下,檔案名和inode號碼是一對一關係,每個inode號碼對應一個檔案名。但是,Unix/Linux系統允許多個檔案名指向同一個inode號碼。

這意味著,可以用不同的檔案名訪問同樣的內容,對檔案內容進行修改,會影響到所有檔案名。

但是,刪除一個檔案名,不影響另一個檔案名的訪問。這種情況就被稱為硬鏈接(hard link)。

//創建硬鏈接:
$ ln 源檔案目標檔案

執行上面的指令以後,源檔案與目標檔案的inode號碼相同,都指向同一個inode。

inode資訊中有一項叫做鏈接數,記錄指向該inode的檔案名總數,這時就會增加1。

反過來,刪除一個檔案名,就會使得inode節點中的鏈接數減1。當這個值減到0,表明沒有檔案名指向這個inode,系統就會回收這個inode號碼,以及其所對應的Block區域。

這裡順便說一下目錄檔案的鏈接數。創建目錄時,默認會生成兩個目錄項:"."和"..",指向當前目錄的inode,因此任何一個目錄的硬鏈接總數,總是等於2加上它的子目錄總數(含隱藏目錄)。

ps: 硬連結不能連結目錄。

檔案A和檔案B的inode號碼雖然不一樣,但是檔案A的內容是檔案B的路徑。

讀取檔案A時,系統會自動將訪問者導向檔案B。因此,無論打開哪一個檔案,最終讀取的都是檔案B。這時,檔案A就稱為檔案B的軟鏈接(soft link)或者符號鏈接(symbolic link)。

這意味著,檔案A依賴於檔案B而存在,如果刪除了檔案B,打開檔案A就會報錯。

這是軟鏈接與硬鏈接最大的不同:檔案A指向檔案B的檔案名,而不是檔案B的inode號碼,檔案B的inode鏈接數不會因此發生變化。

ps: Soft Link 可以跨越不同的分割區,甚至連結到掛載 NFS與SAMBA的目錄內。Soft Link 本身也會佔用一個inode。

// ln -s命令可以創建軟鏈接。
$ ln -s 源文檔案或目錄目標檔案或目錄

示意圖:

inode的特殊作用

由於inode號碼與檔案名分離,這種機制導致了一些Unix/Linux系統特有的現象。

  1. 有時檔案名包含特殊字符無法正常刪除,這時直接刪除inode節點就能起到刪除檔案的作用。
  2. 移動檔案或重命名檔案,只是改變檔案名,不影響inode號碼。
  3. 打開一個檔案以後,系統就以inode號碼來識別這個檔案,不再考慮檔案名。因此,通常來說,系統無法從inode號碼得知檔案名。

第3點使得系統可以在不關閉檔案的情況下進行更新,不需要重啟。

因為系統通過inode號碼,識別運行中的檔案,不通過檔案名。更新的時候,新版檔案以同樣的檔案名生成一個新的inode,不會影響到運行中的檔案。(可搭配示意圖聯想)

等到下一次執行這個檔案的時候,檔案名就自動指向新版檔案,舊版檔案的inode則被回收。