一步一步学linux操作系统: 25 文件系统_硬盘文件系统

硬盘

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统

硬盘→盘片→磁道→扇区(每个 512 字节)
文件系统就是安装在这样的硬盘之上
Linux 下最主流的文件系统格式——ext 系列的文件系统的格式

inode 与块的存储

块(Block)

硬盘分成相同大小的单元,块(Block)
一块的大小是扇区大小的整数倍,默认是 4K(格式化设置)
一个文件不用给他分配一块连续的空间,分散成一个个小块进行存放

inode 结构

需要一个结构 inode 来存放文件通过块来存储的信息

inode 数据结构
\linux-4.13.16\fs\ext4\ext4.h


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 */
......
  __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;
......
};

在这里插入图片描述

  • 基本信息
    文件的数据存放得太散,需要设立一个索引区域,用来维护“某个文件分成几块、每一块在哪里”等等这些基本信息

  • 元数据部分
    例如名字、权限等

  • 文件的读写权限 i_mode,属于哪个用户 i_uid,哪个组 i_gid,大小是多少 i_size_io,占用多少个块 i_blocks_io

  • 维护三个文件相关的时间: i_atime 访问时间; i_ctime 更改 inode 时间; i_mtime 更改文件时间

  • 文件分为多个块, 每个块的位置存放在 inode 的 i_block 中

i_block 如何通过块存储文件

i_block 结构,在 inode 数据结构中

__le32	i_block[EXT4_N_BLOCKS];/* Pointers to blocks */

在这里插入图片描述
EXT4_N_BLOCKS 定义
\linux-4.13.16\fs\ext4\ext4.h


#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)
	

在这里插入图片描述

ext2 和 ext3 如何存储
  • 1、前 12 项直接保存了块的位置
    通过 i_block[0-11],直接得到保存文件内容的块
  • 2、若文件较大, 则第十三项i_block[12]指向间接块
    间接块里面放数据块的位置,通过间接块可以找到数据块
  • 3、如果文件再大一些,i_block[13]会指向一个块,可以用二次间接块
    二次间接块里面存放了间接块的位置,间接块里面存放了数据块的位置,数据块里面存放的是真正的数据
  • 4、如果文件再大一些,i_block[14]会指向三次间接块。原理和上面一样

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统

  • 问题?
    对于大文件来讲,要多次读取硬盘才能找到相应的块,访问速度就会比较慢
ext4 存储改进

ext4 做了一定的改变。它引入了一个新的概念,叫做 Extents

Extents 树形结构, 每个节点由一个头 ext4_extend_header 来描述节点

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统
ext4_extent_header 结构体
\linux-4.13.16\fs\ext4\ext4_extents.h

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 */
};

在这里插入图片描述

eh_entries 表示这个节点里面有多少项,分两种,两种类型的项的大小都是 12 个 byte

  • 叶子节点
    直接指向硬盘上的连续块的地址,称为数据节点 ext4_extent
  • 分支节点
    指向下一层的分支节点或者叶子节点,称为索引节点 ext4_extent_idx

\linux-4.13.16\fs\ext4\ext4_extents.h


/*
 * 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;
};

在这里插入图片描述
文件不大: inode 可放下一个头 + 4 个数据项, eh_depth = 0 表示数据节点

文件较大: 4 个 extent 放不下,就要分裂成为一棵树,eh_depth>0 的节点就是索引节点,其中根节点深度最大 , 除了根节点其他节点都存于一个块中, 4K 能存 340 项, 每项可放 128MB, 总 42.5GB

inode 位图和块位图

专门弄了一个块来保存 inode 的位图,在这个块的 4k 里面,每一位对应一个 inode,如果是1,表示这个 inode 已经被用了;如果是0,则表示没被用。同样的专门用一个块保存 block 的位图

文件系统的格式

块组

数据块的位图是放在一个块里面的,共 4k,每位表示一个数据块,共可以表示 4∗1024∗8=215 个数据块,如果每个数据块按默认的 4K,最大可以表示空间为 215 ∗4∗1024= 227 个 byte,也就是 128M

如果采用,“一个块的位图 + 一系列的块”,外加 “一个块的 inode 的位图 + 一系列的 inode 的结构”,最多能够表示 128M ,这个结构称为一个块组

块组用数据结构 ext4_group_desc 表示,包含 inode 位图 bg_inode_bitmap_lo , block 位图 bg_block_bitmap_lo 和 inode 列表 bg_inode_table_lo

块组描述符表

一个个块组,就基本构成了整个文件系统的结构,多个块组描述符同样组成一个列表,称为块组描述符表

超级块

还需要有一个数据结构,对整个文件系统的情况进行描述,这个就是超级块ext4_super_block。

包含信息

  • 整个文件系统一共有多少 inode,s_inodes_count
  • 一共有多少块,s_blocks_count_lo
  • 每个块组有多少 inode,s_inodes_per_group
  • 每个块组有多少块,s_blocks_per_group 等

对于整个文件系统,第一个块组前 1k 用于启动引导

整个文件系统格式 如下

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统
超级块和块组描述符表都是全局信息,而且这些数据很重要都需要备份

备份策略

默认情况

每一个块组里面都保存超级块和块组描述符表的副本

sparse_super 特性

超级块和块组描述符表的副本只会保存在块组索引为 0、3、5、7 的整数幂里

这有个问题,超级块不是很大备份多了也没有太多问题;但是对于块组描述符表备份太多,一方面很浪费空间,另一个方面由于一个块组最大 128M,而块组描述符表里面有多少项,限制了有多少个块组从而限制了整个文件系统的大小

Meta Block Groups 特性

将块组分成多个组(元块组) ,块组描述符表只保存当前元块组中块组的信息, 并在元块组内备份。避免块组表浪费空间, 或限制文件系统的大小。

每个元块组里面的块组描述符表仅仅包括自己的,一个元块组包含 64 个块组,这样一个元块组中的块组描述符表最多 64 项

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统
根据图中,每一个元块组包含 64 个块组,块组描述符表也是 64 项,备份三份,在元块组的第一个,第二个和最后一个块组的开始处。

目录存储格式

目录也是文件, 也有 inode, inode 里面也是指向一些块, 块中保存目录里各个文件信息,这些信息称为 ext4_dir_entry。普通文件的块里面保存的是文件数据。

ext4_dir_entry 结构


struct ext4_dir_entry {
  __le32  inode;      /* Inode number */
  __le16  rec_len;    /* Directory entry length */
  __le16  name_len;    /* Name length */
  char  name[EXT4_NAME_LEN];  /* File name */
};
struct ext4_dir_entry_2 {
  __le32  inode;      /* Inode number */
  __le16  rec_len;    /* Directory entry length */
  __u8  name_len;    /* Name length */
  __u8  file_type;
  char  name[EXT4_NAME_LEN];  /* File name */
};

有两个版本: ext4_dir_entry 有一个 一个 16 位的 name_len;ext4_dir_entry_2 一个 8 位的 name_len 和 8 位的 file_type

列表的模式保存

在目录文件的块中,最简单的保存格式是列表
每一项都会保存这个目录的下一级的文件的文件名和对应的 inode
第一项 “.” 当前目录; 第二项 “…” 上一级目录

索引的模式保存

目录下面的文件太多的时候,一个个去找,太慢了,添加了索引的模式

inode 中设置 EXT4_INDEX_FL 标志,目录文件的块的组织形式将发生变化


struct dx_root
{
  struct fake_dirent dot;
  char dot_name[4];
  struct fake_dirent dotdot;
  char dotdot_name[4];
  struct dx_root_info
  {
    __le32 reserved_zero;
    u8 hash_version;
    u8 info_length; /* 8 */
    u8 indirect_levels;
    u8 unused_flags;
  }
  info;
  struct dx_entry  entries[0];
};

其中 索引项 dx_entry

struct dx_entry
{
  __le32 hash;
  __le32 block;
};

其实就是文件名的哈希值和数据块的一个映射关系

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统

软链接和硬链接的存储格式

链接即文件的别名: ln -s 创建软链接; ln 创建硬链接

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统

硬链接与原始文件共用一个 inode, 但不能跨文件系统

软链接是一个文件, 有自己的 inode, 该文件内容指向另一个文件, 可跨文件系统

inode 和数据块在文件系统上的关联关系

无论是文件夹还是文件,都有一个 inode。inode 里面会指向数据块,对于文件夹的数据块,里面是一个表,是下一层的文件名和 inode 的对应关系,文件的数据块里面存放的才是真正的数据。

图片来自极客时间趣谈linux操作系统
图片来自极客时间趣谈linux操作系统

参考资料:

趣谈Linux操作系统(极客时间)链接:
http://gk.link/a/10iXZ
欢迎大家来一起交流学习

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

墨1024

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值