Files
ViperOS/VKernel/Kernel/sched/process.vp
T
2026-09-16 16:07:04 +08:00

285 lines
10 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import mm.mm as mm
import paging.paging as paging
import drivers.serial.uart.serial as serial
import drivers.fs.fat32.fat32 as fat32
import drivers.fs.fat32.fat32_types as fat32_types
import drivers.core.cpu.cpu as cpu
import intr.gdt as gdt
import viperlib
import string
import t, c
PROC_READY: t.CDefine = 0
PROC_RUNNING: t.CDefine = 1
PROC_BLOCKED: t.CDefine = 2
PROC_DONE: t.CDefine = 3
MAX_PROCESSES: t.CDefine = 8
MAX_PROC_THREADS: t.CDefine = 8
PROC_MAX_FDS: t.CDefine = 16
PROC_MAX_DIRS: t.CDefine = 8
KERN_STACK_SIZE: t.CDefine = 8192
USER_STACK_SIZE: t.CDefine = 65536
@t.Object
class Process:
pid: int
pml4_root: uint64
threads: t.CArray[int, 8]
thread_count: int
state: int
entry: t.CPtr
elf_base: uint64
heap_start: uint64
heap_size: uint64
name: t.CArray[t.CChar, 32]
fd_table: t.CArray[t.CPtr, PROC_MAX_FDS]
fd_used: t.CArray[uint8, PROC_MAX_FDS]
dir_table: t.CArray[t.CPtr, PROC_MAX_DIRS]
dir_used: t.CArray[uint8, PROC_MAX_DIRS]
dir_pool: t.CArray[fat32_types.dirobj, PROC_MAX_DIRS]
kern_stack: uint64
user_stack: uint64
def addThread(self, tid: int) -> int:
if self.thread_count >= MAX_PROC_THREADS: return -1
self.threads[self.thread_count] = tid
self.thread_count += 1
return 0
def exit(self):
self.state = PROC_DONE
for i in range(PROC_MAX_FDS):
if self.fd_used[i]:
fp: t.CPtr = self.fd_table[i]
if fp:
fat32.close(fp)
self.fd_used[i] = 0
self.fd_table[i] = None
for i in range(PROC_MAX_DIRS):
if self.dir_used[i]:
dp: t.CPtr = self.dir_table[i]
if dp:
fat32.closedir(dp)
self.dir_used[i] = 0
self.dir_table[i] = None
m: ProcessManager | t.CPtr = _proc_mgr_ptr
if m.current_pid == self.pid:
m.current_pid = 0
m.processes[0].state = PROC_RUNNING
if self.pml4_root != 0:
kernel_cr3: uint64 = m.processes[0].pml4_root
if kernel_cr3 != 0:
c.Asm(f"mov cr3, {c.AsmInp(kernel_cr3, t.ASM_DESCR.REG_ANY)}",
op=[t.ASM_DESCR.CLOBBER_MEMORY])
paging.set_active_pml4(kernel_cr3)
@property
def is_running(self) -> int:
if self.state == PROC_RUNNING:
return 1
return 0
@property
def is_done(self) -> int:
if self.state == PROC_DONE:
return 1
return 0
@t.Object
class ProcessManager:
processes: t.CArray[Process, MAX_PROCESSES]
current_pid: int
count: int
def __init__(self):
global _proc_mgr_ptr
_proc_mgr_ptr = c.Addr(_proc_mgr)
m: ProcessManager | t.CPtr = _proc_mgr_ptr
m.current_pid = 0
m.count = 1
p: Process | t.CPtr = c.Addr(m.processes[0])
p.pid = 0
p.state = PROC_RUNNING
p.thread_count = 0
p.pml4_root = 0
p.entry = None
p.elf_base = 0
p.heap_start = 0
p.heap_size = 0
name_str: str = "kernel"
i: int = 0
for ch in name_str:
if i >= 31: break
p.name[i] = ch
i += 1
p.name[i] = 0
j: int
for j in range(MAX_PROC_THREADS):
p.threads[j] = -1
for j in range(PROC_MAX_FDS):
p.fd_used[j] = 0
p.fd_table[j] = None
for j in range(PROC_MAX_DIRS):
p.dir_used[j] = 0
p.dir_table[j] = None
c.Asm(f"""mov {c.AsmOut(p.pml4_root, t.ASM_DESCR.OUTPUT_REG)}, cr3""",
op=[t.ASM_DESCR.CLOBBER_MEMORY])
@staticmethod
def _switch(pid: int) -> int:
global _saved_cr3
m: ProcessManager | t.CPtr = _proc_mgr_ptr
if pid < 0 or pid >= m.count: return -1
p: Process | t.CPtr = c.Addr(m.processes[pid])
if p.state == PROC_DONE: return -1
old_pid: int = m.current_pid
if old_pid == pid: return 0
if old_pid >= 0 and old_pid < m.count:
m.processes[old_pid].state = PROC_READY
p.state = PROC_RUNNING
m.current_pid = pid
c.Asm(f"""mov {c.AsmOut(_saved_cr3, t.ASM_DESCR.OUTPUT_REG)}, cr3
mov rax, {c.AsmInp(p.pml4_root, t.ASM_DESCR.REG_ANY)}
mov cr3, rax""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
return 0
@staticmethod
def current() -> Process | t.CPtr:
m: ProcessManager | t.CPtr = _proc_mgr_ptr
return c.Addr(m.processes[m.current_pid])
@staticmethod
def get_process(pid: int) -> Process | t.CPtr:
m: ProcessManager | t.CPtr = _proc_mgr_ptr
if pid < 0 or pid >= m.count: return None
return c.Addr(m.processes[pid])
@staticmethod
def create_process(name: str, entry: t.CPtr) -> Process | t.CPtr:
m: ProcessManager | t.CPtr = _proc_mgr_ptr
_dbp: t.CArray[t.CChar, 80]
viperlib.snprintf(c.Addr(_dbp), 80, "[process] create_process count=%d MAX=%d\n", m.count, MAX_PROCESSES)
serial.puts(_dbp)
if m.count >= MAX_PROCESSES: return None
pid: int = m.count
m.count += 1
p: Process | t.CPtr = c.Addr(m.processes[pid])
p.pid = pid
p.state = PROC_READY
p.entry = entry
p.elf_base = 0
p.thread_count = 0
p.heap_start = 0
p.heap_size = 0
string.memset(c.Addr(p.name), 0, 32)
i: int = 0
for ch in name:
if i >= 31: break
p.name[i] = ch
i += 1
p.name[i] = 0
j: int
for j in range(MAX_PROC_THREADS):
p.threads[j] = -1
for j in range(PROC_MAX_FDS):
p.fd_used[j] = 0
p.fd_table[j] = None
for j in range(PROC_MAX_DIRS):
p.dir_used[j] = 0
p.dir_table[j] = None
ks: t.CPtr = mm.malloc(KERN_STACK_SIZE)
# [DIAG] 定位跨模块 malloc 返回值(mm 成功但调用方收到 None)
_dbg_ks: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(_dbg_ks), 64, "[proc] ks=0x%lx\n", uint64(ks))
serial.puts(c.Addr(_dbg_ks))
if ks is None:
serial.puts("[process] kernel stack alloc failed\n")
m.count = m.count - 1
return None
ks_size: uint64 = KERN_STACK_SIZE
p.kern_stack = uint64(ks) + ks_size
us: t.CPtr = mm.malloc(USER_STACK_SIZE)
# [DIAG] 定位 user_stack 收到 None(mm 内部打印成功但此处收 0x0)
_dbg_us: t.CArray[t.CChar, 64]
viperlib.snprintf(c.Addr(_dbg_us), 64, "[proc] us=0x%lx\n", uint64(us))
serial.puts(c.Addr(_dbg_us))
if us is None:
serial.puts("[process] user stack alloc failed\n")
mm.free(ks)
m.count = m.count - 1
return None
us_size: uint64 = USER_STACK_SIZE
p.user_stack = uint64(us) + us_size
c.Asm(f"""mov {c.AsmOut(p.pml4_root, t.ASM_DESCR.OUTPUT_REG)}, cr3""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX])
return p
_proc_mgr: ProcessManager = ProcessManager()
_proc_mgr_ptr: ProcessManager | t.CPtr = c.Addr(_proc_mgr)
_saved_cr3: uint64 | t.CExport
def current() -> int:
m: ProcessManager | t.CPtr = _proc_mgr_ptr
return m.current_pid
def switch_pid(new_pid: int):
m: ProcessManager | t.CPtr = _proc_mgr_ptr
if m.current_pid == new_pid: return
if m.current_pid >= 0 and m.current_pid < m.count:
old_p: Process | t.CPtr = c.Addr(m.processes[m.current_pid])
if old_p.state != PROC_DONE:
old_p.state = PROC_READY
if new_pid >= 0 and new_pid < m.count:
new_p: Process | t.CPtr = c.Addr(m.processes[new_pid])
new_p.state = PROC_RUNNING
new_cr3: uint64 = new_p.pml4_root
if new_cr3 != 0:
c.Asm(f"mov cr3, {c.AsmInp(new_cr3, t.ASM_DESCR.REG_ANY)}",
op=[t.ASM_DESCR.CLOBBER_MEMORY])
paging.set_active_pml4(new_cr3)
# Update TSS RSP0 and GS kernel_rsp0 for the new process
# This is needed so interrupts from Ring 3 use the correct kernel stack
if new_p.kern_stack != 0:
gdt.set_tss_rsp0(new_p.kern_stack)
cpu.set_kernel_rsp0(new_p.kern_stack)
m.current_pid = new_pid
def get_pid(tid: int) -> int:
m: ProcessManager | t.CPtr = _proc_mgr_ptr
i: int
for i in range(m.count):
p: Process | t.CPtr = c.Addr(m.processes[i])
j: int
for j in range(p.thread_count):
if p.threads[j] == tid:
return p.pid
return -1
def drop_to_user_mode(entry: t.CPtr, user_rsp: uint64, kern_rsp: uint64):
# NOTE: Do NOT swapgs here. This OS uses reverse GS convention:
# GS_BASE = 0 (kernel & user), KERNEL_GS_BASE = &_per_cpu
# syscall/ISR entry does swapgs to get GS_BASE = &_per_cpu
# syscall/ISR return does swapgs to restore GS_BASE = 0
# GS state is restored in isrCommonhandler before _yield() if needed.
c.Asm(f"""cli
mov rsp, {c.AsmInp(kern_rsp, t.ASM_DESCR.REG_ANY)}
mov rdx, {c.AsmInp(user_rsp, t.ASM_DESCR.REG_ANY)}
mov rcx, {c.AsmInp(entry, t.ASM_DESCR.REG_ANY)}
mov r11, 0x202
push 0x23
push rdx
push r11
push 0x1B
push rcx
iretq""",
op=[t.ASM_DESCR.CLOBBER_MEMORY, t.ASM_DESCR.CLOBBER_RAX,
t.ASM_DESCR.CLOBBER_RCX, t.ASM_DESCR.CLOBBER_RDX,
t.ASM_DESCR.CLOBBER_R11, t.ASM_DESCR.CLOBBER_RSI,
t.ASM_DESCR.CLOBBER_RDI, t.ASM_DESCR.CLOBBER_R8,
t.ASM_DESCR.CLOBBER_R9, t.ASM_DESCR.CLOBBER_R10,
t.ASM_DESCR.CLOBBER_RBP])