决赛开发日志
初赛按照入围排名我们排到了28/58,初赛截止提交的时候应该是25/121,评审阶段某天第一次集体提交的时候主办方不小心露出(?)榜单我们应该是第三,综合文档分结果来到28有点子意外的,但是事已至此只能说T1 fighting(?
7.11~7.16
主要是参考着moss把之前全部塞在uspace的大份进行了拆分和重构,拆分完tree一下顺眼多了
oskernel2026-orays/
├── src/ 这次重构主要修改的地方
│ ├── abi/
│ ├── fs/
│ ├── ipc/
│ ├── memory/
│ ├── net/
│ ├── process/
│ ├── sched/
│ ├── signal/
│ ├── system/
│ ├── time/
│ └── diagnostics/
├── kernel/ ArceOS内核组件层
│ ├── arch/axhal/
│ ├── memory/
│ ├── task/axtask/
│ ├── fs/axfs/
│ ├── net/axnet/
│ ├── drivers/
│ ├── namespace/axns/
│ ├── runtime/axruntime/
│ ├── sync/axsync/
│ ├── smp/axipi/
│ ├── config/axconfig/
│ └── diagnostics/axlog/
├── api/ 内核能力向上暴露的 API 与 feature 汇总
│ ├── arceos_api/
│ ├── arceos_posix_api/
│ └── axfeat/
├── ulib/ 用户态/应用侧兼容库
│ ├── axlibc/
│ └── axstd/
├── configs/ 配置
├── runtime_compat/ musl/oscomp 运行时兼容补丁
├── scripts/ 构建、网络、评测、G002-G013 检查脚本
├── tools/ 板卡、依赖和带宽测试辅助工具
├── vendor/ 本地固定的第三方依赖
├── docs/ 项目文档、图、演示稿、PDF 和视频
不过感觉过两天回来看万一又觉得是大份了再重构一次也有可能(x
应队友要求还添加了EEVDF调度策略
EEVDF调度策略是一个基于虚拟时间的公平调度算法,用以保证每个任务都能获得公平的CPU时间,通过维护每个任务的虚拟时间来决定下一个要运行的任务,从而实现公平性和高效性
参考linux官方文档里的描述:EEVDF 对睡眠任务采用延迟退列和虚拟滞后衰减机制,使任务的负 lag 随运行队列虚拟时间推进而逐渐归零,而不是因短暂休眠被立即清除,从而避免睡眠作弊。同时,合格任务可凭借更早的虚拟截止时间触发抢占;延迟敏感任务还可以请求较短的服务时间片,以获得更早的虚拟截止时间和更快的调度响应
根据目前框的大概思路就是
nice / sched_setscheduler / sched_setattr
↓
src/sched Linux语义层
↓
axtask调度器适配接口
↓
AxRunQueue 传入真实 current_ticks
↓
EEVDF 公平类 / RT 类 / EDF+CBS 类
↓
抢占标记与精确定时器
首先是EEVDF调度实体EEVDFTask,维护了:
EEVDFTask
├── inner 实际任务
├── vruntime 任务经过权重修正后的虚拟运行时间,权重大、优先级高的任务,vruntime 增长较慢,而调度器会认为 vruntime 较小的任务获得的 CPU 服务较少
├── virtual_deadline 虚拟截止时间,EEVDF在满足资格条件的任务中,选择虚拟截止时间最早的任务,权重越大,同样的实际时间片对应的虚拟时间越小,虚拟截止时间也更早
├── priority / weight 优先级和对应权重,默认使用Linux nice 0 对应的权重
├── remaining_slice 当前请求剩余时间片,初始设置为了1,避免deadline与vruntime完全相同,以及任务一创建就被认为时间片耗尽,还有就是出现零长度request
├── last_virtual_time 上次观察到的运行队列虚拟时间
├── last_accounted_at 上次统计实际运行时间的时刻
├── enqueue_sequence 入队顺序
├── initialized 是否首次进入调度器
└── deadline_state 截止时间相关扩展状态
fn begin_request(&self, slice: u64)
这个函数负责建立一个新的EEVDF request,先设置实际剩余时间片,然后计算虚拟截止时间和虚拟运行时间
fn prepare_wakeup(&self, virtual_time: u64, slice: u64, now: u64)
这部分负责唤醒处理,参数包括当前运行队列的虚拟时间,唤醒后分配的request时间片,以及当前真实tick
在任务首次进入调度器时 v = V,让任务以零lag进入系统防止新任务因虚拟运行时间过小而长期独占CPU
非首次进入的任务则是计算旧lag = V - v,这里也是EEVDF的核心要求,lag > 0表示任务vruntime落后于系统虚拟时间,欠服务,lag < 0表示任务vruntime领先于系统虚拟时间,超服务,EEVDF要求欠服务的任务优先调度,超服务的任务延迟调度
但是这样的lag计算其实和linux不太相同,唤醒时正负统一衰减lag,容易惩罚本来就欠服务的任务
还有就是一开始计算vruntime的时候,linux通过权重修正vruntime的增长速度,但是我一开始直接使用了实际时间片,但是反复思考(?)后发现linux的设计真是妙哉妙哉,于是把这个也修了
虚拟时间计算点击一键跳转linux源码:linux源码
总的来说现在核心算法和思想应该大概也许已经很贴近linux的实现了
7.17~7.18
期间我还去看了blueos/moss/asterinas,寻找一些值得参考的东西,然后发现哎asterinas写的是真完善啊
然后回过头看我们的内核还有很多需要修改和完善的地方,初赛的时候为了刷ltp分加入了很多伪实现语义不完整,现在该刻舟求剑精卫填海(?
考虑了很多,我决定先从用户地址访问和错误处理这部分先进行修修补补
首先就是重构拆分后的user_access,pwner后遗症(? 在这部分有俩write_user_value<T:Copy>和read_user_value<T:Copy>函数,作用分别类似于linux中的copy_to_user()和copy_from_user() 问题在于泛型控制太宽,不能保证任意copy类型都能安全地从原始字节构造,如果在传入结构体的时候带有padding,在copy-out的时候可能会把未初始化的padding中的栈残留复制给用户
解决是在当前 crate 内定义一个不unsafe trait,只有同时实现 Copy,并且保证任意输入字节都能构成合法值的类型,才可以实现这个trait
当我写到这里的时候,T1已经被KC抬走了,lec最大的孝子,气得我头疼。。