Ext2文件系统1.宏观认识虽然我们在上一篇中讲到了分区的思想但是一个分区就有几百个G甚至更多也不好进行管理啊那么针对这种问题linux又在分区的基础上引入了块组的概念那么何为块组呢如上图所示我们把一个300GB的分区给划分一下一个块组就占10GB这样就可以将300GB划分为30个块组这样我们就把一个分区管理好转变为把一个块组给管理好而块组这种数量也不是很多容量也不是很大管理起来就刚刚好。而我们今天的任务就是研究这个块组把这个块组给研究明白了管理起来就很容易了下面我们就来看看一个块组中都有些什么东西上面的结构也就是我们一个分区的结构最上面就是不同的分区下面就是我们的文件系统最后就是我们下面要讲的块组。我们可以看到在一个块组中分为不同的区域分别是Super BlockGDTBlock Bitmapinode Bitmapinode TableData Blocks下面我们就一一讲解这几个区域。2.Data Blocks(数据块)我们首先来讲Data Blocks叫做数据块我们现在都知道文件 文件属性 文件内容并且linux中文件的内容和属性是分开存储的。而这个区域就是用来保存文件的内容的。而OS文件系统中和磁盘进行IO的基本单位是4kb也就是一个块所以在这个区域中就分布着大量的块文件的内容就保存在这些块中。并且在一个组中Data Blocks区域占据大头也就是大部分的空间都属于Data Blocks这个区域这点想必大家都能理解毕竟文件的内容加在一起是很多的所以这个区域空间大也在情理之中。而一个文件可能内容为空所以一个文件可能对应0个或多个数据块。而在这里要输出一个结论每一个数据块都有唯一的编号这个结论我们后面会用到那么我们来思考一个问题如何判断Data Blocks中哪些数据块被使用过了哪些是未被使用的处于空闲的块呢可能有人会说那我们遍历一遍这块区域判断一下不就行了但是数据存储在磁盘上是以二进制存储的也就是一堆0和1你该如何判断呢所以说这样是判断不了的那该如何判断呢答案就是下面要讲的Block Bitmap。3.Block Bitmap(块位图)这块区域叫做块位图听名字我们就是到这是运用了位图的思想在这块区域中我们把Data Blocks中的每一个块当作一个比特位比特位的位置就对应着数据块的位置而比特位的内容【01】就表示这个数据块是否被占用0就表示未被占用1就表示被占用了。这块区域有多大呢下面我们举个例子现在Data Blocks区域中有1000000个数据块也就对应着1000000个比特位那么这1000000个比特位也就是125000个字节我们接着换算将其换算成kb我们就可以得出125000个字节就是122kb占据的空间很小这也是位图的特点。4.Inode Table(i节点表)那么上面介绍了关于文件内容的部分而文件 文件属性 文件内容那文件属性保存在哪里呢在linux中通过结构体struct inode来表示文件属性代码语言javascriptAI代码解释/* * Structure of an inode on the disk */ struct ext2_inode { __le16 i_mode; /* File mode */ __le16 i_uid; /* Low 16 bits of Owner Uid */ __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 */ __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) */ __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 */ }; #define EXT2_NDIR_BLOCKS 12 #define EXT2_IND_BLOCK EXT2_NDIR_BLOCKS #define EXT2_DIND_BLOCK (EXT2_IND_BLOCK 1) #define EXT2_TIND_BLOCK (EXT2_DIND_BLOCK 1) #define EXT2_N_BLOCKS (EXT2_TIND_BLOCK 1)上面就是ext2文件系统中对应的inode源码在这里面我们能看到不少熟人比如mode(文件权限)acm(文件相关的三个时间)等等但是在inode结构体中并没有保存文件名这个属性关于文件名的相关知识在文章后面会讲。并且每个文件所对应的inode中的成员变量是一样的只不过每个成员变量所对应的内容不同也就是inode的大小是固定的既然inode的大小是固定的那一个inode结构体有多大呢答案是128个字节或者256个字节当然在我们今天讲的ext2文件系统中inode是128个字节上面我们也说了OS读取文件一次读取4kb的数据而4kb就相当于32个inode也就是OS一次会读取32个inode。那么这个时候就有一个问题每个文件都有其对应的inode那要怎么保证文件的唯一性呢这个问题的答案就和上面的Data Blocks中的数据块是一样的既然每个数据块都有自己的编号那么文件的inode也当然有自己的编号这个编号在inode中保存叫做int inode_number上面ext2中的inode可能没有或者在别的地方因为ext2属于比较早的文件系统了我们知道会有这个编号就行了。那么上面讲的和Inode Table有什么关系呢答案就是每个文件的inode都会保存在Inode Table这块区域中也就是就如上图所示这里面就像表一样展示出了每个inode结构体中的各种属性一目了然。但是此时又会衍生出一个问题既然文件的属性和内容是分开存储的我们未来可通过inode号找到对应的文件属性但是怎么找到文件相对应的内容呢这点linux当然也为我们考虑到了我们来看在上面的inode结构体中有这样一个数组这个数组就是能从inode - block的这个数组中保存的就是的当前文件的内容所对应的数据块的块号或者说编号这也是Data Blocks中每个数据块都有相应的编号的原因。我们未来通过inode编号就能找到文件对应的inode属性它的内部具有和数据块的对应关系就能进一步找到文件的内容了。但是在结构体的下方在这里定义了上面数组的大小为12后面有对其加了3个1也就是这个数组最终大小也就是1515个块号也就对应15个数据块也就是60kb大小的空间但我们一个文件的大小肯定不止60kb那该如何保存文件内容所对应的块号呢在这个数组中前面12个确实直接指向的保存文件内容的块号但第13个它也对应一个块号但是这个数据块中并不保存文件内容它保存的是保存文件内容的块号而一个块号是一个整数也就是4个字节一个块是4kb也就是4096个字节换算下来这样的一个块就能保存1000余个保存文件内容的块号而这样的一个块就叫做一级间接块通过这样的方式不就形成了多叉树的结构了吗当然即使一千个块对应的大小也并不大所以就有了后面的二级间接块二级间接块中保存的是一级间接块的块号那么换算下来一个二级间接块就可以保存1000余个一级间接块的块号也就对应1000000个保存文件内容的块号要是还不够还有三级间接块呢换算方式就和上面是一样的。而Inode Table也同样有和Data Blocks一样的问题我们如何判断Inode Table中那些块已经被占用了哪些是未被使用的或者说空闲的块答案也和上面是一样的Inode Table也同样有自己的位图表。5.Inode Bitmap(inode 位图)这个位图的道理和上面是一样的同样将Inode Table中的每一个块都当作一个比特位比特位的位置就是inode的位置比特位的内容【01】就表示对应的inode是否被占用0就表示未被占用1就表示被占用了。说了这么多inode相关的话题想必大家肯定会有一个疑问我们平常没见过inode号啊这个inode号到底是多少呢下面我们就来看看我们通过ls -li就能看到每个文件所对应的inode号这个i就是inode的意思。有了这个inode号OS就能在磁盘中找到对应组中的文件属性和内容。6.GDT(Group Descriptor Table)这个区域叫做块组描述符表是用来描述块组属性信息整个分区分成多个块组就对应有多少个块组描述符表。每个块组描述符表存储⼀个块组的描述信息如在这个块组中从哪⾥开始是inode Table从哪⾥开始是Data Blocks空闲的inode和数据块还有多少个等等。块组描述符在每个块组的开头都有⼀份拷⻉。也就是说GDT是用来描述块组的一块区域我们来看看它里面有什么代码语言javascriptAI代码解释// 磁盘级blockgroup的数据结构 /* * Structure of a blocks group descriptor */ struct ext2_group_desc { __le32 bg_block_bitmap; /* Blocks bitmap block */ __le32 bg_inode_bitmap; /* Inodes bitmap */ __le32 bg_inode_table; /* Inodes table block*/ __le16 bg_free_blocks_count; /* 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]; };可以看到里面就有上面我们讲过的block bitmap等字眼。7.Super Block(超级块)这个听名字就感觉不一般这块区域是干什么的呢这块区域是用来存放⽂件系统本⾝的结构信息描述整个分区的⽂件系统信息。记录的信息主要有bolck 和 inode的总量未使⽤的block和inode的数量⼀个block和inode的⼤⼩最近⼀次挂载的时间最近⼀次写⼊数据的时间最近⼀次检验磁盘的时间等其他⽂件系统的相关信息。也就是说这块区域是用来描述整个分区的信息的并且Super Block的信息被破坏可以说整个⽂件系统结构就被破坏了它的重要性不言而喻。下面我们来见一见这个超级块代码语言javascriptAI代码解释/* * 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; /* Free inodes count */ __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 */ __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 */ __le16 s_def_resgid; /* 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 doesnt * know about, it should refuse to mount the filesystem. * * e2fscks requirements are more strict; if it doesnt know * about a feature in either the compatible or incompatible * feature set, it must abort and not try to meddle with * things it doesnt 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 */ };既然是描述整个文件系统的它里面的东西肯定是不少的有着各种各样的属性。8.扩展知识虽然讲了前面的内容但也只是单纯的介绍了他们每个的作用这肯定还不够下面我再来为大家补充一些扩展知识。8.1Super Block的扩展知识上面在讲解Super Block的时候不知道大家有没有这样一个问题Super Block既然是描述一个分区的所有分组的情况那它为什么会在一个块组中呢它不应该在文件系统的最前面单独一块区域来存储这个Super Block的相关内容吗我们正常的思维应该就如上面所说在文件系统的卡面单独开一片区域来存储Super Block但是如果是这样大家有没有想过这样一个问题你的硬盘因为某种原因导致前面Super Block的区域被刮花了或者直接损坏了导致整个文件系统的结构被破坏了该如何呢因为只有前面这一块区域是用来存储Super Block的相关内容的既然被破坏了那谁来也没招了这个文件系统或者说这个分区就报废了上面我们举的例子可能就几百个G但要是一个公司几个T甚至更多呢这样不就造成了很大的损失吗所以才没有这样设计而是在多个块组中都有Super Block这块区域这样既是一个块组中的Super Block被破坏了也能通过其他块组中的Super Block来恢复。但是不一定所有的块组中都有Super Block但是会有多个块组中存在同样的Super Block8.2格式化的简单理解我们新建一个分区Super Block和GDT一定是有效数据的因为我们要给特定分区写入管理信息即写入文件系统和分区分组相关的管理数据但是文件数据可以没有这不就是我们常说的格式化吗没有文件数据只有Super Block和GDT等的相关数据在windows下就有格式化的选项道理和上面是一样的格式化后整个分区或者整个盘就没有文件数据了只剩下了Super Block和GDT等的相关数据这里我们说的是完全格式化哈不是快速格式化。快速格式化并不会将文件数据给清理掉只是我们看不见了而已完全格式化才会将文件数据给清理掉。8.3增删查改文件的真实过程当我们讲了inode的相关知识后我们知道了在一个分区内用inode编号来标识一个文件的唯一性那我们就来探讨一下新建文件删除文件修改文件查找文件的真实过程。新建文件既然是新建的文件那肯定要为这个文件创建一个inode结构体那么首先就会在Inode Bitmap中找哪个位置是0也就是哪个比特位是未被使用的然后将其改为1并将这个inode中的相关属性给初始化好保存在Inode Table中。但是既然是新建的文件文件中没有任何内容那么文件系统并不会在Block Bitmap中找一个未分配的比特位给它也就不会占用任何的数据块。删除文件而删除文件就有的说了它的真实过程和我们想的不太一样。如果我们要删除一个文件首先会通过这个文件的inode编号找到它所在的块组中通过inode中的记录的属性找到Inode Bitmap中的相应位置将其改为0Block Bitmap也是一样的但是并不会将Data Blocks中属于该文件的内容给清理掉也就是删除文件只改位图那么这时候就有人问了为什么不将文件的内容一并删除呢那我问你你误删了文件想不想将文件恢复过来包想的这就是不删除文件内容的原因便于我们误删文件后恢复它这也就是为什么我们在向硬盘中传一个几个G的视频需要几分钟而将其删除只需要1~2秒的原因因为它只改位图。如果要将文件的内容也删除的话那传输文件和删除文件的时间应该相差无几的。修改文件和查找文件这两个就很简单了既然能通过inode编号找到相应的inode结构体那么就可以通过这个结构体找到文件的内容了剩下的工作就很简单了就是我们前面章节讲的打开文件的过程呗。8.4关于inode编号和datablock编号关于这两个编号我要讲的是这两个编号是全区统一分配的不是只在分组内有效inode不能跨区域一个分区一个文件系统互相独立。在Super Block中就定义了每个组block和inode的数量也就是每个组的block和inode数量是相同的那么每个组的大小也是相同的。举个例子比如经过计算这个分区需要100000个inode编号那么对于第一个块组而言它的inode编号就是从0开始计数的如果这个块组使用了10000个inode编号那么第二个块组的inode编号就要从10000开始计数以此类推。而此时有一个问题如果一个文件的大小超出了一个组的大小该怎么办呢换句话说也就是这个组的dataclock不够用了该如何呢这个问题正因为block编号在整个分区是有效的而得到解决答案就是可以调用其他组的空闲的数据块来保存文件内容正因为block编号在整个分区内有效所以即使用了其他组的数据块我们照样能通过块号来找到文件的内容。那么针对上面block和inode数量是有限的我在问大家一个问题有没有可能在一个分区中inode用完了而block没用完或者block用完了而inode没用完答案当然是可能的第一种情况无非就是文件太多但是每个文件都很小而第二种情况就是文件不多但是每个文件都太大了。这也就是为什么有的时候明明这个分区中还有空间但是我们无法创建文件的原因了因为inode用完了。9.目录与文件名上面我们在介绍inode结构体时说到文件名这个属性并不在一个文件inode结构体中那它应该在哪里呢在解决这个问题之前我想问大家如何看待一个目录文件既然是文件那根据文件 文件属性 文件内容文件属性我们有inode但是目录文件的内容是什么呢我们想一下一个目录中保存的是什么是不是就是一个个的文件啊那么目录文件的内容的就是文件名和其inode的映射关系但是我们查看一个目录文件的内容时看不到其inode只能看到文件名下面通过一个例子来带大家看看这段代码就利用了系统调用接口来查看一个目录文件的内容可以看到在目录文件中确实保存了文件名和其inode的映射关系。所以从今天开始在文件系统中从存储方式的角度来看存储普通文件和目录有区别吗答案没有任何区别都是文件一视同仁。在有了上面的理解后我们来看两个结论结论一同一个目录下文件名不能重复在指定目录下新建一个文件对于目录来说不过就是将文件的文件名和其inode之间的映射关系写到相应的datablock中罢了。而这就是为什么我们在讲解权限时没有w权限就不能在一个目录下新建文件和删除文件等操作没有r权限我们就不能看到目录下有哪些文件因为这些都是目录文件的内容啊。没有相应的权限我们怎么能进行w和r呢结论二那既然我们要打开一个文件就需要有其inode编号那么这东西在哪儿呢在它的上级目录的内容中保存着也就是我们要打开上级目录文件才能查看当前文件的inode编号那我们要想看到上级目录中的内容是不是就要打开上上级目录文件啊以此类推也就是我们要从根目录开始打开一个个相应的目录文件才能找到我们的目标文件也就是要打开一个特定路径下的文件就要对这个文件所在的路径进行解析打开前面的目录文件这就是为什么在linux下文件路径可以定位文件的根本原因这也就是为什么我们之前在使用open函数时必须要有路径的原因这也就是为什么我们在linux下访问一个文件都需要路径访问这也就是为什么一个进程的PCB中会有cwd的根本原因一切的一切都是要对一个文件进行操作时需要对文件的路径进行解析。10.路径缓存但是此时就有一个问题如果每次要打开一个文件都要进行路径解析打开一个个目录文件这样不慢吗如果真是这样那当然慢了所以为了解决这样的情况就引入了路径缓存的概念而在介绍路径缓存之前我们先了解一部分知识。当我们在linux中安装了tree命令后执行可以看到整个目录的树状结构那么这个树状结构是怎么来的呢实际上在linux的内核中会有一个维护树状路径结构的内核结构体叫做struct dentry每当我们打开一个文件就会在内核中构成一个这样的结构体我们来看看这个结构体中都有什么代码语言javascriptAI代码解释struct dentry { atomic_t d_count; unsigned int d_flags; /* protected by d_lock */ spinlock_t d_lock; /* per dentry lock */ struct inode* d_inode; /* Where the name belongs to - NULL is * negative */ /* * The next three fields are touched by __d_lookup. Place them here * so they all fit in a cache line. */ struct hlist_node d_hash; /* lookup hash list */ struct dentry* d_parent; /* parent directory */ struct qstr d_name; struct list_head d_lru; /* LRU list */ /* * d_child and d_rcu can share memory */ union { struct list_head d_child; /* child of parent list */ struct rcu_head d_rcu; } d_u; struct list_head d_subdirs; /* our children */ struct list_head d_alias; /* inode alias list */ unsigned long d_time; /* used by d_revalidate */ struct dentry_operations* d_op; struct super_block* d_sb; /* The root of the dentry tree */ void* d_fsdata; /* fs-specific data */ #ifdef CONFIG_PROFILING struct dcookie_struct* d_cookie; /* cookie, if any */ #endif int d_mounted; unsigned char d_iname[DNAME_INLINE_LEN_MIN]; /* small names */ };而在这个结构体中我们要关注这几个其中第一个指向的就是当前文件的inode而后面的第二个和第三个我们看着是不是很眼熟有parent和children这不就是我们学过的数据结构多叉树吗结构体中有能够指向parent和children通过这种方式就能构成树状结构我们来看当我们打开的文件更多的时候上面的结构体就会构成这样的树状结构而在有了这样的树状结构后我们后面再打开访问任何文件都会优先在这棵树中根据路径进行查找找到了就返回属性inode和内容没找到就构造一个dentry接入这棵树中。这种树状结构我们叫做路径缓存结构其实就是将你曾经访问过的路径给保存下来下次再访问相同的路径时就会快很多所以在做路径解析时只有第一次是最慢的随着路径缓存结构越来越完善解析的速度就会越来越快那么可能有人问了那如果我们打开的文件越来越多路径缓存结构越来越复杂会不会占据的空间过大导致内核装不下呢答案是不会的因为整个路径缓存结构也同时会隶属于LRU(Least Recently Used最近最少使⽤)结构中会进行节点淘汰这样就不会有上面的问题了并且它还隶属于Hash结构便于快速查找。一个结构隶属于其他的数据结构我们应该都屡见不鲜这里就不过多介绍了。11.挂载分区有了对上面知识的理解后最后一个问题我们上面说了inode编号和block编号只在当前分区内有效不能跨区那么在不同的分区中会出现inode编号和block编号相同的情况此时该如何分辨该文件属于哪个分区呢要解决这个问题我们就需要了解挂载分区的相关知识那么什么叫做挂载分区呢我们要知道一个磁盘必须经过分区格式化才能具有使用的前提但是一个分区要想被真正使用必须挂载到指定的目录下才可以也就是说当一个分区被挂载到一个指定的目录下的时候那么在这个目录下进行的关于文件的操作比如新建文件删除文件等都会被认为是在这个分区中进行的包括文件的inode编号也是在这个分区中进行分配和计数的。我们可以通过df -h命令来查看当前的linux系统下可使用的分区右上角的Mounted on就是挂载的意思在这里面我们可以看到有不同的分区我们现在只看这两个这里我用的是云服务器这两个分区就代表我的服务器中的磁盘也就类似于windows下的C盘D盘等。并且可以看到vda3这个分区被挂载到了根目录而我们日常在新建文件等操作时其实都是在根目录下的某个路径中进行操作的所以其实我们就是在vda3这个分区中进行操作的而上面我们看到的inode编号也只是在这个分区中有效。同样的道理vda2分区被挂载到了/boot/efi这个目录下在这个目录下或者在这个目录的路径中所进行的文件操作都是在vda2分区中进行的。所以我们要想知道目标文件是在哪个分区中需要通过判断目标文件的 路径前缀 与每个分区被挂载的目录进行比较符合哪个就说明目标文件在哪个分区中。下面我们自己来创建一个分区来带大家更好的去理解这第一步我们就需要先创建一个分区如上图所示这里我就创建了一个名为disk.img的分区大小为5MB通过结果我们也可以看到分区的大小确实为5MB。而第二步操作通过mkfs命令就是向这个分区中写入文件系统对其进行格式化的操作这里我向文件中写入的是ext4文件系统。第三步操作就是通过mount命令将我们的分区挂载到指定的目录下完成上面的操作后我们再次通过df -h命令就可以看到我们新创建的分区已经挂在到了上面我们指定的目录下。这里解释一下为什么分区的名字变成了loop因为我们本次创建的分区太小了所以我们所创建的分区被识别为了loop这种设备叫做循环设备这点我们不必担心。此时我们进到hello目录下可以看到有一个lostfound的目录这个目录是根据ext4文件系统的找回机制相关方面所创建的这里不用关心它。这里我们随便创建一个文件通过ls -li来查看文件的inode可以看到我们新创建的文件inode非常小通过比较可以看到在vda3分区中的inode和我们自己创建的分区中的inode还是有比较大的差异的毕竟这些文件属于不同的分区。而我们要卸载相应的分区的话可以借助umount命令来卸载将相应分区所挂载的目录卸载umount后面就可以卸载相应的分区了我们再次通过df -h命令查看就会发现已经没有我们所创建的分区的信息了。12.软硬链接最后我们谈一点额外知识这块知识叫做软硬链接这块是讲什么的呢首先我们要知道在linux中有两种特殊的文件分别为软链接文件和硬链接文件。首先我们先来见见这两个文件通过ln -s target link的命令形式我们就生成了一个test-soft的软链接文件命令中的-s就是soft的意思而生成软链接文件后我们通过名字就可以看出这个软链接文件是链接的哪个文件。