OSCamp周纪要
OSCamp 四阶段 · 周纪要
第 0 周, 准备工作
计划: 无
实际:
4.27: starry-next 的的环境搭建并能成功运行。
放弃了挑战题. 目前最好成绩 \(168/170\text{ of }373\), 以下是挑战题的一些分析:
内存分配规律( \(na\) 表示 \(n\) 次
alloc
, \(nd\) 表示 \(n\) 次dealloc
:- 第一行
println!
会导致一个alloc
, 注意这个不是Vec::new()
造成的, 因为Vec::new()
是惰性分配的 alloc_pass
的主体逻辑是7a 1d 5a 1d 6a
:7a 1d
对应第一次给items: Vec<u8>
分配空间的1a
, 五个vec![]
的1a+1a+1a+1a+1a+(1a+1d)
, 最后那个1a+1d
是pool
满了需要扩充, 先分配一个新空间再释放原空间.5a 1d
对应四个vec![]
, 同上, 是1a+1a+1a+1a+(1a+1d)
6a
是六次vec![]
, 没有触碰到len == capacity
, 不涉及扩容
free_pass
的8d
也显而易见了, 以上一共分了十五个Vec
, 下标为偶数的释放( \(0, 2, 4, ...\) ), 所以释放了 8 个- 随后是
append
.- 第零次循环走完上面这一串之后
append
对应的只有1a
, 也就是惰性给pool
初次分配空间 - 之后的循环中, 如果不涉及
pool
扩容, 则没有分配操作 - 如果涉及
pool
扩容, 则1a+1d
扩容
- 第零次循环走完上面这一串之后
- 扩容规律:
- 第零次循环
append
给pool
惰性分配产生1a
, 第一次循环还需扩容导致一次1a+1d
- 从第二次循环开始, 每循环 \(1, 2, 4, 8,
...\) 次产生一次扩容的
1a+1d
- 第零次循环
- 我的问题主要是:
- 内存分配器如何得知当前处于分配的哪个阶段?
- 可以发现有大量扩容操作, 每次扩容会丢弃之前的内存空间, 开辟新的空间,
因此我的思路”把始终驻留内存的不会被
dealloc
的数据放在一片区域, 可能会被dealloc
的放在一片区域” 不成立, 因为没有不会被dealloc
的数据. 而且这种思路类似bump allocator
, 是没法dealloc
的 (或者dealloc
还是产生碎片) - 在 CSAPP 读过一个 allocator 的实现, 不过比较复杂, 而且也有内存碎片, 很难切中本题
- 第一行
第 1 周(4月28日)
计划: 搭建好环境, 推进进度, 多更博客
实际:
4.28:
- 将 Starry-Next 的代码作为基础 OS 放到评测仓库
- 成功设置好了各种配置, CI 通过了
glibc
/musl
的basic
test, 以及部分libc
test - 目前成绩: 88/644, 再接再厉!
- 阅读 Starry 源代码
- 大概需要做的流程: (感谢郑老师的耐心指导和 debug
!!!)
clone
下oscomp/starry-next
的源代码(注意是oscomp
下的)- 领一下自己的 GitHub Classroom 仓库
- 把 Starry Next 的代码作为基础 OS 放到自己仓库.
- 要保留自己仓库下的
.git
.github
README.md
,.git
是 git 存信息的地方一定程度上相当于这个仓库本身肯定不能换,.github
里面有 CI 评测脚本,README
的话无所谓 - 有很多手段, 比如 git archive ,
rsync
或者直接复制之类的 - 注意直接复制可能有脚本可执行权限缺失的情况, 我是将 Starry Next
的代码复制到另一个目录, 删除 Starry Next 的
.git
.github
README.md
, 然后把自己仓库的.git
.github
README.md
复制过来
- 要保留自己仓库下的
apps/nimbos
之类的目录仅用于本地测试一下代码跑没跑起来, 主要的测例在apps/oscomp
目录- 本地运行测例是
make oscomp_run ARCH=$arch BLK=y NET=y FEATURES=fp_simd,lwext4_rs ACCEL=n
,$arch
为你需要跑的架构. 理论上x86_64
riscv64
loongarch64
aarch64
都需要支持. 但其实大多数情况下跑完x86_64
大概率不会有问题(有例外!!!) - 实测发现
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
) - 坑点:
- Starry Next 的
.gitignore
是忽略.arceos
目录和Cargo.lock
的, 显然我们不能忽略 - 内核跑不通时 CI 不会终止, 甚至最后能全 0 分通过. 例如仅有
x86_64
保留了unlink
的系统调用(即unlinkat
和unlink
共存, 其他架构都只有unlinkat
), 不开#[cfg(target_arch = "x86_64")]
的话只有x86_64
能编译成功, 其他架构都会挂, 此时 CI 不中断停止, 而且 log 太多也很难发现错误
- Starry Next 的
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
以后再说 - 动态分发的类型擦除经常难以调试()
- pipe 的
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 task
的task_ext
,包括Thread
里也只有tid
(并且没发现有tid
到task
的映射) 于是我试图通过 static weak map 实现tid
到task_ext
的全局静态映射,但是发现所有的TaskExt
都是不被暴露出来的,进一步发现是因为TaskInner
的所有权在任务调度队列中 于是陷入了问题: 我该如何维护线程的pending
信号和阻塞的信号掩码呢?问了下郑老师, 然后天色已晚遂休息了, 没能推进进度
第 2 周(5月5日)
计划: 看源代码, 画架构图, 争取吃透细节
实际:
5.5 - 5.9: 准备 5.8 线代考试和 5.18 高数考试; 在
TaskExt
的ThreadData
和ProcessData
中加入 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 两个开发方向的代码)