当前位置: 首页 > Linux

LinuxEXT系列文件系统格式

时间:2023-04-06 03:21:07 Linux

Linux文件系统普通硬盘如上图所示,每个磁盘分为多个磁道,每个磁道又分为多个扇区,每个扇区512字节,是最小的大小的硬盘。存储单元,但在操作系统层面,多个扇区组成一个块(block),是操作系统存储数据的最小单位,通常8个扇区组成一个4K字节的块。对于Linux文件系统,需要考虑以下几点:文件系统需要有严格的组织形式,使文件可以分块存储文件最近经常读写,需要缓存层。文件应以文件夹的形式组织,便于管理和查询。Linux内核必须在自己的内存中维护一套数据结构来保存哪些文件被哪些进程打开并使用Linux中的一切。所有文件都有以下几种文件类型(从ls-l结果的第一个标识符可以看出):-表示普通文件d表示文件夹c表示字符设备文件b表示块设备文件s表示socket套接字文件l表示软链接inode和块存储。下面以EXT系列格式为例,看看硬盘上是否存在该文件。首先,文件会被分成块,分散存储在硬盘上。需要一个索引结构来帮助我们找到这些块,并记录文件的一些元信息。这是inode,其中i代表索引。inode数据结构如下:structext4_inode{__le16i_mode;/*文件模式*/__le16i_uid;/*所有者Uid的低16位*/__le32i_size_lo;/*字节大小*/__le32i_atime;/*访问时间*/__le32i_ctime;/*inode变化时间*/__le32i_mtime;/*修改时间*/__le32i_dtime;/*删除时间*/__le16i_gid;/*GroupId的低16位*/__le16i_links_count;/*链接数*/__le32i_blocks_lo;/*块计数*/__le32i_flags;/*文件标志*/union{struct{__le32l_i_version;}linux1;结构{__u32h_i_translator;}赫德1;结构{__u32m_i_reserved1;}max1;}osd1;/*取决于操作系统1*/__le32i_block[EXT4_N_BLOCKS];/*指向块的指针*/__le32i_generation;/*文件版本(对于NFS)*/__le32i_file_acl_lo;/*文件访问控制列表*/__le32i_size_high;__le32i_obso_faddr;/*废弃的片段地址*/union{struct{__le16l_i_blocks_high;/*为l_i_reserved1*/__le16l_i_file_acl_high;__le16l_i_uid_high;/*这两个字段*/__le16l_i_gid_high;/*保留2[0]*/__le16l_i_checksum_lo;/*crc32c(uuid+inum+inode)LE*/__le16l_i_reserved;}Linux2;结构{__le16h_i_reserved1;/*在ext4中删除的过时片段号/大小*/__u16h_i_mode_high;__u16h_i_uid_high;__u16h_i_gid_high;__u32h_i_author;}赫德2;结构{__le16h_i_reserved1;/*在ext4中删除的过时片段号/大小*/__le16m_i_file_acl_high;__u32m_i_reserved2[2];}maix2;}OSD2;/*依赖操作系统2*/__le16i_extra_isize;__le16i_checksum_hi;/*crc32c(uuid+inum+inode)BE*/__le32i_ctime_extra;/*额外的变化时间(nsec<<2|epoch)*/__le32i_mtime_extra;/*额外修改时间(nsec<<2|epoch)*/__le32i_atime_extra;/*额外的访问时间(nsec<<2|epoch)*/__le32i_crtime;/*文件创建时间*/__le32i_crtime_extra;/*额外的文件创建时间(nsec<<2|epoch)*/__le32i_version_hi;/*64的高32位-位版本*/__le32i_projid;/*项目编号*/};其中__le32i_block[ext4_n_blocks]将引用存储到数据块中,ext4_n_blocks定义如下:#defineext4_ndir_blocks12#defineext4_ind_ind_ind_blockext4_ind_blockext4_ndir_ndir_blocks#而ext3,i_block的前12项存放的是数据块的直接引用,第13项存放的是间接块的引用。间接块中存储数据块的位置,以此类推,第14项存储第二个间接块的位置,第15项存储第三个间接块的位置,如图下图:不难看出,对于大文件,需要多次读取硬盘才能找到对应的block。ext4中提出了ExtentsTree来解决这个问题。它的核心思想是通过将块的数量添加到起始位置而不是一个接一个地表示连续的块。记录每个block的位置,从而节省存储空间首先,将i_block中原来的415=60字节的空间替换为一个extentheader(ext4_extent_header)加上4个extententry(ext4_extent),因为ext4_extent_header和ext4_extent都占12字节。ee_len中的第一位是用来判断是否初始化的,所以它最多也可以存放32K个数,所以一个extententry最多可以存放32K4K=128M的数据,如果一个文件大于4128M=512M或者文件分布到4个以上的不连续块中存储,我们需要在inode中扩展i_block结构。它的extententry会由ext4_extent变为ext4_extent_idx结构,指向一个4K字节的block,除去header占用的12个字节,可以存储340个ext4_extents,最大可以存储340128M=42.5G的数据。可以看出,当文件存储在连续的块中时,这种索引结构是非常有效的。结构ext4_extent_header{__le16eh_magic;/*ext4extentsID:0xF30A*/__le16eh_entries;/*当前关卡有效节点数*/__le16eh_max;/*当前关卡的最大节点数*/__le16eh_depth;/*当前层级在树的深度,0为叶子节点,即数据节点,>0代表索引节点*/__le32eh_generation;}structext4_extent{__le32ee_block;/*extent起始块的逻辑序号*/__le16ee_len;/*extent包含块数*/__le16ee_start_hi;/*extent起始块物理地址的高16位*/__le32ee_start_lo;/*extent起始块物理地址的低32位*/};//数据节点extent_body格式中structext4_extent_idx{__le32ei_block;/*索引覆盖的文件范围起始块的逻辑序号*/__le32ei_leaf_lo;/*存放下一级extents的块物理地址低32位*/__le16ei_leaf_hi;/*存放下一级extents的块物理地址高16位*/__u16ei_unused;};//索引节点中的extent_body格式/var/log/messages文件的例子如下图所示:inodebitmap和blockbitmap硬盘上会有一块专门用来存储块数据的区域,并且会有也是一个存放inode的区域,但是当我们要创建一个新文件的时候,我们需要知道哪个inode区域和哪个block是空的,这就分别需要一个block。存放inode位图,一个block存放块位图,每个bit为1表示占用,0表示未占用。但是,一个块最多有4K*8=32K位,也就是说它最多可以代表32K个块的状态,所以需要将这些块组成一个块组来构建更大的系统。硬链接和软链接硬链接与原文件共享一个inode,inode不能跨文件系统,所以硬链接不能跨文件系统。软链接有自己的inode,但打开文件时指向另一个文件,所以可以跨文件系统,删除原文件后仍然存在。