@TOC


📝ext2 ⽂件系統

🌠 宏觀認識

所有的準備⼯作都已經做完,是時候認識下⽂件系統了。我們想要在硬盤上儲⽂件,必須先把硬盤格式化為某種格式的⽂件系統,才能存儲⽂件。⽂件系統的⽬的就是組織和管理硬盤中的⽂件。在Linux 系統中,最常⻅的是ext2系列的⽂件系統。其早期版本為ext2,後來⼜發展出ext3ext4ext3ext4雖然對ext2進⾏了增強,但是其核⼼設計並沒有發⽣變化,我們仍是以較⽼的ext2作為演⽰對象。

ext2⽂件系統將整個分區劃分成若⼲個同樣⼤⼩的塊組(BlockGroup),如下圖所⽰。只要能管理⼀個分區就能管理所有分區,也就能管理所有磁盤⽂件。

Ext系列文件系統--Ext2 文件系統的認識與構成_位圖

上圖中啓動塊(BootBlock/Sector)的⼤⼩是確定的,為1KB,由PC標準規定,⽤來存儲磁盤分區信息和啓動信息,任何⽂件系統都不能修改啓動塊。啓動塊之後才是ext2⽂件系統的開始。

🌉 Block Group

ext2⽂件系統會根據分區的⼤⼩劃分為數個BlockGroup。⽽每個BlockGroup都有着相同的結構組成。 政府管理各區的例⼦:

  1. 類比政府管理各區來理解BlockGroup結構
  • 超級塊(Superblock)副本 - 相當於區政府管理中心
  • 就像每個區都有一個管理中心(超級塊副本),在文件系統中,每個BlockGroup都有超級塊副本。它存儲了文件系統的關鍵信息,如文件系統的類型(這裏是ext2)、塊大小、inode數量等。
  • 這就好比區政府管理中心掌握着本區的基本規劃信息(如區域面積大小、功能分區數量等)。而且這些信息是非常重要的,當主超級塊損壞時,這些副本可以用於恢復文件系統的基本參數。
  • 塊位圖(Block Bitmap) - 類似土地資源管理部門
  • 塊位圖負責記錄塊的使用情況,就像土地資源管理部門記錄區內土地(塊)的使用狀態一樣。如果塊位圖中的某一位為1,表示對應的塊已經被使用;為0則表示塊未被使用。
  • 當需要分配新的土地(數據塊)用於存儲文件數據時,這個“土地資源管理部門”(塊位圖)就會查找未使用的土地(空閒塊)來進行分配。
  • inode位圖(inode Bitmap) - 類似居民信息登記部門
  • inode位圖用於記錄inode的使用狀態,類似於居民信息登記部門記錄區內居民(inode)的居住(使用)情況。一位為1表示對應的inode已經被使用,為0表示未被使用。
  • 當有新居民(新文件)要入住(創建)時,這個“居民信息登記部門”(inode位圖)就會查找空閒的居民名額(空閒inode)來登記新居民(存儲文件屬性)。
  • inode表(inode Table) - 類似居民檔案庫
  • inode表就像是一個居民檔案庫,每個inode(居民檔案)存儲了文件(居民)的各種屬性信息,如文件的類型(普通文件、目錄、符號鏈接等)、文件的訪問權限、文件的大小、文件的創建時間、修改時間、訪問時間以及最重要的文件數據塊指針。
  • 當需要查詢某個居民(文件)的詳細信息(屬性)時,就會通過居民編號(inode編號)在這個“居民檔案庫”(inode表)中查找對應的檔案(inode)。
  • 數據塊(Data Blocks) - 類似居民住宅和商業用地等實際土地用途區域
  • 數據塊是真正存儲文件數據內容的地方,就好比區內的居民住宅、商業用地等實際使用的土地。對於小文件,可能只佔用少量的數據塊,就像小商店只佔用一小塊土地一樣;而大文件可能會佔用多個數據塊,如同大型商場需要佔用大面積的土地。

🌠塊組內部構成

🌉超級塊(SuperBlock)

存放⽂件系統本⾝的結構信息,描述整個分區的⽂件系統信息。記錄的信息主要有:bolck和inode的總量,未使⽤的block和inode的數量,⼀個block和inode的⼤⼩,最近⼀次掛載的時間,最近⼀次寫⼊數據的時間,最近⼀次檢驗磁盤的時間等其他⽂件系統的相關信息。SuperBlock的信息被破壞,可以説整個⽂件系統結構就被破壞了

超級塊在每個塊組的開頭都有⼀份拷⻉(第⼀個塊組必須有,後⾯的塊組可以沒有)。為了保證⽂ 件系統在磁盤部分扇區出現物理問題的情況下還能正常⼯作,就必須保證⽂件系統的superblock信 息在這種情況下也能正常訪問。所以⼀個⽂件系統的superblock會在多個blockgroup中進⾏備份, 這些superblock區域的數據保持⼀致。

/*
 * Structure of the super block
 */
struct ext2_super_block
{
    __le32 s_inodes_count;
    /* Inodes count */
    __le32 s_blocks_count;
    /* Blocks count */
    __le32 s_r_blocks_count; /* Reserved blocks count */
    __le32 s_free_blocks_count;
    /* Free blocks count */
    __le32 s_free_inodes_count;
    __le32 s_first_data_block;
    /* Free inodes count */
    /* First Data Block */
    __le32 s_log_block_size; /* Block size */
    __le32 s_log_frag_size;
    /* Fragment size */
    __le32 s_blocks_per_group;
    /* # Blocks per group */
    __le32 s_frags_per_group; /* # Fragments per group */
    __le32 s_inodes_per_group;
    __le32 s_mtime;
    __le32 s_wtime;
    __le16 s_mnt_count;
    /* # Inodes per group */
    /* Mount time */
    /* Write time */
    /* Mount count */
    __le16 s_max_mnt_count;
    __le16 s_magic;
    __le16 s_state;
    __le16 s_errors;
    /* Maximal mount count */
    /* Magic signature */
    /* File system state */
    /* Behaviour when detecting errors */
    __le16 s_minor_rev_level; /* minor revision level */
    __le32 s_lastcheck;
    __le32 s_checkinterval;
    __le32 s_creator_os;
    __le32 s_rev_level;
    __le16 s_def_resuid;
    __le16 s_def_resgid;
    /* time of last check */
    /* max. time between checks */
    /* OS */
    /* Revision level */
    /* Default uid for reserved blocks */
    /* Default gid for reserved blocks */
    /*
     * These fields are for EXT2_DYNAMIC_REV superblocks only.
     *
     * Note: the difference between the compatible feature set and
     * the incompatible feature set is that if there is a bit set
     * in the incompatible feature set that the kernel doesn't
     * know about, it should refuse to mount the filesystem.
     *
     * e2fsck's requirements are more strict; if it doesn't know
     * about a feature in either the compatible or incompatible
     * feature set, it must abort and not try to meddle with
     * things it doesn't understand...
     */
    __le32 s_first_ino;              /* First non-reserved inode */
    __le16 s_inode_size;             /* size of inode structure */
    __le16 s_block_group_nr;         /* block group # of this superblock */
    __le32 s_feature_compat;         /* compatible feature set */
    __le32 s_feature_incompat;       /* incompatible feature set */
    __le32 s_feature_ro_compat;      /* readonly-compatible feature set */
    __u8 s_uuid[16];                 /* 128-bit uuid for volume */
    char s_volume_name[16];          /* volume name */
    char s_last_mounted[64];         /* directory where last mounted */
    __le32 s_algorithm_usage_bitmap; /* For compression */
    /*
     * Performance hints.  Directory preallocation should only
     * happen if the EXT2_COMPAT_PREALLOC flag is on.
     */
    __u8 s_prealloc_blocks;     /* Nr of blocks to try to preallocate*/
    __u8 s_prealloc_dir_blocks; /* Nr to preallocate for dirs */
    __u16 s_padding1;
    /*
     * Journaling support valid if EXT3_FEATURE_COMPAT_HAS_JOURNAL set.
     */
    __u8 s_journal_uuid[16]; /* uuid of journal superblock */
    __u32 s_journal_inum;    /* inode number of journal file */
    __u32 s_journal_dev;     /* device number of journal file */
    __u32 s_last_orphan;     /* start of list of inodes to delete */
    __u32 s_hash_seed[4];    /* HTREE hash seed */
    __u8 s_def_hash_version; /* Default hash version to use */
    __u8 s_reserved_char_pad;
    __u16 s_reserved_word_pad;
    __le32 s_default_mount_opts;
    __le32 s_first_meta_bg; /* First metablock block group */
    __u32 s_reserved[190];  /* Padding to the end of the block */
};

🌠GDT(GroupDescriptorTable)

塊組描述符表,描述塊組屬性信息,整個分區分成多個塊組就對應有多少個塊組描述符。每個塊組描述符存儲⼀個塊組的描述信息,如在這個塊組中從哪⾥開始是inodeTable,從哪⾥開始是Data Blocks,空閒的inode和數據塊還有多少個等等。塊組描述符在每個塊組的開頭都有⼀份拷⻉。

// 磁盤級blockgroup的數據結構
/*
 * Structure of a blocks group descriptor
 */
struct ext2_group_desc
{
    __le32 bg_block_bitmap;/* Blocks bitmap block */
    __le32 bg_inode_bitmap;/* Inodes bitmap */
    __le16 bg_free_blocks_count; /* Inodes table block*/
    __le32 bg_inode_table;/* Free blocks count */
    __le16 bg_free_inodes_count;/* Free inodes count */
    __le16 bg_used_dirs_count; /* Directories count */
    __le16 bg_pad;
    __le32 bg_reserved[3];
};

🌉塊位圖(BlockBitmap)

BlockBitmap中記錄着DataBlock中哪個數據塊已經被佔⽤,哪個數據塊沒有被佔⽤

🌉inode位圖(InodeBitmap)

每個bit表⽰⼀個inode是否空閒可⽤。

🌉i節點表(InodeTable)

  • 存放⽂件屬性如⽂件⼤⼩,所有者,最近修改時間等
  • 當前分組所有Inode屬性的集合
  • inode編號以分區為單位,整體劃分,不可跨分區

🌉Data Block

數據區:存放⽂件內容,也就是⼀個⼀個的Block。根據不同的⽂件類型有以下⼏種情況:

🌉塊位圖(BlockBitmap)

  • 對於普通⽂件,⽂件的數據存儲在數據塊中。
  • 對於⽬錄,該⽬錄下的所有⽂件名和⽬錄名存儲在所在⽬錄的數據塊中,除了⽂件名外,ls-l命令看到的其它信息保存在該⽂件的inode中。
  • Block 號按照分區劃分,不可跨分區

🌠inode和datablock映射(弱化)

  • inode內部存在_ _le32 i_block[EXT2_N_BLOCKS];/* Pointers to blocks */ , EXT2_N_BLOCKS =15,就是⽤來進⾏inode和block映射的
  • 這樣⽂件=內容+屬性,就都能找到了。

Ext系列文件系統--Ext2 文件系統的認識與構成_數據塊_02

思考: 請解釋:知道inode號的情況下,在指定分區,請解釋:對⽂件進⾏增、刪、查、改是在做什麼?

  1. 查詢操作
  • 查找文件屬性
  • 首先,根據inode號在inode表中定位對應的inode。inode表通常存儲在每個塊組中,通過inode號可以快速計算出inode在inode表中的位置。
  • 一旦找到inode,就可以讀取其中存儲的文件屬性,如文件類型(是普通文件、目錄、符號鏈接等)、訪問權限、文件大小、創建時間、修改時間和訪問時間等信息。
  • 讀取文件內容
  • inode中有一個i_block數組,它用於存儲文件數據塊的指針。對於小文件,文件數據可能直接通過i_block數組中的直接指針指向的數據塊來讀取。
  • 如果是較大的文件,可能會涉及間接指針。例如,一級間接指針指向的塊中存儲了其他數據塊的地址,通過這些地址可以找到文件的其他數據部分。按照這種方式,根據inode中的指針信息,就可以將文件的數據塊內容讀取出來。
  1. 增加操作(以增加文件內容為例)
  • 分配新的數據塊(如果需要)
  • 首先檢查塊位圖,找到未使用的數據塊。塊位圖記錄了每個數據塊的使用狀態,通過查找其中為0的位對應的塊,就可以確定空閒的數據塊。
  • 然後更新塊位圖,將對應的位設置為1,表示該塊已經被使用。
  • 更新inode指針信息
  • 如果文件原來的數據塊已經存滿,需要增加新的數據塊來存儲新增的文件內容。對於小文件,可能直接使用i_block數組中剩餘的直接指針來指向新分配的數據塊。
  • 對於較大的文件,可能需要更新間接指針。例如,如果一級間接指針指向的塊已經存滿了數據塊地址,就需要分配一個新的塊來存儲更多的數據塊地址,並更新間接指針指向這個新塊。
  • 同時,更新inode中的文件大小屬性,以反映文件內容的增加。
  1. 刪除操作
  • 釋放數據塊
  • 根據inode中的i_block指針,找到文件所佔用的數據塊。然後將這些數據塊在塊位圖中的對應位設置為0,表示這些塊已經空閒,可以被重新分配。
  • 釋放inode
  • 將inode在inode位圖中的對應位設置為0,表示這個inode已經空閒。同時,可能會清除inode表中對應的inode內容,這樣這個inode就可以被重新用於存儲新文件的屬性。
  1. 修改操作(以修改文件內容為例)
  • 小修改(文件大小不變)
  • 如果修改後的文件大小不變,只是內容改變,那麼根據inode中的i_block指針找到文件的數據塊,直接在這些數據塊中修改文件內容即可。
  • 大修改(文件大小改變)
  • 如果修改後的文件變大,需要先按照增加文件內容的方式分配新的數據塊,更新inode指針和文件大小屬性,然後將新的內容寫入新分配的數據塊和原來的數據塊中。
  • 如果修改後的文件變小,需要先釋放多餘的數據塊(將塊位圖中對應的位設置為0),然後更新inode中的文件大小屬性和i_block指針(如果有指針指向了釋放的數據塊,需要進行調整),最後修改剩餘數據塊中的文件內容。

🔴結論:

  • 分區之後的格式化操作,就是對分區進⾏分組,在每個分組中寫⼊SB、GDT、Block Bitmap、InodeBitmap等管理信息,這些管理信息統稱:⽂件系統
  • 只要知道⽂件的inode號,就能在指定分區中確定是哪⼀個分組,進⽽在哪⼀個分組確定是哪⼀個inode
  • 拿到inode⽂件屬性和內容就全部都有了

下⾯,通過touch⼀個新⽂件來看看如何⼯作。

[root@localhost linux]# touch abc
 [root@localhost linux]# ls -i abc
 263466 abc

Ext系列文件系統--Ext2 文件系統的認識與構成_數據塊_03

創建⼀個新⽂件主要有以下4個操作:

  1. 存儲屬性內核先找到⼀個空閒的i節點(這⾥是263466)。內核把⽂件信息記錄到其中。
  2. 存儲數據該⽂件需要存儲在三個磁盤塊,內核找到了三個空閒塊:300,500,800。將內核緩衝區的第⼀塊數據複製到300,下⼀塊複製到500,以此類推。
  3. 記錄分配情況⽂件內容按順序300,500,800存放。內核在inode上的磁盤分佈區記錄了上述塊列表。
  4. 添加⽂件名到⽬錄新的⽂件名abc。linux如何在當前的⽬錄中記錄這個⽂件?內核將⼊⼝(263466,abc)添加到⽬錄⽂件。⽂件名和inode之間的對應關係將⽂件名和⽂件的內容及屬性連接起來。