feat(sched): 协作 + 时间片抢占(IRQ0 返回用户态时切换)

原先 timer_tick 只置 needs_reschedule,唯一消费点是 SPAWN 阻塞循环,纯计算线程永不被切走。① 抢占点:isr32_handler 在 timer_handler(含 pic.eoi(0)/apic.eoi)返回之后、且 from_user != 0(ISR 入口读 CS 低两位)时调用 try_reschedule;栈切换仍在 _do_switch。只对 Ring3 返回路径生效,内核临界区/syscall 中途不切。② 中断帧保留在被切线程内核栈上,靠 rsp 切换自然保存/恢复,不拷贝、不在切换中途 iretq。③ 修正全局 _isr_scratch 跨线程覆盖:ISR_SAVE_CLOBBERED 把 rax/rcx/r8-r11 存在全局 scratch,抢占后跨线程会互相覆盖;_do_switch 现按 tid 保存/恢复(_isr_scratch_tbl[16*6])。④ ISR 走中断门 IF=0,_if_saved=0,切回后不 sti,由 iretq 用用户 RFLAGS 恢复;_yield_lock 防嵌套。⑤ 单核前提(_sched_ptr/tick_count 非 per-CPU)。⑥ yield_now 语义、SCHED_QUANTUM=10、Gargantua 分块均未改。已知缺口:中断帧未保存 XMM/MXCSR,FP 密集 app 在异步抢占下有 XMM 被覆盖风险,待补 fxsave/fxrstor。
This commit is contained in:
TermiNexus committed 2026-10-17 17:08:46 +08:00
1 parent a3c1d221e6
commit f2d8e771c2
3 files changed
+52 -3

No files matched your search

+5 -1
View File
@@ -238,7 +238,11 @@ export def isr28_handler(): isrCommonhandler(28)
export def isr29_handler(): isrCommonhandler(29)
export def isr30_handler(): isrCommonhandler(30)
export def isr31_handler(): isrCommonhandler(31)
export def isr32_handler(): timer.timer_handler()
export def isr32_handler(from_user: int):
timer.timer_handler()
# 定时 IRQ 已 EOI,处理完毕;仅在返回用户态时消费时间片,内核态不抢占。
if from_user != 0:
sched.Scheduler.try_reschedule()
export def isr33_handler():
handler: irq_handler_t = irqGetHandler(1)
+4
View File
@@ -134,6 +134,8 @@ isr\num\()_frame_ready:
push r13
push r14
push r15
# is_user 作为第 1 个参数(rdi)传给处理函数,供其判断"返回用户态"时是否抢占
mov rdi, rcx
call isr\num\()_handler
pop r15
pop r14
@@ -332,6 +334,8 @@ isr\num\()_frame_ready:
push r13
push r14
push r15
# is_user 作为第 1 个参数(rdi)传给处理函数,供其判断"返回用户态"时是否抢占
mov rdi, rcx
call isr\num\()_handler
pop r15
pop r14
+43 -2
View File
@@ -16,6 +16,10 @@ define SCHED_QUANTUM = 10
_yield_cnt: uint32 = 0
_yield_lock: int = 0
# ISR scratch 是全局槽(isr.s);抢占后一个 ISR 会跨越其它线程的 ISR,故切换时按线程保存/恢复。
export _isr_scratch_tbl: [MAX_THREADS * 6]uint64
export _switch_old_tid: int = 0
export _switch_new_tid: int = 0
class Thread:
@@ -92,7 +96,8 @@ class Scheduler:
@staticmethod
def _yield():
global _switch_old_rsp, _switch_new_rsp, _yield_cnt, _yield_lock
global _switch_old_rsp, _switch_new_rsp, _yield_cnt, _yield_lock
global _switch_old_tid, _switch_new_tid
_if_saved: int = 0
__asm__(f"""pushfq
pop rax
@@ -147,6 +152,8 @@ class Scheduler:
if next_ks != 0:
gdt.set_tss_rsp0(next_ks)
cpu.set_kernel_rsp0(next_ks)
_switch_old_tid = old_tid
_switch_new_tid = next_tid
_yield_lock = 0
_do_switch()
if _if_saved:
@@ -304,10 +311,44 @@ def _do_switch():
push r14
push r15
pushfq
# 按线程保存 ISR scratch(被抢占的 ISR 会跨越其它线程的 ISR)
mov eax, dword ptr [rip + _switch_old_tid]
imul rax, rax, 48
lea rdx, [rip + _isr_scratch_tbl]
add rdx, rax
mov rcx, qword ptr [rip + _isr_scratch]
mov qword ptr [rdx], rcx
mov rcx, qword ptr [rip + _isr_scratch + 8]
mov qword ptr [rdx + 8], rcx
mov rcx, qword ptr [rip + _isr_scratch + 16]
mov qword ptr [rdx + 16], rcx
mov rcx, qword ptr [rip + _isr_scratch + 24]
mov qword ptr [rdx + 24], rcx
mov rcx, qword ptr [rip + _isr_scratch + 32]
mov qword ptr [rdx + 32], rcx
mov rcx, qword ptr [rip + _isr_scratch + 40]
mov qword ptr [rdx + 40], rcx
mov rax, qword ptr [rip + _switch_old_rsp]
mov qword ptr [rax], rsp
mov rax, qword ptr [rip + _switch_new_rsp]
mov rsp, qword ptr [rax]
# 恢复切入线程的 scratch(不在 ISR 中时内容无意义,覆盖无害)
mov eax, dword ptr [rip + _switch_new_tid]
imul rax, rax, 48
lea rdx, [rip + _isr_scratch_tbl]
add rdx, rax
mov rcx, qword ptr [rdx]
mov qword ptr [rip + _isr_scratch], rcx
mov rcx, qword ptr [rdx + 8]
mov qword ptr [rip + _isr_scratch + 8], rcx
mov rcx, qword ptr [rdx + 16]
mov qword ptr [rip + _isr_scratch + 16], rcx
mov rcx, qword ptr [rdx + 24]
mov qword ptr [rip + _isr_scratch + 24], rcx
mov rcx, qword ptr [rdx + 32]
mov qword ptr [rip + _isr_scratch + 32], rcx
mov rcx, qword ptr [rdx + 40]
mov qword ptr [rip + _isr_scratch + 40], rcx
popfq
pop r15
pop r14
@@ -317,7 +358,7 @@ def _do_switch():
pop rbx
pop rax
ret""",
op=[ASM_DESCR.CLOBBER_MEMORY])
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX])
# Thread 布局:rsp0/stack8/stack_size16/func24/arg32/state40/tid44/pid48/kern_stack56
# ⇒ sizeof(Thread) = 64,Scheduler.threads[16] 占 1024 字节 ⇒ current_tid 在 +1024。