【Linux】Linux Ext2文件系統

下面以Linux的Ext2爲例介紹文件系統的組成。

Ext2採用了分立式目錄結構,即一個文件的目錄分爲目錄項和索引節點兩個部分。

 

Ext2的索引節點

在一個實際分立式目錄的文件系統中,索引節點(inode)主要需要兩部分內容來支持:一是inode結構;二是對於節點的操作函數。

Ext2的索引節點

Ext2的每個文件(或目錄)都有唯一的i節點ext2_inode,它保存了一個文件所有與存儲有關的屬性。

Linux在文件include/linux/ext2_fs.h中定義的i節點結構ext2_inode如下:

struct ext2_inode {
	__le16	i_mode;		/* 文件模式 */
	__le16	i_uid;		/* 文件擁有者uid的低16位 */
	__le32	i_size;		/* 文件大小 */
	__le32	i_atime;	/* 最後訪問時間 */
	__le32	i_ctime;	/* 創建時間 */
	__le32	i_mtime;	/* 修改時間 */
	__le32	i_dtime;	/* 刪除時間 */
	__le16	i_gid;		/* 塊組id的低16位 */
	__le16	i_links_count;	/* 鏈接計數,即文件別名的數目 */
	__le32	i_blocks;	/* 文件佔用的存儲塊數 */
	__le32	i_flags;	/* 標誌 */
	union {
		struct {
			__le32  l_i_reserved1;
		} linux1;
		struct {
			__le32  h_i_translator;
		} hurd1;
		struct {
			__le32  m_i_reserved1;
		} masix1;
	} osd1;				/* OS dependent 1 */
	__le32	i_block[EXT2_N_BLOCKS];/* 文件索引表 */
	__le32	i_generation;	/* File version (for NFS) */
	__le32	i_file_acl;	/* File ACL */
	__le32	i_dir_acl;	/* Directory ACL */
	__le32	i_faddr;	/* 碎片地址 */
	union {
		struct {
			__u8	l_i_frag;	/* 碎片數目 */
			__u8	l_i_fsize;	/* 碎片大小 */
			__u16	i_pad1;
			__le16	l_i_uid_high;	/* these 2 fields    */
			__le16	l_i_gid_high;	/* were reserved2[0] */
			__u32	l_i_reserved2;
		} linux2;
		struct {
			__u8	h_i_frag;	/* 碎片數目 */
			__u8	h_i_fsize;	/* 碎片大小 */
			__le16	h_i_mode_high;
			__le16	h_i_uid_high;
			__le16	h_i_gid_high;
			__le32	h_i_author;
		} hurd2;
		struct {
			__u8	m_i_frag;	/* 碎片數目 */
			__u8	m_i_fsize;	/* 碎片大小 */
			__u16	m_pad1;
			__u32	m_i_reserved2[2];
		} masix2;
	} osd2;				/* 與操作系統相關的數據 */
};

其中,最重要的成員i_mode和指針i_block[]。i_mode指定文件類型;而指針數組i_block[]則是文件索引表。

i_block[]指針數組的示意圖如下:

i_block[]共有15項,其中前12項爲直接指向文件數據塊的指針,後3項分別爲採用多級索引結構的“一次間接指針”、“二次間接指針”和“三次間接指針”。其作用與內存管理中的多級頁表類似,便於大型文件的存儲處理。

也就是說:如果文件比較小,其數據塊少於12個,其數據塊索引就放在i_block[]的前12項中,如果文件比較大,超過12個數據塊就需要分配間接塊來保存數據塊索引。

Ext2的i節點操作函數

爲了對Ext2的i節點進行操作,系統還定義了Ext2文件i節點的操作函數集:

struct inode_operations ext2_file_inode_operations = {
    .truncate = ext2_truncate,
#ifdef CONFIG_EXT2_FS_XATTR
    .setxattr = generic_setxattr,
    .getxattr = generic_getxattr,
    .listxattr = ext2_listxattr,
    .removexattr = generic_removexattr,
#endif
    .setattr = ext2_setattr,
    .permission = ext2_permission,
};

可以看到,這裏操作集中沒有熟悉的文件操作函數,這是因爲這都是對磁盤文件的底層操作,文件還要再經一層乃至多層的封裝才能變成我們所熟悉的函數。

 

Ext2的目錄文件及目錄項

Ext2的目錄文件實質上是一個目錄項列表,其中每一項都是一個ext2_dir_entry_2結構的數據。它所包含的主要信息:

  • 目錄項中文件名所對應的文件i節點號;
  • 文件類型;
  • 文件名稱。

在文件include/linux/ext2_fs.h中定義的目錄項結構ext2_dir_entry_2如下:

struct ext2_dir_entry_2 {
	__le32	inode;			/* 文件的i節點號 */
	__le16	rec_len;		/* 目錄項的長度 */
	__u8	name_len;		/* 文件名的長度 */
	__u8	file_type;        //文件類型
	char	name[EXT2_NAME_LEN];	/* 文件名 */
};

結構中的域file_type描述文件類型。不同文件類型的取值用枚舉定義如下:

enum {
	EXT2_FT_UNKNOWN,
	EXT2_FT_REG_FILE,            //普通文件
	EXT2_FT_DIR,                 //目錄
	EXT2_FT_CHRDEV,              //字符設備文件
	EXT2_FT_BLKDEV,              //塊設備文件
	EXT2_FT_FIFO,                //管道文件
	EXT2_FT_SOCK,                //Sock文件
	EXT2_FT_SYMLINK,
	EXT2_FT_MAX
};

按照通常的概念,目錄文件應該是ext2_dir_entry_2類型的數組,但Ext2沒有這樣做。爲了用戶方便,結構ext2_dir_entry_2中的文件名是一個可以根據文件名的長度變化的數組,這種做法就使得各個目錄項的長度並不相等,從而難以用數組來組成目錄文件。所以Ext2的目錄文件採用一個比較特殊的鏈表結構,如下圖:

在這種結構中,目錄項是連續存放的,而目錄項的連接則通過結構ext2_dir_entry_2中的域rec_len來實現的,即程序通過rec_len作爲偏移量來查找下一個目錄項。

每個目錄文件中的前兩項爲代表目錄自身的“.”和代表其上一級目錄(父目錄)的“..”。

每當用戶需要打開一個文件需要打開一個文件時,首先要指定待打開文件的路徑和名稱,文件系統會根據路徑和名稱搜索對應的目錄項;然後用該目錄項中的i節點號找到該文件的i節點;最後通過訪問i節點結構中的i_block[]數據塊來訪問文件。

目錄項、索引節點與文件數據塊之間的關係如下所示:

 

Ext2在磁盤上的存儲結構

Ext2文件系統把它所佔用的磁盤空間分成若干個塊組,如下所示:

每個塊組的內部結構如下圖所示:

每個塊組中都有一個內容完全相同的塊——超級塊,這個塊保存着Ext2整個文件系統的信息。在超級塊的後面,依次排序有:用來描述本塊組信息的塊組描述符表、用來表示本組內存儲塊使用情況的存儲塊管理位圖、用來記錄本塊組所有i節點被佔用情況的i節點管理位圖、本塊組的i節點表以及用來存儲各種文件的數據塊五個部分。

Ext2的超級塊

像一本書需要一個前言一樣,文件系統也需要有個類似的說明部分,但它說明的是文件系統基本信息,目的是使文件系統的使用者(操作系統)可以瞭解文件系統的結構、類型等,這個說明部分叫做文件系統的超級塊。不同的文件系統具有不同的超級塊。系統管理員及系統可以利用超級塊中的信息來對文件系統進行維護。

照理說,每個文件系統只要有一個超級塊就夠了,但Ext2爲了保險起見,在每一個塊組中都配置了一個超級塊。在正常情況下,Ext2只使用第一個塊組(塊組0)中的超級塊,而其他塊組中的超級塊只是一個備份。

在文件include/linux/ext2_fs.h中定義的超級塊數據結構ext2_super_block如下:

struct ext2_super_block {
	__le32	s_inodes_count;		/* 文件系統中節點的總數 */
	__le32	s_blocks_count;		/* 文件系統中塊的總數 */
	__le32	s_r_blocks_count;	/* 超級用戶保留塊的數目 */
	__le32	s_free_blocks_count;	/* 空閒塊的總數目 */
	__le32	s_free_inodes_count;	/* 空閒索引節點總數 */
	__le32	s_first_data_block;	/* 第一個數據塊 */

	__le32	s_log_block_size;	/* Block size */
	__le32	s_log_frag_size;	/* Fragment size */

	__le32	s_blocks_per_group;	/* 每個塊組中的塊數 */
	__le32	s_frags_per_group;	/* 每組中的片數 */
	__le32	s_inodes_per_group;	/* 每組中的節點數 */
	__le32	s_mtime;		/* 文件系統的安裝時間 */
	__le32	s_wtime;		/* 對超級塊寫操作的左後時間 */
	__le16	s_mnt_count;		/* 文件系統的安裝計數 */
	__le16	s_max_mnt_count;	/* 文件系統的最大安裝數 */
	__le16	s_magic;		/* 幻數 */
	__le16	s_state;		/* 文件系統的狀態 */
	__le16	s_errors;		/* Behaviour when detecting errors */
	__le16	s_minor_rev_level; 	/* minor revision level */
	__le32	s_lastcheck;		/* time of last check */
	__le32	s_checkinterval;	/* max. time between checks */
	__le32	s_creator_os;		/* OS */
	__le32	s_rev_level;		/* Revision level */
	__le16	s_def_resuid;		/* Default uid for reserved blocks */
	__le16	s_def_resgid;		/* Default gid for reserved blocks */
	__le32	s_first_ino; 		/* First non-reserved inode */
	__le16   s_inode_size; 		/* size of inode structure */

	__le16	s_block_group_nr; 	/* 本超級塊所在的塊組號 */

	__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位uuid */
	char	s_volume_name[16]; 	/* 卷名 */

	char	s_last_mounted[64]; 	/* directory where last mounted */
	__le32	s_algorithm_usage_bitmap; /* For compression */
	__u8	s_prealloc_blocks;	/* Nr of blocks to try to preallocate*/
	__u8	s_prealloc_dir_blocks;	/* Nr to preallocate for dirs */
	__u16	s_padding1;
	__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 */
};

由上述定義中可知,Ext2中的超級塊中主要具有如下一些內容:

  • 幻數。文件系統的一個標識,在安裝文件系統時用於確認Ext2文件系統;
  • 文件系統的版本號;
  • 文件系統安裝計數;
  • 超級塊所在的塊組號;
  • 數據塊的大小;
  • 塊組中數據塊的數目;
  • 文件系統中空閒塊的數目;
  • 文件系統中空閒索引節點的數目;
  • 文件系統中第一個索引節點的號碼。

在Ext2文件系統中,第一個索引節點時根目錄的入口。

塊組描述符表

Ext2的一個塊組可以看做文件系統空間的一個分區,與超級塊的用途類似,爲了向使用者提供塊組的相關組織信息,每個塊組有一個塊組描述符表,其中主要提供塊組的位圖存放位置和i節點位圖存放位置等信息。

Linux在文件include/linux/ext2_fs.h中定義的塊組描述符表結構ext2_group_desc如下:

struct ext2_group_desc
{
	__le32	bg_block_bitmap;		/* 指向塊組的塊位圖的指針 */
	__le32	bg_inode_bitmap;		/* 指向i節點位圖的指針 */
	__le32	bg_inode_table;		/* i節點表的首地址 */
	__le16	bg_free_blocks_count;	/* 本組塊空閒塊的數目 */
	__le16	bg_free_inodes_count;	/* 本組塊空閒i節點的數目 */
	__le16	bg_used_dirs_count;	/* 本組塊分配給目錄文件的i節點數目 */
	__le16	bg_pad;
	__le32	bg_reserved[3];
};

塊組的塊位圖

Ext2文件系統用位圖來記錄塊組中數據塊的使用情況。數據塊位圖中的每一位表示該塊組中每一個塊的使用情況,如果爲1,則表示該對應塊已經被分配佔用;如果爲0,則表示該位還未被分配,是空閒塊。

塊組的i節點位圖

Ext2文件系統用位圖來記錄塊組中i節點使用情況。i節點位圖中的每一位表示該塊組中每一個i節點的使用情況,如果爲1,則表示該結點已被分配佔用;如果爲0,則表示結點還未被分配,是空閒節點。

塊組的i節點表

顧名思義,i節點表就是存放文件i節點的表格。每個塊組中所有i節點都按i節點號的順序存儲在i節點表中。i節點表通常需要佔用若干個數據塊。

 

Ext2文件的用戶操作函數集

作爲一個爲用戶服務的文件系統,Ext2位用戶提供的文件操作函數集如下:

const struct file_operations ext2_file_operations = {
	.llseek		= generic_file_llseek,
	.read		= do_sync_read,
	.write		= do_sync_write,
	.aio_read	= generic_file_aio_read,
	.aio_write	= generic_file_aio_write,
	.unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= ext2_compat_ioctl,
#endif
	.mmap		= generic_file_mmap,
	.open		= generic_file_open,
	.release	= ext2_release_file,
	.fsync		= ext2_sync_file,
	.splice_read	= generic_file_splice_read,
	.splice_write	= generic_file_splice_write,
};

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章