Table of Contents

Core/IO 使用指南——文件 IO 原语层(IFileSystem / IFileHandle)

给谁看:需要文件级 IO 的组件开发者(引擎、数据结构、工具);外部业务通常经 IStorageEngine 间接用(见 ../TC.Tier.Runtime/docs/storage-engine.md)。 回答什么:怎么构造文件系统、怎么读写、怎么落盘、能力怎么协商、什么不要做。 本篇只讲怎么用——机制细节(spec 全参数/句柄池内部/网络桥内部)在链接与源码注释。


0. 快速开始(30 秒上手)

using TC.Tier.Core.IO;

// ── 1. 构造文件系统(spec 一行切换——§1.1)──
using var fs     = TierFs.New("local:///" + rootDir);                // 本地文件系统(New = 建空镜像)
using var mem    = TierFs.Open("memory:");                           // 内存文件系统私有卷(测试隔离)
using var remote = TierFs.Open("network:///s3/minio:9000/tier-logs?tls=0&cred=env:MINIO_KEY");

// ── 2. 建结构 + 提前创建(建段协议:创建成本移出运行时热路径)──
fs.EnsureRoot();                                        // 根存在保证(幂等)
fs.CreateDirectory("struct1/eng0/compact");             // mkdir -p 幂等 + 耐久
fs.CreateFile("struct1/eng0/data.0", preallocateSize: 1 << 30,
              extra: header);                           // 真预留 + FileExtra 创建即写;已存在抛 AlreadyExists

// ── 3. 运行时打开读写 ──
using (var h = fs.Open("struct1/eng0/data.0", new FileOpenOptions
       { Access = AccessMode.ReadWrite, Mode = FileOpenMode.OpenExisting, Sharing = FileSharing.ReadWrite }))
{
    h.Write(4096, record);                              // pwrite:不读不推进游标;越过 EOF 零洞扩展
    var landing = h.Append(record);                     // 多写者原子追加(返回落点)
    h.SetFileExtra(meta);                               // FileExtra ≤1.5K
    h.Flush();                                          // ★ 网络文件系统 = 唯一持久化点(置顶一)
}

// ── 4. 枚举 / 恢复扫描 ──
var segs = fs.EnumerateFiles("struct1/eng0", "data.*");          // 模式匹配
var all  = fs.EnumerateEntries("struct1", "*", recursive: true); // 混合递归——恢复扫盘
var st   = fs.Stat("struct1/eng0/data.0");                       // 完整信息(含 FileExtra)

1. 概念(术语在此定义)

  • 两平面 × 四类文件系统 × 一个注入点
    • 命名空间平面 IFileSystem(目录/创建/枚举/FileExtra/卷锁——一个实例 = 根空间);
    • 数据平面 IFileHandle(pread/pwrite/追加/空间管理/映射/锁);
    • 四类文件系统:本地(DiskFileSystem · local://)/ 内存(MemoryFileSystem · memory:)/ 网络(RemoteFileSystem · network://,对象存储协议)/ 虚拟(TierVolumeFs · virtual://.tier 文件 / Linux 块设备,自持一致性 + 自管页缓存;深指南 virtual-file-system.md);
    • 一个注入点:网络文件系统的扩展面 = IObjectStore(厂商实现它即接入完整网络文件系统栈; 网络文件系统桥层语义见 network-file-system.md; S3 参考实现见 ../TC.Tier.Core.IO.S3/docs/s3-protocol.md)。
  • spec = 协议头即术语local:// / memory: / virtual:// / network:///s3——换 URI 即换文件系统, 代码零改动(TierFs 工厂,可序列化、可配置驱动)。
  • 能力协商:每文件系统/句柄的 Capabilities/UnbufferedSupport 如实报告能力位(DIO/稀疏/回退), 消费者按能力位决策,不按文件系统硬编码分支

⚠️ 置顶一(网络文件系统):持久化唯一入口是 Flush

RemoteFileSystem 写句柄是 staging 写回层Write/Append 只进 staging 即返回;任何 Dispose 都不上传 (池内 = 归还;池外 = 关闭且未 Flush 的 staging 丢弃——语义同构"未 fsync 即丢")。用完即持久必须显式 h.Flush()。池的三出口(Release(close:true)/RemoveAll/pool.Dispose)同样不 flush——RemoveAll 不得 flush(删段后 flush 会复活已删对象)。

⚠️ 置顶二(内存/虚拟):Dispose 方向差异(最反直觉)

文件系统 fs.Dispose 语义 已开句柄
本地 / 网络 "离开目录"——仅释放 fs 自持资源 继续有效(OS 句柄/staging 归消费者)
内存文件系统 "拔盘"——销毁卷(数据内存可能复用,必须失效) ObjectDisposedException
虚拟文件系统 "关卷"——提交 + 置 clean + superblock 轮写 ObjectDisposedException

可移植规范:按"先映射、再句柄、最后 fs"顺序释放。


2. 怎么选(四类文件系统)

文件系统 spec 头 生产特长 关键支撑 深指南
内存文件系统 memory: 高性能运行时——直址零拷贝、纳秒级元数据 Reserved 直址;持久化 = 运行时导出镜像(§5.2) memory-file-system.md
本地文件系统 local:// 可视化目录 / 性能稳定——宿主工具直接查看 目录树形态;fsync 持久化 local-file-system.md
网络文件系统 network:// 本地不落地 / 容量无界 / 共享 staging 写回层;Flush = PUT network-file-system.md
虚拟文件系统 virtual:// 功能最全 / 稳定性最高 / 快速迁移 自持崩溃一致性;能力位覆盖最全;dd 快道 virtual-file-system.md

"易失"是内存文件系统的机制属性不是概念缺席——持久化经导出镜像全额成立(§5.2);单元测试/CI 无需本地盘只是它的场景之一。选文件系统 = 选运行形态,不是选完成度——数据面速度四类带内持平, 差异在元数据面与一致性语义(实测见 perf/io-performance.md §9)。

2.1 构造(spec / 动词 / options 三件套)

using var db  = TierFs.New ("local:///var/lib/tier?quota=100G&label=prod");
using var mem = TierFs.Open("memory:?label=test-a");
using var vol = TierFs.New ("virtual:///data/vol.tier?label=wal-a");        // 无 quota = 按需自动扩容
using var s3  = TierFs.Open("network:///s3/cos.example.com/bucket/pfx?vhost=1&cred=env:TIER_S3");

// 工厂 × options 合流(spec 定身份 + options 补调优;优先级 = spec 显式胜出 → options → 类型缺省)
using var db2 = TierFs.New("local:///var/lib/tier", new DiskFileSystemOptions { MetadataMode = DiskMetadataMode.Sidecar });

// 类型层直构(工厂底座——三段式签名 (位置[, options][, 日志]))
using var d3 = DiskFileSystem.New("/var/lib/tier", new DiskFileSystemOptions { QuotaBytes = 100L << 30 });
using var v3 = TierVolumeFs.New(TierVolumeCarrier.File("/data/vol.tier"), new TierVolumeFormatOptions { BlockSize = 4096 });

动词New = 创建空镜像(已存在且非空抛 AlreadyExists);Open = 打开既有(不存在抛 NotFound); OpenOrCreate = 懒初始化糖。四类文件系统全部同契约。

2.2 spec 语法(一分钟)

local:///var/...        本地文件系统(绝对)      local:data/tier   相对(构造时对 CWD 固化)
local:///C:/data        Windows 盘符             local://host/share  UNC
local | local:          快捷:CWD 为根           memory:            内存文件系统(私有卷)
virtual:///data/v.tier   虚拟文件系统·文件载体     virtual:///dev/nvme0n1  设备载体
network:///s3/host[:port]/bucket/prefix           网络文件系统(协议首段必填——s3 随 IO.S3 程序集自动注册)
?label=..&quota=100G&access=ro|wo|rw&exclusive=1&cred=env:NAME&spill=local:///var/tmp

quota 一词制 = 空间根硬上限(-1/缺省 = 无上限;虚拟文件系统文件载体 = 按需自动扩容)。 cred 永远是引用(env:NAME)不携值。完整参数表见 medium-parity-matrix.md


3. 命名空间平面(IFileSystem)

  • 路径契约:层级相对路径,'/' 唯一合法分隔符(\ 拒);空组件/./.. 越根/盘符/保留集 → 抛。
  • 目录族CreateDirectory(mkdir -p 幂等 + 耐久)/ DirectoryExists / DeleteDirectory (仅限空——不提供递归删)/ MoveDirectory(整树移动,原子性看能力位)。
  • 文件操作与创建解耦Exists / Delete(耐久)/ Move(overwrite: true)(overwrite 必须显式)/ CreateFile(preallocateSize:, extra:)(提前创建——与 Open 解耦,创建成本移出热路径)。
  • 枚举族EnumerateFiles/Directories/Entries*/? 模式匹配;任一组件以 . 开头 → 枚举不可见, pattern 首字符 . 豁免;递归 Name = 相对所枚举目录的多组件路径)。
  • 卷锁fs.AcquireExclusive(Timeout)(RAII + 异常安全;非重入;本地 = lock file 崩溃自愈; 网络 = 尽力型 fencing;内存 = 进程内真锁)。

4. 数据平面(IFileHandle)

4.1 打开语义四要素

using var h = fs.Open("log-0001.data", new FileOpenOptions
{
    Access   = AccessMode.ReadWrite,   // Read / Write / ReadWrite
    Mode     = FileOpenMode.OpenOrCreate,  // OpenExisting / OpenOrCreate / CreateNew / Truncate / Append
    Sharing  = FileSharing.ReadWrite,      // advisory:None / Read / Write / ReadWrite / Delete
    Hints    = FileOpenHints.None,         // NoBuffering(DIO) / WriteThrough / SequentialScan / RandomAccess
    PreallocateSize = 64L << 20,           // >0 → open 即幂等预分配
});
  • 组合合法性构造时校验(写模式配 Access=Read → 抛)。
  • DIO 语义链Hints.NoBuffering):句柄 UnbufferedSupport/RequiredAlignment = 三重对齐单一 事实源——对齐 buffer 必须走 AlignedMemoryManager/PinnedBufferPool(§7 陷阱 1)。

4.2 位置读写与追加

h.Write(4096, record);                     // pwrite:不读不推进游标;越过 EOF 零洞扩展
int n = h.Read(4096, dst);                 // 返回实际读数(EOF 处可能 < dst.Length)
await h.WriteAsync(4096, mem, ct);         // 异步族语义同同步
var landing = h.Append(record);            // ★ 文件级原子预留(同 fs 任意句柄并发追加——落点两两不交)

三个位置概念各管各的:Write/Read(offset) 无状态 / Position/Seek 句柄级书签 / Append 文件级原子预留。FileOpenMode.Append 只初始化游标于 EOF——不是强制追加

4.3 空间管理

Preallocate()(幂等)/ SetLength()(截断/零扩展)/ PunchHole(0, len)(打洞,按 AllocationUnit 对齐;区间读零)/ AllocatedSize(物理占用)/ EnumerateAllocatedRanges()(块粒度区间)/ CollapseRange(区间移除/插入,看 RangeShift 位)。

4.4 持久化(持久化 = 把数据从"会丢的层"推进到"不会丢的层")

h.Flush();                          // fsync/FlushFileBuffers/F_FULLFSYNC;网络文件系统 = PUT(唯一持久化点)
h.FlushData();                      // fdatasync 语义(仅 Linux 与 Flush 可区分;否则 ≡ Flush 不抛)
  • 本地/虚拟/网络经 Flush 持久;内存文件系统无原地持久化层——持久化点 = 运行时导出镜像(§5.2)。
  • group commit 模式:写 → 攒批 → Flush() 一次。

4.5 FileExtra 平面(文件附加数据——统一唯一概念)

一个文件 = 一份不透明附加数据 ≤1536 字节h.FileExtra(全量读)/ ReadFileExtra(offset, dst) / WriteFileExtra(offset, patch) / SetFileExtra(meta)(完全覆盖)。Delete 必清除 / Move 必保留。 "1536 数学":S3 用户元数据 HTTP 头 2048 字节总预算 − 键前缀,base64 3:4 折算——三类文件系统同限。

4.6 映射 / 范围锁 / 向量与拷贝

h.Map(0, 4096, AccessMode.ReadWrite)必须 Dispose——独立引用,泄漏)/ Lock/TryLock(advisory 契约)/ WriteVector(readv/writev 或回退)/ src.CopyRange(dst, ...)(部分失败不回滚, CompletedLength 携带)/ CloneRange(整文件克隆)。


5. 句柄池与镜像导出

5.1 句柄池(FileHandlePool——键控共享缓存)

场景 选择
长生命周期反复访问同一批文件(引擎段、索引页) (同 key 命中同实例——省 open/探测开销)
一次性临时文件(Compact 临时段) fs.Open + using
需要"现在就关" 裸 Open 或 Release(h, close: true)

真关闭只有三条出口Release(h, close: true) / RemoveAll(pred) / pool.Dispose()(须先于 fs.Dispose)。 归还后不得再触碰该引用。删段顺序:pool.RemoveAll(...)fs.Delete(...)(Windows 句柄打开时删除被拒)。

5.2 采集/还原/迁移(RootSpaceImage——四类文件系统全部支持)

一套代码通吃四类文件系统(只认 IFileSystem 接口平面):4 源 × 4 目标 = 16 格全部成立 ——任意文件系统可导出为存档、任意存档可还原到任意文件系统、任意两文件系统可直转。 产物两种形态:

  • TCA1 结构化流(清单 + 数据帧 + CRC)——内容镜像,可跨文件系统(内存 → 本地 / 本地 → 网络等);
  • 字节直拷快道ContiguousCapture)——整卷镜像(仅虚拟 ↔ 虚拟,.tier 文件 ↔ 块设备互拷, dd 快道;镜像后目标重载内存态)。
using TC.Tier.Core.IO.Image;                              // RootSpaceImage

// 导出:任何文件系统 → TCA1 存档
using (var out_ = File.Create("backup.tca"))
    RootSpaceImage.Capture(sourceFs, out_, new ImageOptions
    {
        Compression = ImageCompression.Zstd,   // None(配快道零拷贝)/ ZLib / Zstd
        QuietSource = true,                    // ★ 自动进维护门闩——静默快照
    });

// 还原:存档 → 任何空根空间(目标必须为空,非空抛 AlreadyExists)
using (var in_ = File.OpenRead("backup.tca"))
    RootSpaceImage.Restore(in_, targetFs, new ImageOptions { VerifyChecksums = true });

// 直转:任意两文件系统(能力位自动路由——虚拟↔虚拟走字节快道,其余走 TCA1)
var summary = RootSpaceImage.Transfer(sourceFs, targetFs, options);

启动的四种形态(同一份存档,按需选择落点):

启动形态 落点 一句话
测试环境复活 新内存文件系统 CI/调试无需本地盘启动——内容/Extra/稀疏全保真
落盘部署 本地文件系统目录 解档为宿主可直接查看的目录树
制成存档卷 虚拟文件系统 还原进 .tier = 单工件(存档即活卷——可只读挂载检视/抽取)
云归档 网络文件系统 存档上云(TCA1 + 压缩)

保真清单:目录树全结构、文件内容(逐帧 CRC)、稀疏洞边界、unwritten 预分配(PreallocateSize 语义重建——物理预留 + 读零 + 写转换)、FileExtra(≤1536B)。 ⚠️ 压缩会杀死文件→文件零拷贝(必须过用户态);采集前静默责任:QuietSource=true 只挡 fs 层写—— 消费者自己的后台任务先自行收敛再采集。


6. 想深入?指路

想懂什么 去哪
网络文件系统(差异表/staging/Flush 编排/fencing) network-file-system.md 与源码 IO/Remote/
S3 协议(SigV4/厂商矩阵/COS vhost/chunked 直传) ../TC.Tier.Core.IO.S3/docs/s3-protocol.md
虚拟文件系统(TierVolume 深指南:多载体/两档 IO/维护门闩/dd 快道) virtual-file-system.md
能力位矩阵与回退表(逐能力详解) 源码 IO/ 注释(使用面按能力位决策即可)
常见陷阱(精简版) 本篇 §7;完整版见源码 IO/ 注释
性能基线(四类文件系统矩阵/并发扩展) perf/io-performance.md
采集/还原/迁移管线(TCA1/快道/启动四形态) IO/Image/RootSpaceImage.cs 注释与设计稿

7. 常见陷阱(踩坑概率排序,精简版)

# 陷阱 要点
1 DIO 三重对齐 offset/length/buffer 按 h.RequiredAlignment;对齐 buffer 必须走池
2 FileSharing 是 advisory 仅约束同进程同 fs 实例;跨进程用卷锁
3 删除语义平台差异 Win 拒删被开文件;POSIX/内存延迟释放——删前先回收句柄
4 Append ≠ 强制追加 Mode.Append 只初始化游标;多写者追加 = h.Append()
5 Append 失败预留不回滚 回退会吞噬他人预留;异常带 ReservedOffset(失败区间 = 读零洞)
6 mem Sparse 映射写时差 视图写 Flush/Dispose 才写回;实时可见用 Reserved 或 Write 路径
7 IMappedSection 必须 Dispose 独立 fd——不 Dispose = 泄漏;View 越界 = 段错误
8 池归还协议 Dispose=归还不是关闭;真关闭仅三出口;归还后不得触碰引用
9 memfs Default 全局共享 测试隔离一律 TierFs.Open("memory:") 私有卷
10 mmap × DIO 混用 一致性边界由内核决定;视图生命周期与 Move/Delete 互斥由调用方协调
11 Dispose × 在途异步 先收敛(await/取消)再 Dispose

完整 18 条(含 memfs 分配模式选型/模拟边界/范围锁 OFD/卷锁成对释放)见源码 IO/ 注释。