EXT文件系统族-Ext2文件系统

一、学习内容

1.Ext2物理结构
2.Ext2数据结构
3.Ext2文件系统操作

二、Ext2物理结构

Ext2第二代扩展文件系统(Second extended filesystem),是LINUX内核使用的文件系统
Ext2文件系统特性:
1.磁盘块分为组
2.支持快速符号链接
3.在启动时支持对文件系统的状态进行自动的一致性检查
4.Ext2索引节点中引入新的字段(块、删除逻辑、日志)
必须建立各种结构(在内核中定义为C语言数据类型),来存放文件系统的数据,包括文件内容、目录层次结构的表示、相关的管理数据(如访问权限与用户和组的关联),以及用于管理文件系统内部信息的元数据。这些对从块设备读取数据进行分析而言,都是必要的。这些结构的持久副本显然需要存储在硬盘上,这样数据再两次会话之间不会丢失。下一次启动重新激活内核时,数据仍然时可用的。因为硬盘和物理内存的需求不同,同一数据结构通常会有两个版本,一个用在磁盘上的持久存储,另一个用在内存中的处理
Ext2文件系统专注于高性能
1.支持可变块长,使得文件系统能够处理预期的应用;
2.快速符号链接,如果链接目标的路径足够短,则将其存储在inode自身中;
3.将扩展能力集成到设计当中,从旧版本迁移到新版本时,无需重新格式化和重新加载硬盘;
4.Ext2有一个非常大的有点(与现代文件系统相比,Ext2文件系统的代码非常紧凑,使用不到10000行代码就足以实现了)。
块(block)的两个含义
1.有些文件系统存储在面向块的设备上,与设备之间的数据传输都以块为单位进行,不会传输单个字符。
2.Ext2文件系统是一种基于块的文件系统,他将硬盘分为若干块,每个块的长度都相同,按块管理元数据和文件内容。
块组
块组是该文件系统的基本成分,容纳文件系统其他结构,每个文件系统都由大量块组成,直接在硬盘上相继排列。
块组是Ext2文件系统的核心要素。块组是该文件系统的基本成分,容纳了文件系统的其他结构。
EXT文件系统族-Ext2文件系统_第1张图片
为什么Ext2文件系统允许这样浪费空间?有两个原因,可以证明提供额外的空间的做法是正确的。
1.如果系统崩溃破坏的超级块,有关文件系统结构和内容所有信息都会丢失。如果有冗余的副本,该信息是可能恢复的。
2.通过使文件和管理数据尽可能的接近,减少磁头寻道和旋转,这可以提高文件系统的性能。
超级块:用于存储文件系统自身元数据的核心结构,内核只使用第一个块组的超级块读取文件系统的元信息。
块描述符:包含的信息反映文件系统各个块组的状态,比如:块组中空闲块和inode数目,每个块组都包含了文件系统中所有块组的组描述符信息。
indoe表:包含块组中所有的inode,inode用于保存文件系统中与各个文件和目录相关的所有元数据。
数据块:包含文件系统中的文件的有用数据。

三、EXT2的数据结构

超级块
超级块:超级块是文件系统的核心结构,保存了文件系统所有的特征数据。内核在装载文件系统时,最先看到的就是超级块的内容。使用ext2_super_block结构定义超级块。

/*
 * Structure of the super block
 */
struct ext2_super_block {
	__le32	s_inodes_count;		/* Inodes count (inode数目)*/
	__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;	/* Free inodes count(空闲inode数目) */
	__le32	s_first_data_block;	/* 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;	/* # Inodes per group (每个块组的inode数目))*/
	__le32	s_mtime;		/* Mount time (装载时间)*/
	__le32	s_wtime;		/* Write time (写入时间)*/
	__le16	s_mnt_count;		/* Mount count (装载计数)*/
	__le16	s_max_mnt_count;	/* Maximal mount count (最大装载数)*/
	__le16	s_magic;		/* Magic signature (魔数,标记文件系统类型)*/
	__le16	s_state;		/* File system 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 (能够使用的保留块的默认uid)*/
	__le16	s_def_resgid;		/* Default gid for reserved blocks (能够使用保留块的默认GID)*/
	/*
	 * 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 (第一个非保留的inode)*/
	__le16   s_inode_size; 		/* size of inode structure (inode结构的长度)*/
	__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 卷的128位的uuid*/
	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 */
};

s_magic字段存储一个魔数,该数值确认装载的文件系统确实是Ext2类型。s_minor_rev_level用于区分文件系统的不同版本。
组描述符
前面如图所示,每个块组都有一个组描述符的集合,紧随超级块之后,其中保存的信息反应了文件系统每个块组的内容,因此,不仅关系到当前块组的数据块,还与其他块组的数据块和inode块相关。用于定义单个组描述符的数据结构比超级块结构短的多。

/*
 * Structure of a blocks group descriptor
 */
struct ext2_group_desc
{
	__le32	bg_block_bitmap;		/* Blocks bitmap block (块位图块)*/
	__le32	bg_inode_bitmap;		/* Inodes bitmap block (inode位图块)*/
	__le32	bg_inode_table;		/* Inodes table block (inode表块)*/
	__le16	bg_free_blocks_count;	/* Free blocks count (空闲块数目)*/
	__le16	bg_free_inodes_count;	/* Free inodes count (空闲inode数目)*/
	__le16	bg_used_dirs_count;	/* Directories count (目录数目)*/
	__le16	bg_pad; 			
	__le32	bg_reserved[3];
};

inode
每个块组都包含一个inode位图和一个本地的inode表inode表可能延续到几个块。位图的内容与本地块组相关,不会复制到文件系统中的其他位置。inode位图用于概述块组中已用和空闲的inode

/*
 * Structure of an inode on the disk
 */
struct ext2_inode {
	__le16	i_mode;		/* File mode (文件模式)*/
	__le16	i_uid;		/* Low 16 bits of Owner Uid (所有者UID的低16位)*/
	__le32	i_size;		/* Size in bytes (长度,按字节计算)*/
	__le32	i_atime;	/* Access time (访问时间)*/
	__le32	i_ctime;	/* Creation time (创建时间)*/
	__le32	i_mtime;	/* Modification time (修改时间)*/
	__le32	i_dtime;	/* Deletion Time (删除时间)*/
	__le16	i_gid;		/* Low 16 bits of Group Id (组ID的低16位)*/
	__le16	i_links_count;	/* Links count (链接计数)*/
	__le32	i_blocks;	/* Blocks count (块数据)*/
	__le32	i_flags;	/* File 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];/* Pointers to blocks 块指针*/
	__le32	i_generation;	/* File version (for NFS) (文件版本,用于NFS)*/
	__le32	i_file_acl;	/* File ACL */
	__le32	i_dir_acl;	/* Directory ACL */
	__le32	i_faddr;	/* Fragment address 碎片地址*/
	union {
		struct {
			__u8	l_i_frag;	/* Fragment number */
			__u8	l_i_fsize;	/* Fragment size */
			__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;	/* Fragment number */
			__u8	h_i_fsize;	/* Fragment size */
			__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;	/* Fragment number */
			__u8	m_i_fsize;	/* Fragment size */
			__u16	m_pad1;
			__u32	m_i_reserved2[2];
		} masix2;
	} osd2;				/* OS dependent 2 */
};

四、目录和文件

文件系统的拓扑结构,在LINUX中,目录是一种特殊的文件,其中是inode指针和对应的文件名列表,表示了当前目录下的文件和子目录,对于EXT2文件系统,也是这样,每个目录表示为一个inode,会对其分配数据块,数据块中包含了用于描述目录项的结构。在内核源代码中目录项结构定义如下,

/*
 * The new version of the directory entry.  Since EXT2 structures are
 * stored in intel byte order, and the name_len field could never be
 * bigger than 255 chars, it's safe to reclaim the extra byte for the
 * file_type field.
 */
struct ext2_dir_entry_2 {
	__le32	inode;			/* Inode number (inode编号)*/
	__le16	rec_len;		/* Directory entry length */
	__u8	name_len;		/* Name length */
	__u8	file_type;
	char	name[];			/* File name, up to EXT2_NAME_LEN */
};
/*
 * Ext2 directory file types.  Only the low 3 bits are used.  The
 * other bits are reserved for now.
 */
enum {
	EXT2_FT_UNKNOWN		= 0,
	EXT2_FT_REG_FILE	= 1,
	EXT2_FT_DIR		= 2, //普通文件
	EXT2_FT_CHRDEV		= 3,//字符特殊文件和快特殊文件
	EXT2_FT_BLKDEV		= 4,
	EXT2_FT_FIFO		= 5,//命名管道
	EXT2_FT_SOCK		= 6,//套接字
	EXT2_FT_SYMLINK		= 7,
	EXT2_FT_MAX
};

五、EXT2文件系统操作

虚拟文件系统和具体实现之间的关联大体上由3个结构建立,结构中包含了一系列的函数指针。所有的文件系统都必须实现该关联。
1.用于操作文件内容的操作保存在file_operations中;
2.用于此类文件对象自身操作的操作保存在inode_operations中;
3.用于一般地址空间操作保存在address_space_operations中。

(1)Ext2文件系统对不同的文件类型提供了不同的file_operations实例:

/*
 * We have mostly NULL's here: the current defaults are ok for
 * the ext2 filesystem.
 */
const struct file_operations ext2_file_operations = {
	.llseek		= generic_file_llseek,
	.read_iter	= generic_file_read_iter,
	.write_iter	= generic_file_write_iter,
	.unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= ext2_compat_ioctl,
#endif
	.mmap		= ext2_file_mmap,
	.open		= dquot_file_open,
	.release	= ext2_release_file,
	.fsync		= ext2_fsync,
	.splice_read	= generic_file_splice_read,
	.splice_write	= iter_file_splice_write,
};

(2)目录也有自身的file_operations实例,具体源码如下:

const struct file_operations ext2_dir_operations = {
	.llseek		= generic_file_llseek,
	.read		= generic_read_dir,
	.iterate	= ext2_readdir,
	.unlocked_ioctl = ext2_ioctl,
#ifdef CONFIG_COMPAT
	.compat_ioctl	= ext2_compat_ioctl,
#endif
	.fsync		= ext2_fsync,
};

(3)目录有更多的inode操作源码如下:

const struct inode_operations ext2_dir_inode_operations = {
	.create		= ext2_create,
	.lookup		= ext2_lookup,
	.link		= ext2_link,
	.unlink		= ext2_unlink,
	.symlink	= ext2_symlink,
	.mkdir		= ext2_mkdir,
	.rmdir		= ext2_rmdir,
	.mknod		= ext2_mknod,
	.rename		= ext2_rename,
#ifdef CONFIG_EXT2_FS_XATTR
	.setxattr	= generic_setxattr,
	.getxattr	= generic_getxattr,
	.listxattr	= ext2_listxattr,
	.removexattr	= generic_removexattr,
#endif
	.setattr	= ext2_setattr,
	.get_acl	= ext2_get_acl,
	.set_acl	= ext2_set_acl,
	.tmpfile	= ext2_tmpfile,
};

(4)文件系统和块层通过address_space_operations关联。

const struct address_space_operations ext2_aops = {
	.readpage		= ext2_readpage,
	.readpages		= ext2_readpages,
	.writepage		= ext2_writepage,
	.write_begin		= ext2_write_begin,
	.write_end		= ext2_write_end,
	.bmap			= ext2_bmap,
	.direct_IO		= ext2_direct_IO,
	.writepages		= ext2_writepages,
	.migratepage		= buffer_migrate_page,
	.is_partially_uptodate	= block_is_partially_uptodate,
	.error_remove_page	= generic_error_remove_page,
};

(5)此结构(super_operations)用于与超级块交互(读、写、分配inode)。

static const struct super_operations ext2_sops = {
	.alloc_inode	= ext2_alloc_inode,
	.destroy_inode	= ext2_destroy_inode,
	.write_inode	= ext2_write_inode,
	.evict_inode	= ext2_evict_inode,
	.put_super	= ext2_put_super,
	.sync_fs	= ext2_sync_fs,
	.freeze_fs	= ext2_freeze,
	.unfreeze_fs	= ext2_unfreeze,
	.statfs		= ext2_statfs,
	.remount_fs	= ext2_remount,
	.show_options	= ext2_show_options,
#ifdef CONFIG_QUOTA
	.quota_read	= ext2_quota_read,
	.quota_write	= ext2_quota_write,
	.get_dquots	= ext2_get_dquots,
#endif
};

你可能感兴趣的:(linux内核源码学习,linux,服务器,运维)