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 换线程)⇒ 若不在这里开中断,被切到的线程就会带着 # "中断关着"继续跑 ⇒ 定时器再也进不来 ⇒ 整机卡死;后续再出异常就是三重故障。