From 3ee35ad5868dfbcdb12ad721cc582c1030c87e46 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Fri, 16 Oct 2026 16:47:00 +0800 Subject: [PATCH] =?UTF-8?q?fix(sched):=20exit=5Fcurrent=5Fthread=20?= =?UTF-8?q?=E5=85=88=E6=A0=87=E8=AE=B0=20THREAD=5FDONE=20=E5=86=8D?= =?UTF-8?q?=E5=9B=9E=E6=94=B6=E8=B5=84=E6=BA=90=EF=BC=8C=E5=A0=B5=E4=BD=8F?= =?UTF-8?q?=E5=B7=B2=E9=80=80=E7=BA=BF=E7=A8=8B=E8=A2=AB=E5=86=8D=E8=B0=83?= =?UTF-8?q?=E5=BA=A6=E7=9A=84=E7=AA=97=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原顺序是 free_user_thread 在前、state=THREAD_DONE 在后:在已释放用户栈/上下文但 state 仍为 RUNNING 的窗口里,若发生一次再调度(定时器 IRQ → try_reschedule → _yield 仅按 state 过滤),本线程会被再次选中并带着失效上下文回用户态,在已回收的栈上取指 0(现场 [0E] rip=0x0 err=0x15 cr2=0x0 rsp=0x7fffffde0000,紧跟 [syscall] exit called)。_yield 的选择逻辑(THREAD_READY/RUNNING 才可选)本身已正确,故只需把死亡标记提前。 --- VKernel/Kernel/sched/sched.vp | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/VKernel/Kernel/sched/sched.vp b/VKernel/Kernel/sched/sched.vp index 88a5fe3..033c0d7 100644 --- a/VKernel/Kernel/sched/sched.vp +++ b/VKernel/Kernel/sched/sched.vp @@ -249,12 +249,20 @@ class Scheduler: @staticmethod def exit_current_thread(): - """用户线程显式退出(THREAD_EXIT):回收资源 → 标记 DONE → 让出,永不返回。""" + """用户线程显式退出(THREAD_EXIT):标记 DONE → 回收资源 → 让出,永不返回。 + + 次序很重要:**先**置 THREAD_DONE,**再** free_user_thread。反过来的话,在 + "已释放用户栈/上下文、但 state 还是 RUNNING" 这段窗口里只要发生一次再调度 + (定时器 IRQ → try_reschedule → _yield 的选择只按 state 过滤),本线程就会被 + 再次选中并带着失效上下文回到用户态 ⇒ 在已回收的栈上取指 0: + 现场 [0E] rip=0x0 err=0x15(用户态取指缺页)cr2=0x0 rsp=0x7fffffde0000,tid 为该 app 线程, + 紧跟在 [syscall] exit called 之后。 + """ s: Scheduler* = _sched_ptr tid: int = s.current_tid - proc.free_user_thread(proc.find_user_slot_by_tid(tid)) if tid > 0 and tid < MAX_THREADS: s.threads[tid].state = THREAD_DONE + proc.free_user_thread(proc.find_user_slot_by_tid(tid)) Scheduler._yield() # _yield 可能因"只有自己可运行"而直接返回:此后绝不能再跑用户代码(栈已回收)。 while True: