Created by: hjg0603
将赛事 oscomp/testsuits-for-oskernel 的 sdcard-rv 镜像换上后,basic 套件从「只能跑通第一个 brk,第二个测例必挂」推进到 28/28 全部通过。本次共定位 4 个内核 bug + 1 个 initproc 测试驱动配置问题。
Task::fork 缺失 a0 归零导致第二个测例必挂(task/task.rs、syscall/process.rs):
- 旧
sys_clone存在 fast-path:flags=17 且其它 4 个参数全 0 时走Task::fork;但Task::fork漏写trap_cx.x[10] = 0,子进程fork()偶发返回父进程进 ecall 时 a0 残留值(=17)而不是 0 - 子进程
if pid==0分支判错,跳过 exec 直接去 wait,父子互等死锁 - 是否触发取决于用户态调
fork()时 a3/a4 寄存器残留是否恰好为 0,因此现象表现为「换顺序之后还是第二个挂」 - 修法:删除
Task::fork与 sys_clone fast-path,永远走kernel_clone(已在第 446 行正确写入trap_cx.x[10] = 0)
timer ISR 盲调 SBI legacy console_getchar 卡死整个 hart(fs/stdio.rs、fs/mod.rs、net/mod.rs):
-
poll_background_work()在每次时钟中断都调poll_console_input()→sbi_rt::legacy::console_getchar() - 老版 RustSBI-QEMU 0.2.0-alpha.2 在 -nographic 模式无输入时该 SBI 不立即返回,M 模式长时间不退出(QEMU monitor 五次采样 PC 均停在 firmware
0x80002d3a,mepc 指向li a7,2; ecall,scause STIP 一直挂着) - 修法:新增
console_has_waiters(),仅当真有任务阻塞在 stdin 上时 timer ISR 才发起 SBI 补货
PipeRingBuffer 内联 64KB 数组撑爆 8KB 内核栈(fs/pipe.rs):
-
buf: [u8; PIPE_BUF_SIZE](65536 字节)使PipeRingBuffer::new()的返回值槽位落在调用方栈上,之后才由Arc::newmemcpy 到堆 - 内核栈仅 2 页(8KB),sp 减 64KB 直接戳穿到非 canonical SV39 地址区域,触发
__alltraps_k入口的sd ra, 8(sp)页错误 → CPU 重入 __alltraps_k → 再次addi sp,-272; sd再次错 → 无限递归 trap,hart 死锁 - QEMU monitor dump 看到 sp=
0xffffffa1ddcaa150(bit 38=0 但 bit 39=1,违反 SV39 canonical 要求)佐证 - 修法:
buf: [u8; N]→buf: Vec<u8>,用vec![0u8; PIPE_BUF_SIZE]直接堆分配,绕开栈中转
Pipe read/write lost-wakeup race(fs/pipe.rs):
- 旧路径
drop(ring); self.waiters.readers.wait();,timer 抢占可在 drop ring 锁和 wait 入队之间切到 writer;writer 写完数据调 wake_one 时等待队列还是空,唤醒被丢,reader 永远等不到 - 修法:改用
WaitQueue::wait_unless,在持 waiters 锁的同一临界区里重检条件并入队,原子化「检查 + 入队睡眠」(不是这次 hang 的直接原因,但确实修了一个潜在的丢唤醒)
initproc cwd 没切导致 fstat / open / read 三个 fail(user/src/lib.rs、user/src/bin/initproc.rs):
- 这三个测例都用
./text.txt等相对路径取测试数据,实际文件在/glibc/basic/text.txt - initproc 启动时 cwd 是
/,fork+exec 出来的 child 继承,于是./text.txt解析成/text.txt,open 返回 -2,测例首个assert(fd > 0)失败退出 -100 - 修法:user_lib 新增
chdir()封装(syscall 49 内核侧早有实现),initproc 进入测试循环前先chdir("/glibc/basic")