From 453c353e012eb4b14ef16cd9d9e10ee7288d9975 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 16 Oct 2026 20:57:55 +0800 Subject: [PATCH] =?UTF-8?q?fix(sched):=20sched=5Fyield=20=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E6=97=A0=E6=9D=A1=E4=BB=B6=E5=88=87=E6=8D=A2=EF=BC=8C?= =?UTF-8?q?=E5=8E=9F=E5=AE=9E=E7=8E=B0=E5=8F=AA=E5=9C=A8=E6=97=B6=E9=97=B4?= =?UTF-8?q?=E7=89=87=E5=88=B0=E6=9C=9F=E6=97=B6=E6=89=8D=E5=88=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 现场: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() 仍保留给中断/抢占路径使用。 --- VKernel/Kernel/intr/syscall.vp | 4 +++- VKernel/Kernel/sched/sched.vp | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/VKernel/Kernel/intr/syscall.vp b/VKernel/Kernel/intr/syscall.vp index 09076b0..e94ff15 100644 --- a/VKernel/Kernel/intr/syscall.vp +++ b/VKernel/Kernel/intr/syscall.vp @@ -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) diff --git a/VKernel/Kernel/sched/sched.vp b/VKernel/Kernel/sched/sched.vp index 033c0d7..4440b67 100644 --- a/VKernel/Kernel/sched/sched.vp +++ b/VKernel/Kernel/sched/sched.vp @@ -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