feat: Complete the basic arranger
This commit is contained in:
parent
fdaeda079c
commit
02d4d17119
@ -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() {
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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()
|
||||
}
|
||||
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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
130
kernel/src/tty/device.rs
Normal 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()
|
||||
}
|
||||
@ -1,4 +1,5 @@
|
||||
// src/kernel/tty/mod.rs
|
||||
|
||||
pub mod tty;
|
||||
pub mod font;
|
||||
pub mod font;
|
||||
pub mod device;
|
||||
Loading…
x
Reference in New Issue
Block a user