fix(kernel): 用户异常终止不再提前 swapgs,避免拖垮整机

用户态 #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”的再调度窗口
This commit is contained in:
TermiNexus committed 2026-10-27 14:30:16 +08:00
1 parent f9e91a9d6e
commit b6ea47277c
1 file changed
+9 -16
+9 -16
View File
@@ -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 换线程)⇒ 若不在这里开中断,被切到的线程就会带着
# "中断关着"继续跑 ⇒ 定时器再也进不来 ⇒ 整机卡死;后续再出异常就是三重故障。