OSCamp周纪要

OSCamp 四阶段 · 周纪要

第 0 周, 准备工作

计划: 无


实际:

  • 4.27: starry-next 的的环境搭建并能成功运行。

  • 放弃了挑战题. 目前最好成绩 \(168/170\text{ of }373\), 以下是挑战题的一些分析:

    内存分配规律( \(na\) 表示 \(n\)alloc, \(nd\) 表示 \(n\)dealloc:

    1. 第一行 println! 会导致一个 alloc, 注意这个不是 Vec::new() 造成的, 因为 Vec::new() 是惰性分配的
    2. alloc_pass 的主体逻辑是 7a 1d 5a 1d 6a:
      1. 7a 1d 对应第一次给 items: Vec<u8> 分配空间的 1a, 五个 vec![]1a+1a+1a+1a+1a+(1a+1d), 最后那个 1a+1dpool 满了需要扩充, 先分配一个新空间再释放原空间.
      2. 5a 1d 对应四个 vec![], 同上, 是 1a+1a+1a+1a+(1a+1d)
      3. 6a 是六次 vec![], 没有触碰到 len == capacity, 不涉及扩容
    3. free_pass8d 也显而易见了, 以上一共分了十五个 Vec, 下标为偶数的释放( \(0, 2, 4, ...\) ), 所以释放了 8 个
    4. 随后是 append.
      1. 第零次循环走完上面这一串之后 append 对应的只有 1a, 也就是惰性给 pool 初次分配空间
      2. 之后的循环中, 如果不涉及 pool 扩容, 则没有分配操作
      3. 如果涉及 pool 扩容, 则 1a+1d 扩容
    5. 扩容规律:
      1. 第零次循环 appendpool 惰性分配产生 1a, 第一次循环还需扩容导致一次 1a+1d
      2. 从第二次循环开始, 每循环 \(1, 2, 4, 8, ...\) 次产生一次扩容的 1a+1d
    6. 我的问题主要是:
      1. 内存分配器如何得知当前处于分配的哪个阶段?
      2. 可以发现有大量扩容操作, 每次扩容会丢弃之前的内存空间, 开辟新的空间, 因此我的思路”把始终驻留内存的不会被 dealloc 的数据放在一片区域, 可能会被 dealloc 的放在一片区域” 不成立, 因为没有不会被 dealloc 的数据. 而且这种思路类似 bump allocator, 是没法 dealloc 的 (或者 dealloc 还是产生碎片)
      3. 在 CSAPP 读过一个 allocator 的实现, 不过比较复杂, 而且也有内存碎片, 很难切中本题

第 1 周(4月28日)

计划: 搭建好环境, 推进进度, 多更博客


实际:

  • 4.28:

    • 将 Starry-Next 的代码作为基础 OS 放到评测仓库
    • 成功设置好了各种配置, CI 通过了 glibc/muslbasic test, 以及部分 libc test
    • 目前成绩: 88/644, 再接再厉!
    • 阅读 Starry 源代码
    • 大概需要做的流程: (感谢郑老师的耐心指导和 debug !!!)
      1. cloneoscomp/starry-next 的源代码(注意是 oscomp 下的)
      2. 领一下自己的 GitHub Classroom 仓库
      3. 把 Starry Next 的代码作为基础 OS 放到自己仓库.
        1. 要保留自己仓库下的 .git .github README.md, .git 是 git 存信息的地方一定程度上相当于这个仓库本身肯定不能换, .github 里面有 CI 评测脚本, README 的话无所谓
        2. 有很多手段, 比如 git archive , rsync 或者直接复制之类的
        3. 注意直接复制可能有脚本可执行权限缺失的情况, 我是将 Starry Next 的代码复制到另一个目录, 删除 Starry Next 的 .git .github README.md, 然后把自己仓库的 .git .github README.md 复制过来
      4. apps/nimbos 之类的目录仅用于本地测试一下代码跑没跑起来, 主要的测例在 apps/oscomp 目录
      5. 本地运行测例是 make oscomp_run ARCH=$arch BLK=y NET=y FEATURES=fp_simd,lwext4_rs ACCEL=n, $arch 为你需要跑的架构. 理论上 x86_64 riscv64 loongarch64 aarch64 都需要支持. 但其实大多数情况下跑完 x86_64 大概率不会有问题(有例外!!!)
      6. 实测发现 judge_*.py 似乎改了也没什么用, oscomp_test.sh 也没什么能改的地方, 影响评测内容的就只有 apps/oscomp/testcase_list, 在这里添加测例可以在 make oscomp_run 和 CI 评测时运行更多测例拿到更多分数. 但要注意每个测例集合需要在前后输出#### OS COMP TEST GROUP START {{testcases}} ######## OS COMP TEST GROUP END {{testcases}} ####, 例如添加 /musl/busybox echo "#### OS COMP TEST GROUP START basic-glibc ####" (以及 END )
      7. 坑点:
        1. Starry Next 的 .gitignore 是忽略 .arceos 目录和 Cargo.lock 的, 显然我们不能忽略
        2. 内核跑不通时 CI 不会终止, 甚至最后能全 0 分通过. 例如仅有 x86_64 保留了 unlink 的系统调用(即unlinkatunlink 共存, 其他架构都只有 unlinkat), 不开 #[cfg(target_arch = "x86_64")] 的话只有 x86_64 能编译成功, 其他架构都会挂, 此时 CI 不中断停止, 而且 log 太多也很难发现错误
  • 4.29: 看 Starry Next 源代码, 但其实主要还是在补高数线代课. 试图过 busybox 测例, 但没弄懂评测 CI 怎么工作的, 遂放弃, 去做 libctest. 通过了 fdopen:

    sys_lseek, sys_unlink(分割字符串, 通过路径获取 Directory, 从目录下删除 filename)

  • 4.30: 把 Starry Next 代码以及其和 ArceOS 的结构大概梳理了一遍, 完成了 fscanf 测例

    • pipe 的 read (POSIX read 标准):
      • 管道中无数据, 写端已关闭: 返回 EOF
      • 管道中无数据, 写端未关闭: 说明可能还有数据将要达到, 应当阻塞, yield 或者 spin
      • 管道中有数据: 尽可能多地读完并返回大小
    • syscall writev 的参数 iov: *const ctypes::iovec 中莫名会多一个 base=0, len=0 的元素, 然后访问 0x0 地址导致 BadAddress, 暂时未发现原因, 先 continue 以后再说
    • 动态分发的类型擦除经常难以调试()
  • 5.1: 补校内作业

  • 5.2: 补校内作业, 补高数课

  • 5.3: 试图开 dlopen, 但是没搞懂执行流程, 感觉依赖的 syscall 已经实现得差不多了, 而且还找不到那个 unsupported 哪里报的, glibc 源代码都翻了一遍, 遂放弃; 然后开下一个是线程取消, 发现需要实现信号处理之类的, 任务量也不小, 于是重新看了下 Starry 管理任务的数据结构就收工了

  • 5.4: 写信号相关的实现,

    先是从 man 7 signal 获得 Linux 的几个信号, 但是发现是字典顺序不是编号顺序, 而操作系统和 libc 是靠编号约定的, 所以肯定不行, 又去 /usr/include/asm-generic/signal.h 找到了信号的编号, 剔除保证兼容性的重复旧信号; 折腾了很久通过 enum Signal 生成 bitflags SigMask 的宏, 然后发现是 Rust Analyzer 抽风了

    TaskExt 里加了 pending: VecDeque<Signal>blocked: SigMask , 但是我发现目前已有接口只能获得 current tasktask_ext,包括 Thread 里也只有 tid(并且没发现有 tidtask 的映射) 于是我试图通过 static weak map 实现 tidtask_ext 的全局静态映射,但是发现所有的 TaskExt 都是不被暴露出来的,进一步发现是因为 TaskInner 的所有权在任务调度队列中 于是陷入了问题: 我该如何维护线程的 pending 信号和阻塞的信号掩码呢?问了下郑老师, 然后天色已晚遂休息了, 没能推进进度

第 2 周(5月5日)

计划: 看源代码, 画架构图, 争取吃透细节


实际:

  • 5.5 - 5.9: 准备 5.8 线代考试和 5.18 高数考试; 在 TaskExtThreadDataProcessData 中加入 Signal 的 pending 队列和 shared 进程级别的待定信号队列, 在每次用户态->Trap进内核态->从内核态返回中”从内核态返回”前进行信号处理操作: 在 axhal 不同架构的相关 handle trap 函数里加一个 post_trap_callback, 而这个函数借助 linkme 收集各种 callback 函数并逐个调用(其中就有检查 from_user 并且 check_signals 的函数). check_signal 从信号队列中拉出一个未被阻塞的, 然后匹配对应的 actions.

  • 5.10: 休息一天, 说实话感觉进度有点慢了, 信号这边一直卡着推不动(唉唉), 但是也得强制休息一下缓解焦虑, 不然到时候心理上有问题再学不动反而更浪费时间.

第 3 周(5月12日)

计划: 写 futex


实际:

  • 5.12: 学习了 sys_futex 期待的基本行为: WAIT 操作是如果提供的 val 相等则令一个线程 yield 走直到超时/中断/futex wake, WAKE 操作不管线程是否还需 yield 直接唤醒

  • 5.13: 试着写了下但是感觉比上面说的复杂, 涉及到 futex 自己的 wait queue 等等, 所以确实在这块有点乏力, 于是转变计划

  • 5.14: 开始先看一些简单一些的测例(本身的)代码是怎么样的, Starry Next 以及 ArceOS 的代码是如何通过测例的

  • 5.15 - 5.18: 准备18号的高数期中考试,但其实复习得已经差不多了,单纯是有个考试在心里咯着喘不过来气

第 4 周(5月19日)

  • 5.19-5.20: 准备离散数学期中考试, 写 gitlet

  • 5.21-5.22: 写 gitlet

  • 5.23: 将 oscamp/arceos 的工具链更新到 nightly-2025-05-20

  • 5.24: 将 arceos-hypervisor/arceos 的工具链更新到 nightly-2025-05-20, 并准备查看”第二个任务”(合并 oscamp 和 hypervisor 两个开发方向的代码)