跳转至

简易文件系统

(简易)文件系统

貌似没有带来新的系统观,于是没有很多感悟,仅仅是OS的抽象落地了。。。。。。 * 了解需求(对接讲义,需求和系统级的实现有不同,教学级实现一般个性化) * 状态机心流(在脑子里构思,纸上大概写一下执行流,控制流) * 确定约束(对接手册具体细节,以及框架代码中具体数据类型实现,考虑需求转换可行性) * 实现最小闭环(整个机制流畅通过测试) * 优化(strace配套,个性化实现)

一切皆文件

为什么认为对每个设备进行系统调用是低可行性的?


先建立基准:syscall 的本质是什么? syscall 是内核对外暴露的、有限且稳定的服务接口。 关键词:有限、稳定


核心矛盾:设备空间是开放集合,syscall 表是封闭集合 | | 设备 | syscall 表 | |---|---|---| | 规模 | 无上界,随硬件发展持续增长 | 有限,x86 Linux 目前约 300+ | | 变化 | 随时可以接入新设备 | ABI 级别的承诺,几乎不可变 | | 多样性 | 每类设备语义完全不同 | 必须在编译内核时静态确定 | 根本矛盾:你无法用一个静态的、有限的表,去覆盖一个动态的、无界的集合。


具体的不可行性 ① 数量爆炸且无法穷举 鼠标、键盘、磁盘、网卡、GPU、声卡、摄像头…… 每类设备的每种操作都要一个 syscall 号, 这个表在设计时就无法写完,更无法预见未来设备。 ② 接入新设备 = 修改内核 ABI 新设备需要新 syscall → 内核版本变动 → 所有依赖旧接口的用户程序面临兼容性断裂。 这违背了 OS 最基本的职责:对上层提供稳定抽象③ 用户程序必须感知设备类型 程序要操作设备,必须知道"这是什么设备,该调哪个 syscall"。 这意味着设备的硬件细节泄漏到了用户态——抽象彻底失败。


一句话总结

设备是运行时动态接入的开放世界,而 syscall 表是编译时静态确定的封闭接口。用封闭接口直接对接开放世界,在工程上根本无法成立。 "一切皆文件"的本质,就是在两者之间插入一个语义稳定的中间层,把无界的设备操作,折叠进有界的 open/read/write/close 原语里。

对于一切皆文件——水木石的思考

设备的本质:人类直接需求的硬件化表达。用户程序是人类需求的软件化表达。两者同属需求语义空间——无界、异构、不可枚举。

syscall 直接抽象设备为何失败:试图在需求语义空间上建立有限映射,数学上不可能。不是因为设备"太多"或"差异太大"这种工程表象,而是用有限接口覆盖无界空间,在逻辑上就不成立

破局:换抽象维度:放弃在语义层抽象,下沉到物理层。所有设备无论语义多复杂,在物理层只做一件事——在某地址空间上读写二进制序列。这个维度有界、同构、可被有限原语完备覆盖。

"扁平化"的代价与收益:将设备从需求空间压扁投影到物理空间,代价是主动丢弃语义;收益是获得稳定、有限、同构的接口。丢失的语义不是消失了,而是责任上移,交由用户程序和库自行重建。

结论:"一切皆文件"不是一个工程技巧,而是一次抽象维度的根本性转换:内核不理解设备在说什么,只负责搬运字节;意义由人来赋予。

对水木石的思考——的一点思考

本体论优先(Ontology First)

在问"怎么做"之前,先问"这个东西本质上是什么"。

大多数人的路径:

现象 → 工程解法 → 记住结论

水木石的路径:

现象 → 追问本质 → 本质暴露矛盾 → 矛盾逼出解法 → 结论是推导的必然

后者的结论不需要记忆,因为它是可以被重新推导出来的。


能否复用?能,而且有具体操作

这不是灵光一现,它是一个可以显式执行的方法:

① 对任何设计决策,先做本体论定位

"X 的本质是什么?它属于哪个空间?有什么固有属性?"

② 找空间之间的结构性矛盾

"现有方案试图在什么空间上操作?这个空间的属性和需求兼容吗?"

③ 矛盾不可调和时,换维度而不是修补

"有没有另一个空间,其固有属性天然匹配需求?"


这个方法有一个容易失效的地方:本体论定位做错了,后续推导全部作废

把设备定位为"需求空间的东西"是准确的,所以推导成立。但如果第一步定位偏了,逻辑链越严密,跑偏越远。

所以复用这个方法时,第一步的定位需要反复质疑,不能因为后续推导流畅就认为前提正确。

功能实现的套路与哲学——水木石的思考

>本质——路径依赖——自顶向下的规约与架构设计 (Top-Down Specification & Architecture)

需求思考:RRM (Requirement-Resource-Mapping) 架构思维模型

阻抗匹配 (Impedance Matching)。一个成熟的系统工程师在写下第一行代码前,必须在脑内或白板上完成以下四个步骤:

    1. 目标规约 (Target Specification)
  • 定义:剥离所有实现细节,用最精确的语言描述最终状态与约束。
  • 关注点:输入是什么?输出是什么?生命周期是什么?有哪些隐藏的非功能性约束(如时间复杂度、单调性)?

    1. 资源盘点 (Resource Inventory)
  • 定义:向下看,底层(硬件、OS、库)为你提供了哪些原语 (Primitives)?
  • 关注点:这些原语的数据结构、单位、精度、副作用是什么?

    1. 阻抗匹配 (Impedance Matching / Gap Analysis)
  • 定义:对比“目标规约”与“底层资源”,精准定位它们在维度、单位、状态上的断层 (Gap)。

    1. 逻辑合成 (Logic Synthesis)
  • 定义:设计转换逻辑(数学运算、状态机、缓冲机制)来填补断层。
💡 简要案例:NDL_GetTicks 的 RRM 推演
  • 1. 目标规约:需要一个函数,返回自 NDL_Init 被调用起,经过的毫秒数 (ms)。类型为 uint32_t。要求相对时间
  • 2. 资源盘点:底层只有 gettimeofday。它返回的是自 1970 年以来的绝对时间,单位是秒 (s) 和微秒 (us),数据结构是 struct timeval
  • 3. 阻抗匹配 (断层分析)
    • 单位断层:底层是 s 和 us,目标是 ms。
    • 维度断层:底层是绝对时间,目标是相对时间。
    • 类型断层:底层是结构体,目标是 32 位无符号整数。
  • 4. 逻辑合成
    • 解决单位断层:$$ms = s \times 1000 + us / 1000$$。
    • 解决维度断层:在 NDL_Init 中引入全局静态变量 boot_time 记录初始绝对时间。每次调用时计算差值:$$\Delta t = current_time - boot_time$$。
    • 解决类型断层:将计算结果强制转换为 uint32_t

编码细节+需求与原理的双重刺穿

(综合性能考虑)与(编码逻辑链路)

架构认知升级 (Architecture Insights)
  1. 路径分离 (Cold/Hot Path Isolation)
  2. 反思:模块在用户层被调用时,必须基于其执行频率指导编码。
  3. 实践poll_event 属于极高频的数据流(Fast Path),每秒执行成千上万次。寻找文件 (fs_open) 和变量初始化属于控制流(Slow Path),必须剥离并前置到初始化函数中。否则将引发严重的性能雪崩(如游戏掉帧)。
需求在系统中的思考方式
  • 双向推演模型 (Bi-directional Deduction) 面对贯穿全栈的复杂链路,单向追踪极易迷失。必须采用“两头堵截法”:
  • Top-Down:从用户态 read -> Syscall (a0 寄存器传递) -> VFS (fs_read)。
  • Bottom-Up:从物理按键 -> SDL 窗口捕获 -> MMIO 寄存器 -> events_read 插桩 (printf 验证)。 通过双向推演,在中间层(VFS 路由)完成逻辑闭环。