最低 FS

MinFS 是一个专为 Zircon 构建的简单类 Unix 文件系统。

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

使用 MinFS

主机设备(仅限 QEMU)

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

    $ truncate --size=16G blk.bin
    
  • 在平台上执行运行 zircon 脚本,并使用“--”将参数直接传递给 QEMU,然后使用“-hda”指向该文件。如果您想附加其他设备,可以使用“-hdb”“-hdc”等提供这些设备。

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

目标设备(QEMU 和真实硬件)

请务必小心,不要格式化错误的设备。如有疑问,请仅通过 QEMU 运行以下命令。 lsblk 命令可用于查看有关可从 Zircon 访问的设备的更多信息。

  • 在 zircon 中,lsblk 可用于列出系统上当前的块存储设备。在下面的示例系统中,/block/000/fuchsia.storage.block.Block 是原始块存储设备。

    > lsblk
    ID  DEV      DRV      SIZE TYPE           LABEL
    000 block    block     16G
    
  • 我们来为此块存储设备添加一个 GPT。

    > gpt init /block/000/fuchsia.storage.block.Block
    ...
    > lsblk
    ID  DEV      DRV      SIZE TYPE           LABEL
    002 block    block     16G
    
  • 现在,我们已经在此设备上安装了 GPT,接下来看看我们可以用它做什么。 (注意:操作 GPT 后,设备编号可能会发生变化。使用 lsblk 跟踪如何引用块存储设备)。

    > gpt dump /block/002/fuchsia.storage.block.Block
    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 /block/002/fuchsia.storage.block.Block
    
  • 在 Zircon 中,将分区格式化为 MinFS。使用 lsblk,您应该会看到一个块存储设备(即整个磁盘)和一个稍小的设备(即分区)。在上述输出中,分区是设备 003,其路径为 /block/003/fuchsia.storage.block.Block

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

    > gpt edit 0 type fuchsia-data <DEVICE_PATH>
    
  • 在日后的任何启动中,该分区都会自动装载到 /data。

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

    > mount <PARTITION_PATH> /data
    
  • 写入 /data(此 GUID 的装载点)的所有文件都会在启动后保留。如需对此进行测试,请尝试在新的 MinFS 卷上创建一个文件,然后重新启动,并观察该文件是否仍然存在。

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

    > df <PATH>
    

Minfs 操作

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

假设

  • 任何操作(读取或写入)都不会缓存或批处理。其中每个操作都类似于在设置了同步和直接 IO 的情况下进行调用。
  • 对于重命名:目标文件不存在。如果重命名操作的目标是有效文件,则重命名操作可能会删除文件。此假设可简化数学运算。
  • “写入”操作会向 vnode 之前未访问过的部分发出单个数据块写入。
  • “覆盖”操作会向之前通过“写入”操作分配的数据块部分发出单个数据块写入。

列的键。

  1. OPERATION:文件系统客户端请求的操作。
  2. 块类型:每项文件系统操作都会导致访问一种或多种类型的块。
    • 数据:包含用户数据和目录条目。
    • 间接:文件块映射树中的间接块
    • Dindirect:文件块映射树中的双间接块。
    • Inode 表:包含一个或多个 inode 的 inode 表块。
    • inode 位图:包含一个位数组,每个位表示 inode 的空闲/已用状态。
    • 数据位图:包含一个位数组,每个位表示数据块的空闲/已用状态。
    • 超级块:包含描述文件系统布局和状态的数据。
  3. IO TYPE:IO 访问的类型(读/写)。
  4. JOURNALED:是否将 IO 记录到日志中。读取操作不会记录到日志中,但部分写入操作会记录到日志中。
  5. CONDITIONALLY ACCESSED:根据 OPERATION 的输入参数和文件系统的状态,某些块会被有条件地访问。
    • 否:始终执行 IO。
    • 是:文件系统状态和输入参数决定是否需要此 IO。
  6. 读取次数:读取的文件系统块数。
  7. 写入计数(忽略日志):写入的文件系统块数。写入日志或日志记录开销不计入此数字。
  8. 写入计数(含日志):写入日志然后写入最终位置的文件系统块数。这不包括日志为维护日志状态而写入的块。

行 <operation> Total(例如“Create Total”)会给出读取/写入的块总数。对于涉及日志记录的操作,日志会为每个操作额外写入两个块,即日志条目头和提交块。 “总计”的写入计数下的数字是写入计数(带日志记录)和日志记录开销(每次操作 2 个块)的总和。

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

操作 块类型 IO 类型 已记录 满足条件时可访问 读取次数 写入次数(忽略日志) 写入计数(带日志) 评论
查找/打开 数据 读取 否 否 >=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
Inode 表 写入 是 否 0 1 2 新文件的 inode。
inode 位图 写入 是 否 0 1 2 将 inode 标记为已分配。
数据位图 写入 是 否 0 >=0 >=0 目录可能会扩大,以包含新的目录条目。
超级街区 写入 是 否 0 1 2 除其他事项外,分配的 inode 编号也会发生变化。
创建总计 >=1 >=4 >=10 包含 2 个用于日志条目的组成块。
重命名 数据 读取 否 否 >=1 0 0 重命名涉及在源目录中进行查找。
间接 读取 否 是 >=0 0 0
DIndirect 读取 否 是 >=0 0 0
数据 写入 是 否 0 >=1 >=2 源目录条目。
间接 写入 是 是 0 >=0 >=0
DIndirect 写入 是 是 0 >=0 >=0
Inode 表 写入 是 否 0 1 2 更新源目录 inode。
数据 读取 否 否 >=0 0 0 重命名涉及在源目录中进行查找。
间接 读取 否 是 >=0 0 0
DIndirect 读取 否 是 >=0 0 0
数据 写入 是 是 0 >=0 >=0 正在写入目标目录条目。
间接 写入 是 是 0 >=0 >=0
DIndirect 写入 是 是 0 >=0 >=0
Inode 表 写入 是 是 0 1 2 更新目标目录 inode。
Inode 表 写入 是 否 0 1 2 重命名文件的 mtime。
数据位图 写入 是 否 0 >=0 >=0 如果我们分配了数据,则为间接或 Dindirect 块。
超级街区 写入 是 否 0 1 2
重命名总计 >=1 >=5 >=12 包含 2 个用于日志条目的组成块。
读取 数据 读取 否 否 >=1 0 0
间接 读取 否 是 >=0 0 0
DIndirect 读取 否 是 >=0 0 0
读取总数 >=1 0 0
写入 间接 读取 否 是 >=0 0 0 即使写入操作不是覆盖,我们也可能会与现有数据共享(间接)块。导致读取-修改-写入。
DIndirect 读取 否 是 >=0 0 0
数据 写入 否 否 0 1 1
间接 写入 是 是 0 >=0 >=0
DIndirect 写入 是 是 0 >=0 >=0
Inode 表 写入 是 否 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
Inode 表 写入 是 否 0 1 2
数据位图 写入 是 否 0 1 2 写入新分配。
数据位图 写入 是 否 0 >=0 >=0 释放旧块。此块位可能属于已分配的块位图。
超级街区 写入 是 否 0 1 2
覆盖总数 >=0 >=4 >=9 包含 2 个用于日志条目的组成块。