From f2d8e771c2541acd3bfbc8257b1aef3f5d271126 Mon Sep 17 00:00:00 2001 From: TermiNexus Date: Sat, 17 Oct 2026 17:08:46 +0800 Subject: [PATCH] =?UTF-8?q?feat(sched):=20=E5=8D=8F=E4=BD=9C=20+=20?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E7=89=87=E6=8A=A2=E5=8D=A0=EF=BC=88IRQ0=20?= =?UTF-8?q?=E8=BF=94=E5=9B=9E=E7=94=A8=E6=88=B7=E6=80=81=E6=97=B6=E5=88=87?= =?UTF-8?q?=E6=8D=A2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 原先 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。 --- VKernel/Kernel/intr/idt.vp | 6 ++++- VKernel/Kernel/intr/isr.s | 4 ++++ VKernel/Kernel/sched/sched.vp | 45 +++++++++++++++++++++++++++++++++-- 3 files changed, 52 insertions(+), 3 deletions(-) diff --git a/VKernel/Kernel/intr/idt.vp b/VKernel/Kernel/intr/idt.vp index a369e45..f5af2cb 100644 --- a/VKernel/Kernel/intr/idt.vp +++ b/VKernel/Kernel/intr/idt.vp @@ -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) diff --git a/VKernel/Kernel/intr/isr.s b/VKernel/Kernel/intr/isr.s index 3e88b07..3a3906a 100644 --- a/VKernel/Kernel/intr/isr.s +++ b/VKernel/Kernel/intr/isr.s @@ -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 diff --git a/VKernel/Kernel/sched/sched.vp b/VKernel/Kernel/sched/sched.vp index 43b6d97..b1c1fba 100644 --- a/VKernel/Kernel/sched/sched.vp +++ b/VKernel/Kernel/sched/sched.vp @@ -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。