fix(sched): sched_yield 改为无条件切换,原实现只在时间片到期时才切

现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready(explorer 负责 format 并置 ready=1),却全部超时——三个 app 都打出 [win] desk not ready (attach timeout) / create_window: connect FAILED / geometry win_id=-1,最后静默退出;而 explorer 的 [elf] wrapper pid=5 出现在所有 app 退完之后。根因:YIELD syscall 走 sched.Scheduler.try_reschedule(),而它只在 needs_reschedule(时间片到期,由 tick 置位)时才真正 _yield(),因此 N 次 sched_yield 全落在同一个时间片内一次都不切换 ⇒ 主动等待外设的进程被饿死、初始化者永远轮不上。修法:新增 Scheduler.yield_now() 无条件切换,YIELD 分支改用它;try_reschedule() 仍保留给中断/抢占路径使用。
This commit is contained in:
TermiNexus committed 2026-10-16 20:57:55 +08:00
1 parent 2f20cf42aa
commit 453c353e01
2 files changed
+15 -1

No files matched your search

+3 -1
View File
@@ -463,7 +463,9 @@ export def syscall_handler(num: uint64, arg1: uint64, arg2: uint64, arg3: uint64
elif n == int(Syscall.GETPID):
return GETPID()
elif n == int(Syscall.YIELD):
sched.Scheduler.try_reschedule()
# sched_yield 是"主动放弃剩余时间片",必须无条件切换;try_reschedule() 只在
# 时间片到期时才切,作为 syscall 会让等待外设的进程白等(见 Scheduler.yield_now 注释)。
sched.Scheduler.yield_now()
return 0
elif n == int(Syscall.SPAWN):
path_ptr_sp: rawptr = rawptr(arg1)
+12
View File
@@ -169,6 +169,18 @@ class Scheduler:
s.needs_reschedule = 0
Scheduler._yield()
@staticmethod
def yield_now():
"""显式让出 CPU:**无条件**切换。
与 try_reschedule() 的区别很关键:后者只在 needs_reschedule(时间片到期)时切换,
那是"抢占式调度器在中断里做的判断"。而 sched_yield 的语义是"我主动放弃剩余时间片",
必须立刻切走 —— 否则主动等待外设的进程会白等:它的 N 次 yield 全落在同一个时间片内、
一次都不切换。现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready,全部超时退出,
而 explorer(共享区初始化者)直到所有 app 退完才被调度 ⇒ 建窗全失败 ⇒ 静默退出。
"""
Scheduler._yield()
@staticmethod
def disable():
s: Scheduler* = _sched_ptr