feat: Basic process scheduling is completed
This commit is contained in:
parent
939705bd40
commit
fdaeda079c
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -192,6 +192,7 @@ dependencies = [
|
||||
"futures-util",
|
||||
"lazy_static",
|
||||
"linked_list_allocator",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"spin 0.10.0",
|
||||
"x86_64",
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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)
|
||||
})
|
||||
}
|
||||
@ -1 +1,3 @@
|
||||
mod scheduler;
|
||||
pub mod scheduler;
|
||||
pub mod process;
|
||||
pub mod stack;
|
||||
67
kernel/src/process/process.rs
Normal file
67
kernel/src/process/process.rs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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
141
kernel/src/process/stack.rs
Normal 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
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user