当前位置: 首页 > 科技观察

图解Linux文件系统

时间:2023-03-13 20:57:59 科技观察

本文转载自微信公众号《Linux内核那些事儿》,作者songsong001。转载本文请联系Linux内核那些事儿公众号。之前写过一篇Linux文件系统源码分析的文章,但是从源码的角度分析文件系统有点枯燥(对新手不友好),所以这次主要讲解一下Linux文件系统的原理通过图形和文本,而不会陷入源代码的深渊。一、硬盘介绍在介绍文件系统之前,我们先来了解一下硬盘。众所周知,断电后内存中的数据会丢失,所以现代电脑都采用硬盘来存储数据。也就是说,断电后硬盘中的数据仍然可以保存。比较流行的硬盘分为:机械硬盘(HDD)和固态硬盘(SSD)。由于本文的对象是文件系统,所以硬盘的原理就不做过多介绍了。下面是机械硬盘和固态硬盘的对比图:我们可以把硬盘想象成一个巨大的数组,数组的每个元素代表一个数据块,如下图所示:在Linux内核中,每个数据块定义为4KB的大小,所以一个128GB的硬盘可以分成33554432个数据块,内核以数据块的数量来读写硬盘。2、什么是文件系统前面说到,内核以数据块的形式读写硬盘,但这对人类来说是非常不直观的,因为我们不可能记住每个数据块保存的是什么数据。为了让用户使用起来更加方便直观,Linux内核抽象出两个概念来管理硬盘中的数据:文件(File)和目录(Directory)。文件:用于保存数据。目录:用来保存文件列表,当然目录也可以保存目录。由于数据存储在硬盘数据块中,文件只需要记录当前文件属于哪些数据块即可。如下图所示:从上图可以看出,目录下既可以保存文件,也可以保存目录。文件保存的是当前文件所属数据块的编号,所以读写文件时,只需要找到文件对应的数据块进行读写即可。3.MINIX文件系统的实现下面,我们以MINIX文件系统为例,详细介绍一下文件系统的设计原理。由于MINIX文件系统非常简单,适合教学使用。1.MINIX文件和目录在MINIX文件系统中,使用一个minix2_inode对象来描述一个文件。我们看一下minix2_inode的定义:structminix2_inode{__u16i_mode;//mode__u16i_nlinks;//链接数__u16i_uid;//用户UID__u16i_gid;//组ID__u32i_size;//文件大小__u32i_atime;//访问时间__u32i_mtime;//修改时间__u32i_time;///Creationtime__u32i_zone[10];//文件号对应的数据块号};我们需要特别注意minix2_inode对象的i_zone字段,它用来记录属于当前文件序号的数据块。从定义上看,i_zone是一个10个元素的整数数组,那么是不是意味着MINIX文件只能保存40KB的数据呢?答案是否定的,因为MINIX文件系统将i_zone数组分为4部分:前7个元素直接指向保存数据的数据块号,即数据将直接存储在这些数据块上,而第8个元素为一级间接指向,第9个元素为二级间接指向,第10个元素为三级间接指向。我们通过下图来说明这种关系:通过这种多级指向的方式,一个MINIX文件可以保存超过40KB的数据。如果有描述文件的对象,那么应该有描述目录的对象,对吧?在MINIX文件系统中,目录也使用minix2_inode对象来描述。那么如何区分文件和目录呢?minix2_inode对象中有一个名为i_mode的字段,存储了minix2_inode对应的类型。普通文件由S_IFREG标志表示,而目录由S_IFDIR表示。所以本质上,目录也是一种特殊的文件。一个普通文件的数据块存放的是文件的数据,那么目录的数据块存放的是什么呢?答案是文件列表,文件列表中的每一项由一个minix_dir_entry对象表示,定义如下:structminix_dir_entry{__u16inode;charname[0];};inode:minix2_inode对象所在的inode数组的索引对应当前文件所在的位置。这个字段的作用我们暂时可以忽略,下面会介绍。name:用于记录当前文件的文件名。由于文件名的长度不固定,这里用一个灵活的数组(可变大小的数据)来表示。我们用下图来表示文件和目录指向的数据内容的区别:上图是文件和目录两个明显的区别:文件的i_mode字段设置为S_IFREG,i_mode目录的字段设置为S_IFDIR。文件的i_zone字段指向的数据块存储文件的数据,而目录的i_zone字段指向的数据块存储文件列表。2.MINIX文件系统格式化现在,我们对MINIX文件系统如何存储文件和目录有了基本的了解。下面介绍一下MINIX文件系统是如何管理硬盘中的文件和目录的,也就是我们常说的格式化。.前面说了,我们可以把硬盘看成是一个由数据块组成的巨大数组,那么MINIX文件系统会将硬盘分为以下几个部分,如下图所示:下面我们来分别解释一下这几个部分:Bootblock:占用一个数据块供操作系统启动时使用,我们可以忽略。超级块:占用一个数据块,用于保存文件系统的信息。MINIX文件系统使用minix_super_block对象来保存文件系统的信息,比如inode位图占用了多少个数据块,数据块位图占用了多少个数据块等。inode位图:占用了几个数据blocks,用于描述inode表中哪些成员被使用过,每一位代表一个inode的使用情况。数据块位图:占用若干个数据块,用于描述数据块列表中哪些成员被使用过,每一位代表一个数据块的使用情况。inode表:占用若干个数据块,由多个minix2_inode对象组成,每个minix2_inode对象代表一个文件或目录。数据块列表:占用若干个数据块,用于保存文件数据。上图是硬盘中MINIX文件系统的格式化结构。我们来看看超级块中记录的信息。superblock用minix_super_block对象表示,其定义如下:structminix_super_block{__u16s_ninodes;//inode表的元素个数__u16s_nzones;//数据块列表中的元素个数(v1版本)__u16s_imap_blocks;//个数ofdatablocksoccurredbytheinodebitmap__u16s_zmap_blocks;//数据块占用的数据块个数theMINIXfilesystem)__u16s_state;//文件系统状态__u32s_zones;//数据块列表中的元素个数(v2版本)};minix_super_block的各个字段的作用在注释中都有说明。通过minix_super_block对象,我们可以知道MINIX文件系统的信息。3.读取文件的过程了解了MINIX文件系统的结构和组织之后,我们就来介绍一下MINIX文件系统中读取文件的过程。例如,我们要读取/home/file.txt文件的内容。MINIX文件系统是如何准确找到文件并读取内容的呢?让我们逐步描述这个过程。第一步:读取根目录要读取/home/file.txt文件,首先要从根目录/开始,MINIX文件系统约定根目录使用inode表的第一个元素存储。如下图所示:如上图所示,使用inode表的第一个元素存储根目录,然后从根目录的文件列表中查找目录home。从上图可以看出,home目录的inode索引为5,说明home目录存放在inode表的第5个元素中。第二步:读取home目录,知道home目录的inode索引为5,然后读取inode表的第5个元素,然后从home目录的文件列表中查找文件file.txt。过程如上图所示:如图所示,从家目录的文件列表中找到file.txt文件的inode索引为9,所以现在可以通过以下方式获取file.txt文件对应的inode节点读取inode表的第九个元素。第三步:读取file.txt文件的内容现在我们知道了file.txt文件对应的inode索引,那么从inode表中读取第9个元素,得到file.txt文件的inode节点,然后通过inode节点的i_zone字段指向的数据块可以读取文件的内容,如下图:inode表的第9个元素,可以通过inode节点的i_zone字段指向的数据块读取文件内容。另外,inode位图和数据块位图用于快速查找创建文件时哪些inode节点和数据块没有被使用。4.小结本文介绍如何通过一个简单的文件系统如MINIX来设计一个文件系统。Linux系统中虽然有很多文件系统,但其基本思想是如何有效地管理硬盘上的数据。因此,掌握MINIX文件系统的设计,对于理解其他不同的文件系统很有帮助。