Sync computers

This commit is contained in:
2026-03-01 15:41:36 +01:00
parent 783c76252a
commit 392af94345
16 changed files with 479 additions and 315 deletions

View File

@@ -3,50 +3,29 @@
//!
//! This module exposes the global process table, the scheduler initialization
//! and a simple round-robin scheduler used by the kernel.
use core::{arch::riscv64::wfi, array, cell::LazyCell, time::Duration};
use core::{arch::riscv64::wfi, cell::LazyCell, ops::Bound};
use alloc::{boxed::Box, string::String};
use hashbrown::HashMap;
use alloc::{boxed::Box, collections::BTreeMap};
use log::info;
use crate::{
process::{create_process, ExecutionContext, Process, ProcessState},
process::{ExecutionContext, Process, ProcessState},
sync::Mutex,
time,
};
/// Maximum number of simultaneous processes supported by the kernel.
pub const PROCESS_COUNT: usize = 16;
#[derive(Debug)]
pub struct Scheduler {
pub next_pid: u64,
pub active_pid: u64,
pub process_table: BTreeMap<u64, Box<Process>>,
}
/// Currently active PID.
///
/// Updated by the scheduler when switching contexts.
pub static mut ACTIVE_PID: usize = 0;
/// Global process table stored in a lazily-initialized container.
///
/// Each entry represents a process slot which may be `Dead`, `Activable`,
/// `Active` or `Asleep`.
pub static mut PROCESS_TABLE: LazyCell<[Process; PROCESS_COUNT]> = LazyCell::new(|| {
array::from_fn(|_| Process {
pid: -1,
name: String::new(),
state: ProcessState::Dead,
wake_time: Duration::new(0, 0),
ctx: ExecutionContext {
ra: core::ptr::null(),
sp: core::ptr::null(),
gp: 0,
tp: 0,
a: [0; _],
t: [0; _],
s: [0; _],
mepc: core::ptr::null(),
mstatus: 0,
},
stack: [0; _],
entry: None,
fd_table: HashMap::new()
})
});
pub static SCHEDULER: Mutex<LazyCell<Scheduler>> = Mutex::new(LazyCell::new(|| Scheduler {
next_pid: 0,
active_pid: 0,
process_table: BTreeMap::new(),
}));
/// Idle loop executed when there is no runnable process.
///
@@ -61,53 +40,60 @@ pub fn idle() {
}
}
/// Initialize the scheduler and create the idle process.
///
/// Marks all process slots as `Dead` then creates the idle process and sets
/// it as the active process.
pub fn scheduler_init() {
info!("scheduler init");
for pid in 0..PROCESS_COUNT {
impl Scheduler {
/// Initialize the scheduler and create the idle process.
///
/// Marks all process slots as `Dead` then creates the idle process and sets
/// it as the active process.
pub fn init(&mut self) {
info!("scheduler init");
self.create_process(Box::new(idle), "idle");
self.process_table.get_mut(&0).unwrap().state = ProcessState::Active;
}
/// Round-robin scheduler used to select the next runnable process.
///
/// Saves the provided interrupt context into the previous process slot and
/// updates `ACTIVE_PID` to point to the chosen process. This function does
/// not return but instead updates `interrupt_state` to the context of the
/// next process to run.
pub fn schedule(&mut self, interrupt_state: &mut *mut ExecutionContext) {
// info!("scheduler");
unsafe {
PROCESS_TABLE[pid].state = ProcessState::Dead;
let prev_pid = self.active_pid;
if let Some(previous_process) = self.process_table.get_mut(&prev_pid) {
previous_process.ctx = **interrupt_state;
if previous_process.state == ProcessState::Active {
previous_process.state = ProcessState::Activable;
}
}
let mut current_process_iter = self
.process_table
.range_mut((Bound::Excluded(prev_pid), Bound::Unbounded));
self.active_pid = loop {
if let Some((pid, current_process)) = current_process_iter.next() {
if current_process.state == ProcessState::Asleep
&& time::elapsed_time_since_startup() > current_process.wake_time
{
current_process.state = ProcessState::Activable;
}
if current_process.state == ProcessState::Activable {
current_process.state = ProcessState::Active;
*interrupt_state = &raw mut current_process.ctx;
break *pid;
};
} else {
current_process_iter = self
.process_table
.range_mut((Bound::Unbounded, Bound::Included(prev_pid)))
}
};
}
}
create_process(Box::new(idle), "idle");
unsafe {
PROCESS_TABLE[0].state = ProcessState::Active;
}
}
/// Round-robin scheduler used to select the next runnable process.
///
/// Saves the provided interrupt context into the previous process slot and
/// updates `ACTIVE_PID` to point to the chosen process. This function does
/// not return but instead updates `interrupt_state` to the context of the
/// next process to run.
pub fn scheduler_without_ret(interrupt_state: &mut *mut ExecutionContext) {
// info!("scheduler");
unsafe {
let prev_pid = ACTIVE_PID;
PROCESS_TABLE[prev_pid].ctx = **interrupt_state;
if PROCESS_TABLE[prev_pid].state == ProcessState::Active {
PROCESS_TABLE[prev_pid].state = ProcessState::Activable;
}
loop {
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Asleep
&& time::elapsed_time_since_startup() > PROCESS_TABLE[ACTIVE_PID].wake_time
{
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Activable;
}
ACTIVE_PID = (ACTIVE_PID + 1) % PROCESS_COUNT;
if PROCESS_TABLE[ACTIVE_PID].state == ProcessState::Activable {
break;
}
}
PROCESS_TABLE[ACTIVE_PID].state = ProcessState::Active;
*interrupt_state = &raw mut PROCESS_TABLE[ACTIVE_PID].ctx
pub fn get_current_process(&mut self) -> &mut Process {
self.process_table.get_mut(&self.active_pid).unwrap()
}
}