feat: Basic process scheduling is completed

This commit is contained in:
ParrotXray 2025-10-19 20:03:41 +08:00
parent 939705bd40
commit fdaeda079c
10 changed files with 488 additions and 7 deletions

1
Cargo.lock generated
View File

@ -192,6 +192,7 @@ dependencies = [
"futures-util",
"lazy_static",
"linked_list_allocator",
"once_cell",
"raw-cpuid",
"spin 0.10.0",
"x86_64",

View File

@ -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"

View File

@ -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

View File

@ -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);

View File

@ -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() {

View File

@ -1,4 +1,42 @@
pub mod allocator;
pub mod paging;
pub mod vmm;
pub mod vma;
pub mod vma;
use x86_64::structures::paging::OffsetPageTable;
use spin::{Mutex, Once};
// 使用 Once 來保證只初始化一次
pub static KERNEL_MAPPER: Once<Mutex<&'static mut OffsetPageTable<'static>>> = Once::new();
pub static FRAME_ALLOCATOR: Once<Mutex<&'static mut allocator::frame::BootInfoFrameAllocator>> = 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, R>(f: F) -> Option<R>
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, R>(f: F) -> Option<R>
where
F: FnOnce(&mut allocator::frame::BootInfoFrameAllocator) -> R,
{
FRAME_ALLOCATOR.get().map(|allocator| {
let mut guard = allocator.lock();
f(*guard)
})
}

View File

@ -1 +1,3 @@
mod scheduler;
pub mod scheduler;
pub mod process;
pub mod stack;

View File

@ -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<Input, Yield, Return, Stack>
pub coroutine: Option<Coroutine<(), (), (), ProcessStack>>,
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<F>(mut self, f: F) -> Option<Self>
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
}
}
}

View File

@ -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<Process>; MAX_PROCESSES] = {
const NONE: Option<Process> = 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: F, priority: u8) -> Option<ProcessId>
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)
}
}

141
kernel/src/process/stack.rs Normal file
View File

@ -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<Self> {
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::<Size4KiB>::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::<Size4KiB>::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::<Size4KiB>::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
}
}