最低 FS

MinFS 是为 Zircon 构建的一种类似于 Unix 的简单文件系统。

目前支持的文件大小上限为 4 GB。

使用 MinFS

主机设备(仅限 QEMU)

  • 创建用于存储 MinFS 的磁盘映像

    # (Linux)
    $ truncate --size=16G blk.bin
    # (Mac)
    $ mkfile -n 16g blk.bin
    
  • 使用“--”在您的平台上执行运行 zircon 脚本,以将参数直接传递给 QEMU,然后使用“-hda”指向该文件。如果您想连接其他设备,可以向他们提供“-hdb”“-hdc”等。

    fx set bringup.x64
    fx build
    fx qemu -- -hda blk.bin
    

目标设备(QEMU 和 Real 硬件)

请谨慎选择错误的设备格式。如有疑问,请仅通过 QEMU 运行以下命令。 lsblk 命令可用于查看有关可从 Zircon 访问的设备的更多信息。

  • 在 zircon 中,lsblk 可用于列出系统中当前存在的块设备。在下面的示例系统中,/dev/class/block/000 是原始块设备。

    > lsblk
    ID  DEV      DRV      SIZE TYPE           LABEL
    000 block    block     16G
    
  • 我们来向这个块设备添加一个 GPT

    > gpt init /dev/class/block/000
    ...
    > lsblk
    ID  DEV      DRV      SIZE TYPE           LABEL
    002 block    block     16G
    
  • 既然设备上已经有 GPT,我们看看能用它来做些什么。 (注意:操作 GPS 后,设备编号可能会更改。请使用 lsblk 跟踪如何引用块存储设备)。

    > gpt dump /dev/class/block/002
    blocksize=512 blocks=33554432
    Partition table is valid
    GPT contains usable blocks from 34 to 33554398 (inclusive)
    Total: 0 partitions
    
  • gpt dump 会告诉我们一些重要信息:它告诉我们:(1) 块大小,以及 (2) 我们实际可以使用哪些块。让我们用 MinFS 文件系统填充部分磁盘。

    > gpt add 34 20000000 minfs /dev/class/block/002
    
  • 在 Zircon 中,将分区的格式设置为 MinFS。使用 lsblk,您应该会看到一个块设备(即整个磁盘)和一个略小的设备(即分区)。在上面的输出中,分区为设备 003,路径为 /dev/class/block/003

    > mkfs <PARTITION_PATH> minfs
    
  • 如果您希望设备在重新启动时自动装载,请使用 GPT 工具设置其类型。如上所述,必须再次使用 lsblk 来查找磁盘条目。我们希望修改第 0 个分区的类型。在这里,我们使用关键字“fuchsia-data”来设置类型 GUID,但如果要使用任意 GUID,则需要在使用“fuchsia-data”的地方提供它。

    > gpt edit 0 type fuchsia-data <DEVICE_PATH>
    
  • 在每次启动时,该分区都将自动装载到 /data 下。

  • 如果您不希望自动装载分区,可以更新分区的可见性(或 GUID),只需手动装载即可。

    > mount <PARTITION_PATH> /data
    
  • 写入 /data(此 GUID 的装载点)的任何文件都将在多次启动后保留下来。如需对此进行测试,请尝试在新的 MinFS 卷上创建一个文件,然后重新启动,观察到它仍然存在。

    > touch /data/foobar
    > dm reboot
    > ls /data
    
  • 如需了解在给定路径下的每个子目录中装载了哪些块设备/文件系统,请使用以下命令:

    > df <PATH>
    

Minfs 操作

以下部分将介绍执行哪些 IO 来完成简单的最终用户操作(例如 read()/write())。

假设

  • 没有任何操作(读取或写入)、缓存或批处理。其中的每项操作都类似于使用 Sync 和 Direct io set 进行调用。
  • 重命名:目标文件不存在。如果重命名操作的目标是有效文件,则重命名可以删除文件。这个假设使数学变得简单。
  • “写入”操作将单个数据块写入到 vnode 中之前未访问的部分。
  • “覆盖”操作将单个数据块写入到之前从较早的“写入”操作分配的块的一部分。

列的键。

  1. OPERATION:文件系统的客户端请求的操作。
  2. 块类型:每个文件系统操作都会访问一种或多种类型的块。
    • 数据:包含用户数据和目录条目。
    • Indirect:文件块映射树中的间接块
    • Dindirect:文件块映射树中的 Double indirect 块。
    • Inode 表:包含一个或多个 inode 的 Inode 表块。
    • 节点位图:包含一个位数组,每个位表示 inode 的空闲/使用状态。
    • 数据位图:包含一个位数组,每个位表示数据块的空闲/使用状态。
    • Superblock:包含描述文件系统布局和状态的数据。
  3. IO 类型: IO 访问的类型(读/写)。
  4. JOURNALED:是否记录 IO 日志。读取不记录日志,但部分写入记录为日志。
  5. 有条件地访问:根据 OPERATION 的输入参数和文件系统的状态,有条件地访问某些块。
    • 否:始终执行 IO。
    • 是:文件系统状态和输入参数决定了是否需要此 IO。
  6. 读取计数:读取的文件系统块数。
  7. WRITE COUNT(IGNORING JOURNAL):写入的文件系统块数。写入日志或日志的开销不会计入此数值。
  8. WRITE COUNT (WITH JOURNAL):写入日志,然后写入最终位置的文件系统块数。这不包括用于维护日志状态的块日志写入。

<operation> 行总计(例如“Create Total”)提供了读取/写入的块总数。对于涉及日记的操作,日记功能会为每个操作再写入两个块,即日记条目标头和提交块。 Total 的写入计数是写入 COUNT (WITH JOURNALING) 和日志开销的总和,即每项操作 2 个块。

启动(mount/fsck) 文件系统时,Superblock、Inode 表、Inode 位图、数据位图和部分日志会缓存在内存中。因此,系统绝不会针对这些块类型发出读取 IO

运营 屏蔽类型 广告订单类型 已上报 有条件访问 读取计数 写入计数(忽略新闻) 写入 COUNT(带简报) 评论
查询/打开 数据 已读 大于等于 1 0 0 如果目录很大,则会读取多个块。
间接 已读 大于等于 0 0 0 查找可通过直接块执行。因此,间接销售是可选方法。
DIndirect 已读 大于等于 0 0 0 查找可通过直接块执行。因此,dindirect 是可选的。
查询/打开总计 大于等于 1 0 0
创建 数据 已读 大于等于 1 0 0 创建涉及到名称冲突时先查询。
间接 已读 大于等于 0 0 0
DIndirect 已读 大于等于 0 0 0
数据 写入 0 大于等于 1 大于等于 2
间接 写入 0 大于等于 0 大于等于 0
DIndirect 写入 0 大于等于 0 大于等于 0
节点表 写入 0 1 2 新文件的 Inode。
节点位图 写入 0 1 2 将 inode 标记为已分配。
数据位图 写入 0 大于等于 0 大于等于 0 目录可能会增大,以包含新的目录条目。
超级方块 写入 0 1 2 此外,已分配的索引节点编号也会发生变化。
创建总计 大于等于 1 >=4 大于等于 10 包含用于日志条目的 2 个块。
重命名 数据 已读 大于等于 1 0 0 重命名涉及在源目录中查找。
间接 已读 大于等于 0 0 0
DIndirect 已读 大于等于 0 0 0
数据 写入 0 大于等于 1 大于等于 2 源目录条目。
间接 写入 0 大于等于 0 大于等于 0
DIndirect 写入 0 大于等于 0 大于等于 0
节点表 写入 0 1 2 更新源目录 inode。
数据 已读 大于等于 0 0 0 重命名涉及在源目录中查找。
间接 已读 大于等于 0 0 0
DIndirect 已读 大于等于 0 0 0
数据 写入 0 大于等于 0 大于等于 0 正在写入目标目录条目。
间接 写入 0 大于等于 0 大于等于 0
DIndirect 写入 0 大于等于 0 大于等于 0
节点表 写入 0 1 2 更新目标目录 inode。
节点表 写入 0 1 2 重命名了文件的 mtime。
数据位图 写入 0 大于等于 0 大于等于 0 如果我们分配了数据,则会分配间接或间接区块。
超级方块 写入 0 1 2
重命名总计 大于等于 1 >=5 12 周岁以上 包含用于日志条目的 2 个块。
已读 数据 已读 大于等于 1 0 0
间接 已读 大于等于 0 0 0
DIndirect 已读 大于等于 0 0 0
读取总计 大于等于 1 0 0
写入 间接 已读 大于等于 0 0 0 即使写入未覆盖,我们也可以与现有数据共享 (D) indirect 块。导致读取、修改和写入。
DIndirect 已读 大于等于 0 0 0
数据 写入 0 1 1
间接 写入 0 大于等于 0 大于等于 0
DIndirect 写入 0 大于等于 0 大于等于 0
节点表 写入 0 1 2 Inode 的 mtime 更新。
数据位图 写入 0 1 2 针对分配的块。
超级方块 写入 0 1 2 分配的块数发生更改。
写入总计 大于等于 0 >=4 >=9 包含用于日志条目的 2 个块。
覆盖 数据 已读 大于等于 0 0 0 读取/修改/写入。
间接 已读 大于等于 0 0 0
DIndirect 已读 大于等于 0 0 0
数据 写入 0 1 1
间接 写入 0 大于等于 0 大于等于 0
DIndirect 写入 0 大于等于 0 大于等于 0
节点表 写入 0 1 2
数据位图 写入 0 1 2 写入新的分配。
数据位图 写入 0 大于等于 0 大于等于 0 释放旧方块。该块位可能属于“分配的块位图”。
超级方块 写入 0 1 2
覆盖总计 大于等于 0 >=4 >=9 包含用于日志条目的 2 个块。