feat: Complete the basic arranger

This commit is contained in:
ParrotXray 2025-10-20 19:31:15 +08:00
parent fdaeda079c
commit 02d4d17119
9 changed files with 310 additions and 168 deletions

View File

@ -88,8 +88,8 @@ pub fn handle_scancode(raw: u8) {
}
match scancode {
0x1C => print_char(b'\n'), // Numpad Enter
0x35 => print_char(b'/'), // Numpad /
0x1C => tty::device::receive_char(b'\n'), // Numpad Enter
0x35 => tty::device::receive_char(b'/'), // Numpad /
0x47 => kprintln!("[Home]"),
0x48 => kprintln!("[Up]"),
0x49 => kprintln!("[PgUp]"),
@ -190,26 +190,21 @@ pub fn handle_scancode(raw: u8) {
if state.ctrl_pressed {
match ascii {
b'c' | b'C' => { kprintln!("^C"); return; }
b'd' | b'D' => { kprintln!("^D"); return; }
b'l' | b'L' => { tty::tty::clear(0x000000); return; }
_ => {}
b'c' | b'C' => {
crate::tty::device::receive_char(3); // Ctrl+C
return;
}
b'l' | b'L' => {
tty::device::receive_char(12); // Ctrl+L
return;
}
_ => return,
}
}
print_char(ascii);
tty::device::receive_char(ascii);
}
/// Print a character to the screen
fn print_char(c: u8) {
if c == b'\n' {
shell::process_keyboard_char('\n');
} else if c == 8 { // Backspace
shell::process_keyboard_char('\x08');
} else if c.is_ascii_graphic() || c == b' ' {
shell::process_keyboard_char(c as char);
}
}
/// Initialize keyboard driver
pub fn init() {

View File

@ -3,7 +3,7 @@ use bootloader_api::BootInfo;
use bootloader_api::info::MemoryRegionKind;
use x86_64::structures::paging::OffsetPageTable;
use x86_64::{PhysAddr, VirtAddr};
use crate::mm::{allocator::{frame, heap, pmm}, paging, vma, vmm};
use crate::mm::{allocator::{frame, heap, pmm}, init_globals, paging, vma, vmm};
use crate::arch::amd64::{gdt, idt};
use crate::tty::tty;
use crate::kprintln;
@ -15,6 +15,7 @@ use crate::drivers::keyboard;
use crate::hal::{acpi, cpu, rtc, timer};
use crate::hal::apic::{ioapic, lapic};
use crate::hal::cpu::cpu_enable_interrupts;
use crate::process::scheduler::Scheduler;
use crate::task::executor;
fn _logger_init() {
@ -55,7 +56,7 @@ fn _memory_init(
let mut total_usable = 0u64;
for region in memory_regions.iter() {
if region.kind == bootloader_api::info::MemoryRegionKind::Usable {
if region.kind == MemoryRegionKind::Usable {
usable_start = usable_start.min(region.start);
usable_end = usable_end.max(region.end);
total_usable += region.end - region.start;
@ -275,29 +276,23 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
&boot_info.memory_regions,
physical_memory_offset
);
let acpi_info = _acpi_init(rsdp_addr, physical_memory_offset);
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);
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();
@ -307,7 +302,6 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
}
}
_boot_report(&boot_info.memory_regions, physical_memory_offset);
kprintln!();
@ -316,6 +310,7 @@ pub fn _kernel_init(boot_info: &'static mut BootInfo) -> ! {
let mut executor = executor::Executor::new();
Scheduler::init();
k_main::_kernel_main();
} else {

View File

@ -31,46 +31,32 @@ pub fn _kernel_main() -> ! {
if let time = rtc::get_time() {
kprintln!("Date/Time: {}", time.format());
}
kprintln!();
kprintln!("Type 'help' for available commands");
kprintln!();
Scheduler::init();
// 創建測試進程 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
// Process A
// Scheduler::spawn(|yielder, _input| {
// crate::shell::init();
//
// loop {
// // 定期 yield 讓其他進程運行
// for _ in 0..1000 {
// Scheduler::check_reschedule();
// crate::hal::cpu::cpu_pause(0);
// }
// for i in 0..5 {
// crate::kprintln!("Process A: iteration {}", i);
// yielder.suspend(());
// }
// }, 10); // 低優先級
// crate::kprintln!("Process A finished");
// }, 1);
//
// Process B
// Scheduler::spawn(|yielder, _input| {
// for i in 0..5 {
// crate::kprintln!("Process B: iteration {}", i);
// yielder.suspend(());
// }
// crate::kprintln!("Process B finished");
// }, 1);
//
// Scheduler::init();
Scheduler::spawn(|yielder, _input| {
let pid = Scheduler::current_pid().unwrap();
shell::shell_main(pid, yielder);
}, 5);
// 運行調度器(永遠不返回)
Scheduler::run()
}

View File

@ -15,7 +15,6 @@ pub enum ProcessState {
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,
@ -36,13 +35,9 @@ impl Process {
where
F: FnOnce(&Yielder<(), ()>, ()) + 'static,
{
// 創建 stack
let stack = ProcessStack::new()?;
// 創建 coroutine
let coro = Coroutine::with_stack(stack, f);
self.coroutine = Some(coro);
Some(self)
}
@ -52,12 +47,15 @@ impl Process {
match coro.resume(()) {
CoroutineResult::Yield(()) => {
self.state = ProcessState::Ready;
true // 還需要繼續運行
// Set to Ready only when not in Blocked state
if self.state == ProcessState::Running {
self.state = ProcessState::Ready;
}
true
}
CoroutineResult::Return(()) => {
self.state = ProcessState::Terminated;
false // 已完成
false
}
}
} else {

View File

@ -1,7 +1,7 @@
// 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};
use crate::{hal, log_debug, log_info, log_warn};
const MAX_PROCESSES: usize = 256;
const DEFAULT_TIME_SLICE: u64 = 10;
@ -21,18 +21,17 @@ pub struct Scheduler;
impl Scheduler {
pub fn init() {
log_info!("Initializing preemptive scheduler");
log_info!("Initializing scheduler with blocking support");
SCHEDULER_ENABLED.store(true, Ordering::Release);
}
/// 創建新進程
/// Create a new process
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 => {
@ -46,7 +45,7 @@ impl Scheduler {
if PROCESSES[i].is_none() {
PROCESSES[i] = Some(process);
PROCESS_COUNT.fetch_add(1, Ordering::Release);
log_debug!("Spawned process {} at slot {}", pid, i);
// log_debug!("Spawned process {} at slot {}", pid, i);
return Some(pid);
}
}
@ -56,7 +55,36 @@ impl Scheduler {
None
}
/// Timer 中斷處理器調用
/// Block the current process
pub fn block_current() {
let current_idx = CURRENT_PROCESS.load(Ordering::Acquire);
unsafe {
if let Some(ref mut process) = PROCESSES[current_idx] {
process.state = ProcessState::Blocked;
// log_debug!("Process {} (slot {}) blocked", process.id, current_idx);
}
}
}
/// Wake up the specified process
pub fn wake_process(pid: u64) {
unsafe {
for i in 0..MAX_PROCESSES {
if let Some(ref mut process) = PROCESSES[i] {
if process.id == pid && process.state == ProcessState::Blocked {
process.state = ProcessState::Ready;
// log_debug!("Process {} (slot {}) woken up", process.id, i);
NEED_RESCHEDULE.store(true, Ordering::Release);
return;
}
}
}
}
log_warn!("Tried to wake non-existent or non-blocked process {}", pid);
}
/// Timer interrupt call
pub fn on_timer_tick() {
if !SCHEDULER_ENABLED.load(Ordering::Acquire) {
return;
@ -79,26 +107,24 @@ impl Scheduler {
}
}
/// 在安全點檢查是否需要調度
/// Check if rescheduling is needed
pub fn check_reschedule() {
if NEED_RESCHEDULE.swap(false, Ordering::AcqRel) {
Self::schedule();
}
}
/// 執行調度
/// Execute scheduling
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如果還在運行
let current_idx = CURRENT_PROCESS.load(Ordering::Acquire);
// Set the current process to Ready (if it is running)
if let Some(ref mut process) = PROCESSES[current_idx] {
if process.state == ProcessState::Running {
process.state = ProcessState::Ready;
@ -106,63 +132,62 @@ impl Scheduler {
}
}
// Round-robin 查找下一個可運行的進程
// Round-robin to find the next Ready process
let mut next_idx = current_idx;
let mut attempts = 0;
loop {
current_idx = (current_idx + 1) % MAX_PROCESSES;
next_idx = (next_idx + 1) % MAX_PROCESSES;
attempts += 1;
if let Some(ref mut process) = PROCESSES[current_idx] {
if attempts > MAX_PROCESSES {
// There is no Ready process, all processes are blocked
// log_debug!("All processes blocked or terminated");
return;
}
if let Some(ref mut process) = PROCESSES[next_idx] {
if process.state == ProcessState::Ready {
CURRENT_PROCESS.store(current_idx, Ordering::Release);
log_debug!("Switching to process {} (slot {})", process.id, current_idx);
CURRENT_PROCESS.store(next_idx, Ordering::Release);
// log_debug!("Switching to process {} (slot {})", process.id, next_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;
// log_debug!("Process {} terminated", process.id);
PROCESSES[next_idx] = None;
PROCESS_COUNT.fetch_sub(1, Ordering::Release);
continue;
}
found = true;
break;
return;
}
}
// 遍歷一圈
if current_idx == start_idx {
break;
}
}
if !found {
log_debug!("No runnable process found");
}
}
}
/// 主調度循環
/// Main scheduling loop
pub fn run() -> ! {
log_info!("Starting scheduler main loop");
loop {
Self::schedule();
if PROCESS_COUNT.load(Ordering::Acquire) == 0 {
crate::hal::cpu::cpu_halt();
let count = PROCESS_COUNT.load(Ordering::Acquire);
if count == 0 {
log_info!("No processes remaining, system idle");
hal::cpu::cpu_halt();
}
crate::hal::cpu::cpu_pause(0);
hal::cpu::cpu_pause(500);
}
}
/// 獲取統計信息
pub fn stats() -> (usize, usize) {
let count = PROCESS_COUNT.load(Ordering::Acquire);
let current = CURRENT_PROCESS.load(Ordering::Acquire);
(count, current)
/// Get the current process ID
pub fn current_pid() -> Option<u64> {
let current_idx = CURRENT_PROCESS.load(Ordering::Acquire);
unsafe {
PROCESSES[current_idx].as_ref().map(|p| p.id)
}
}
}

View File

@ -4,7 +4,7 @@ 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};
use crate::{log_debug, log_error, mm};
const PROCESS_STACK_SIZE: usize = 64 * 1024; // 64 KiB
@ -19,12 +19,12 @@ 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)?;
// Allocate virtual addresses from the VMM
let vaddr = mm::vmm::VMM.lock().allocate_pages(page_count)?;
log_debug!("Allocating process stack at {:#x}, {} pages", vaddr.as_u64(), page_count);
// log_debug!("Allocating process stack at {:#x}, {} pages", vaddr.as_u64(), page_count);
// 映射所有頁面
// Map all pages
let mapper = KERNEL_MAPPER.get()?;
let allocator = FRAME_ALLOCATOR.get()?;
@ -32,18 +32,18 @@ impl ProcessStack {
let page_vaddr = vaddr + (i * 4096) as u64;
let page = Page::<Size4KiB>::containing_address(page_vaddr);
// 分配物理頁面
// Allocate physical pages
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);
mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count);
return None;
}
};
// 映射頁面
// Mapping page
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
let mut mapper_guard = mapper.lock();
@ -60,17 +60,17 @@ impl ProcessStack {
drop(mapper_guard);
drop(alloc_guard);
Self::cleanup_mapped_pages(vaddr, i);
crate::mm::vmm::VMM.lock().deallocate_pages(vaddr, page_count);
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());
// log_debug!("Mapped stack page {} at {:#x} -> frame {:#x}",
// i, page_vaddr.as_u64(), frame.start_address().as_u64());
}
// 計算對齊的 stack base 和 limit
// Calculate the aligned stack base and limit
let base_addr = vaddr.as_u64() + PROCESS_STACK_SIZE as u64;
let limit_addr = vaddr.as_u64();
@ -85,7 +85,7 @@ impl ProcessStack {
})
}
/// 清理已映射的頁面
/// Clean up mapped pages
fn cleanup_mapped_pages(vaddr: VirtAddr, count: usize) {
if let Some(mapper) = KERNEL_MAPPER.get() {
let mut mapper_guard = mapper.lock();
@ -94,7 +94,7 @@ impl ProcessStack {
let page_vaddr = vaddr + (i * 4096) as u64;
let page = Page::<Size4KiB>::containing_address(page_vaddr);
// 明確指定類型
// Explicitly specify the type
if let Ok((frame, flush)) = mapper_guard.unmap(page) {
flush.flush();
pmm::deallocate_frame(frame);
@ -106,9 +106,9 @@ impl ProcessStack {
impl Drop for ProcessStack {
fn drop(&mut self) {
log_debug!("Dropping process stack at {:#x}", self.vaddr.as_u64());
// log_debug!("Dropping process stack at {:#x}", self.vaddr.as_u64());
// 取消映射並釋放物理頁面
// Unmap and release physical pages
if let Some(mapper) = KERNEL_MAPPER.get() {
let mut mapper_guard = mapper.lock();
@ -116,17 +116,17 @@ impl Drop for ProcessStack {
let page_vaddr = self.vaddr + (i * 4096) as u64;
let page = Page::<Size4KiB>::containing_address(page_vaddr);
// 明確指定類型參數
// Explicitly specify type parameters
if let Ok((frame, flush)) = mapper_guard.unmap(page) {
flush.flush();
pmm::deallocate_frame(frame);
log_debug!("Unmapped and freed stack page {}", i);
// log_debug!("Unmapped and freed stack page {}", i);
}
}
}
// 釋放虛擬地址
crate::mm::vmm::VMM.lock().deallocate_pages(self.vaddr, self.page_count);
// Release the virtual address
mm::vmm::VMM.lock().deallocate_pages(self.vaddr, self.page_count);
}
}

View File

@ -1,55 +1,67 @@
// kernel/src/shell/mod.rs
use alloc::string::String;
use spin::Mutex;
use crate::{kprint, kprintln, tty, hal::{rtc, timer}};
use crate::{kprint, kprintln, tty};
use crate::process::scheduler::Scheduler;
use crate::tty::device;
pub mod commands;
pub mod math;
static COMMAND_BUFFER: Mutex<String> = Mutex::new(String::new());
/// Shell main loop (blocking)
pub fn shell_main(pid: u64, yielder: &corosensei::Yielder<(), ()>) {
kprintln!();
kprintln!("Shell started (PID: {})", pid);
kprintln!("Type 'help' for available commands");
kprintln!();
pub fn init() {
show_prompt();
}
loop {
// Display the prompt
tty::tty::write_str("cure > ", 0x00FF00);
pub fn show_prompt() {
tty::tty::write_str("cure > ", 0x00FF00);
}
// Blocking read of a line
let line = read_line_blocking(pid, yielder);
pub fn process_keyboard_char(c: char) {
let mut buffer = COMMAND_BUFFER.lock();
// Parse into a string
let cmd = match core::str::from_utf8(&line) {
Ok(s) => s.trim(),
Err(_) => {
kprintln!("Invalid UTF-8 input");
continue;
}
};
if c == '\n' {
let cmd = buffer.clone();
buffer.clear();
kprintln!();
execute_command(&cmd);
show_prompt();
} else if c == '\x08' {
if !buffer.is_empty() {
buffer.pop();
kprint!("\x08 \x08");
// Execute command
if !cmd.is_empty() {
execute_command(cmd);
}
} else if c.is_ascii_graphic() || c == ' ' {
buffer.push(c);
kprint!("{}", c);
}
}
/// Blocking read of a line
fn read_line_blocking(pid: u64, yielder: &corosensei::Yielder<(), ()>) -> alloc::vec::Vec<u8> {
loop {
// Try to read
if let Some(line) = device::read_line(pid) {
return line;
}
// No data, blocking the current process
Scheduler::block_current();
// Yield to the scheduler
yielder.suspend(());
// After being woken up, continue to try to read
}
}
fn execute_command(cmd: &str) {
let mut cmd = cmd.trim();
if cmd.is_empty() {
return;
}
match cmd {
"help" => commands::cmd_help(),
"clear" | "clr" => commands::cmd_clear(),
"time" => commands::cmd_time(),
"uptime" => commands::cmd_uptime(),
"sysinfo" | "sys" => commands::cmd_sysinfo(),
"sysinfo" | "sys" => commands::cmd_sysinfo(),
"meminfo" | "mem" => commands::cmd_meminfo(),
"reboot" => commands::cmd_reboot(),
"halt" | "shutdown" | "poweroff" => commands::cmd_shutdown(),

130
kernel/src/tty/device.rs Normal file
View File

@ -0,0 +1,130 @@
// kernel/src/tty/device
use alloc::{collections::VecDeque, vec::Vec};
use spin::Mutex;
use crate::{kprint, process};
/// TTY device
pub struct Device {
/// Input buffer (complete line)
input_buffer: VecDeque<u8>,
/// The line currently being edited
line_buffer: Vec<u8>,
/// Waiting to read the process ID
waiting_process: Option<u64>,
/// Whether to echo
echo: bool,
}
impl Device {
pub const fn new() -> Self {
Self {
input_buffer: VecDeque::new(),
line_buffer: Vec::new(),
waiting_process: None,
echo: true,
}
}
/// Receive a character from the keyboard (interrupt context call)
pub fn receive_char(&mut self, c: u8) {
match c {
b'\n' => {
// Enter: Complete the current line
self.line_buffer.push(b'\n');
// echo newline
if self.echo {
kprint!("\n");
}
// Move the entire line to the input buffer
self.input_buffer.extend(self.line_buffer.drain(..));
//Wake up the waiting process
if let Some(pid) = self.waiting_process.take() {
process::scheduler::Scheduler::wake_process(pid);
}
}
8 | 127 => { // Backspace
if !self.line_buffer.is_empty() {
self.line_buffer.pop();
if self.echo {
kprint!("\x08 \x08");
}
}
}
3 => { // Ctrl+C
if self.echo {
kprint!("^C\n");
}
self.line_buffer.clear();
// Wake up the process (but return a blank line or a special mark)
if let Some(pid) = self.waiting_process.take() {
crate::process::scheduler::Scheduler::wake_process(pid);
}
}
12 => { // Ctrl+L (clear screen)
if self.echo {
crate::tty::tty::clear(0x000000);
}
}
c if c >= 32 && c < 127 => {
self.line_buffer.push(c);
if self.echo {
kprint!("{}", c as char);
}
}
_ => {
// Ignore other characters
}
}
}
/// Read a line (blocking call)
/// Returning None indicates a blocking wait.
pub fn read_line(&mut self, pid: u64) -> Option<Vec<u8>> {
// If there is a complete line, return immediately
if let Some(pos) = self.input_buffer.iter().position(|&c| c == b'\n') {
let mut line = Vec::new();
for _ in 0..=pos {
if let Some(c) = self.input_buffer.pop_front() {
line.push(c);
}
}
return Some(line);
}
// Otherwise, record the waiting process and return None
self.waiting_process = Some(pid);
None
}
/// Check if there is a complete line to read
pub fn has_line(&self) -> bool {
self.input_buffer.iter().any(|&c| c == b'\n')
}
}
// 全局 TTY 設備
static TTY0: Mutex<Device> = Mutex::new(Device::new());
/// 接收字符(給鍵盤驅動調用)
pub fn receive_char(c: u8) {
TTY0.lock().receive_char(c);
}
/// 讀取一行(給進程調用)
pub fn read_line(pid: u64) -> Option<Vec<u8>> {
TTY0.lock().read_line(pid)
}
/// 檢查是否有數據
pub fn has_input() -> bool {
TTY0.lock().has_line()
}

View File

@ -1,4 +1,5 @@
// src/kernel/tty/mod.rs
pub mod tty;
pub mod font;
pub mod font;
pub mod device;