|
|
|
@ -1,273 +1,208 @@
|
|
|
|
|
use alloc::{boxed::Box, collections::BTreeMap};
|
|
|
|
|
use scheduler::*;
|
|
|
|
|
use event_hub::EventHub;
|
|
|
|
|
use util::GetMut2;
|
|
|
|
|
use core::fmt::Debug;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub struct Process<T> {
|
|
|
|
|
pid: Pid,
|
|
|
|
|
parent: Pid,
|
|
|
|
|
status: Status,
|
|
|
|
|
context: T,
|
|
|
|
|
use alloc::boxed::Box;
|
|
|
|
|
use alloc::vec::Vec;
|
|
|
|
|
use alloc::sync::Arc;
|
|
|
|
|
use spin::Mutex;
|
|
|
|
|
use scheduler::Scheduler;
|
|
|
|
|
use core::cell::UnsafeCell;
|
|
|
|
|
|
|
|
|
|
/// Process executor
|
|
|
|
|
///
|
|
|
|
|
/// Per-CPU struct. Defined at global.
|
|
|
|
|
/// Only accessed by associated CPU with interrupt disabled.
|
|
|
|
|
#[derive(Default)]
|
|
|
|
|
pub struct Processor {
|
|
|
|
|
inner: UnsafeCell<Option<ProcessorInner>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub type Pid = usize;
|
|
|
|
|
pub type ErrorCode = usize;
|
|
|
|
|
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
|
|
|
pub enum Status {
|
|
|
|
|
Ready,
|
|
|
|
|
Running,
|
|
|
|
|
Waiting(Pid),
|
|
|
|
|
Sleeping,
|
|
|
|
|
Exited(ErrorCode),
|
|
|
|
|
}
|
|
|
|
|
unsafe impl Sync for Processor {}
|
|
|
|
|
|
|
|
|
|
pub trait Context: Debug {
|
|
|
|
|
unsafe fn switch(&mut self, target: &mut Self);
|
|
|
|
|
fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Self;
|
|
|
|
|
struct ProcessorInner {
|
|
|
|
|
id: usize,
|
|
|
|
|
proc: Option<(Pid, Box<Context>)>,
|
|
|
|
|
loop_context: Box<Context>,
|
|
|
|
|
manager: Arc<ProcessManager>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct Processor_<T: Context, S: Scheduler> {
|
|
|
|
|
procs: BTreeMap<Pid, Process<T>>,
|
|
|
|
|
current_pid: Pid,
|
|
|
|
|
event_hub: EventHub<Event>,
|
|
|
|
|
// WARNING: if MAX_PROCESS_NUM is too large, will cause stack overflow
|
|
|
|
|
scheduler: S,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T> Process<T> {
|
|
|
|
|
fn exit_code(&self) -> Option<ErrorCode> {
|
|
|
|
|
match self.status {
|
|
|
|
|
Status::Exited(code) => Some(code),
|
|
|
|
|
_ => None,
|
|
|
|
|
}
|
|
|
|
|
impl Processor {
|
|
|
|
|
pub const fn new() -> Self {
|
|
|
|
|
Processor { inner: UnsafeCell::new(None) }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: 除schedule()外的其它函数,应该只设置进程状态,不应调用schedule
|
|
|
|
|
impl<T: Context, S: Scheduler> Processor_<T, S> {
|
|
|
|
|
pub fn new(init_context: T, scheduler: S) -> Self {
|
|
|
|
|
let init_proc = Process {
|
|
|
|
|
pid: 0,
|
|
|
|
|
parent: 0,
|
|
|
|
|
status: Status::Running,
|
|
|
|
|
context: init_context,
|
|
|
|
|
};
|
|
|
|
|
Processor_ {
|
|
|
|
|
procs: {
|
|
|
|
|
let mut map = BTreeMap::<Pid, Process<T>>::new();
|
|
|
|
|
map.insert(0, init_proc);
|
|
|
|
|
map
|
|
|
|
|
},
|
|
|
|
|
current_pid: 0,
|
|
|
|
|
event_hub: EventHub::new(),
|
|
|
|
|
scheduler,
|
|
|
|
|
pub unsafe fn init(&self, id: usize, context: Box<Context>, manager: Arc<ProcessManager>) {
|
|
|
|
|
unsafe {
|
|
|
|
|
*self.inner.get() = Some(ProcessorInner {
|
|
|
|
|
id,
|
|
|
|
|
proc: None,
|
|
|
|
|
loop_context: context,
|
|
|
|
|
manager,
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn set_priority(&mut self, priority: u8) {
|
|
|
|
|
self.scheduler.set_priority(self.current_pid, priority);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn yield_now(&mut self) {
|
|
|
|
|
let pid = self.current_pid;
|
|
|
|
|
self.set_status(pid, Status::Ready);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn alloc_pid(&self) -> Pid {
|
|
|
|
|
let mut next: Pid = 0;
|
|
|
|
|
for &i in self.procs.keys() {
|
|
|
|
|
if i != next {
|
|
|
|
|
return next;
|
|
|
|
|
} else {
|
|
|
|
|
next = i + 1;
|
|
|
|
|
fn inner(&self) -> &mut ProcessorInner {
|
|
|
|
|
unsafe { &mut *self.inner.get() }.as_mut()
|
|
|
|
|
.expect("Processor is not initialized")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Begin running processes after CPU setup.
|
|
|
|
|
///
|
|
|
|
|
/// This function never returns. It loops, doing:
|
|
|
|
|
/// - choose a process to run
|
|
|
|
|
/// - switch to start running that process
|
|
|
|
|
/// - eventually that process transfers control
|
|
|
|
|
/// via switch back to the scheduler.
|
|
|
|
|
pub fn run(&self) -> ! {
|
|
|
|
|
let inner = self.inner();
|
|
|
|
|
loop {
|
|
|
|
|
let proc = inner.manager.run(inner.id);
|
|
|
|
|
trace!("CPU{} begin running process {}", inner.id, proc.0);
|
|
|
|
|
inner.proc = Some(proc);
|
|
|
|
|
unsafe {
|
|
|
|
|
inner.loop_context.switch_to(&mut *inner.proc.as_mut().unwrap().1);
|
|
|
|
|
}
|
|
|
|
|
let (pid, context) = inner.proc.take().unwrap();
|
|
|
|
|
trace!("CPU{} stop running process {}", inner.id, pid);
|
|
|
|
|
inner.manager.stop(pid, context);
|
|
|
|
|
}
|
|
|
|
|
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),
|
|
|
|
|
_ => {}
|
|
|
|
|
/// Called by process running on this Processor.
|
|
|
|
|
/// Yield and reschedule.
|
|
|
|
|
pub fn yield_now(&self) {
|
|
|
|
|
let inner = self.inner();
|
|
|
|
|
unsafe {
|
|
|
|
|
inner.proc.as_mut().unwrap().1.switch_to(&mut *inner.loop_context);
|
|
|
|
|
}
|
|
|
|
|
trace!("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.yield_now();
|
|
|
|
|
}
|
|
|
|
|
self.event_hub.tick();
|
|
|
|
|
while let Some(event) = self.event_hub.pop() {
|
|
|
|
|
debug!("event {:?}", event);
|
|
|
|
|
match event {
|
|
|
|
|
Event::Schedule => {
|
|
|
|
|
self.event_hub.push(10, Event::Schedule);
|
|
|
|
|
self.yield_now();
|
|
|
|
|
},
|
|
|
|
|
Event::Wakeup(pid) => {
|
|
|
|
|
self.set_status(pid, Status::Ready);
|
|
|
|
|
self.yield_now();
|
|
|
|
|
self.scheduler.move_to_head(pid);
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
pub fn pid(&self) -> Pid {
|
|
|
|
|
self.inner().proc.as_ref().unwrap().0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn get_time(&self) -> usize {
|
|
|
|
|
self.event_hub.get_time()
|
|
|
|
|
pub fn manager(&self) -> &ProcessManager {
|
|
|
|
|
&*self.inner().manager
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn add(&mut self, context: T) -> Pid {
|
|
|
|
|
let pid = self.alloc_pid();
|
|
|
|
|
let process = Process {
|
|
|
|
|
pid,
|
|
|
|
|
parent: self.current_pid,
|
|
|
|
|
status: Status::Ready,
|
|
|
|
|
context,
|
|
|
|
|
};
|
|
|
|
|
self.scheduler.insert(pid);
|
|
|
|
|
self.procs.insert(pid, process);
|
|
|
|
|
pid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Called every interrupt end
|
|
|
|
|
/// Do schedule ONLY IF current status != Running
|
|
|
|
|
pub fn schedule(&mut self) {
|
|
|
|
|
if self.get(self.current_pid).status == Status::Running {
|
|
|
|
|
return;
|
|
|
|
|
pub fn tick(&self) {
|
|
|
|
|
let need_reschedule = self.manager().tick(self.pid());
|
|
|
|
|
if need_reschedule {
|
|
|
|
|
self.yield_now();
|
|
|
|
|
}
|
|
|
|
|
let pid = self.scheduler.select().unwrap();
|
|
|
|
|
self.switch_to(pid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Switch to process `pid`.
|
|
|
|
|
/// The current status must be set before, and not be `Running`.
|
|
|
|
|
/// The target status must be `Ready`.
|
|
|
|
|
fn switch_to(&mut self, pid: Pid) {
|
|
|
|
|
// for debug print
|
|
|
|
|
let pid0 = self.current_pid;
|
|
|
|
|
|
|
|
|
|
if pid == self.current_pid {
|
|
|
|
|
if self.get(self.current_pid).status != Status::Running {
|
|
|
|
|
self.set_status(pid, Status::Running);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
self.current_pid = pid;
|
|
|
|
|
struct Process {
|
|
|
|
|
id: Pid,
|
|
|
|
|
status: Status,
|
|
|
|
|
status_after_stop: Status,
|
|
|
|
|
context: Option<Box<Context>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let (from, to) = self.procs.get_mut2(pid0, pid);
|
|
|
|
|
type Pid = usize;
|
|
|
|
|
type ExitCode = usize;
|
|
|
|
|
const MAX_PROC_NUM: usize = 32;
|
|
|
|
|
|
|
|
|
|
assert_ne!(from.status, Status::Running);
|
|
|
|
|
assert_eq!(to.status, Status::Ready);
|
|
|
|
|
to.status = Status::Running;
|
|
|
|
|
self.scheduler.remove(pid);
|
|
|
|
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
|
|
|
pub enum Status {
|
|
|
|
|
Ready,
|
|
|
|
|
Running(usize),
|
|
|
|
|
Waiting(Pid),
|
|
|
|
|
Sleeping,
|
|
|
|
|
Exited(ExitCode),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info!("switch from {} to {} {:x?}", pid0, pid, to.context);
|
|
|
|
|
unsafe { from.context.switch(&mut to.context); }
|
|
|
|
|
}
|
|
|
|
|
pub trait Context {
|
|
|
|
|
unsafe fn switch_to(&mut self, target: &mut Context);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn get(&self, pid: Pid) -> &Process<T> {
|
|
|
|
|
self.procs.get(&pid).unwrap()
|
|
|
|
|
}
|
|
|
|
|
fn get_mut(&mut self, pid: Pid) -> &mut Process<T> {
|
|
|
|
|
self.procs.get_mut(&pid).unwrap()
|
|
|
|
|
}
|
|
|
|
|
pub fn current_context(&self) -> &T {
|
|
|
|
|
&self.get(self.current_pid).context
|
|
|
|
|
}
|
|
|
|
|
pub fn current_pid(&self) -> Pid {
|
|
|
|
|
self.current_pid
|
|
|
|
|
}
|
|
|
|
|
pub struct ProcessManager {
|
|
|
|
|
procs: [Mutex<Option<Process>>; MAX_PROC_NUM],
|
|
|
|
|
scheduler: Mutex<Box<Scheduler>>,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn kill(&mut self, pid: Pid) {
|
|
|
|
|
self.exit(pid, 0x1000); // TODO: error code for killed
|
|
|
|
|
}
|
|
|
|
|
impl ProcessManager {
|
|
|
|
|
|
|
|
|
|
pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) {
|
|
|
|
|
info!("{} exit, code: {}", pid, error_code);
|
|
|
|
|
self.set_status(pid, Status::Exited(error_code));
|
|
|
|
|
if let Some(waiter) = self.find_waiter(pid) {
|
|
|
|
|
info!(" then wakeup {}", waiter);
|
|
|
|
|
self.set_status(waiter, Status::Ready);
|
|
|
|
|
self.scheduler.move_to_head(waiter);
|
|
|
|
|
pub fn new(scheduler: Box<Scheduler>) -> Self {
|
|
|
|
|
ProcessManager {
|
|
|
|
|
procs: Default::default(),
|
|
|
|
|
scheduler: Mutex::new(scheduler),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn sleep(&mut self, pid: Pid, time: usize) {
|
|
|
|
|
self.set_status(pid, Status::Sleeping);
|
|
|
|
|
self.event_hub.push(time, Event::Wakeup(pid));
|
|
|
|
|
}
|
|
|
|
|
pub fn sleep_(&mut self, pid: Pid) {
|
|
|
|
|
self.set_status(pid, Status::Sleeping);
|
|
|
|
|
}
|
|
|
|
|
pub fn wakeup_(&mut self, pid: Pid) {
|
|
|
|
|
self.set_status(pid, Status::Ready);
|
|
|
|
|
fn alloc_pid(&self) -> Pid {
|
|
|
|
|
for i in 0..MAX_PROC_NUM {
|
|
|
|
|
if self.procs[i].lock().is_none() {
|
|
|
|
|
return i;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
panic!("Process number exceeded");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Let current process wait for another
|
|
|
|
|
pub fn current_wait_for(&mut self, pid: Pid) -> WaitResult {
|
|
|
|
|
info!("current {} wait for {:?}", self.current_pid, pid);
|
|
|
|
|
if self.procs.values().filter(|&p| p.parent == self.current_pid).next().is_none() {
|
|
|
|
|
return WaitResult::NotExist;
|
|
|
|
|
}
|
|
|
|
|
let pid = self.try_wait(pid).unwrap_or_else(|| {
|
|
|
|
|
let current_pid = self.current_pid;
|
|
|
|
|
self.set_status(current_pid, Status::Waiting(pid));
|
|
|
|
|
self.schedule(); // yield
|
|
|
|
|
self.try_wait(pid).unwrap()
|
|
|
|
|
/// Add a new process
|
|
|
|
|
pub fn add(&self, context: Box<Context>) -> Pid {
|
|
|
|
|
let pid = self.alloc_pid();
|
|
|
|
|
// TODO: check parent
|
|
|
|
|
*self.procs[pid].lock() = Some(Process {
|
|
|
|
|
id: pid,
|
|
|
|
|
status: Status::Ready,
|
|
|
|
|
status_after_stop: Status::Ready,
|
|
|
|
|
context: Some(context),
|
|
|
|
|
});
|
|
|
|
|
let exit_code = self.get(pid).exit_code().unwrap();
|
|
|
|
|
info!("{} wait end and remove {}", self.current_pid, pid);
|
|
|
|
|
self.procs.remove(&pid);
|
|
|
|
|
WaitResult::Ok(pid, exit_code)
|
|
|
|
|
self.scheduler.lock().insert(pid);
|
|
|
|
|
pid
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Try to find a exited wait target
|
|
|
|
|
fn try_wait(&mut self, pid: Pid) -> Option<Pid> {
|
|
|
|
|
match pid {
|
|
|
|
|
0 => self.procs.values()
|
|
|
|
|
.find(|&p| p.parent == self.current_pid && p.exit_code().is_some())
|
|
|
|
|
.map(|p| p.pid),
|
|
|
|
|
_ => self.get(pid).exit_code().map(|_| pid),
|
|
|
|
|
/// Make process `pid` time slice -= 1.
|
|
|
|
|
/// Return true if time slice == 0.
|
|
|
|
|
/// Called by timer interrupt handler.
|
|
|
|
|
pub fn tick(&self, pid: Pid) -> bool {
|
|
|
|
|
self.scheduler.lock().tick(pid)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Called by Processor to get a process to run.
|
|
|
|
|
/// The manager first mark it `Running`,
|
|
|
|
|
/// then take out and return its Context.
|
|
|
|
|
pub fn run(&self, cpu_id: usize) -> (Pid, Box<Context>) {
|
|
|
|
|
let mut scheduler = self.scheduler.lock();
|
|
|
|
|
let pid = scheduler.select()
|
|
|
|
|
.expect("failed to select a runnable process");
|
|
|
|
|
scheduler.remove(pid);
|
|
|
|
|
let mut proc_lock = self.procs[pid].lock();
|
|
|
|
|
let mut proc = proc_lock.as_mut().unwrap();
|
|
|
|
|
proc.status = Status::Running(cpu_id);
|
|
|
|
|
(pid, proc.context.take().unwrap())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Called by Processor to finish running a process
|
|
|
|
|
/// and give its context back.
|
|
|
|
|
pub fn stop(&self, pid: Pid, context: Box<Context>) {
|
|
|
|
|
let mut proc_lock = self.procs[pid].lock();
|
|
|
|
|
let mut proc = proc_lock.as_mut().unwrap();
|
|
|
|
|
proc.status = proc.status_after_stop.clone();
|
|
|
|
|
proc.status_after_stop = Status::Ready;
|
|
|
|
|
proc.context = Some(context);
|
|
|
|
|
if proc.status == Status::Ready {
|
|
|
|
|
self.scheduler.lock().insert(pid);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn find_waiter(&self, pid: Pid) -> Option<Pid> {
|
|
|
|
|
self.procs.values().find(|&p| {
|
|
|
|
|
p.status == Status::Waiting(pid) ||
|
|
|
|
|
(p.status == Status::Waiting(0) && self.get(pid).parent == p.pid)
|
|
|
|
|
}).map(|ref p| p.pid)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
pub enum WaitResult {
|
|
|
|
|
/// The target process is exited with `ErrorCode`.
|
|
|
|
|
Ok(Pid, ErrorCode),
|
|
|
|
|
/// The target process is not exist.
|
|
|
|
|
NotExist,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
|
enum Event {
|
|
|
|
|
Schedule,
|
|
|
|
|
Wakeup(Pid),
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl<T: Context> GetMut2<Pid> for BTreeMap<Pid, Process<T>> {
|
|
|
|
|
type Output = Process<T>;
|
|
|
|
|
fn get_mut(&mut self, id: Pid) -> &mut Process<T> {
|
|
|
|
|
self.get_mut(&id).unwrap()
|
|
|
|
|
/// Switch the status of a process.
|
|
|
|
|
/// Insert/Remove it to/from scheduler if necessary.
|
|
|
|
|
pub fn set_status(&self, pid: Pid, status: Status) {
|
|
|
|
|
let mut scheduler = self.scheduler.lock();
|
|
|
|
|
let mut proc_lock = self.procs[pid].lock();
|
|
|
|
|
let mut proc = proc_lock.as_mut().unwrap();
|
|
|
|
|
match (&proc.status, &status) {
|
|
|
|
|
(Status::Ready, Status::Ready) => return,
|
|
|
|
|
(Status::Ready, _) => scheduler.remove(pid),
|
|
|
|
|
(Status::Running(_), _) => {},
|
|
|
|
|
(_, Status::Ready) => scheduler.insert(pid),
|
|
|
|
|
_ => {}
|
|
|
|
|
}
|
|
|
|
|
trace!("process {} {:?} -> {:?}", pid, proc.status, status);
|
|
|
|
|
match proc.status {
|
|
|
|
|
Status::Running(_) => proc.status_after_stop = status,
|
|
|
|
|
_ => proc.status = status,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|