diff --git a/src/consts.rs b/src/consts.rs index 5567824..f259632 100644 --- a/src/consts.rs +++ b/src/consts.rs @@ -1,5 +1,6 @@ #![allow(dead_code)] pub const MAX_CPU_NUM: usize = 8; +pub const MAX_PROCESS_NUM: usize = 32; // Copy from Redox consts.rs: diff --git a/src/process/mod.rs b/src/process/mod.rs index e141cac..f2e5855 100644 --- a/src/process/mod.rs +++ b/src/process/mod.rs @@ -8,6 +8,7 @@ pub use self::processor::*; mod process; mod processor; +mod scheduler; pub fn init(mut mc: MemoryController) { diff --git a/src/process/process.rs b/src/process/process.rs index 9128433..0e31d33 100644 --- a/src/process/process.rs +++ b/src/process/process.rs @@ -21,7 +21,7 @@ pub struct Process { pub type Pid = usize; pub type ErrorCode = usize; -#[derive(Debug, Eq, PartialEq)] +#[derive(Debug, Clone, Eq, PartialEq)] pub enum Status { Ready, Running, diff --git a/src/process/processor.rs b/src/process/processor.rs index 07828a3..322d830 100644 --- a/src/process/processor.rs +++ b/src/process/processor.rs @@ -1,6 +1,7 @@ use alloc::BTreeMap; use memory::{ActivePageTable, InactivePageTable}; use super::process::*; +use super::scheduler::*; use core::cell::RefCell; use core::fmt::{Debug, Formatter, Error}; use util::{EventHub, GetMut2}; @@ -15,6 +16,8 @@ pub struct Processor { kernel_page_table: Option, /// Choose what on next schedule ? next: Option, + // WARNING: if MAX_PROCESS_NUM is too large, will cause stack overflow + scheduler: RRScheduler, } impl Processor { @@ -22,19 +25,16 @@ impl Processor { Processor { procs: BTreeMap::::new(), current_pid: 0, - event_hub: { - let mut e = EventHub::new(); - e.push(10, Event::Schedule); - e - }, + event_hub: EventHub::new(), kernel_page_table: None, next: None, + scheduler: RRScheduler::new(100), } } pub fn set_reschedule(&mut self) { let pid = self.current_pid; - self.get_mut(pid).status = Status::Ready; + self.set_status(pid, Status::Ready); } fn alloc_pid(&self) -> Pid { @@ -49,9 +49,25 @@ impl Processor { return next; } + fn set_status(&mut self, pid: Pid, status: Status) { + let status0 = self.get(pid).status.clone(); + match (&status0, &status) { + (&Status::Ready, &Status::Ready) => return, + (&Status::Ready, _) => self.scheduler.remove(pid), + (_, &Status::Ready) => self.scheduler.insert(pid), + _ => {} + } + trace!("Processor: process {} {:?} -> {:?}", pid, status0, status); + self.get_mut(pid).status = status; + } + /// Called by timer. /// Handle events. pub fn tick(&mut self) { + let current_pid = self.current_pid; + if self.scheduler.tick(current_pid) { + self.set_reschedule(); + } self.event_hub.tick(); while let Some(event) = self.event_hub.pop() { debug!("Processor: event {:?}", event); @@ -61,7 +77,7 @@ impl Processor { self.set_reschedule(); }, Event::Wakeup(pid) => { - self.get_mut(pid).status = Status::Ready; + self.set_status(pid, Status::Ready); self.set_reschedule(); self.next = Some(pid); }, @@ -76,6 +92,9 @@ impl Processor { pub fn add(&mut self, mut process: Process) -> Pid { let pid = self.alloc_pid(); process.pid = pid; + if process.status == Status::Ready { + self.scheduler.insert(pid); + } self.procs.insert(pid, process); pid } @@ -86,17 +105,10 @@ impl Processor { if self.current().status == Status::Running { return; } - let pid = self.next.take().unwrap_or_else(|| self.find_next()); + let pid = self.next.take().unwrap_or_else(|| self.scheduler.select().unwrap()); self.switch_to(pid); } - fn find_next(&self) -> Pid { - *self.procs.keys() - .find(|&&i| i > self.current_pid - && self.get(i).status == Status::Ready) - .unwrap_or(self.procs.keys().next().unwrap()) - } - /// Switch process to `pid`, switch page table if necessary. /// Store `rsp` and point it to target kernel stack. /// The current status must be set before, and not be `Running`. @@ -105,6 +117,9 @@ impl Processor { let pid0 = self.current_pid; if pid == self.current_pid { + if self.current().status != Status::Running { + self.set_status(pid, Status::Running); + } return; } self.current_pid = pid; @@ -114,6 +129,7 @@ impl Processor { assert_ne!(from.status, Status::Running); assert_eq!(to.status, Status::Ready); to.status = Status::Running; + self.scheduler.remove(pid); // switch page table if from.is_user || to.is_user { @@ -132,8 +148,10 @@ impl Processor { info!("Processor: switch from {} to {}\n rsp: ??? -> {:#x}", pid0, pid, to.rsp); unsafe { + use core::mem::forget; super::PROCESSOR.try().unwrap().force_unlock(); switch(&mut from.rsp, to.rsp); + forget(super::PROCESSOR.try().unwrap().lock()); } } @@ -156,16 +174,16 @@ impl Processor { pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) { info!("Processor: {} exit, code: {}", pid, error_code); - self.get_mut(pid).status = Status::Exited(error_code); + self.set_status(pid, Status::Exited(error_code)); if let Some(waiter) = self.find_waiter(pid) { info!(" then wakeup {}", waiter); - self.get_mut(waiter).status = Status::Ready; + self.set_status(waiter, Status::Ready); self.switch_to(waiter); // yield } } pub fn sleep(&mut self, pid: Pid, time: usize) { - self.get_mut(pid).status = Status::Sleeping; + self.set_status(pid, Status::Sleeping); self.event_hub.push(time, Event::Wakeup(pid)); } @@ -178,7 +196,7 @@ impl Processor { let pid = self.try_wait(pid).unwrap_or_else(|| { info!("Processor: {} wait for {}", self.current_pid, pid); let current_pid = self.current_pid; - self.get_mut(current_pid).status = Status::Waiting(pid); + self.set_status(current_pid, Status::Waiting(pid)); self.schedule(); // yield self.try_wait(pid).unwrap() }); diff --git a/src/process/scheduler.rs b/src/process/scheduler.rs new file mode 100644 index 0000000..4e420c4 --- /dev/null +++ b/src/process/scheduler.rs @@ -0,0 +1,93 @@ +use super::*; +use consts::MAX_PROCESS_NUM; + +/// +pub trait Scheduler { + fn insert(&mut self, pid: Pid); + fn remove(&mut self, pid: Pid); + fn select(&self) -> Option; + fn tick(&mut self, current: Pid) -> bool; // need reschedule? +} + +pub struct RRScheduler { + max_time_slice: usize, + infos: [RRProcInfo; MAX_PROCESS_NUM], +} + +#[derive(Debug, Default, Copy, Clone)] +struct RRProcInfo { + present: bool, + rest_slice: usize, + prev: Pid, + next: Pid, +} + +impl Scheduler for RRScheduler { + fn insert(&mut self, pid: Pid) { + let pid = pid + 1; + { + let info = &mut self.infos[pid]; + assert!(!info.present); + info.present = true; + if info.rest_slice == 0 { + info.rest_slice = self.max_time_slice; + } + } + self._list_add_before(pid, 0); + trace!("RRScheduler: insert {}", pid - 1); + } + + fn remove(&mut self, pid: Pid) { + let pid = pid + 1; + assert!(self.infos[pid].present); + self.infos[pid].present = false; + self._list_remove(pid); + trace!("RRScheduler: remove {}", pid - 1); + } + + fn select(&self) -> Option { + let ret = match self.infos[0].next { + 0 => None, + i => Some(i - 1), + }; + trace!("RRScheduler: select {:?}", ret); + ret + } + + fn tick(&mut self, current: Pid) -> bool { + let current = current + 1; + assert!(!self.infos[current].present); + + let rest = &mut self.infos[current].rest_slice; + if *rest > 0 { + *rest -= 1; + } else { + warn!("current process rest_slice = 0, need reschedule") + } + *rest == 0 + } +} + +impl RRScheduler { + pub fn new(max_time_slice: usize) -> Self { + RRScheduler { + max_time_slice, + infos: [RRProcInfo::default(); MAX_PROCESS_NUM], + } + } + fn _list_add_before(&mut self, i: Pid, at: Pid) { + let prev = self.infos[at].prev; + self.infos[i].next = at; + self.infos[i].prev = prev; + self.infos[prev].next = i; + self.infos[at].prev = i; + } + fn _list_remove(&mut self, i: Pid) { + let next = self.infos[i].next; + let prev = self.infos[i].prev; + self.infos[next].prev = prev; + self.infos[prev].next = next; + self.infos[i].next = 0; + self.infos[i].prev = 0; + } +} \ No newline at end of file