From b6ea47277c663b4d75ca418db2a25ecc465f9786 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Tue, 27 Oct 2026 14:30:16 +0800 Subject: [PATCH] =?UTF-8?q?fix(kernel):=20=E7=94=A8=E6=88=B7=E5=BC=82?= =?UTF-8?q?=E5=B8=B8=E7=BB=88=E6=AD=A2=E4=B8=8D=E5=86=8D=E6=8F=90=E5=89=8D?= =?UTF-8?q?=20swapgs=EF=BC=8C=E9=81=BF=E5=85=8D=E6=8B=96=E5=9E=AE=E6=95=B4?= =?UTF-8?q?=E6=9C=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 用户态 #PF 的终止路径直接从 ISR 调用 _yield,不经过 ISR 出口。 原实现在 _yield 前把 GS_BASE swap 回 0;但下一个被调度的线程如果是 从用户态 timer IRQ 或 syscall 挂起的,它的恢复路径会自己 swapgs 回 0。 提前 swap 会让它 swap 反,下一次系统调用用 gs:[8] 取内核栈时读到 线性地址 8,进而三重故障/整机卡死。 - 删除终止路径的 swapgs 块,保持 ISR 入口后的 GS 视图,与其他 timer/syscall 让出路径一致 - 先置 THREAD_DONE 再 free_user_thread,与 Scheduler.exit_current_thread 的次序一致,避免“资源已释放但 state 仍是 RUNNING”的再调度窗口 --- VKernel/Kernel/intr/idt.vp | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/VKernel/Kernel/intr/idt.vp b/VKernel/Kernel/intr/idt.vp index 4a3c547..9d3aff0 100644 --- a/VKernel/Kernel/intr/idt.vp +++ b/VKernel/Kernel/intr/idt.vp @@ -302,23 +302,16 @@ def isrCommonhandler(interrupt: int): buf2: [64]char viperlib.snprintf(&buf2, 64, " exc in tid=%d, terminating\n", tid) serial.puts(buf2) - # 用户线程被异常终止时回收其用户栈等资源(主线程/内核线程没有对应槽位,自动跳过)。 - proc.free_user_thread(proc.find_user_slot_by_tid(tid)) + # 用户线程被异常终止:**先**标 DONE,**再**回收资源(与 + # Scheduler.exit_current_thread 的次序一致)。反过来的话,在 + # “用户栈已释放、state 仍为 RUNNING”的窗口里若发生一次再调度, + # 本线程会被再次选中并带着失效上下文回用户态。 s.threads[tid].state = 3 - # Restore GS to kernel-normal state before _yield(). - # ISR entry does swapgs for user-mode exceptions (GS_BASE=&_per_cpu). - # Since _yield() switches to another thread and never returns via ISR exit, - # the swapgs at ISR exit is never executed. We must swap back here. - # Check IA32_GS_BASE (MSR 0xC0000101): if non-zero, swapgs was done at entry. - __asm__(f"""mov ecx, 0xC0000101 - rdmsr - shl rdx, 32 - or rax, rdx - test rax, rax - jz 1f - swapgs - 1:""", - op=[ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX]) + proc.free_user_thread(proc.find_user_slot_by_tid(tid)) + # 本路径直接 _yield,不经过 ISR 出口。**不要**在这里 swapgs 回 GS_BASE=0: + # 被切到的线程如果是从用户态 timer ISR / syscall 挂起的,它的恢复路径会 + # 自己 swapgs 回用户态 GS;这里提前 swap 会让它 swap 反,下一次系统调用 + # 用 gs:[8] 取内核栈时读到线性地址 8,直接三重故障。 # 【修复】本路径是从**异常上下文**进来(用户态异常时 CPU 已把 IF 清 0),而且这里 # **永不 iretq 返回**(直接 _yield 换线程)⇒ 若不在这里开中断,被切到的线程就会带着 # "中断关着"继续跑 ⇒ 定时器再也进不来 ⇒ 整机卡死;后续再出异常就是三重故障。