Linux-Kernel-Notes

Thinking in linux kernel

View on GitHub

3. 缓存、I/O 模式与一致性模型

[中文] English 教程目录
上一篇 下一篇

1. 三类缓存

1.1 dentry cache

dentry 缓存“父目录 + 名字 -> inode 或不存在”。成功 LOOKUP 的 entry_valid 控制 positive dentry;不存在的名字也可形成 negative dentry。

在有效期内,VFS path walk 可以不询问 daemon。有效期结束不等于立即删除,而是下次需要时重新验证。

1.2 attribute cache

inode 属性由 attr_valid 控制,包含:

属性缓存影响的不只是 stat(2)。陈旧 i_size 会影响 EOF、写扩展、mmap fault 和 truncate 判断;陈旧 mode/uid 可能影响内核侧 permission check。

1.3 data cache

cached I/O 使用 address_space 和 folio:

data cache 没有一个简单的 data_valid 秒数。它由 open flags、INIT flags、属性变化、显式 notification、writeback 状态和 inode invalidation 共同控制。

2. 缓存有效期是 lease,不是所有权转移

daemon 返回 timeout,相当于承诺:在有效期内,内核可以使用这份答案。如果后端可能被其他参与者修改,daemon 应选择以下策略之一:

  1. 使用短 timeout,接受周期性重新验证;
  2. 监听后端变化并发送 invalidation notification;
  3. 确保所有修改都经过 daemon,从源头维持一致性;
  4. 明确只提供弱一致性。

timeout 越长,元数据性能越好,但外部变化可见性越差。timeout 为 0 会增加往返,却不自动解决已经映射或正在使用的数据一致性。

3. 主动失效

daemon 可发送:

主动失效减少了“必须把 timeout 设得很短”的压力,但引入顺序问题:notification 与普通请求可以并发,daemon 必须定义某次修改、应答和失效消息之间的 happens-before 关系。

4. 四种主要 I/O 模式

4.1 Cached I/O

优点:

代价:

4.2 Direct I/O

FOPEN_DIRECT_IO 让该 open 实例绕过普通页缓存数据路径。它不意味着单个请求或零拷贝,而是 iov_iter 按连接限制拆成一个或多个 FUSE READ/WRITE。

优点是状态直接、适合 daemon 自己管理缓存;代价是每次 I/O 都跨边界,小随机 I/O 容易受调度开销支配。

4.3 Passthrough

passthrough 把数据操作交给 backing file,减少 daemon 数据面处理。控制面仍由 FUSE 管理,因此需要保证:

4.4 virtio-fs DAX

DAX 将文件区间映射到共享 window,使 guest CPU 经页表直接访问。它消除了普通 READ/WRITE 数据请求,但新增:

5. Writeback cache 的语义变化

未启用 writeback cache 时,daemon 更接近数据和属性事实的权威来源。启用后,guest 内核可以先接受写入并在稍后回写,因此:

writeback cache 是协议语义变化,不只是性能 flag。

6. open flags 如何影响缓存

常见应答 flag:

Flag 含义
FOPEN_DIRECT_IO 该 open 使用 direct-I/O 语义
FOPEN_KEEP_CACHE open 时保留已有页缓存
FOPEN_NONSEEKABLE 文件不支持正常 seek
FOPEN_CACHE_DIR 允许缓存目录内容
FOPEN_STREAM 流式文件语义

若没有 KEEP_CACHE,内核通常在 open 时失效旧页,以避免 daemon 外部变化留下陈旧数据。具体行为还取决于 writeback、inode I/O mode 和当前 mmap。

7. 一致性场景分析

场景 A:所有修改都经过一个 daemon

可以使用较长 timeout 和 writeback cache。daemon 仍需正确处理同一对象上的并行请求、rename 与 open handle。

场景 B:后端文件也被 host 进程修改

需要 inotify/fanotify 或后端机制驱动 invalidation,或者使用短 timeout/direct I/O。仅缩短 attr timeout 不能自动失效已经缓存的数据页。

场景 C:多个 guest 共享 virtio-fs 后端

每个 guest 有独立 VFS cache。host daemon 必须向各连接传播失效;DAX 映射还要考虑页表撤销和 pin。

场景 D:生成式或远程 namespace

negative dentry timeout 往往比 positive timeout 更敏感,因为新创建的远程对象可能被旧 negative cache 遮蔽。

8. 模式切换为何困难

同一 inode 上的 cached I/O、direct I/O、passthrough 和 DAX 可能共享:

切换前通常需要某种组合:等待在途 I/O、写回、失效页缓存、撤销页表映射、取得 inode/invalidate lock。只在某次请求失败时“直接换一条路径”可能破坏可见性或重复部分 I/O。

9. 配置检查表

小结

FUSE 一致性是多个 lease、页缓存状态、open 模式和主动通知的组合。下一篇讨论这些状态在并发、信号、背压和失败下如何保持可控。

上一篇 下一篇