fix(sched): exit_current_thread 先标记 THREAD_DONE 再回收资源,堵住已退线程被再调度的窗口
原顺序是 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 才可选)本身已正确,故只需把死亡标记提前。
This commit is contained in:
1 parent
424233dcea
commit
3ee35ad586
1 file changed
+10
-2
@@ -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:
|
||||
|
||||
Reference in new issue
Block a user