• 最近在看ext4系统的extent相关内容
  • 对于文件系统,每个文件会对应一系列磁盘块,通过在inode中有序的存放磁盘块号,也就保存下了<文件逻辑块号, 磁盘块号>的映射关系
  • 一个文件的逻辑块号必然是连续的,而磁盘块号则不必连续
  • 通常一个block大小为4KB,所以一个比较大的文件,就需要存相当多的块号——而这是一个十分笨拙的办法
  • 对于很大的文件,有一种解决办法就是间接存放块号,也就是说inode中有部分块号指向的block不是存放这个file的数据,而是存放块号——即一种间接寻址的逻辑。通常会包括三级,一部分直接指向文件的数据块,一部分指向存有块号的块,一部分指向存有“存有块号的块的块号”的块,哈哈
  • 而ext4采用的办法是使用extent来保存<文件逻辑块号, 磁盘块号>的映射关系:一个extent对应一系列连续的块号,故可以想到,一个extent最基本的几个域有——文件逻辑块号,起始磁盘块号,块数量
  • ext4中一个inode可以直接存放4个extent
  • 对于很大的文件,ext4采用extent_tree的方式,其本质同样是一种间接寻址的关系
  • 那接下来就详细道来

术语简介

  • 磁盘块:块设备对磁盘的一个抽象,对于文件系统而言,磁盘就是一个一个连续的块,每个块通常大小为4KB,并且磁盘块有序编码,每个块对应有一个磁盘块号
  • 文件逻辑块号:从逻辑上讲,一个文件可以看做一系列连续的数据块,每个数据块的大小和磁盘块大小相同;
    • 从物理上讲,一个文件可以对应磁盘上若干个磁盘块,这些磁盘块可以在物理上不连续。
    • 逻辑快号和磁盘块号的对应关系,很像虚拟地址和物理地址的关系:文件给你的感觉就是连续的数据,也就是连续的逻辑块,但实际上文件对应的磁盘块是可以不连续的,也就是不连续的物理块
  • inode:保存文件的元数据,元数据可以描述一个文件
    • 很可惜inode里没有一下子就能想到的filename。关于filename下一节会讲到
    • 一个inode最基本也最重要的信息就两个
      • 用来标识inode的inode号,每个inode都不一样
      • 用来指明这个文件对应着哪些磁盘块的信息——即逻辑块号和磁盘块号的对应关系

从文件名到磁盘块

  • 前面有提到,inode存放文件元数据,但是并没有存放filename——那么ext4是如何把一个filename和一个inode绑定在一起呢?
  • 也就是说存在一个filename和inode号之间的对应关系,而这个关系也是存放在一个文件里——目录文件。如根目录/就是一个文件,这个文件也对应一个inode,文件的数据就是根目录下的文件名和对应的inode号

简略的过程

  • 基于文件系统,我们看到的就是一个个文件,比如我现在有一个文件/home/niugen/testfile
$ pwd
/home/niugen
$ echo 'This is a test file for learning ext4' > testfile
$ cat testfile
This is a test file for learning ext4
  1. 当我们输入cat testfile时,cat命令接收到testfile参数,进而根据当前工作目录计算出这个文件的绝对路径为/home/niugen/testfile
  2. 解析这个路径,首先是/即根目录,根目录这个文件对应的inode号固定为2,所以可以直接找到根目录的inode
  3. 根据根目录的inode中存放的磁盘块号信息,可以知道数据存放在哪些磁盘块中,于是从这些磁盘块里读出数据
  4. 目录文件的数据,简单的看来就是一个表,有两列,一列是文件名,一列是对应的inode号。对于根目录,文件名就是常见的dev、usr、home等等,于是找到了home对应的inode号
  5. 于是读出了/home这个文件的inode,发现这也是一个目录文件,继续读出数据,找到niugen对应的inode号
  6. 于是读出了/home/niugen这个文件的inode,发现这还是一个目录文件,继续读出数据,找到testfile对应的inode号
  7. 于是读出了/home/niugen/testfile这个文件的inode,发现这是一个普通文件,可以使用cat命令,于是读出数据并打印在屏幕上

实际的过程

  • 以上过程也就是核心部分了
  • 关于根目录的inode号——Why is root directory always stored in inode two?
  • 实际中不可能对于每个路径都要读取路径上所有的目录文件来层层深入最终找到目标文件,故在内存中会有目录文件的缓存,称为DEntry。这个数据结构是内存数据结构,用来存放一个文件路径(如/home)和其对应的数据信息(文件名和inode号的表)。
  • Linux对文件系统有一个统一的抽象模型,即VFS,主要有四种数据结构:
    • 超级快对象 Superblock:文件系统信息
    • 索引节点对象 Inode:文件元数据
    • 目录项对象 DEntry:提供目录缓存
    • 文件对象 File:进程视角,用来描述打开的文件
  • VFS的这四种数据结构都是内存数据结构
    • 对于具体的文件系统比如Ext4,在挂载的时候根据其磁盘布局来构造出这四种数据结构
    • 应用程序(如cat命令)均基于VFS提供的文件操作接口进行编程,每种具体的文件系统的实现,如ext4、fat32、ntfs等,需要对其特有的磁盘数据结构进行封装来实现VFS的这四种数据结构
  • VFS不是本文重点,本文重点是探索EXT4文件系统的磁盘数据结构的实现

inode结构体

  • 数据类型介绍

    • __le16 字节小端序的2字节大小
    • __le32 字节小端序的4字节大小

Ext2中的inode

  • 首先介绍一下ext2中的inode结构体:ext2_inode

    • 最重要的字段:i_block[EXT2_N_BLOCKS],存放磁盘块号
    • ext2_inode的大小为128字节,一个4KB的块可以存放32个inode
struct ext2_inode{__le16 i_mode;  //文件类型和访问权限__le16 i_uid;   //拥有者id__le32 i_size;  //文件大小,单位byte__le32 i_atime,i_ctime,i_mtime,i_dtime; //时间相关信息__le16 i_gid;  //用户组id__le16 i_links_count; //硬链接计数器__le32 i_blocks; //文件数据块数__le32 i_flags;  //标志union osd1;      //操作系统相关信息__le32 i_block[EXT2_N_BLOCKS]; //指向数据块的指针,即磁盘块号__le32 i_generation;  //文件版本,用于网络文件系统__le32 i_file_acl;    //文件访问控制列表__le32 i_dir_acl;     //目录访问控制列表__le32 i_faddr;       //片地址(不懂)union osd2;           //操作系统相关信息
}
  • i_block字段是一个有EXT2_N_BLOCKS个元素的数组,若EXT2_N_BLOCKS的默认值为15

    • [0,11]为直接寻址,即i_block[0,11]直接存放磁盘块号
    • [12,13,14]均为简介寻址,[12]指向一个存放磁盘块号的块,[13]为二级指针,[14]为三级指针
    • 若一个block的大小为4KB,直接寻址的范围为48KB,即只使用[0,11];若加上一次间接的[12],则为4.04MB;再加上二次间接的[13]则为4GB;再加上三次间接的[13]则约为4TB

Ext4中的inode

  • ext4中就出现了extent,ext4_inode 定义于/fs/ext4/ext4.hext4_inode的大小为256字节,一个4KB的块可以保存16个inode

    • 字段i_block[EXT4_N_BLOCKS]
/** Constants relative to the data blocks*/
#define EXT4_NDIR_BLOCKS        12
#define EXT4_IND_BLOCK          EXT4_NDIR_BLOCKS
#define EXT4_DIND_BLOCK         (EXT4_IND_BLOCK + 1)
#define EXT4_TIND_BLOCK         (EXT4_DIND_BLOCK + 1)
#define EXT4_N_BLOCKS           (EXT4_TIND_BLOCK + 1)/** Structure of an inode on the disk*/
struct ext4_inode {__le16   i_mode;     /* File mode */__le16   i_uid;      /* Low 16 bits of Owner Uid */__le32    i_size_lo;  /* Size in bytes */__le32   i_atime;    /* Access time */__le32 i_ctime;    /* Inode Change 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_lo;    /* Blocks count */__le32    i_flags;    /* File flags */union {struct {__le32  l_i_version;} linux1;struct {__u32  h_i_translator;} hurd1;struct {__u32  m_i_reserved1;} masix1;} osd1;             /* OS dependent 1 */__le32  i_block[EXT4_N_BLOCKS];/* Pointers to blocks */__le32   i_generation;   /* File version (for NFS) */__le32  i_file_acl_lo;  /* File ACL */__le32    i_size_high;__le32  i_obso_faddr;   /* Obsoleted fragment address */union {struct {__le16   l_i_blocks_high; /* were l_i_reserved1 */__le16 l_i_file_acl_high;__le16    l_i_uid_high;   /* these 2 fields */__le16  l_i_gid_high;   /* were reserved2[0] */__u32    l_i_reserved2;} linux2;struct {__le16   h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */__u16 h_i_mode_high;__u16 h_i_uid_high;__u16  h_i_gid_high;__u32  h_i_author;} hurd2;struct {__le16   h_i_reserved1;  /* Obsoleted fragment number/size which are removed in ext4 */__le16    m_i_file_acl_high;__u32 m_i_reserved2[2];} masix2;} osd2;               /* OS dependent 2 */__le16  i_extra_isize;__le16    i_pad1;__le32  i_ctime_extra;  /* extra Change time      (nsec << 2 | epoch) */__le32  i_mtime_extra;  /* extra Modification time(nsec << 2 | epoch) */__le32  i_atime_extra;  /* extra Access time      (nsec << 2 | epoch) */__le32  i_crtime;       /* File Creation time */__le32  i_crtime_extra; /* extra FileCreationtime (nsec << 2 | epoch) */__le32  i_version_hi;    /* high 32 bits for 64-bit version */
};
  • 可见字段i_block的大小为60个字节,即__le32 i_block[EXT4_N_BLOCKS]EXT4_N_BLOCKS=15

    • 前12个字节为extent头,为extent的基本信息
    • 后48个字节可以保存4个extent节点,每个extent节点为12字节大小
      • extent以树的形式组织,叶节点和非页节点的大小均12字节
      • 叶节点即直接保存了文件逻辑块号、起始磁盘块号、块数
      • 非叶节点同样具有文件逻辑块号,后面内容指向了一个磁盘块号,有两个字节未使用
  • extent相关结构体定义于/fs/ext4/ext4_extents.h

/** Each block (leaves and indexes), even inode-stored has header.*/
struct ext4_extent_header {__le16   eh_magic;   /* probably will support different formats */__le16 eh_entries; /* number of valid entries */__le16 eh_max;     /* capacity of store in entries */__le16    eh_depth;   /* has tree real underlying blocks? */__le32    eh_generation;  /* generation of the tree */
};/** This is the extent on-disk structure.* It's used at the bottom of the tree.*/
struct ext4_extent {__le32  ee_block;   /* first logical block extent covers */__le16   ee_len;     /* number of blocks covered by extent */__le16  ee_start_hi;    /* high 16 bits of physical block */__le32  ee_start_lo;    /* low 32 bits of physical block */
};/** This is index on-disk structure.* It's used at all the levels except the bottom.*/
struct ext4_extent_idx {__le32  ei_block;   /* index covers logical blocks from 'block' */__le32  ei_leaf_lo; /* pointer to the physical block of the next ** level. leaf or next index could be there */__le16   ei_leaf_hi; /* high 16 bits of physical block */__u16   ei_unused;
};

磁盘布局

  • 这里简单的认识一下ext2的磁盘布局,ext4在ext2之上发展起来,基本概念相通

概念

  • 引导块:磁盘的第一个块,ext2对其不管理,其余的块以块组的形式管理
  • 磁盘布局如下
引导块 块组0 块组n
第1个块 m个块 m个块
  • 每个块组有如下内容

    • 超级块:1个块
    • 组描述符:n个块
    • 数据块bitmap:1个块
    • 索引节点bitmap:1个块
    • 索引节点表:n个块
    • 数据块:n个块
  • 其中超级块和组描述符,对于所有块组均相同,且总是缓存在内存中

  • 其余则用于描述本块组管理的inode块和数据块

Ext4中的磁盘数据结构

  • 超级块superblock实在太大,不值得搬上来,源码见此ext4_sb.h
  • 组描述符ext4_group_desc如下

/** Structure of a blocks group descriptor*/
struct ext4_group_desc
{__le32 bg_block_bitmap_lo; /* Blocks bitmap block */__le32 bg_inode_bitmap_lo; /* Inodes bitmap block */__le32 bg_inode_table_lo;  /* 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_flags;       /* EXT4_BG_flags (INODE_UNINIT, etc) */__u32    bg_reserved[2];     /* Likely block/inode bitmap checksum */__le16  bg_itable_unused;   /* Unused inodes count */__le16  bg_checksum;       /* crc16(sb_uuid+group+desc) */__le32 bg_block_bitmap_hi; /* Blocks bitmap block MSB */__le32 bg_inode_bitmap_hi; /* Inodes bitmap block MSB */__le32 bg_inode_table_hi;  /* Inodes table block MSB */__le16  bg_free_blocks_count_hi;/* Free blocks count MSB */__le16   bg_free_inodes_count_hi;/* Free inodes count MSB */__le16   bg_used_dirs_count_hi;  /* Directories count MSB */__le16   bg_itable_unused_hi;    /* Unused inodes count MSB */__u32  bg_reserved2[3];
};

寻找一个文件

  • 那么接下来让我们从磁盘上寻找一个文件的内容,还记得文章开头部分新建的一个文件么,即testfile
$ pwd
/home/niugen
$ echo 'This is a test file for learning ext4' > testfile
$ cat testfile
This is a test file for learning ext4

找到文件的inode号

  • cat命令可以打印一个普通文件的内容,那么对于目录文件就是常见的ls命令,使用ls -i可以打印出相应的inode号
$ ls -i testfile
2629310 testfile

根据inode号打印出inode内容

  • 首先确认一下这个文件所在的设备

    • df命令列出挂载的设备,可知/home对应设备/dev/sda3
$ df
文件系统           1K-块     已用      可用 已用% 挂载点
udev             4010336        0   4010336    0% /dev
tmpfs             805968    17880    788088    3% /run
/dev/sda2      192115292 54064596 128268680   30% /
tmpfs            4029832    23168   4006664    1% /dev/shm
tmpfs               5120        4      5116    1% /run/lock
tmpfs            4029832        0   4029832    0% /sys/fs/cgroup
/dev/sda3       44049544  6435924  35352936   16% /home
tmpfs             805968       12    805956    1% /run/user/126
tmpfs             805968       64    805904    1% /run/user/1000
$ pwd
/home/niugen
$ ls -li testfile
2629310 -rw-rw-r-- 1 niugen niugen 38 6月  26 21:13 testfile
  • istat命令可以打印出某个设备上的某个inode信息

    • Group:320表示这个inode存在于块组320上
    • size:38表示文件的大小为38个字节
    • Direct Blocks:10622354则是这个命令根据i_block解析出来的磁盘快号
      • 那么我们不直接使用这个块号,我们自己来算一算它,这就需要直接查看inode所在的磁盘数据块才能看到完整的信息
$ sudo istat /dev/sda3 2629310
inode: 2629310
Allocated
Group: 320
Generation Id: 2014416585
uid / gid: 1000 / 1000
mode: rrw-rw-r--
Flags: Extents,
size: 38
num of links: 1Inode Times:
Accessed:       2017-06-26 21:13:12.184134514 (CST)
File Modified:  2017-06-26 21:13:10.628122013 (CST)
Inode Modified: 2017-06-26 21:13:10.628122013 (CST)
File Created:   2017-06-26 10:18:07.690160310 (CST)Direct Blocks:
10622354

查找inode所在的磁盘块

  • 根据前一步可知这个inode位于块组320中,inode号为2629310

  • fsstat列出文件系统的信息,找到320块组的信息

    • 该块组管理的inode的inode号范围为2621441 - 2629632,可知包含了2629310这个inode
    • 该块组的inode所在的块的块号范围为10485792 - 10486303
$ sudo fsstat /dev/sda3 > dev.sda3.fsstat
$ cat dev.sda3.fsstat | grep "Group: 320" -n
4560:Group: 320:
$ cat dev.sda3.fsstat | head -n 4572 | tail -n 13
Group: 320:Block Group Flags: [INODE_ZEROED] Inode Range: 2621441 - 2629632Block Range: 10485760 - 10518527Layout:Data bitmap: 10485760 - 10485760Inode bitmap: 10485776 - 10485776Inode Table: 10485792 - 10486303Data Blocks: 10493984 - 10518527Free Inodes: 8 (0%)Free Blocks: 23488 (71%)Total Directories: 534Stored Checksum: 0x03D2
  • 那么我们可以来算一下:一个inode大小256B,一个块4KB

    • 一个块可以容纳4KB/256B=16个inode
    • inode号范围起始为2621441,2629310号inode为第2629310-2621441=7869个inode(从0计数)
    • 7869/16=491余13,即这个inode位于该块组用来存放inode的磁盘块的第491个磁盘块(从0计数),且位于该块的16个inode中的第13+1个(从1计数)
    • inode所在块的起始块号为10485792,所以该inode位于块号为10485792+491=10486283的磁盘块

读取磁盘块内容分析inode

  • blkcat命令读出一个磁盘块的内容
$ sudo blkcat /dev/sda3 10486283 > dev.sda3.blk.10486283
  • 使用十六进制编辑器hexedit查看内容数据,其中该inode是这个块的第14个inode

  • 根据ext4_inode结构体的定义,以及小端字节序

    • 字节4-7为文件大小: 0x00000026=38
    • 字节40-51为extent头信息
      • 字节40-41为魔数:0xF30A
      • 字节42-43为extent数量:0x0001=1
      • 字节44-45为extent最大数量:0x0004=4
    • 字节52-63为第一个extent,也是唯一一个extent
      • 字节52-55为该extent对应的第一个文件逻辑块号:0x00000000=0
      • 字节56-57为该extent对应的块数量:0x0001=1
      • 字节78-59为磁盘块号的高16位:0x0000
      • 字节60-63为磁盘快号的低32位:0x00A21592
      • 故该extent对应的磁盘块号为0x000000A21592=10622354,和之前istat告诉我们的Direct Blocks一样~~~

读取文件数据所在的磁盘块

  • 终于找到头了(虽然istatDirect Blocks已经告诉我们了)
$ sudo blkcat /dev/sda3 10622354
This is a test file for learning ext4

拓展

  • 有兴趣的可以再研究一下extent_tree,即使用extent如何实现间接的寻址
  • 其实有了结构体的定义已经很清楚了

参考内容

  • wiki —— Ext4 Disk Layout
  • sans —— understanding ext4【系列】
  • csdn —— 理解Ext4【系列】前一行的中译版本,感谢作者的翻译
  • 深入理解Linux内核 —— 第十八章 Ext2和Ext3文件系统:电子版下载地址
  • linux源码在线 —— lxr.linux.no
  • 在线进制转换工具 —— 进制转换

Linux.ext4文件系统.inode和extent相关推荐

  1. Linux ext4文件系统inode信息修改

    引文(引用博客:https://blog.csdn.net/stringnewname/article/details/73740155):Linux访问文件过程 1.当我们输入cat testfil ...

  2. linux ext4文件系统为什么目录大小是4k?

    Why does every directory have a size 4096 bytes (4 K)?](https://askubuntu.com/questions/186813/why-d ...

  3. Linux文件系统中错误的是,linux – ext4文件系统损坏 – 可能是硬件错误?

    我打开电脑约半小时后,我在dmesg中收到这些错误: [ 1355.677957] EXT4-fs error (device sda2): htree_dirblock_to_tree: inode ...

  4. linux系统硬盘数量,Linux ext4文件系统划分磁盘inode数量

    最近因特殊场景,需要往磁盘上写入大量小文件,然而在操作过程中磁盘空间未满但是却提示无法写入-- 错误分析 在本次操作过程,需要往磁盘上写入大概 150w 个小文件,文件大小约为 1~100KB,大概预 ...

  5. Linux32位ext4最大文件容量,linux – ext4文件系统最大inode限制 – 任何人都可以解释一下吗?...

    对于ext4没有默认值,它取决于设备的大小和创建时选择的选项.您可以使用检查现有限制 tune2fs -l /path/to/device 例如, root@xwing:~# tune2fs -l / ...

  6. Linux ext4文件系统下 extundelete 恢复 误删除的文件

    1.文件基本操作 1.1 查看文件 # ls 1.2 创建 1.2.1 创建文件 # touch {file_name} # vim {file_name} 1.2.2 创建目录 # mkdir -p ...

  7. 【Linux】文件系统/inode/软硬链接

    目录 一.了解磁盘 1.磁盘的概念 2.磁盘的物理结构 3.磁盘的逻辑结构 4.磁盘区域的划分 二.linux文件系统 三.inode 四.软硬链接 1.软链接 2.硬链接 一.了解磁盘 前言: 一般 ...

  8. ceph rbd双挂载导致ext4文件系统inode链接数据污染

    转载自:https://my.oschina.net/xueyi28/blog/1596003 ###故障现象 /data/rbd1/dir1/a/file1 /data/rbd1/dir2/a/fi ...

  9. linux ext4文件系统超级块损坏修复

    前言 误操作 dd if=/var/www/html/admin.php of=/dev/sdb 之后系统报磁盘空间不足,意识到出事了 解决 debugfs -w /dev/sdb 返回,/dev/s ...

最新文章

  1. echarts地图罗平县的json_Echarts全国省市区县地图数据文件(含js及json)
  2. 在一个.net sln中包含多个project,project引用同一个dll导致的错误
  3. 【Kotlin】Kotlin 委托 ( 使用 by 关键字进行接口委托 )
  4. .net 读word中文字方法
  5. 【Rollo的Python之路】Scrapy Selector选择器的学习
  6. jmeter的性能监控框架搭建记录(Influxdb+Grafana+Jmeter)
  7. Boost:符号symbols找到null的测试程序
  8. dwz(jui)刷新当前dialog的方法
  9. MySQL数据库引擎详解
  10. SpringCloud(第 054 篇)简单 Quartz-Cluster 微服务,采用注解配置 Quartz 分布式集群...
  11. 在 IE 中使用 HTML5 元素
  12. 「代码随想录」本周学习小结!(动态规划系列三)
  13. WebStorm汉化教程
  14. 【PC工具】windows免安装录屏绿色软件,无需注册无水印绿色录屏软件
  15. 小学计算机上机评分表,海安市实验小学信息技术学科素养考核方案
  16. pip 添加trusted host 一劳永逸
  17. 2019高考(高中)数学必背重点公式大全
  18. Win7 X64 SQL SERVER 2000企业管理器无法建立新表
  19. Qt中嵌入Directx11
  20. 点焊机器人焊接超时_机器人点焊自动化 I 附机器人点焊焊接工艺

热门文章

  1. omwin密立根油滴实验数据处理程序_大学物理实验重修秋季学期开课通知
  2. 2016实习生笔试面试总结思考
  3. Fiddler屏蔽某些url的抓取方法
  4. PTN设备中为什么不支持PHP,ptn和otn的区别是什么
  5. 【 ICLR 2022】MoleR: Learning to Extend Molecular Scaffolds with Structural Motifs
  6. 关于颈椎病,上班族必须知道的TOP5
  7. 开源自定义表单提交系统源码 支持自定义字段功能强大
  8. zynq7 双核处理器的最简单例子
  9. 世界那么大,能遇见真的很不容易
  10. 正大期货:关于MSCI中国A50上市,与国内股指期货的优势