0%

2026春夏季开源操作系统训练营结营报告

摘要

本次训练营我的工作主线是:围绕 StarryOS 的 Linux 语义兼容与 systemd 启动链路,持续做「问题复现 -> 根因定位 -> 最小修复 -> 回归测试固化」。

阶段性成果可以概括为三点:

  1. 完成 System V 共享内存一致性测试与内核语义修复,补齐 shmget/shmat/shmdt/shmctl 关键行为。
  2. 推动 systemd 分阶段支持,连续修复 execveatPID 1、mount API 回退、/sys/fs/cgroup 挂载点、/dev/kmsg 写侧等阻塞问题。
  3. 启动共享内核日志环建设(dmesg / /dev/kmsg / journald 共用后端),为后续日志子系统统一打基础。

一、训练营阶段工作

1.1 第一阶段:System V 共享内存语义完善

我在第一阶段以 Linux man-pages 与内核行为为基准,系统梳理了 System V 共享内存接口语义,并围绕 StarryOS 编写了一致性测试。

具体工作

  1. 建立语义基线
  • 对照 Linux 文档逐条梳理 shmget/shmat/shmdt/shmctl 的行为与错误码。
  • 把“正常路径”和“错误路径”都转为可断言的检查项,避免只验证 happy path。
  1. 落地 conformance 测试 test-shm-family
  • 组织 11 段测试、70 条断言,覆盖:
    • shmgetIPC_CREAT/IPC_EXCL/ENOENT/EEXIST/EINVAL
    • shmat 的只读附加、跨进程共享、错误 shmid
    • shmdt 的“必须精确传回原始附加地址”语义。
    • shmctlIPC_STAT/IPC_SET/IPC_RMID 与延迟销毁。
  • 增加 fork 读写验证,确保共享段在进程间真正共享,而非单进程假通过。
  1. 修复内核实现偏差(以测试失败驱动修复)
  • 修复前问题:
    • 未正确处理 IPC_CREAT/IPC_EXCL 组合。
    • 对已有段 size 检查过严,偏离 Linux 语义。
  • 修复后行为:
    • key 不存在且无 IPC_CREAT 返回 ENOENT
    • IPC_CREAT|IPC_EXCL 命中已有 key 返回 EEXIST
    • 仅在请求 size 大于现有段时返回 EINVAL
  1. 多架构验证
  • 在 x86_64/riscv64/aarch64/loongarch64 QEMU 跑通回归。
  • 将“测试可发现、可自动执行”作为合入前门槛,而不是手工验证截图。

对应 issue / PR

关键结果

  • 测例形成了可复用的 conformance 思路:先在 Linux 验证测试,再用同一测试验证 StarryOS。
  • IPC_CREAT/IPC_EXCL/size 等关键语义对齐,避免「功能可用但行为不兼容」的问题。

1.2 第二阶段:systemd 分阶段支持

在这一阶段,我围绕「让 Debian trixie 的 systemd 作为 PID 1 启动」持续打通阻塞链路。工作方式是:每发现一个失败点,就把它拆成独立、可回归验证的小修复。

主线 issue

关键修复与重构

  1. execveat(2) 支持与语义落地
  • 现象:systemd 相关路径需要 execveat,缺失会直接阻塞启动流程。
  • 第一阶段实现:补齐 syscall 分发、dirfd + path + flags 解析、错误码返回与回归测试。
  • 评审中暴露的深层问题:
    • 主可执行文件在已解析后又被“按路径二次解析”,导致 AT_SYMLINK_NOFOLLOW 语义被覆盖。
  • 关键重构:
    • 把 loader 改为“解析一次、按 Location 对象加载”,对齐 Linux 的对象加载模型。
    • 保留 shebang 与 .sh 解释器路径的按路径解析(这部分应当 follow 链接)。
  • 结果:execveat 不只是“能调用”,而是关键 flag 语义可正确端到端生效。

相关 PR:

  1. 让 init 成为真实 PID 1
  • 现象:/proc/1 看起来是 1,但 getpid() 不是 1,systemd 会按“非 system manager”路径退出。
  • 根因:用户可见 pid/tid 与调度器内部 id 混用,procfs 只是“显示层伪装”。
  • 修复:
    • init 进程固定用户可见 pid/tid 为 1。
    • TASK_TABLEkillcloneset_child_tid、未实现 syscall 日志等路径统一使用用户可见 tid。
  • 回归:新增 PID 1 语义测试(如 kill(1,0)getpgid(1)getsid(1))。
  • 结果:PID 1 从“显示为 1”变成“语义上真实为 1”。

相关 PR:

  1. 修复新 mount API 误导探测
  • 现象:fsopen 返回假成功 fd,systemd 误判内核支持新 mount API,然后在后续未实现接口失败,且不回退。
  • 修复策略:
    • 对未实现入口 fsopen/fspick/open_tree 明确返回 ENOSYS(而非 dummy fd)。
    • fsconfig/fsmount/move_mount 的未实现语义保持一致。
  • 回归:新增用例直接断言三个调用返回 -1errno=ENOSYS
  • 结果:systemd 能走回退路径,复用已可用的 mount(2) 链路。

相关 PR:

  1. 补齐 /sys/fs/cgroup 挂载点
  • 现象:即使回退到经典 mount,仍因 /sys/fs/cgroup 不存在导致 ENOENT
  • 根因:sysfs 挂载后会覆盖 rootfs 的同名目录,而自建 sysfs 树缺少 fs/cgroup 节点。
  • 修复:在 sysfs 树显式补齐 fs/cgroup 空目录。
  • 回归:扩展 sysfs 测试,校验目录存在性与 tmpfs 挂载/卸载成功。
  • 结果:cgroup 层级挂载链路被完整打通。

相关 PR:

  1. 新增 /dev/kmsg 写侧设备
  • 背景:journald 在早期启动阶段依赖 kmsg 写入通道。
  • 实现:
    • 新增 char 1:11 设备节点。
    • 每次 write() 视作一条记录,解析可选 <priority> 前缀并映射到日志级别。
    • 读侧暂按 EOF 语义返回,先保证写侧可用。
  • 回归:新增测试覆盖设备号、带/不带前缀写入、无换行写入与读侧 EOF。
  • 结果:systemd/journald 早期日志有了可用落点。

相关 PR:

1.3 第三阶段:共享内核日志环(进行中)

在前面 systemd 启动链路基本可用后,我开始把日志子系统从「多处各自缓存」收敛到「统一日志环」。目标是让 dmesg、/dev/kmsg 读端、journald 共用一套后端。

当前进展

  • 在 axlog 的统一落点捕获每条记录(级别、时间、消息体),避免多条日志链路各自格式化与缓存。
  • 新增 ring.rs 作为当前可用后端,提供 push/read_all/read_record/latest_seq 等基础能力,优先保证接口稳定与可接入。
  • 并行引入 prb.rs(printk ringbuffer 风格)作为下一阶段后端,当前完成主干结构与核心路径,但尚未切换为默认实现。
  • 已明确后续里程碑:
    • 完成 prb 接线与替换。
    • 做 SMP 场景并发压测,验证一致性与开销。
    • 再把 StarryOS 侧 /dev/kmsg 读端、syslog(2) 接到共享环上,形成完整闭环。

相关 PR:


二、方法与经验

2.1 我在本次训练营中形成的方法

  1. 先语义后实现:先明确 Linux 基线行为,再写代码。
  2. 先测试后修复:先用最小失败样例钉住问题,再做实现。
  3. 小步合并:每个 PR 只解决一个明确问题,避免多变量叠加。
  4. 回归前置:每修一个 bug 都补可自动执行的回归用例,防止未来重现。

2.2 典型教训

  1. 「看起来能跑」不等于兼容:像 mount API 假成功这类问题,短期可通过,长期会误导上层。
  2. 系统调用标志位语义不能只做“接受参数”:必须确保 end-to-end 语义真正生效。
  3. 路径层重构往往是功能正确性的前提:execveat 问题本质上是 loader 模型偏差。

三、产出索引

3.1 方案 issue

3.2 PR 列表


四、后续计划

  1. 继续推进共享日志环方案:补齐 prb 接线、并发压测与切换验证。
  2. 补齐 /dev/kmsg 读侧与 syslog(2) 路径对接,形成完整日志闭环。
  3. 继续以回归测试驱动 systemd 兼容缺口,逐步提升发行版可启动、可运行范围。

结营总结

这次训练营对我最大的提升,不只是“写了多少代码”,而是工程方法上的变化:我更重视语义对齐、证据链和回归护栏,也更能把一个复杂问题拆成可验证的小问题逐步推进。

从共享内存一致性到 systemd 启动链路,再到日志子系统统一,我逐步体会到操作系统开发里「小差异会放大成大故障」的现实,也因此建立了更稳健的开发节奏。后续我会继续沿着这条路径,把已完成的阶段成果打磨成可持续迭代的基础能力。