IO详解四 linux 文件系统简介 Shepard-Wang

五 Linux 文件系统结构

磁盘上存储的是 文件内容 + 文件信息(元数据,比如文件大小,类型等)。

linux 讲文件内容与元数据 分离存储

inode 就是记录元数据的结构。

ls -i 可以看到 inode 号,用来标识 inode

 656318 -rw-r--r-- 1 root root    4 May 20 16:33 c_file
1052936 drwxr-xr-x 4 root root 4.0K Jan  4 16:51 dou
 656096 drwxr-xr-x 3 root root 4.0K May 20 12:06 git
 655656 -rw-r--r-- 2 root root    0 May 15 19:53 hard_main.c
 655799 -rwxr-xr-x 1 root root  17K May 21 23:10 main
 672302 -rw-r--r-- 1 root root 1.4K May 21 23:10 main.c
 655833 -rw-r--r-- 1 root root   35 May 21 23:01 new_file
 656326 lrwxrwxrwx 1 root root    6 May 15 20:00 soft_main.c -> main.c
 655638 drwxr-xr-x 3 root root 4.0K May 16 23:21 source-git
 669823 drwxr-xr-x 2 root root 4.0K May 16 20:41 test
1055373 drwxr-xr-x 3 root root 4.0K Jan  1 03:10 tool
1052962 drwxr-xr-x 2 root root 4.0K Dec  9 16:00 work

1、磁盘

磁盘的扇区大小一般为 512B。磁盘虽然是圆形的,但是可以抽象成一段连续的空间(磁带拿出来拉直)。

2、文件系统

由于磁盘可能很大,我们需要设计一种结构组织磁盘的空间,让其更好的被利用。这就是文件系统做的事情。其中包含分区,格式化等。

我们以 ext2 文件系统为例。

一块磁盘被文件系统分为不同的 分区,每个分区又被分为不同的 块组

io13.png

磁盘中最小存储单元是扇区(1 扇区 = 512Bytes),而文件系统的最小存储单元就是 Block(一般地,1Block = 4KB = 8 扇区)。扇区大小在磁盘出厂就已经定了,而Block大小是在格式化时决定的。

例如, mke2fs -b 4096 /dev/sda6 在格式化 /dev/sda6 时指定了 Block 大小为 4096Bytes。

MBR是主引导记录,它的作用是检查分区表是否正确以及确定哪个分区为引导分区,并在程序结束时把该分区的启动程序调入内存加以执行。这里我们主要分析 ext2 文件系统,ext2 文件系统由以下几部分组成:

  1. Boot Sector:上图启动块(Boot Sector),是用来存储磁盘分区信息和启动信息,任何文件系统给都不能缺少启动块。启动块大小并不是我们前面所说的4KB,而是1KB,是由PC标准定义的。开机时,bios 会根据它来创建什么类型的文件系统。

  2. Block Group:启动块之后才是ext2文件系统的开始。ext2文件系统将整个分区划分为大小相等的块组(Block Group),每个块组由以下部分组成:

  3. Super Block: 大小为 1 个 block 超级块主要有两个功能:

    1)超级块结构给出了文件系统的全局信息。例如块大小,文件系统的版本等等。

    2)超级块结构包含一些函数指针,例如 super_operation 的成员函数 read_inode 提供了读取 inode 信息的功能。每个具体的文件系统一般都要提供这个函数来实现对 inode 信息的读取,例如 ext2 文件系统提供的具体函数是 ext2_read_inode。

    可能有多个块组存在 super block,这是一种冗余备份。如果 0 号块组的 super block 损坏了,可以用后面的进行恢复,多个块组也会进行一致性检查。

    struct super_block {
        unsigned long s_blocksize;//指定了文件系统的块大小
        unsigned char s_blocksize_bits;
        ……/*省略超级块的链表、设备号等代码*/
        unsigned long long s_maxbytes; /* 指定文件系统中最大文件的尺寸 */
       
        struct file_system_type *s_type;/*s_type是指向file_system_type结构的指针。*/
        struct super_operations *s_op;
       
        unsigned long s_magic;
        struct dentry *s_root;/*s_root是指向文件系统根dentry的指针。*/
       
        struct list_head s_inodes; /* all inodes */
        struct list_head  s_dirty; /* dirty inodes */
       
        struct block_device *s_bdev;
        void  *s_fs_info; /* Filesystem private info */
     };
    
  4. GDT(Group Descriptor Table),组描述符表。由很多组描述符组成,整个分区分成多少个组就对应有多少个组描述符,大小为 n 个 block(n 为组数)。每个组描述符(Group Descriptor)存储一个组的描述信息,例如在这个组中从哪里开始是 inode 表,从哪里开始是数据块,空闲的 inode 和数据块还有多少个等等。

    从上图可以看出,超级块和组描述符被复制到每个块组中,因为一旦超级块意外损坏就会丢失整个分区的数据,一旦块组描述符意外损坏就会丢失整个块组的数据,因此它们都有多份拷贝只有块组 0 中包含的超级块和组描述符才被内核使用,当执行 e2fsck 检查文件系统一致性时,第 0 个块组中的超级块和块组描述符表就会拷贝到其它块组,这样当第0个块组的开头意外损坏时就可以用其它拷贝来恢复,从而减少损失。

  5. Block Bitmap,块位图。块位图就是用来描述整个块组中哪些块已用哪些块空闲的,它本身占一个块,其中的每个 bit 代表本块组中的一个块,这个 bit 为 1 表示该块已用,这个 bit 为 0 表示该块空闲可用。这里引出两个问题:

    1.为什么用df命令比du命令统计整个磁盘的已用空间非常快呢? 因为df命令只需要查看每个块组的块位图即可,而不需要搜遍整个分区。相反,用du命令查看一个较大目录的已用空间就非常慢,因为不可避免地要搜遍整个目录的所有文件。

    Linux df命令用于显示目前在Linux系统上的文件系统的磁盘使用情况统计。 Linux du命令用于显示目录或文件的大小。 详情可参照:http://www.runoob.com/linux/linux-comm-df.html http://www.runoob.com/linux/linux-comm-du.html

    2. 在格式化一个分区时究竟会划出多少个块组呢? 这主要取决于分区大小和块的大小。我们考虑一个 32GB 的 ext2 分区,块大小为 4KB,Block Bitmap占一个块,也就是 4KB,可以标注 4K*8=32K 个块,即 128 MB。因此,一个 32GB 的 ext2 分区需要 32GB/128MB=256 个块组。

  6. inode Bitmap,inode 位图,和块位图类似,本身占一个块,其中每个 bit 表示一个 inode 是否空闲可用。

  7. inode Table,inode 表。一个文件除了数据需要存储之外,一些描述信息也需要存储,例如文件类型(常规、目录、符号链接等),权限,文件大小,创建/修改/访问时间等,也就是ls -l命令看到的那些信息,这些信息存在 inode 中而不是数据块中。每个文件都有一个inode,一个块组中的所有 inode 组成了 inode 表。每个inode 占 128B。

    inode表占多少个块在格式化时就要决定并写入块组描述符中,mke2fs格式化工具的默认策略是一个块组有多少个8KB就分配多少个inode。

  8. Data Block:数据块。 对于常规文件的内容存储在数据块中。 对于目录,使用一个目录项的数据结构存储在数据块中。每个目录项存储了文件的文件名,inode号,文件类型,记录长度。

1、文件和目录的索引过程

1. 要存一个hello的文件,具体步骤是?

  1. 内核加载块组 0 中的GDT,从GDT中找出inode bitmap,从inode bitmap中找出inode table中空闲的inode。
  2. 申请一个inode。inode主要包含两部分内容:文件属性(68Bytes),数据块指针(60Bytes)。数据块指针指向存储hello文件目录项和文件内容的Data Block。
  3. 将文件内容存在对应的 Data Block 中。
  4. 将文件名和 inode 指针添加到文件所在目录的 data block 中
  5. 修改对应的inode Bitmap 和 Block Bitmap。

目录也是文件,目录的 data block 中存放的是当前目录中所有文件和目录的 名称 和 inode 指针。

文件名并没有存贮到文件的 inode 中,查找文件首先要通过文件名找到记录在文件所属目录文件中的 inode 指针。

2. 给定文件路径“/home/hello”,操作系统时如何找到该文件的位置?

  1. 查找根目录的目录项。Linux有规定,根目录的目录项必须存放在 2 号 inode 中。
  2. 根目录的目录项中存着根目录下的子目录目录项和文件的数据块信息。通过根目录的目录项可以找到 home 对应的 inode。
  3. 根据 home 对应的 inode 找到 home 的目录项。
  4. 在home 目录项中找到 hello 文件的 inode。
  5. 根据 hello 文件的 inode 中的数据块指针找到存储有 hello 文件内容的数据块。

3. 如何删除hello文件?

  1. 找到hello文件位置。
  2. 将Block Bitmap中对应bit置为0
  3. 将inode Bitmap中对应bit置为0

删除文件并没有真的把文件删掉,只是把文件的 inode 以及使用的 data block 对应的 bitmap 中的位置为 0,数据块上的内容依然存在。这也是为什么删除文件比拷贝文件要快。

如果文件被误删,最好的处理方式是什么也不做,找技术人员恢复数据。

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)   // 三级索引 

在第一节里面我们提到,操作系统在查找文件时,会根据inode中数据块指针找到对应的Data Block。本节我们介绍数据块寻址。下图为寻址过程。

io14.png

上文我们说到一个 inode 中数据块指针占了 60Bytes,其中每一个指针占 4 字节,一共有 60/4=15 个指针。从上图可以看出,索引项Blocks[12-13] 分别指向一级,二级,三级的间接寻址块。因此对于一个inode来说,最多可存储(12+256+2562+2563)*4KB的数据。

这种寻址方式对于访问不超过12个数据块的小文件是非常快的,访问文件中的任意数据只需要两次读盘操作,一次读inode(也就是读索引项)一次读数据块。而访问大文件中的数据则需要最多五次读盘操作:inode、一级间接寻址块、二级间接寻址块、三级间接寻址块、数据块。实际上,磁盘中的inode和数据块往往已经被内核缓存了,读大文件的效率也不会太低

  • https://www.jianshu.com/p/3355a35e7e0a

3、一个文件什么时候可以被删除?

linux 是通过link的数量控制文件删除的,只有当文件不存在任何链接时,该文件才会被删除,一般每个文件有两个link计数器: i_counti_nlink,从VFS inode结构体中可以找到:

struct inode {
    struct hlist_node   i_hash;    /* hash链表的指针 */
    struct list_head    i_list;    /* backing dev IO list */
    struct list_head    i_sb_list; /* 超级块的inode链表 */
    struct list_head    i_dentry;  /* 引用inode的目录项对象链表头 */
    unsigned long    i_ino;   /* 索引节点号 */
    atomic_t     	 i_count; /* 引用计数器 */
    unsigned int     i_nlink; /* 硬链接数目 */
}
  • i_count: 引用计数器,文件被一进程引用,i_count 数增加 ,可以认为是当前文件使用者的数量;

  • i_nlink硬链接数目(可以理解为磁盘的引用计数器),创建硬链接对应的 i_nlink 就会增加

对于rm命令来说,实际就是减少磁盘的引用计数 i_nlink 。当 i_nlink 和 i_count 均为 0 时,文件才会被删除(这里的删除是指将文件名到 inode 的链接删除了,但文件在磁盘上的block数据块并未被删除)。

  • https://www.zhihu.com/question/55786422/answer/146250606

4、深入理解软硬链接

创建硬链接并没有创建新的 inode,只是建立了文件名和已有 inode 的映射,并写入所属目录文件中,对应 inode 结构体中的硬链接数目增加 1

创建软连接实际上创建了新的文件,自然创建了新的 inode,将指向的文件 路径和文件名记录在 data block 中。

5、硬链接数目

  1. 文件的硬链接数目

    ❯ touch file
       
    ❯ ll
    total 0
    -rw-r--r-- 1 root root 0 May 22 11:10 file
       
    ❯ ln file hard_file
       
    ❯ ll
    total 0
    -rw-r--r-- 2 root root 0 May 22 11:10 file
    -rw-r--r-- 2 root root 0 May 22 11:10 hard_file
       
    ❯ stat file
      File: file
      Size: 0               Blocks: 0          IO Block: 4096   regular empty file
    Device: fc01h/64513d    Inode: 1052087     Links: 2
    Access: (0644/-rw-r--r--)  Uid: (    0/    root)   Gid: (    0/    root)
    Access: 2022-05-22 11:10:19.640259728 +0800
    Modify: 2022-05-22 11:10:19.640259728 +0800
    Change: 2022-05-22 11:10:34.724571016 +0800
     Birth: -
        
    ❯ ll -i
    total 0
    1052087 -rw-r--r-- 2 root root 0 May 22 11:10 file
    1052087 -rw-r--r-- 2 root root 0 May 22 11:10 hard_file
    
  2. 目录的硬链接数

    ❯ mkdir dir
    ❯ ll
    total 4.0K
    drwxr-xr-x 2 root root 4.0K May 22 11:11 dir
    

    dir 目录一经创建硬链接数就是 2,为什么?

    因为除了 dir 以外,dir/. 也表示该目录

    ❯ ll -i
    total 4.0K
    1052108 drwxr-xr-x 2 root root 4.0K May 22 11:11 dir
       
    ❯ cd dir
       
    ❯ ll -ai
    total 8.0K
    1052108 drwxr-xr-x 2 root root 4.0K May 22 11:11 .
    

    如果我们在 dir 目录中再创建一个目录:

    ❯ mkdir new_dir
       
    ❯ ll -i ../
    total 4.0K
    1052108 drwxr-xr-x 3 root root 4.0K May 22 11:15 dir
       
       
    ❯ ll -ai new_dir
    total 8.0K
    1052108 drwxr-xr-x 3 root root 4.0K May 22 11:15 ..
    

    由于 new_dir/.. 也引用了 dir 所以硬链接数目变为了 3

    目录的硬链接数 = 目录的子目录数 + 2

6、文件的三个时间

❯ stat main
  File: main
  Size: 16912           Blocks: 40         IO Block: 4096   regular file
Device: fc01h/64513d    Inode: 655799      Links: 1
Access: (0755/-rwxr-xr-x)  Uid: (    0/    root)   Gid: (    0/    root)

Access: 2022-05-21 23:10:37.728488934 +0800
Modify: 2022-05-21 23:10:35.968452572 +0800
Change: 2022-05-21 23:10:35.968452572 +0800

access 指最近一次的程序对文件(目录)的直接存取时间,通俗来讲是文件最近一次被访问的时间,但这里的访问是直接存取,而不是从缓存存取。对于目录来说,只进入目录不会更新其Access Time,但是通过 ls 查看目录内容时,Access Time就会更新。ls 命令默认看到的是 Access Time。文件如果具有可执行权限,文件执行时,Access Time 也会被修改。

modify 是指文件内容最后一次被修改的时间

change 是指文件的元数据最后一次被修改的时间

比如修改文件的 rwx 权限

touch 一个已经存在的文件可以将全部三个时间都更新为当前时间(还有一个 access time)

touch 支持的三个参数含义如下:

  • -a 文件名:将文件的 Access Time 修改为当前系统时间
  • -m 文件名: 将文件的 Modify Time 修改为当前系统时间
  • -c 文件名: 将文件的 Change Time 修改为当前系统时间

3、ext2 重要结构体

linux 版本 2.6.11.12

文件:ext2_fs.h

1)ext2_super_block

/*
 * Structure of the super block
 */
/**
 * ext2在磁盘上的超级块。
 */
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 */
	/**
	 * 状态标志。0已经安装或者没有正常卸载。1被正常卸载。2包含错误。
	 */
	__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 */
	/**
	 * 创建文件系统的OS
	 */
	__le32	s_creator_os;		/* OS */
	/**
	 * 版本号。
	 */
	__le32	s_rev_level;		/* Revision level */
	/**
	 * 保留块的缺省UID。
	 */
	__le16	s_def_resuid;		/* Default uid for reserved blocks */
	/**
	 * 保留块的缺省用户组ID。
	 */
	__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 doesn't
	 * know about, it should refuse to mount the filesystem.
	 * 
	 * e2fsck's requirements are more strict; if it doesn't know
	 * about a feature in either the compatible or incompatible
	 * feature set, it must abort and not try to meddle with
	 * things it doesn't 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 */
	/**
	 * 128位文件系统标识符。
	 */
	__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 */
	/**
	 * 用NULL填充1024字节。 
	 */
	__u32	s_reserved[190];	/* Padding to the end of the block */
};

2)ext2_group_desc

GDT

/*
 * Structure of a blocks group descriptor
 */
/**
 * ext2文件系统的块组描述符,内存、磁盘中的数据结构是一致的。
 */
struct ext2_group_desc
{
	/**
	 * 块位图的块号
	 */
	__le32	bg_block_bitmap;		/* Blocks bitmap block */
	/**
	 * 索引节点位图的块号
	 */
	__le32	bg_inode_bitmap;		/* Inodes bitmap block */
	/**
	 * 第一个索引节点表块的块号。
	 */
	__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;
	/**
	 * NULL,保留
	 */
	__le32	bg_reserved[3];
};

3)ext2_inode

struct ext2_inode {
	/**
	 * 文件类型和访问权限。
	 *		0:未知文件
	 *		1:普通文件
	 *		2:目录
	 *		3:字符设备
	 *		4:块设备
	 *		5:命名管道
	 *		6:套接字
	 *		7:符号链接
	 */
	__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 */
	/**
	 * 文件的数据块数。以512B为单位
	 */
	__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 */
	/**
	 * 文件版本,用于NFS
	 */
	__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 */
};
/**
 * 长度为32的无符号数,低位在前
 */
typedef __u32 __bitwise __le32;
/**
 * 长度为32的无符号数,高位在前
 */
typedef __u32 __bitwise __be32;

参考:

4、有关命令

1)fsstat

可以查看文件系统的详细信息

安装方式:

apt install sleuthkit    

使用:

❯ df
Filesystem     1K-blocks     Used Available Use% Mounted on

/dev/loop0         99180     5864     88200   7% /mnt/mydisk

❯ fsstat  /dev/loop0

显示内容:

FILE SYSTEM INFORMATION
--------------------------------------------
File System Type: Ext2
Volume Name: 
Volume ID: 46bbf4d9891d12a4d042b40e9a344c3d

Last Written at: 2022-05-16 09:48:28 (CST)
Last Checked at: 2022-05-16 01:11:22 (CST)

Last Mounted at: 2022-05-16 09:48:28 (CST)
Unmounted Improperly
Last mounted on: /mnt/mydisk

Source OS: Linux
Dynamic Structure
Compat Features: Ext Attributes, Resize Inode, Dir Index
InCompat Features: Filetype, 
Read Only Compat Features: Sparse Super, Large File, 

METADATA INFORMATION
--------------------------------------------
Inode Range: 1 - 25601
Root Directory: 2
Free Inodes: 25589

CONTENT INFORMATION
--------------------------------------------
Block Range: 0 - 25598
Block Size: 4096
Free Blocks: 24783

BLOCK GROUP INFORMATION
--------------------------------------------
Number of Block Groups: 1
Inodes per group: 25600
Blocks per group: 32768

Group: 0:
  Inode Range: 1 - 25600
  Block Range: 0 - 25598
  Layout:
    Super Block: 0 - 0
    Group Descriptor Table: 1 - 1
    Data bitmap: 8 - 8
    Inode bitmap: 9 - 9
    Inode Table: 10 - 809
    Data Blocks: 810 - 25598
  Free Inodes: 25575 (99%)
  Free Blocks: 23329 (91%)
  Total Directories: 7

Ubuntu Manpage: fsstat - Display general details of a file system