554 lines
21 KiB
Plaintext
554 lines
21 KiB
Plaintext
import drivers.serial.uart.serial as serial
|
||
import sched.process as proc
|
||
import mm.mm as mm
|
||
import intr.gdt as gdt
|
||
import drivers.core.cpu.cpu as cpu
|
||
import viperlib
|
||
import asm
|
||
|
||
define THREAD_READY = 0
|
||
define THREAD_RUNNING = 1
|
||
define THREAD_BLOCKED = 2
|
||
define THREAD_DONE = 3
|
||
define MAX_THREADS = 64
|
||
define THREAD_STACK_SIZE = 65536
|
||
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
|
||
|
||
# Per-thread IA32_FS_BASE (glibc TLS). Kept outside Thread: Thread has a
|
||
# hard-coded 600-byte layout checked by _thread_entry.
|
||
define IA32_FS_BASE = 0xC0000100
|
||
_fs_base_tbl: [MAX_THREADS]uint64
|
||
_syscall_frame_tbl: [MAX_THREADS]uint64
|
||
_clear_child_tid: [MAX_THREADS]uint64
|
||
|
||
def _msr_read(msr: uint32) -> uint64:
|
||
lo: uint32
|
||
hi: uint32
|
||
__asm__(f"""mov ecx, {asminp(msr, ASM_DESCR.REG_ANY)}
|
||
rdmsr
|
||
mov {asmout(lo, ASM_DESCR.OUTPUT_REG)}, eax
|
||
mov {asmout(hi, ASM_DESCR.OUTPUT_REG)}, edx""",
|
||
out=[asmout(lo, ASM_DESCR.OUTPUT_REG), asmout(hi, ASM_DESCR.OUTPUT_REG)],
|
||
op=[ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RDX])
|
||
return uint64(lo) | (uint64(hi) << 32)
|
||
|
||
def _msr_write(msr: uint32, val: uint64):
|
||
lo: uint32 = uint32(val & uint64(0xFFFFFFFF))
|
||
hi: uint32 = uint32(val >> 32)
|
||
__asm__(f"""mov ecx, {asminp(msr, ASM_DESCR.REG_ANY)}
|
||
mov eax, {asminp(lo, ASM_DESCR.REG_ANY)}
|
||
mov edx, {asminp(hi, ASM_DESCR.REG_ANY)}
|
||
wrmsr""",
|
||
op=[ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RAX, ASM_DESCR.CLOBBER_RDX])
|
||
|
||
|
||
|
||
class Thread:
|
||
rsp: uint64
|
||
stack: rawptr
|
||
stack_size: uint64
|
||
func: rawptr
|
||
arg: rawptr
|
||
state: int
|
||
tid: int
|
||
pid: int
|
||
# Ring3 → Ring0 的落栈点(TSS.RSP0 / gs:[8]):用户线程才非 0,内核线程为 0。
|
||
# 必须按线程切换 —— 同进程多用户线程共用进程级内核栈会互相压栈帧。
|
||
kern_stack: uint64
|
||
# FXSAVE 区:528 = 512 + 16 对齐余量;fpu_ptr 指向其中 16B 对齐起点(内嵌,无需单独释放)。
|
||
fpu_area: [528]uint8
|
||
fpu_ptr: rawptr
|
||
|
||
def block(self):
|
||
self.state = THREAD_BLOCKED
|
||
Scheduler._yield()
|
||
|
||
def unblock(self):
|
||
if self.state == THREAD_BLOCKED:
|
||
self.state = THREAD_READY
|
||
|
||
@property
|
||
def is_running(self) -> bool: return self.state == THREAD_RUNNING
|
||
@property
|
||
def is_blocked(self) -> bool: return self.state == THREAD_BLOCKED
|
||
@property
|
||
def is_done(self) -> bool: return self.state == THREAD_DONE
|
||
|
||
|
||
# 取 fpu_area 内 16B 对齐的 512B FXSAVE 区;直接写 fninit 默认镜像(不碰实时 FPU 状态)。
|
||
def _fpu_attach(th: Thread*):
|
||
fp: uint8* = uint8*((uint64(&th.fpu_area[0]) + 15) & ~uint64(0xF))
|
||
th.fpu_ptr = rawptr(uint64(fp))
|
||
for i in range(512):
|
||
*(fp + uint64(i)) = 0
|
||
# FCW=0x037F、MXCSR=0x1F80(全异常屏蔽、就近舍入)、MXCSR_MASK=0xFFFF
|
||
*(fp) = uint8(0x7F)
|
||
*(fp + uint64(1)) = uint8(0x03)
|
||
*(fp + uint64(24)) = uint8(0x80)
|
||
*(fp + uint64(25)) = uint8(0x1F)
|
||
*(fp + uint64(28)) = uint8(0xFF)
|
||
*(fp + uint64(29)) = uint8(0xFF)
|
||
|
||
|
||
class Scheduler:
|
||
threads: [MAX_THREADS]Thread
|
||
current_tid: int
|
||
thread_count: int
|
||
enabled: int
|
||
needs_reschedule: int
|
||
tick_count: int
|
||
|
||
def __init__(self):
|
||
global _sched_ptr
|
||
_sched_ptr = &_sched_storage
|
||
s: Scheduler* = _sched_ptr
|
||
s.current_tid = 0
|
||
s.thread_count = 1
|
||
s.enabled = 0
|
||
s.needs_reschedule = 0
|
||
s.tick_count = 0
|
||
for i in range(MAX_THREADS):
|
||
s.threads[i].rsp = 0
|
||
s.threads[i].stack = None
|
||
s.threads[i].stack_size = 0
|
||
s.threads[i].func = None
|
||
s.threads[i].arg = None
|
||
s.threads[i].state = THREAD_DONE
|
||
s.threads[i].tid = i
|
||
s.threads[i].pid = 0
|
||
_fpu_attach(&s.threads[i])
|
||
s.enabled = 1
|
||
s.threads[0].state = THREAD_RUNNING
|
||
s.threads[0].tid = 0
|
||
main_rsp_ptr: rawptr = &s.threads[0].rsp
|
||
__asm__(f"""mov [{asminp(main_rsp_ptr, ASM_DESCR.REG_ANY)}], rsp""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY])
|
||
# fail-fast:_thread_entry 的 asm 以硬编码偏移访问线程表(sizeof(Thread)=600、
|
||
# current_tid=+38400=64*600)。增删 Thread 字段会让它们失配 ⇒ 线程入口会按错误的
|
||
# 偏移取 func/arg 并 call 任意地址。这里直接核对,不符立即停机暴露。
|
||
thr0_addr: uint64 = uint64(&s.threads[0])
|
||
curt_addr: uint64 = uint64(&s.current_tid)
|
||
if Thread.__sizeof__() != 600 or (curt_addr - thr0_addr) != 38400:
|
||
serial.puts("[sched] FATAL: Thread 布局与 _thread_entry 硬编码偏移(600/38400)不一致\n")
|
||
while True:
|
||
asm.sti()
|
||
asm.hlt()
|
||
|
||
@staticmethod
|
||
def _yield():
|
||
global _switch_old_rsp, _switch_new_rsp, _yield_cnt, _yield_lock
|
||
global _switch_old_tid, _switch_new_tid
|
||
global _switch_old_fpu, _switch_new_fpu
|
||
global _fs_base_tbl
|
||
_if_saved: int = 0
|
||
__asm__(f"""pushfq
|
||
pop rax
|
||
shr rax, 9
|
||
and eax, 1
|
||
mov {asmout(_if_saved, ASM_DESCR.OUTPUT_REG)}, eax""",
|
||
op=[ASM_DESCR.CLOBBER_RAX])
|
||
asm.cli()
|
||
if _yield_lock:
|
||
if _if_saved:
|
||
asm.sti()
|
||
return
|
||
_yield_lock = 1
|
||
s: Scheduler* = _sched_ptr
|
||
s.needs_reschedule = 0
|
||
s.tick_count = 0
|
||
old_tid: int = s.current_tid
|
||
next_tid: int = old_tid + 1
|
||
if next_tid >= s.thread_count:
|
||
next_tid = 0
|
||
found: int = 0
|
||
for count in range(s.thread_count):
|
||
st: int = s.threads[next_tid].state
|
||
if st == THREAD_READY or st == THREAD_RUNNING:
|
||
found = 1
|
||
break
|
||
next_tid += 1
|
||
if next_tid >= s.thread_count:
|
||
next_tid = 0
|
||
if found == 0:
|
||
_yield_lock = 0
|
||
if _if_saved:
|
||
asm.sti()
|
||
return
|
||
if next_tid == old_tid:
|
||
_yield_lock = 0
|
||
if _if_saved:
|
||
asm.sti()
|
||
return
|
||
if s.threads[old_tid].state == THREAD_RUNNING:
|
||
s.threads[old_tid].state = THREAD_READY
|
||
s.threads[next_tid].state = THREAD_RUNNING
|
||
_switch_old_rsp = &s.threads[old_tid].rsp
|
||
_switch_new_rsp = &s.threads[next_tid].rsp
|
||
_switch_old_fpu = s.threads[old_tid].fpu_ptr
|
||
_switch_new_fpu = s.threads[next_tid].fpu_ptr
|
||
s.current_tid = next_tid
|
||
next_pid: int = s.threads[next_tid].pid
|
||
_yield_cnt += 1
|
||
proc.switch_pid(next_pid)
|
||
# 按**线程**设置 Ring3→Ring0 的落栈点:switch_pid 只按进程设置(会把全进程的用户线程
|
||
# 指到同一个内核栈上),两个线程都在 syscall 里 yield 时后者的栈帧会压掉前者的。
|
||
next_ks: uint64 = s.threads[next_tid].kern_stack
|
||
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
|
||
# Per-thread TLS: save the outgoing FS base, load the incoming one.
|
||
_fs_base_tbl[old_tid] = _msr_read(uint32(IA32_FS_BASE))
|
||
_msr_write(uint32(IA32_FS_BASE), _fs_base_tbl[next_tid])
|
||
_yield_lock = 0
|
||
_do_switch()
|
||
if _if_saved:
|
||
asm.sti()
|
||
|
||
@staticmethod
|
||
def sched_tick():
|
||
s: Scheduler* = _sched_ptr
|
||
if not s.enabled: return
|
||
if s.thread_count <= 1: return
|
||
s.tick_count += 1
|
||
if s.tick_count >= SCHED_QUANTUM:
|
||
s.tick_count = 0
|
||
s.needs_reschedule = 1
|
||
|
||
@staticmethod
|
||
def try_reschedule():
|
||
s: Scheduler* = _sched_ptr
|
||
if s.needs_reschedule:
|
||
s.needs_reschedule = 0
|
||
Scheduler._yield()
|
||
|
||
@staticmethod
|
||
def yield_now():
|
||
"""显式让出 CPU:**无条件**切换。
|
||
|
||
与 try_reschedule() 的区别很关键:后者只在 needs_reschedule(时间片到期)时切换,
|
||
那是"抢占式调度器在中断里做的判断"。而 sched_yield 的语义是"我主动放弃剩余时间片",
|
||
必须立刻切走 —— 否则主动等待外设的进程会白等:它的 N 次 yield 全落在同一个时间片内、
|
||
一次都不切换。现场:app 用 4000 次 yield_cpu() 等 /dev/desk ready,全部超时退出,
|
||
而 explorer(共享区初始化者)直到所有 app 退完才被调度 ⇒ 建窗全失败 ⇒ 静默退出。
|
||
"""
|
||
Scheduler._yield()
|
||
|
||
@staticmethod
|
||
def disable():
|
||
s: Scheduler* = _sched_ptr
|
||
s.enabled = 0
|
||
|
||
@staticmethod
|
||
def enable():
|
||
s: Scheduler* = _sched_ptr
|
||
s.enabled = 1
|
||
|
||
@staticmethod
|
||
def current() -> Thread*:
|
||
s: Scheduler* = _sched_ptr
|
||
return &s.threads[s.current_tid]
|
||
|
||
@staticmethod
|
||
def get_thread(tid: int) -> Thread*:
|
||
s: Scheduler* = _sched_ptr
|
||
if tid < 0 or tid >= s.thread_count: return None
|
||
return &s.threads[tid]
|
||
|
||
@staticmethod
|
||
def create_thread(func: rawptr, arg: rawptr, thread_pid: int = 0) -> Thread*:
|
||
s: Scheduler* = _sched_ptr
|
||
if s.thread_count >= MAX_THREADS: return None
|
||
tid: int = s.thread_count
|
||
s.thread_count += 1
|
||
th: Thread* = &s.threads[tid]
|
||
th.state = THREAD_READY
|
||
th.tid = tid
|
||
th.pid = thread_pid
|
||
th.func = func
|
||
th.arg = arg
|
||
th.kern_stack = 0
|
||
_fpu_attach(th)
|
||
stack_size: uint64 = THREAD_STACK_SIZE
|
||
stack_ptr: rawptr = mm.malloc(stack_size)
|
||
if not stack_ptr:
|
||
s.thread_count -= 1
|
||
return None
|
||
th.stack = stack_ptr
|
||
th.stack_size = stack_size
|
||
stack_top: uint64 = uint64(stack_ptr) + stack_size - 16
|
||
stack_top = stack_top & ~uint64(0xF)
|
||
entry_addr: uint64 = uint64(&_thread_entry)
|
||
sp: rawptr = rawptr(stack_top)
|
||
__asm__(f"""mov rax, {asminp(sp, ASM_DESCR.REG_ANY)}
|
||
mov qword ptr [rax - 64], 0x202
|
||
mov qword ptr [rax - 56], 0
|
||
mov qword ptr [rax - 48], 0
|
||
mov qword ptr [rax - 40], 0
|
||
mov qword ptr [rax - 32], 0
|
||
mov qword ptr [rax - 24], 0
|
||
mov qword ptr [rax - 16], 0
|
||
mov qword ptr [rax - 8], 0
|
||
mov [rax], {asminp(entry_addr, ASM_DESCR.REG_ANY)}""",
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX])
|
||
stack_top = stack_top - 64
|
||
th.rsp = stack_top
|
||
return th
|
||
|
||
@staticmethod
|
||
def create_user_thread(thread_pid: int, entry: rawptr, arg: rawptr) -> Thread*:
|
||
"""创建**用户态**线程:入口在 Ring 3 执行(THREAD_CREATE 用)。
|
||
|
||
app 传来的函数指针是用户地址,绝不能用 create_thread 直接 call —— 那是在 Ring 0
|
||
执行用户代码,且与同进程其它线程共用一套内核栈。这里给线程配独立用户栈 + 独立
|
||
内核栈,由 _user_thread_entry 走 drop_to_user_mode 进 Ring 3。
|
||
"""
|
||
slot: int = proc.alloc_user_thread(thread_pid, entry, arg)
|
||
if slot < 0: return None
|
||
ctx: proc.UserThreadCtx* = proc.user_thread_ctx(slot)
|
||
if ctx is None: return None
|
||
th: Thread* = Scheduler.create_thread(&_user_thread_entry, rawptr(uint64(slot)), thread_pid)
|
||
if th is None: return None
|
||
th.kern_stack = ctx.kern_stack
|
||
ctx.tid = th.tid
|
||
# 出口跳板模板(process.vp 的 _tramp_code)仍写着旧调用号 34,而 THREAD_EXIT = 60:
|
||
# 用户线程自然 return 会执行一次无人处理的 syscall ⇒ iretq 回 Ring3 `jmp $` 空转;
|
||
# 内核只在 syscall 返回路径消费 needs_reschedule ⇒ 全系统不再切换(关 Scene3D 即冻结)。
|
||
# 模板首字节是 `mov eax, imm32`(B8),立即数从 +1 开始,把低字节改成 60 即可。
|
||
tramp_page: uint8* = uint8*(uint64(ctx.tramp) & ~uint64(0xFFF))
|
||
*(tramp_page + uint64(1)) = uint8(60)
|
||
return th
|
||
|
||
@staticmethod
|
||
def exit_current_thread():
|
||
"""用户线程显式退出(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 之后。
|
||
"""
|
||
global _clear_child_tid
|
||
s: Scheduler* = _sched_ptr
|
||
tid: int = s.current_tid
|
||
ct: uint64 = _clear_child_tid[tid]
|
||
if ct != 0:
|
||
cw: uint32* = uint32*(ct)
|
||
cw[0] = uint32(0)
|
||
_clear_child_tid[tid] = 0
|
||
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:
|
||
asm.sti()
|
||
asm.hlt()
|
||
Scheduler._yield()
|
||
|
||
@staticmethod
|
||
def exit_process(pid: int):
|
||
"""Linux exit_group: retire every thread of `pid`, release the process, never return."""
|
||
global _clear_child_tid
|
||
s: Scheduler* = _sched_ptr
|
||
i: int = 0
|
||
while i < s.thread_count:
|
||
if s.threads[i].pid == pid:
|
||
ct: uint64 = _clear_child_tid[i]
|
||
if ct != 0:
|
||
cw: uint32* = uint32*(ct)
|
||
cw[0] = uint32(0)
|
||
_clear_child_tid[i] = 0
|
||
s.threads[i].state = THREAD_DONE
|
||
slot: int = proc.find_user_slot_by_tid(i)
|
||
if slot >= 0:
|
||
proc.free_user_thread(slot)
|
||
i += 1
|
||
p: proc.Process* = proc.ProcessManager.get_process(pid)
|
||
if p is not None:
|
||
p.exit()
|
||
Scheduler._yield()
|
||
while True:
|
||
asm.sti()
|
||
asm.hlt()
|
||
Scheduler._yield()
|
||
|
||
|
||
_sched_storage: Scheduler = Scheduler()
|
||
export _sched_ptr: Scheduler* = &_sched_storage
|
||
export _switch_old_rsp: rawptr
|
||
export _switch_new_rsp: rawptr
|
||
export _switch_old_fpu: rawptr
|
||
export _switch_new_fpu: rawptr
|
||
|
||
def set_fs_base(base: uint64):
|
||
global _fs_base_tbl
|
||
s: Scheduler* = _sched_ptr
|
||
_fs_base_tbl[s.current_tid] = base
|
||
_msr_write(uint32(IA32_FS_BASE), base)
|
||
|
||
def set_thread_fs_base(tid: int, base: uint64):
|
||
global _fs_base_tbl
|
||
if tid >= 0 and tid < MAX_THREADS:
|
||
_fs_base_tbl[tid] = base
|
||
|
||
def get_fs_base() -> uint64:
|
||
return _msr_read(uint32(IA32_FS_BASE))
|
||
|
||
def set_syscall_frame(f: uint64):
|
||
global _syscall_frame_tbl
|
||
s: Scheduler* = _sched_ptr
|
||
_syscall_frame_tbl[s.current_tid] = f
|
||
|
||
def get_syscall_frame() -> uint64:
|
||
s: Scheduler* = _sched_ptr
|
||
return _syscall_frame_tbl[s.current_tid]
|
||
|
||
def set_clear_child_tid(tid: int, addr: uint64):
|
||
global _clear_child_tid
|
||
if tid >= 0 and tid < MAX_THREADS:
|
||
_clear_child_tid[tid] = addr
|
||
|
||
@__attribute__(naked)
|
||
def _do_switch():
|
||
__asm__(f"""push rax
|
||
push rbx
|
||
push rbp
|
||
push r12
|
||
push r13
|
||
push r14
|
||
push r15
|
||
pushfq
|
||
# 切栈前保存 FPU/ISR
|
||
mov rax, [rip + _switch_old_fpu]
|
||
fxsave [rax]
|
||
# 保存 ISR scratch
|
||
lea rsi, [rip + _isr_scratch]
|
||
mov eax, dword ptr [rip + _switch_old_tid]
|
||
imul rax, rax, 48
|
||
lea rdx, [rip + _isr_scratch_tbl]
|
||
add rdx, rax
|
||
mov rcx, [rsi]
|
||
mov [rdx], rcx
|
||
mov rcx, [rsi + 8]
|
||
mov [rdx + 8], rcx
|
||
mov rcx, [rsi + 16]
|
||
mov [rdx + 16], rcx
|
||
mov rcx, [rsi + 24]
|
||
mov [rdx + 24], rcx
|
||
mov rcx, [rsi + 32]
|
||
mov [rdx + 32], rcx
|
||
mov rcx, [rsi + 40]
|
||
mov [rdx + 40], rcx
|
||
mov rax, [rip + _switch_old_rsp]
|
||
mov [rax], rsp
|
||
mov rax, [rip + _switch_new_rsp]
|
||
mov rsp, [rax]
|
||
# 换栈后恢复 FPU/ISR
|
||
mov rax, [rip + _switch_new_fpu]
|
||
fxrstor [rax]
|
||
# 恢复 ISR scratch
|
||
lea rsi, [rip + _isr_scratch]
|
||
mov eax, dword ptr [rip + _switch_new_tid]
|
||
imul rax, rax, 48
|
||
lea rdx, [rip + _isr_scratch_tbl]
|
||
add rdx, rax
|
||
mov rcx, [rdx]
|
||
mov [rsi], rcx
|
||
mov rcx, [rdx + 8]
|
||
mov [rsi + 8], rcx
|
||
mov rcx, [rdx + 16]
|
||
mov [rsi + 16], rcx
|
||
mov rcx, [rdx + 24]
|
||
mov [rsi + 24], rcx
|
||
mov rcx, [rdx + 32]
|
||
mov [rsi + 32], rcx
|
||
mov rcx, [rdx + 40]
|
||
mov [rsi + 40], rcx
|
||
popfq
|
||
pop r15
|
||
pop r14
|
||
pop r13
|
||
pop r12
|
||
pop rbp
|
||
pop rbx
|
||
pop rax
|
||
ret""",
|
||
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/fpu_area64/fpu_ptr592
|
||
# ⇒ sizeof(Thread) = 600,Scheduler.threads[64] 占 38400 字节 ⇒ current_tid 在 +38400。
|
||
# 增删 Thread 字段时必须同步下面 asm 里的 38400 / 600(以及 +24/+32/+40 三个字段偏移)。
|
||
@__attribute__(naked)
|
||
def _thread_entry():
|
||
__asm__(f"""sub rsp, 8
|
||
mov rax, [rip + _sched_ptr]
|
||
mov ecx, dword ptr [rax + 38400]
|
||
movsxd rcx, ecx
|
||
imul rcx, rcx, 600
|
||
mov rdi, [rax + rcx + 32]
|
||
mov rax, [rax + rcx + 24]
|
||
call rax
|
||
mov rax, [rip + _sched_ptr]
|
||
mov ecx, dword ptr [rax + 38400]
|
||
movsxd rcx, ecx
|
||
imul rcx, rcx, 600
|
||
mov dword ptr [rax + rcx + 40], 3
|
||
add rsp, 8""",
|
||
# `call rax` 破坏全部 caller-saved 寄存器(x64 SysV):除 RAX/RCX/RDX/
|
||
# R8-R11 外,RSI 与 RDI 也必须声明,否则编译器可能假设其值存活(N9)。
|
||
# RSP 由 `sub rsp,8` / `add rsp,8` 配对抵消,净变化为 0,无需声明。
|
||
op=[ASM_DESCR.CLOBBER_MEMORY, ASM_DESCR.CLOBBER_RAX,
|
||
ASM_DESCR.CLOBBER_RCX, ASM_DESCR.CLOBBER_RDX,
|
||
ASM_DESCR.CLOBBER_RSI, ASM_DESCR.CLOBBER_RDI,
|
||
ASM_DESCR.CLOBBER_R8, ASM_DESCR.CLOBBER_R9,
|
||
ASM_DESCR.CLOBBER_R10, ASM_DESCR.CLOBBER_R11])
|
||
Scheduler._yield()
|
||
while True:
|
||
asm.sti()
|
||
asm.hlt()
|
||
Scheduler._yield()
|
||
|
||
def _user_thread_entry(arg: rawptr) -> int:
|
||
"""用户态线程的内核侧引导:配好 TSS/GS 落栈点后 iretq 进 Ring 3,永不返回。"""
|
||
slot: int = int(uint64(arg))
|
||
ctx: proc.UserThreadCtx* = proc.user_thread_ctx(slot)
|
||
if ctx is None: return 0
|
||
# 与 execrunner/elf.vp 的 _elf_entry_wrapper 同款:直到 iretq 之前必须关中断
|
||
# (@pml4/TSS/GS 都是全局量,被定时器抢占时别的线程的 switch_pid 会把它们改掉);
|
||
# iretq 用用户态帧里的 RFLAGS.IF(0x202) 重新开中断。
|
||
asm.cli()
|
||
proc.switch_pid(ctx.pid)
|
||
gdt.set_tss_rsp0(ctx.kern_stack)
|
||
cpu.set_kernel_rsp0(ctx.kern_stack)
|
||
proc.drop_to_user_mode(ctx.entry, ctx.user_stack_entry_rsp, ctx.kern_stack, ctx.arg)
|
||
return 0
|
||
|
||
def thread_current() -> int:
|
||
s: Scheduler* = _sched_ptr
|
||
return s.current_tid
|
||
|
||
def needs_reschedule() -> int:
|
||
s: Scheduler* = _sched_ptr
|
||
return s.needs_reschedule
|
||
|
||
def sched_dump():
|
||
s: Scheduler* = _sched_ptr
|
||
dl: [80]char
|
||
viperlib.snprintf(&dl, 80, "[sched] dump: cur=%d cnt=%d yields=%u\n", s.current_tid, s.thread_count, _yield_cnt)
|
||
serial.puts(dl)
|
||
for i in range(s.thread_count):
|
||
sl: [80]char
|
||
viperlib.snprintf(&sl, 80, " t%d: st=%d pid=%d rsp=0x%lx\n", i, s.threads[i].state, s.threads[i].pid, s.threads[i].rsp)
|
||
serial.puts(sl)
|