@TOC
📝ext2 ⽂件系統
🌠 宏觀認識
所有的準備⼯作都已經做完,是時候認識下⽂件系統了。我們想要在硬盤上儲⽂件,必須先把硬盤格式化為某種格式的⽂件系統,才能存儲⽂件。⽂件系統的⽬的就是組織和管理硬盤中的⽂件。在Linux 系統中,最常⻅的是ext2系列的⽂件系統。其早期版本為ext2,後來⼜發展出ext3和ext4。ext3 和ext4雖然對ext2進⾏了增強,但是其核⼼設計並沒有發⽣變化,我們仍是以較⽼的ext2作為演⽰對象。
ext2⽂件系統將整個分區劃分成若⼲個同樣⼤⼩的塊組(BlockGroup),如下圖所⽰。只要能管理⼀個分區就能管理所有分區,也就能管理所有磁盤⽂件。
上圖中啓動塊(BootBlock/Sector)的⼤⼩是確定的,為1KB,由PC標準規定,⽤來存儲磁盤分區信息和啓動信息,任何⽂件系統都不能修改啓動塊。啓動塊之後才是ext2⽂件系統的開始。
🌉 Block Group
ext2⽂件系統會根據分區的⼤⼩劃分為數個BlockGroup。⽽每個BlockGroup都有着相同的結構組成。 政府管理各區的例⼦:
- 類比政府管理各區來理解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映射的- 這樣⽂件=內容+屬性,就都能找到了。
思考: 請解釋:知道inode號的情況下,在指定分區,請解釋:對⽂件進⾏增、刪、查、改是在做什麼?
- 查詢操作
- 查找文件屬性:
- 首先,根據inode號在inode表中定位對應的inode。inode表通常存儲在每個塊組中,通過inode號可以快速計算出inode在inode表中的位置。
- 一旦找到inode,就可以讀取其中存儲的文件屬性,如文件類型(是普通文件、目錄、符號鏈接等)、訪問權限、文件大小、創建時間、修改時間和訪問時間等信息。
- 讀取文件內容:
- inode中有一個
i_block數組,它用於存儲文件數據塊的指針。對於小文件,文件數據可能直接通過i_block數組中的直接指針指向的數據塊來讀取。 - 如果是較大的文件,可能會涉及間接指針。例如,一級間接指針指向的塊中存儲了其他數據塊的地址,通過這些地址可以找到文件的其他數據部分。按照這種方式,根據inode中的指針信息,就可以將文件的數據塊內容讀取出來。
- 增加操作(以增加文件內容為例)
- 分配新的數據塊(如果需要):
- 首先檢查塊位圖,找到未使用的數據塊。塊位圖記錄了每個數據塊的使用狀態,通過查找其中為0的位對應的塊,就可以確定空閒的數據塊。
- 然後更新塊位圖,將對應的位設置為1,表示該塊已經被使用。
- 更新inode指針信息:
- 如果文件原來的數據塊已經存滿,需要增加新的數據塊來存儲新增的文件內容。對於小文件,可能直接使用
i_block數組中剩餘的直接指針來指向新分配的數據塊。 - 對於較大的文件,可能需要更新間接指針。例如,如果一級間接指針指向的塊已經存滿了數據塊地址,就需要分配一個新的塊來存儲更多的數據塊地址,並更新間接指針指向這個新塊。
- 同時,更新inode中的文件大小屬性,以反映文件內容的增加。
- 刪除操作
- 釋放數據塊:
- 根據inode中的
i_block指針,找到文件所佔用的數據塊。然後將這些數據塊在塊位圖中的對應位設置為0,表示這些塊已經空閒,可以被重新分配。
- 釋放inode:
- 將inode在inode位圖中的對應位設置為0,表示這個inode已經空閒。同時,可能會清除inode表中對應的inode內容,這樣這個inode就可以被重新用於存儲新文件的屬性。
- 修改操作(以修改文件內容為例)
- 小修改(文件大小不變):
- 如果修改後的文件大小不變,只是內容改變,那麼根據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
創建⼀個新⽂件主要有以下4個操作:
- 存儲屬性內核先找到⼀個空閒的i節點(這⾥是263466)。內核把⽂件信息記錄到其中。
- 存儲數據該⽂件需要存儲在三個磁盤塊,內核找到了三個空閒塊:300,500,800。將內核緩衝區的第⼀塊數據複製到300,下⼀塊複製到500,以此類推。
- 記錄分配情況⽂件內容按順序300,500,800存放。內核在inode上的磁盤分佈區記錄了上述塊列表。
- 添加⽂件名到⽬錄新的⽂件名abc。linux如何在當前的⽬錄中記錄這個⽂件?內核將⼊⼝(263466,abc)添加到⽬錄⽂件。⽂件名和inode之間的對應關係將⽂件名和⽂件的內容及屬性連接起來。