From fdaeda079c6be6cc76488fc085e67b9ab45750fe Mon Sep 17 00:00:00 2001 From: ParrotXray Date: Sun, 19 Oct 2025 20:03:41 +0800 Subject: [PATCH] feat: Basic process scheduling is completed --- Cargo.lock | 1 + kernel/Cargo.toml | 1 + kernel/src/hal/timer.rs | 2 + kernel/src/kernel/k_init.rs | 31 +++++- kernel/src/kernel/k_main.rs | 40 +++++++- kernel/src/mm/mod.rs | 40 +++++++- kernel/src/process/mod.rs | 4 +- kernel/src/process/process.rs | 67 +++++++++++++ kernel/src/process/scheduler.rs | 168 ++++++++++++++++++++++++++++++++ kernel/src/process/stack.rs | 141 +++++++++++++++++++++++++++ 10 files changed, 488 insertions(+), 7 deletions(-) create mode 100644 kernel/src/process/process.rs create mode 100644 kernel/src/process/stack.rs diff --git a/Cargo.lock b/Cargo.lock index 7eba42c..da5075e 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -192,6 +192,7 @@ dependencies = [ "futures-util", "lazy_static", "linked_list_allocator", + "once_cell", "raw-cpuid", "spin 0.10.0", "x86_64", diff --git a/kernel/Cargo.toml b/kernel/Cargo.toml index fe849b6..ea77c6c 100644 --- a/kernel/Cargo.toml +++ b/kernel/Cargo.toml @@ -12,6 +12,7 @@ spin = "0.10.0" raw-cpuid = "11.6.0" acpi = "6.0.1" linked_list_allocator = "0.10.5" +once_cell = { version = "1.19", default-features = false, features = ["alloc"] } [dependencies.crossbeam-queue] version = "0.3.12" diff --git a/kernel/src/hal/timer.rs b/kernel/src/hal/timer.rs index dc85d6d..db7d254 100644 --- a/kernel/src/hal/timer.rs +++ b/kernel/src/hal/timer.rs @@ -254,6 +254,8 @@ pub fn apic_calibration_handler() { #[inline] pub fn timer_tick_handler() { TICK_COUNTER.fetch_add(1, Ordering::Relaxed); + + crate::process::scheduler::Scheduler::on_timer_tick(); } /// Get timer information diff --git a/kernel/src/kernel/k_init.rs b/kernel/src/kernel/k_init.rs index ed23f0a..f609b9a 100644 --- a/kernel/src/kernel/k_init.rs +++ b/kernel/src/kernel/k_init.rs @@ -277,7 +277,36 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! { ); let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset); - _post_init(acpi_info.as_ref(), &mut mapper, &mut frame_allocator); + unsafe { + // 將 mapper 和 frame_allocator 轉換為 'static 引用 + let mapper_static: &'static mut OffsetPageTable = + core::mem::transmute(&mut mapper); + let allocator_static: &'static mut frame::BootInfoFrameAllocator = + core::mem::transmute(&mut frame_allocator); + + // 初始化全局變量 + crate::mm::init_globals(mapper_static, allocator_static); + + log_info!("Global mapper and frame allocator initialized"); + } + + // 不要 drop,因為我們把引用給了全局變量 + core::mem::forget(mapper); + core::mem::forget(frame_allocator); + // ============================================ + + let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset); + + // 現在可以安全地訪問全局 mapper 和 allocator + if let Some(mapper_once) = crate::mm::KERNEL_MAPPER.get() { + if let Some(allocator_once) = crate::mm::FRAME_ALLOCATOR.get() { + let mut mapper_guard = mapper_once.lock(); + let mut allocator_guard = allocator_once.lock(); + + _post_init(acpi_info.as_ref(), *mapper_guard, *allocator_guard); + } + } + _boot_report(&boot_info.memory_regions, physical_memory_offset); diff --git a/kernel/src/kernel/k_main.rs b/kernel/src/kernel/k_main.rs index 419f0e8..66b0c1a 100644 --- a/kernel/src/kernel/k_main.rs +++ b/kernel/src/kernel/k_main.rs @@ -4,6 +4,7 @@ use crate::{kprint, kprintln, shell}; use crate::{log_trace, log_debug, log_info, log_warn, log_error, log_fatal}; use crate::mm::{vma, vmm}; use crate::mm::allocator::pmm; +use crate::process::scheduler::Scheduler; pub fn _kernel_main() -> ! { kprintln!(); @@ -35,11 +36,42 @@ pub fn _kernel_main() -> ! { kprintln!("Type 'help' for available commands"); kprintln!(); - shell::init(); + Scheduler::init(); - loop { - cpu::cpu_halt(); - } + // 創建測試進程 A + Scheduler::spawn(|yielder, _input| { + for i in 0..5 { + crate::kprintln!("Process A: iteration {}", i); + yielder.suspend(()); + } + crate::kprintln!("Process A finished"); + }, 1); + + // 創建測試進程 B + Scheduler::spawn(|yielder, _input| { + for i in 0..5 { + crate::kprintln!("Process B: iteration {}", i); + yielder.suspend(()); + } + crate::kprintln!("Process B finished"); + }, 1); + + // // 創建 Shell 進程(低優先級,在測試進程完成後運行)會 panic + // Scheduler::spawn(|yielder, _input| { + // crate::shell::init(); + // + // loop { + // // 定期 yield 讓其他進程運行 + // for _ in 0..1000 { + // Scheduler::check_reschedule(); + // crate::hal::cpu::cpu_pause(0); + // } + // yielder.suspend(()); + // } + // }, 10); // 低優先級 + + // 運行調度器(永遠不返回) + Scheduler::run() } pub fn test_apic_timer() { diff --git a/kernel/src/mm/mod.rs b/kernel/src/mm/mod.rs index bba8d26..2184a6d 100644 --- a/kernel/src/mm/mod.rs +++ b/kernel/src/mm/mod.rs @@ -1,4 +1,42 @@ pub mod allocator; pub mod paging; pub mod vmm; -pub mod vma; \ No newline at end of file +pub mod vma; + +use x86_64::structures::paging::OffsetPageTable; +use spin::{Mutex, Once}; + +// 使用 Once 來保證只初始化一次 +pub static KERNEL_MAPPER: Once>> = Once::new(); +pub static FRAME_ALLOCATOR: Once> = Once::new(); + +/// 初始化全局 mapper 和 allocator +pub unsafe fn init_globals( + mapper: &'static mut OffsetPageTable<'static>, + allocator: &'static mut allocator::frame::BootInfoFrameAllocator, +) { + KERNEL_MAPPER.call_once(|| Mutex::new(mapper)); + FRAME_ALLOCATOR.call_once(|| Mutex::new(allocator)); +} + +/// 獲取全局 mapper(輔助函數) +pub fn with_mapper(f: F) -> Option +where + F: FnOnce(&mut OffsetPageTable<'static>) -> R, +{ + KERNEL_MAPPER.get().map(|mapper| { + let mut guard = mapper.lock(); + f(*guard) + }) +} + +/// 獲取全局 frame allocator(輔助函數) +pub fn with_frame_allocator(f: F) -> Option +where + F: FnOnce(&mut allocator::frame::BootInfoFrameAllocator) -> R, +{ + FRAME_ALLOCATOR.get().map(|allocator| { + let mut guard = allocator.lock(); + f(*guard) + }) +} \ No newline at end of file diff --git a/kernel/src/process/mod.rs b/kernel/src/process/mod.rs index 7d1ef6c..d09740b 100644 --- a/kernel/src/process/mod.rs +++ b/kernel/src/process/mod.rs @@ -1 +1,3 @@ -mod scheduler; \ No newline at end of file +pub mod scheduler; +pub mod process; +pub mod stack; \ No newline at end of file diff --git a/kernel/src/process/process.rs b/kernel/src/process/process.rs new file mode 100644 index 0000000..4c3b086 --- /dev/null +++ b/kernel/src/process/process.rs @@ -0,0 +1,67 @@ +// kernel/src/process/process.rs +use corosensei::{Coroutine, CoroutineResult, Yielder}; +use super::stack::ProcessStack; + +pub type ProcessId = u64; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum ProcessState { + Ready, + Running, + Blocked, + Terminated, +} + +pub struct Process { + pub id: ProcessId, + pub state: ProcessState, + // Coroutine + pub coroutine: Option>, + pub time_slice: u64, + pub priority: u8, +} + +impl Process { + pub fn new(id: ProcessId, priority: u8, time_slice: u64) -> Self { + Self { + id, + state: ProcessState::Ready, + coroutine: None, + time_slice, + priority, + } + } + + pub fn spawn(mut self, f: F) -> Option + where + F: FnOnce(&Yielder<(), ()>, ()) + 'static, + { + // 創建 stack + let stack = ProcessStack::new()?; + + // 創建 coroutine + let coro = Coroutine::with_stack(stack, f); + self.coroutine = Some(coro); + + Some(self) + } + + pub fn resume(&mut self) -> bool { + if let Some(ref mut coro) = self.coroutine { + self.state = ProcessState::Running; + + match coro.resume(()) { + CoroutineResult::Yield(()) => { + self.state = ProcessState::Ready; + true // 還需要繼續運行 + } + CoroutineResult::Return(()) => { + self.state = ProcessState::Terminated; + false // 已完成 + } + } + } else { + false + } + } +} \ No newline at end of file diff --git a/kernel/src/process/scheduler.rs b/kernel/src/process/scheduler.rs index e69de29..e9d2bb3 100644 --- a/kernel/src/process/scheduler.rs +++ b/kernel/src/process/scheduler.rs @@ -0,0 +1,168 @@ +// kernel/src/process/scheduler.rs +use super::process::{Process, ProcessId, ProcessState}; +use core::sync::atomic::{AtomicBool, AtomicU64, AtomicUsize, Ordering}; +use crate::{log_debug, log_info, log_warn}; + +const MAX_PROCESSES: usize = 256; +const DEFAULT_TIME_SLICE: u64 = 10; + +static mut PROCESSES: [Option; MAX_PROCESSES] = { + const NONE: Option = None; + [NONE; MAX_PROCESSES] +}; + +static NEXT_PID: AtomicU64 = AtomicU64::new(1); +static CURRENT_PROCESS: AtomicUsize = AtomicUsize::new(0); +static PROCESS_COUNT: AtomicUsize = AtomicUsize::new(0); +static SCHEDULER_ENABLED: AtomicBool = AtomicBool::new(false); +static NEED_RESCHEDULE: AtomicBool = AtomicBool::new(false); + +pub struct Scheduler; + +impl Scheduler { + pub fn init() { + log_info!("Initializing preemptive scheduler"); + SCHEDULER_ENABLED.store(true, Ordering::Release); + } + + /// 創建新進程 + pub fn spawn(f: F, priority: u8) -> Option + where + F: FnOnce(&corosensei::Yielder<(), ()>, ()) + 'static, + { + let pid = NEXT_PID.fetch_add(1, Ordering::Relaxed); + + // 創建進程(可能失敗) + let process = match Process::new(pid, priority, DEFAULT_TIME_SLICE).spawn(f) { + Some(p) => p, + None => { + log_warn!("Failed to create process: stack allocation failed"); + return None; + } + }; + + unsafe { + for i in 0..MAX_PROCESSES { + if PROCESSES[i].is_none() { + PROCESSES[i] = Some(process); + PROCESS_COUNT.fetch_add(1, Ordering::Release); + log_debug!("Spawned process {} at slot {}", pid, i); + return Some(pid); + } + } + } + + log_warn!("Failed to spawn process: no free slots"); + None + } + + /// Timer 中斷處理器調用 + pub fn on_timer_tick() { + if !SCHEDULER_ENABLED.load(Ordering::Acquire) { + return; + } + + let current_idx = CURRENT_PROCESS.load(Ordering::Acquire); + + unsafe { + if let Some(ref mut process) = PROCESSES[current_idx] { + if process.state == ProcessState::Running { + if process.time_slice > 0 { + process.time_slice -= 1; + } + + if process.time_slice == 0 { + NEED_RESCHEDULE.store(true, Ordering::Release); + } + } + } + } + } + + /// 在安全點檢查是否需要調度 + pub fn check_reschedule() { + if NEED_RESCHEDULE.swap(false, Ordering::AcqRel) { + Self::schedule(); + } + } + + /// 執行調度 + fn schedule() { + let count = PROCESS_COUNT.load(Ordering::Acquire); + if count == 0 { + return; + } + + let mut current_idx = CURRENT_PROCESS.load(Ordering::Acquire); + let start_idx = current_idx; + let mut found = false; + + unsafe { + // 將當前進程設為 Ready(如果還在運行) + if let Some(ref mut process) = PROCESSES[current_idx] { + if process.state == ProcessState::Running { + process.state = ProcessState::Ready; + process.time_slice = DEFAULT_TIME_SLICE; + } + } + + // Round-robin 查找下一個可運行的進程 + loop { + current_idx = (current_idx + 1) % MAX_PROCESSES; + + if let Some(ref mut process) = PROCESSES[current_idx] { + if process.state == ProcessState::Ready { + CURRENT_PROCESS.store(current_idx, Ordering::Release); + log_debug!("Switching to process {} (slot {})", process.id, current_idx); + + // Resume 進程 + log_debug!("About to resume process {}", process.id); + let result = process.resume(); + log_debug!("Process {} resume returned: {}", process.id, result); + + if !result { + log_debug!("Process {} terminated", process.id); + PROCESSES[current_idx] = None; + PROCESS_COUNT.fetch_sub(1, Ordering::Release); + continue; + } + + found = true; + break; + } + } + + // 遍歷一圈 + if current_idx == start_idx { + break; + } + } + + if !found { + log_debug!("No runnable process found"); + } + } + } + + /// 主調度循環 + pub fn run() -> ! { + log_info!("Starting scheduler main loop"); + + loop { + Self::schedule(); + + if PROCESS_COUNT.load(Ordering::Acquire) == 0 { + crate::hal::cpu::cpu_halt(); + } + + crate::hal::cpu::cpu_pause(0); + } + } + + /// 獲取統計信息 + pub fn stats() -> (usize, usize) { + let count = PROCESS_COUNT.load(Ordering::Acquire); + let current = CURRENT_PROCESS.load(Ordering::Acquire); + (count, current) + } +} \ No newline at end of file diff --git a/kernel/src/process/stack.rs b/kernel/src/process/stack.rs new file mode 100644 index 0000000..f197b19 --- /dev/null +++ b/kernel/src/process/stack.rs @@ -0,0 +1,141 @@ +// kernel/src/process/stack.rs +use core::num::NonZeroUsize; +use corosensei::stack::{Stack, StackPointer, STACK_ALIGNMENT}; +use crate::mm::{KERNEL_MAPPER, FRAME_ALLOCATOR}; +use crate::mm::allocator::pmm; +use x86_64::{VirtAddr, structures::paging::{Page, PageTableFlags, Size4KiB, Mapper}}; +use crate::{log_debug, log_error}; + +const PROCESS_STACK_SIZE: usize = 64 * 1024; // 64 KiB + +pub struct ProcessStack { + base: StackPointer, + limit: StackPointer, + vaddr: VirtAddr, + page_count: usize, +} + +impl ProcessStack { + pub fn new() -> Option { + let page_count = (PROCESS_STACK_SIZE + 4095) / 4096; + + // 從 VMM 分配虛擬地址 + let vaddr = crate::mm::vmm::VMM.lock().allocate_pages(page_count)?; + + log_debug!("Allocating process stack at {:#x}, {} pages", vaddr.as_u64(), page_count); + + // 映射所有頁面 + let mapper = KERNEL_MAPPER.get()?; + let allocator = FRAME_ALLOCATOR.get()?; + + for i in 0..page_count { + let page_vaddr = vaddr + (i * 4096) as u64; + let page = Page::::containing_address(page_vaddr); + + // 分配物理頁面 + let frame = match pmm::allocate_frame() { + Some(f) => f, + None => { + log_error!("Failed to allocate physical frame for stack page {}", i); + Self::cleanup_mapped_pages(vaddr, i); + crate::mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); + return None; + } + }; + + // 映射頁面 + let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE; + + let mut mapper_guard = mapper.lock(); + let mut alloc_guard = allocator.lock(); + + unsafe { + match mapper_guard.map_to(page, frame, flags, &mut **alloc_guard) { + Ok(flush) => { + flush.flush(); + } + Err(e) => { + log_error!("Failed to map stack page {}: {:?}", i, e); + pmm::deallocate_frame(frame); + drop(mapper_guard); + drop(alloc_guard); + Self::cleanup_mapped_pages(vaddr, i); + crate::mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count); + return None; + } + } + } + + log_debug!("Mapped stack page {} at {:#x} -> frame {:#x}", + i, page_vaddr.as_u64(), frame.start_address().as_u64()); + } + + // 計算對齊的 stack base 和 limit + let base_addr = vaddr.as_u64() + PROCESS_STACK_SIZE as u64; + let limit_addr = vaddr.as_u64(); + + let base_aligned = base_addr & !(STACK_ALIGNMENT as u64 - 1); + let limit_aligned = (limit_addr + STACK_ALIGNMENT as u64 - 1) & !(STACK_ALIGNMENT as u64 - 1); + + Some(Self { + base: NonZeroUsize::new(base_aligned as usize)?, + limit: NonZeroUsize::new(limit_aligned as usize)?, + vaddr, + page_count, + }) + } + + /// 清理已映射的頁面 + fn cleanup_mapped_pages(vaddr: VirtAddr, count: usize) { + if let Some(mapper) = KERNEL_MAPPER.get() { + let mut mapper_guard = mapper.lock(); + + for i in 0..count { + let page_vaddr = vaddr + (i * 4096) as u64; + let page = Page::::containing_address(page_vaddr); + + // 明確指定類型 + if let Ok((frame, flush)) = mapper_guard.unmap(page) { + flush.flush(); + pmm::deallocate_frame(frame); + } + } + } + } +} + +impl Drop for ProcessStack { + fn drop(&mut self) { + log_debug!("Dropping process stack at {:#x}", self.vaddr.as_u64()); + + // 取消映射並釋放物理頁面 + if let Some(mapper) = KERNEL_MAPPER.get() { + let mut mapper_guard = mapper.lock(); + + for i in 0..self.page_count { + let page_vaddr = self.vaddr + (i * 4096) as u64; + let page = Page::::containing_address(page_vaddr); + + // 明確指定類型參數 + if let Ok((frame, flush)) = mapper_guard.unmap(page) { + flush.flush(); + pmm::deallocate_frame(frame); + log_debug!("Unmapped and freed stack page {}", i); + } + } + } + + // 釋放虛擬地址 + crate::mm::vmm::VMM.lock().deallocate_pages(self.vaddr, self.page_count); + } +} + +unsafe impl Stack for ProcessStack { + fn base(&self) -> StackPointer { + self.base + } + + fn limit(&self) -> StackPointer { + self.limit + } +} \ No newline at end of file