Linux EXT4文件系统分析

时间:2022-08-17 03:14:10

Linux EXT4文件系统分析

摘要:介绍EXT4文件系统的原理和数据结构,重点与EXT3文件系统做了比较,通过分析WRITE操作来解释EXT4文件系统的工作,为用户选择Linux内核提供参考。

关键词:EXT4;日志;句柄;i节点

中图分类号:TP391文献标识码:A文章编号:1009-3044(2011)14-3443-04

The Analysis of Linux EXT4 File System

LU Ya-wen

(Hangzhou Vocational College of Science and Technology, Hangzhou 310016, China)

Abstract: Compared with EXT3 file system, the theory and data structure of EXT4 file system was introduced.By analyzing the WRITE operation,the procedcure of EXT4 file system was explained,and the study will supply some references for users who choosing core of Linux.

Key words: EXT4; daily record; handle; inode

EXT4文件系统是日志文件系统,100%兼容EXT3文件系统,与EXT3文件系统相比的主要区别是它能快速更新文件存储。计算机开始从磁盘上读取或写入数据都必须保证文件系统中文件与目录的一致性,所有日志文件中的数据均以数据块的形式存放在存储设备中,当磁盘分区时文件系统即被创建,按照文件形式、目录形式支持存储数据,组织数据的使用。EXT4提供并使用了一个通用日志层 (jbd) [1],该层既可在文件系统中使用,还能够应用到其它设备中,对NVRAM设备,EXT4就能支持。当由于软件或硬件错误导致文件系统崩溃时, EXT4使用与e2fsck同样代码来修复崩溃的文件系统,因此在出现数据崩溃时,EXT4具有和EXT3同样的防止数据丢失的优点[2]。但是,上述这些优点不是EXT4独有的,但只有EXT4才尽数具备,这正是EXT4的优势。

1 EXT4文件系统的重要数据结构

以下的数据结构都来自2.6.28内核。

1.1 超级块super_block

/include/linux/EXT4_fs.h

struct EXT4_super_block {

/*00*/ __le32 s_inodes_count; /* 节点数目 */

__le32 s_blocks_count; /* 文件块数 */

__le32 s_r_blocks_count; /* 保留未用的文件块数 */

__le32 s_free_blocks_count; /* 可用的文件块数 */

/*10*/ __le32 s_free_inodes_count; /* 可用的节点数 */

__le32 s_first_data_block; /* 第一个数据块的索引 */

__le32 s_log_block_size; /* 数据块大小 */

__le32 s_log_frag_size; /* 文件碎片大小 */

/*20*/ __le32 s_blocks_per_group; /* 每一组文件块的数目 */

__le32 s_frags_per_group; /* 每一组碎片数目 */

__le32 s_inodes_per_group; /* # 每一组节点数目 */

__le32 s_mtime; /* 最近被安装到内存的时间 */

…………

/*150*/ __le32s_blocks_count_hi;/* 文件块数的高32位 */

__le32s_r_blocks_count_hi;/*保留未用的文件块数高32位*/

__le32s_free_blocks_count_hi;} /* 可用文件块数高32位 */

合适的特定设备和不适合的特定设备的不同是在,如果在不合适的特定设备中有一位设备是内核无法识别的,内核将会拒绝启动文件系统。在EXT4中,e2fsck的要求更加严格,如果他既不能在合适的特定设备中,也不能在不合适的特定设备中识别出一个设备 ,他一定会终止程序并且不会将它所不能识别的设备模块化。

与日志相关的数据结构,只有在EXT4文件系统中才会起作用,同样在EXT3文件系统中也有定义,但是在EXT4文件系统中配合日志文件数据结构才能起到日志文件的功能。

1.2 组描述符 Group Descriptor

组描述符合超级块一样,记录的信息与整个文件系统相关。当某一个组的超级块或inode受损时,这些信息可以用于恢复文件系统。因此,为了更好的维护文件系统,每个块组中都保存关于文件系统的备份信息。

struct EXT4_group_desc

{__le32 bg_block_bitmap; /* 文件块位图所在的块的索引*/

__le32 bg_inode_bitmap; /* 存放文件节点位图的块的索引*/

__le32 bg_inode_table; /* 文件节点表在外存中的第一个块的索引*/

__le16 bg_free_blocks_count; /* 可用的文件块数 */

__le16 bg_free_inodes_count; /* 可用的节点数 */

__le16 bg_used_dirs_count; /* 使用中的目录数 */

__u16 bg_flag;/*用于32位地址对齐*/

__u32 bg_reserved[3];

__le32bg_block_bitmap_hi; /*文件块位图所在的块的索引的高32位*/

__le32bg_inode_bitmap_hi; /*存放文件节点位图的块的索引高32位*/

__le32bg_inode_table_hi;};/*文件节点表在外存中的第一个块的索引高32位*/

1.3i节点数据结构

struct EXT4_inode {

__le16 i_mode; /* 文件模式,表示文件类型以及存取权限 */

__le16 i_uid; /* 所属用户的id的低16位 */

__le32 i_size; /* 节点字节大小 */

__le32 i_atime; /* 节点存取时间 */

__le32 i_ctime; /* 节点创建时间 */

__le32 i_mtime; /* 节点修改时间 */

__le32 i_dtime; /* 节点时间 */

__le16 i_gid; /* 组标志的低16位 */

__le16 i_links_count; /* 连接数目 */

__le32 i_blocks; /* I节点文件块数 */

__le32 i_flags; /* 文件标志 */

union {

struct {

__le32l_i_reserved1;

} linux1;

struct {

__le32h_i_translator;

} hurd1;

struct {

__le32m_i_reserved1;

} masix1;

} osd1; /* OS dependent 1 */

__le32 i_block[EXT4_N_BLOCKS];/* 文件块索引数组,数组大小15*/

__le32 i_generation; /* 文件修改 (for NFS) */

__le32 i_file_acl; /* 文件 ACL */

__le32 i_dir_acl; /* 目录 ACL */

__le32 i_faddr; /* 碎片地址 */

union {

struct {

__le8 l_i_frag; /* 碎片号 */

__le8 l_i_fsize; /* 碎片大小 */

__le16 i_pad1;

__le16 l_i_uid_high; /* I节点用户id高位 */

__le16 l_i_gid_high; /* I节点组号高位 */

__le32 l_i_reserved2;

} linux2;//1级间接指针数据结构

struct {

__le8 h_i_frag; /* 碎片号 */

__le8 h_i_fsize; /* 碎片大小 */

__le16 h_i_mode_high;

__le16 h_i_uid_high;

__le16 h_i_gid_high;

__le32 h_i_author;

} hurd2; //直接指向物理块的节点的数据结构的补充

struct {

__le8 m_i_frag; /* 碎片号 */

__le8 m_i_fsize; /* 碎片大小 */

__le16 m_pad1;

__le32 m_i_reserved2[2];

} masix2;//2级间接指针数据结构

} osd2; /* OS dependent 2 */

__le16i_EXTra_isize;

__le16i_pad1;

__le32i_ctime_EXTra;/* 节点扩展的创建时间(nsec

__le32i_mtime_EXTra;/* 扩展的修改时间(nsec

__le32i_atime_EXTra;/* 扩展的存储时间(nsec

__le32i_crtime; /* 文件创建时间*/

__le32i_crtime_EXTra;};/* 扩展文件创建时间 (nsec

上述结构体中有两个联合体,是为了节省空间考虑,因为i节点的索引有三种情况前12个直接指向物理块,第13个是一级间接指针,第14个是二级间接指针,第15个是三级间接指针,将这三种情况都写在一个联合体中,根据实际的情况选择节点号,根据节点的类型来选择用联合体中的那个数据结构。与EXT3不同的是,在 EXT4 文件系统中,增加了5个项,用于扩充索引节点,通过这个结构解决了这对于对精度要求很高的程序的要求。

1.4 i节点的扩展信息的数据结构

这个结构体是日志式文件系统所特有的,用来支持完成日志文件的各种操作[3]。也是2.6内核与2.4内核的不同之处。i节点文件块组是包含一系列文件节点的块组。包含了节点的生命期,它通常用于为分配文件块的决定。我们将文件的数据块放到他的i节点块的附近,新的节点放到它的父目录节点的附近。存放i节点大小的栈不在内存中而在外存上。在缩减的过程中,节点的大小被虚拟文件系统调用EXT4_truncate函数而设置成新的大小,但是文件系统不会将节点的磁盘大小设为0,除非这样的缩减真的执行了。目的在于节点的大小总是表示正在使用的文件的块数。当节点数据写入的时候我们要重新改写节点磁盘的大小来代替节点的大小[4]。只有在缩减的进行时节程执点的大小和节点所在的磁盘空间的大小才会不一样,其他情况都应该是一致的。只有在执行文件块增长或缩减的时候存放节点大小的栈才会被改动。

struct EXT4_inode_info {

__le32 i_data[15];

__le32 i_flags; //i节点标志

#ifdef EXT4_FRAGMENTS

__le32 i_faddr; //第一个碎片的索引

__le8 i_frag_no;//碎片数目

__le8 i_frag_size;//碎片大小

#endif

__le32 i_file_acl;

__le32 i_dir_acl;

__le32 i_dtime;

__le32 i_block_group;//i节点文件块组

__le32 i_state; /* EXT3的动态的状态标志 */

__le32 i_nEXT_alloc_block;

/* i_nEXT_alloc_block 是在该文件中最近分配的块的逻辑号。当然,他没

有命名,我们用这个来决定相关的分配请求所分配的文件块的逻辑号。

/*

__le32 i_nEXT_alloc_goal;

/* i_nEXT_alloc_goal是下一个分配块号的物理标志。他是与此文件分配的文件块号最相近的物理块号。当用户请求分配一个新的物理块的时候将这个物理块分配给用户*/

struct semaphore truncate_sem;//元数据块,数据的修改信息等

struct inode vfs_inode;

/*虚拟文件系统的节点,通过这个数据可以将日志文件系统EXT4和linux文件系统的顶层虚拟文件系统连接起来*/

};

1.5 句柄handle_t数据结构

此结构是EXT4文件系统用来实现日志式文件操作的有利的数据结构。

struct handle_s

{ transaction_t *h_transaction;//文件操作过程

int h_buffer_credits;//要被弄脏的缓冲区的数目

int h_ref;//句柄中的参照数

int h_err;//出错数

struct list_head h_jcb;// 磁盘操作的头节点

unsigned int h_sync: 1; /* sync-on-close */

unsigned int h_jdata: 1; /* 日志数据 */

unsigned int h_aborted: 1; /* 句柄中致命的错误 */

};

transaction_t 类型是日志系统的一个内容。它记录下了数据在各种状态下的改变情况。

各种状态为:

* RUNNING: 正在进行新的修改

* LOCKED: 修改在进行但是句柄不接受这样的改变

* RUNDOWN: 修改在整理中但是还没有完成对缓冲区的修改。

* FLUSH: 所有的修改完成了但是我们还没有写到磁盘上。

* COMMIT: 所有的数据都在磁盘上,写操作执行

* FINISHED: 完成保持准备接受下一次修改

2 EXT4的文件操作

2.1 EXT的文件操作的数据结构

struct file_operations EXT3_file_operations = {

.llseek = generic_file_llseek,

.read = do_sync_read,

.write = do_sync_write,

.aio_read = generic_file_aio_read,

.aio_write = EXT3_file_write,

.readv = generic_file_readv,

.writev = generic_file_writev,

.ioctl = EXT3_ioctl,

.mmap = generic_file_mmap,

.open = EXT3_open_file,

.release = EXT3_release_file,

.fsync = EXT3_sync_file,

.sendfile = generic_file_sendfile,

};

这个结构体说明了EXT4文件系统的read操作是由do_sync_read函数来实现的,write操作是由do_sync_write函数来实现的,以此类推。只要我们找到相关的函数就能分析EXT4文件系统是怎么实现读写操作的。

2.2 EXT4的写操作

以下分析写操作的过程,因为日志式的文件的写操作和其他的文件系统有很大的不同所以以写操作为例:

图1是写操作执行时各个函数嵌套的顺序图。

① file->f_op->write即调用EXT4文件系统中的write操作,在operation结构的解释中指出write操作用do_sync_write操作完成。

② filp->f_op->aio_write即调用EXT4文件系统中的aio_write操作,在operation结构的解释中指出aio_write操作用EXT4_file_write操作完成。

LINUX中写操作是通过sys_write系统调用来实现的,在sys_write函数中完成对文件file 的读入,判断正确后调用了vfs_write函数进一步判断文件的模式是否为写是否与其他写操作冲突,如果判断正确后调用file->f_op->write,这个调用其实是去调用EXT3文件系统中的write操作(即do_sync_write)再调用aio_write(即EXT4_file_write)来完成读写制定大小的文件内容到缓冲区中包括对日志的写。

EXT4_file_write函数的分析如图2。

3 结束语

本文讨论的对象是Linux操作系统中EXT4内核程序,介绍了EXT4的主要数据结构,并通过分析写操作来解释日志的工作方式。日志文件系统提高了文件系统的效率,但是也增加了系统的开销。2.6.28内核中的EXT4文件系统比2.4内核做了较多的改动,如前面介绍的节点的扩展信息,和Htree结构的修正(文中没有介绍),NTFS文件系统的修正等。总体来说EXT4文件系统是比较完善的日志文件系统。

参考文献:

[1] 李善平,陈文智.边学边干LINUX内核指导[M].浙江:浙江大学出版社,2002.

[2] 涂健,孙辉.Linux2.6(内核下)EXT3文件系统的数据结构及性能分析[J].南昌水专学报,2004(6):8-10.

[3] 李巍.Ext―扩展文件系统的研究[J].信息系统工程,2010(8):133-136.

[4] 王俊伟,吴俊海.Linux标准教程[M].北京:清华大学出版社,2006.

注:本文中所涉及到的图表、注解、公式等内容请以PDF格式阅读原文

上一篇:网络安全中高交互蜜罐系统的设计 下一篇:基于简单网络管理协议的网络拓扑发现算法