# Conflicts: # kernel/src/process/mod.rs # kernel/src/process/structs.rs # kernel/src/syscall.rsmaster
commit
7927c7c168
@ -1,93 +0,0 @@
|
|||||||
use alloc::collections::VecDeque;
|
|
||||||
use core::cmp::{Ordering, PartialOrd};
|
|
||||||
|
|
||||||
type Time = usize;
|
|
||||||
|
|
||||||
struct Timer<T> {
|
|
||||||
time: Time,
|
|
||||||
data: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> PartialEq for Timer<T> {
|
|
||||||
fn eq(&self, other: &Self) -> bool {
|
|
||||||
self.time.eq(&other.time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Eq for Timer<T> {}
|
|
||||||
|
|
||||||
impl<T> PartialOrd for Timer<T> {
|
|
||||||
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
|
|
||||||
other.time.partial_cmp(&self.time)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> Ord for Timer<T> {
|
|
||||||
fn cmp(&self, other: &Self) -> Ordering {
|
|
||||||
self.partial_cmp(&other).unwrap()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EventHub<T> {
|
|
||||||
tick: Time,
|
|
||||||
timers: VecDeque<Timer<T>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: PartialEq> EventHub<T> {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
EventHub {
|
|
||||||
tick: 0,
|
|
||||||
timers: VecDeque::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn tick(&mut self) {
|
|
||||||
self.tick += 1;
|
|
||||||
}
|
|
||||||
pub fn pop(&mut self) -> Option<T> {
|
|
||||||
match self.timers.front() {
|
|
||||||
None => return None,
|
|
||||||
Some(timer) if timer.time != self.tick => return None,
|
|
||||||
_ => {}
|
|
||||||
};
|
|
||||||
self.timers.pop_front().map(|t| t.data)
|
|
||||||
}
|
|
||||||
pub fn push(&mut self, time_after: Time, data: T) {
|
|
||||||
//debug!("{:?} {:?}", self.tick, time_after);
|
|
||||||
let time = self.tick + time_after;
|
|
||||||
let timer = Timer { time, data };
|
|
||||||
let mut it = self.timers.iter();
|
|
||||||
let mut i : usize = 0;
|
|
||||||
loop {
|
|
||||||
let now = it.next();
|
|
||||||
if now == None {
|
|
||||||
break
|
|
||||||
};
|
|
||||||
if now.unwrap() < &timer {
|
|
||||||
break
|
|
||||||
};
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
self.timers.insert(i, timer);
|
|
||||||
}
|
|
||||||
#[allow(dead_code)]
|
|
||||||
pub fn get_time(&self) -> Time {
|
|
||||||
self.tick
|
|
||||||
}
|
|
||||||
pub fn remove(&mut self, data: T) {
|
|
||||||
let mut it = self.timers.iter();
|
|
||||||
let mut i : usize = 0;
|
|
||||||
loop {
|
|
||||||
let now = it.next();
|
|
||||||
if now == None {
|
|
||||||
break
|
|
||||||
};
|
|
||||||
if now.map(|t| &t.data).unwrap() == &data {
|
|
||||||
break
|
|
||||||
};
|
|
||||||
i += 1;
|
|
||||||
}
|
|
||||||
if i < self.timers.len() {
|
|
||||||
self.timers.remove(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,222 +0,0 @@
|
|||||||
use alloc::boxed::Box;
|
|
||||||
use alloc::vec::Vec;
|
|
||||||
use spin::Mutex;
|
|
||||||
use log::*;
|
|
||||||
use crate::scheduler::Scheduler;
|
|
||||||
use crate::event_hub::EventHub;
|
|
||||||
|
|
||||||
struct Process {
|
|
||||||
#[allow(dead_code)]
|
|
||||||
id: Pid,
|
|
||||||
status: Status,
|
|
||||||
status_after_stop: Status,
|
|
||||||
context: Option<Box<Context>>,
|
|
||||||
parent: Pid,
|
|
||||||
children: Vec<Pid>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Pid = usize;
|
|
||||||
type ExitCode = usize;
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
|
||||||
pub enum Status {
|
|
||||||
Ready,
|
|
||||||
Running(usize),
|
|
||||||
Sleeping,
|
|
||||||
Waiting(Pid),
|
|
||||||
/// aka ZOMBIE. Its context was dropped.
|
|
||||||
Exited(ExitCode),
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Eq, PartialEq)]
|
|
||||||
enum Event {
|
|
||||||
Wakeup(Pid),
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Context {
|
|
||||||
unsafe fn switch_to(&mut self, target: &mut Context);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ProcessManager {
|
|
||||||
procs: Vec<Mutex<Option<Process>>>,
|
|
||||||
scheduler: Mutex<Box<Scheduler>>,
|
|
||||||
event_hub: Mutex<EventHub<Event>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ProcessManager {
|
|
||||||
pub fn new(scheduler: Box<Scheduler>, max_proc_num: usize) -> Self {
|
|
||||||
ProcessManager {
|
|
||||||
procs: new_vec_default(max_proc_num),
|
|
||||||
scheduler: Mutex::new(scheduler),
|
|
||||||
event_hub: Mutex::new(EventHub::new()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn alloc_pid(&self) -> Pid {
|
|
||||||
for (i, proc) in self.procs.iter().enumerate() {
|
|
||||||
if proc.lock().is_none() {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic!("Process number exceeded");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Add a new process
|
|
||||||
pub fn add(&self, context: Box<Context>, parent: Pid) -> Pid {
|
|
||||||
let pid = self.alloc_pid();
|
|
||||||
*(&self.procs[pid]).lock() = Some(Process {
|
|
||||||
id: pid,
|
|
||||||
status: Status::Ready,
|
|
||||||
status_after_stop: Status::Ready,
|
|
||||||
context: Some(context),
|
|
||||||
parent,
|
|
||||||
children: Vec::new(),
|
|
||||||
});
|
|
||||||
self.scheduler.lock().insert(pid);
|
|
||||||
self.procs[parent].lock().as_mut().expect("invalid parent proc")
|
|
||||||
.children.push(pid);
|
|
||||||
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 {
|
|
||||||
let mut event_hub = self.event_hub.lock();
|
|
||||||
event_hub.tick();
|
|
||||||
while let Some(event) = event_hub.pop() {
|
|
||||||
match event {
|
|
||||||
Event::Wakeup(pid) => self.set_status(pid, Status::Ready),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
self.scheduler.lock().tick(pid)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Set the priority of process `pid`
|
|
||||||
pub fn set_priority(&self, pid: Pid, priority: u8) {
|
|
||||||
self.scheduler.lock().set_priority(pid, priority);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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().expect("process not exist");
|
|
||||||
proc.status = Status::Running(cpu_id);
|
|
||||||
(pid, proc.context.take().expect("context not exist"))
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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().expect("process not exist");
|
|
||||||
proc.status = proc.status_after_stop.clone();
|
|
||||||
proc.status_after_stop = Status::Ready;
|
|
||||||
proc.context = Some(context);
|
|
||||||
match proc.status {
|
|
||||||
Status::Ready => self.scheduler.lock().insert(pid),
|
|
||||||
Status::Exited(_) => self.exit_handler(pid, proc),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Switch the status of a process.
|
|
||||||
/// Insert/Remove it to/from scheduler if necessary.
|
|
||||||
fn set_status(&self, pid: Pid, status: Status) {
|
|
||||||
let mut proc_lock = self.procs[pid].lock();
|
|
||||||
let mut proc = proc_lock.as_mut().expect("process not exist");
|
|
||||||
trace!("process {} {:?} -> {:?}", pid, proc.status, status);
|
|
||||||
match (&proc.status, &status) {
|
|
||||||
(Status::Ready, Status::Ready) => return,
|
|
||||||
(Status::Ready, _) => self.scheduler.lock().remove(pid),
|
|
||||||
(Status::Exited(_), _) => panic!("can not set status for a exited process"),
|
|
||||||
(Status::Sleeping, Status::Exited(_)) => self.event_hub.lock().remove(Event::Wakeup(pid)),
|
|
||||||
(_, Status::Ready) => self.scheduler.lock().insert(pid),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
match proc.status {
|
|
||||||
Status::Running(_) => proc.status_after_stop = status,
|
|
||||||
_ => proc.status = status,
|
|
||||||
}
|
|
||||||
match proc.status {
|
|
||||||
Status::Exited(_) => self.exit_handler(pid, proc),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_status(&self, pid: Pid) -> Option<Status> {
|
|
||||||
self.procs[pid].lock().as_ref().map(|p| p.status.clone())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Remove an exited proc `pid`.
|
|
||||||
/// Its all children will be set parent to 0.
|
|
||||||
pub fn remove(&self, pid: Pid) {
|
|
||||||
let mut proc_lock = self.procs[pid].lock();
|
|
||||||
let proc = proc_lock.as_ref().expect("process not exist");
|
|
||||||
match proc.status {
|
|
||||||
Status::Exited(_) => {}
|
|
||||||
_ => panic!("can not remove non-exited process"),
|
|
||||||
}
|
|
||||||
// orphan procs
|
|
||||||
for child in proc.children.iter() {
|
|
||||||
(&self.procs[*child]).lock().as_mut().expect("process not exist").parent = 0;
|
|
||||||
}
|
|
||||||
// remove self from parent's children list
|
|
||||||
self.procs[proc.parent].lock().as_mut().expect("process not exist")
|
|
||||||
.children.retain(|&i| i != pid);
|
|
||||||
// release the pid
|
|
||||||
*proc_lock = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sleep `pid` for `time` ticks.
|
|
||||||
/// `time` == 0 means sleep forever
|
|
||||||
pub fn sleep(&self, pid: Pid, time: usize) {
|
|
||||||
self.set_status(pid, Status::Sleeping);
|
|
||||||
if time != 0 {
|
|
||||||
self.event_hub.lock().push(time, Event::Wakeup(pid));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wakeup(&self, pid: Pid) {
|
|
||||||
self.set_status(pid, Status::Ready);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn wait(&self, pid: Pid, target: Pid) {
|
|
||||||
self.set_status(pid, Status::Waiting(target));
|
|
||||||
}
|
|
||||||
pub fn wait_child(&self, pid: Pid) {
|
|
||||||
self.set_status(pid, Status::Waiting(0));
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_children(&self, pid: Pid) -> Vec<Pid> {
|
|
||||||
self.procs[pid].lock().as_ref().expect("process not exist").children.clone()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn exit(&self, pid: Pid, code: ExitCode) {
|
|
||||||
// NOTE: if `pid` is running, status change will be deferred.
|
|
||||||
self.set_status(pid, Status::Exited(code));
|
|
||||||
}
|
|
||||||
/// Called when a process exit
|
|
||||||
fn exit_handler(&self, pid: Pid, proc: &mut Process) {
|
|
||||||
// wakeup parent if waiting
|
|
||||||
let parent = proc.parent;
|
|
||||||
match self.get_status(parent).expect("process not exist") {
|
|
||||||
Status::Waiting(target) if target == pid || target == 0 => self.wakeup(parent),
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
// drop its context
|
|
||||||
proc.context = None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn new_vec_default<T: Default>(size: usize) -> Vec<T> {
|
|
||||||
let mut vec = Vec::new();
|
|
||||||
vec.resize_default(size);
|
|
||||||
vec
|
|
||||||
}
|
|
@ -1,7 +1,8 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rcore-process"
|
name = "rcore-thread"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
authors = ["WangRunji <wangrunji0408@163.com>"]
|
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||||
|
description = "Bare-metal thread scheduler and executor"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
@ -0,0 +1,19 @@
|
|||||||
|
[package]
|
||||||
|
name = "example"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["WangRunji <wangrunji0408@163.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
log = "0.4"
|
||||||
|
linked_list_allocator = "0.6"
|
||||||
|
blog_os = { git = "https://github.com/phil-opp/blog_os.git" }
|
||||||
|
rcore-thread = { path = ".." }
|
||||||
|
|
||||||
|
[package.metadata.bootimage]
|
||||||
|
default-target = "x86_64-blog_os.json"
|
||||||
|
run-command = ["qemu-system-x86_64",
|
||||||
|
"-drive", "format=raw,file={}",
|
||||||
|
"-serial", "mon:stdio",
|
||||||
|
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04"
|
||||||
|
]
|
@ -0,0 +1,5 @@
|
|||||||
|
# Example of `rcore-thread` crate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bootimage run --release
|
||||||
|
```
|
@ -0,0 +1,199 @@
|
|||||||
|
#![no_std]
|
||||||
|
#![no_main]
|
||||||
|
#![feature(asm)]
|
||||||
|
#![feature(alloc)]
|
||||||
|
#![feature(naked_functions)]
|
||||||
|
#![feature(panic_info_message)]
|
||||||
|
#![feature(lang_items)]
|
||||||
|
|
||||||
|
extern crate alloc;
|
||||||
|
|
||||||
|
use core::alloc::Layout;
|
||||||
|
use core::panic::PanicInfo;
|
||||||
|
use alloc::{boxed::Box, sync::Arc};
|
||||||
|
|
||||||
|
use blog_os::{exit_qemu, gdt, interrupts::init_idt, serial_println};
|
||||||
|
use linked_list_allocator::LockedHeap;
|
||||||
|
use rcore_thread::{*, std_thread as thread};
|
||||||
|
|
||||||
|
const STACK_SIZE: usize = 0x2000;
|
||||||
|
const HEAP_SIZE: usize = 0x100000;
|
||||||
|
const MAX_CPU_NUM: usize = 1;
|
||||||
|
const MAX_PROC_NUM: usize = 32;
|
||||||
|
|
||||||
|
|
||||||
|
/// The entry of the kernel
|
||||||
|
#[no_mangle]
|
||||||
|
pub extern "C" fn _start() -> ! {
|
||||||
|
// init x86
|
||||||
|
gdt::init();
|
||||||
|
init_idt();
|
||||||
|
// init log
|
||||||
|
init_log();
|
||||||
|
// init heap
|
||||||
|
unsafe { HEAP_ALLOCATOR.lock().init(HEAP.as_ptr() as usize, HEAP_SIZE); }
|
||||||
|
// init processor
|
||||||
|
let scheduler = Box::new(scheduler::RRScheduler::new(5));
|
||||||
|
let thread_pool = Arc::new(ThreadPool::new(scheduler, MAX_PROC_NUM));
|
||||||
|
unsafe { processor().init(0, Thread::init(), thread_pool); }
|
||||||
|
// init threads
|
||||||
|
thread::spawn(|| {
|
||||||
|
let tid = processor().tid();
|
||||||
|
serial_println!("I'm thread {}! yield...", tid);
|
||||||
|
thread::yield_now();
|
||||||
|
serial_println!("I'm thread {}! exit...", tid);
|
||||||
|
});
|
||||||
|
thread::spawn(|| {
|
||||||
|
let tid = processor().tid();
|
||||||
|
serial_println!("I'm thread {}! yield...", tid);
|
||||||
|
thread::yield_now();
|
||||||
|
serial_println!("I'm thread {}! exit...", tid);
|
||||||
|
});
|
||||||
|
// run threads
|
||||||
|
processor().run();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn init_log() {
|
||||||
|
use log::*;
|
||||||
|
struct SimpleLogger;
|
||||||
|
impl Log for SimpleLogger {
|
||||||
|
fn enabled(&self, _metadata: &Metadata) -> bool {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
fn log(&self, record: &Record) {
|
||||||
|
serial_println!("[{:>5}] {}", record.level(), record.args());
|
||||||
|
}
|
||||||
|
fn flush(&self) {}
|
||||||
|
}
|
||||||
|
static LOGGER: SimpleLogger = SimpleLogger;
|
||||||
|
set_logger(&LOGGER).unwrap();
|
||||||
|
set_max_level(LevelFilter::Trace);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The context of a thread.
|
||||||
|
///
|
||||||
|
/// When a thread yield, its context will be stored at its stack.
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
#[repr(C)]
|
||||||
|
struct ContextData {
|
||||||
|
rdi: usize, // arg0
|
||||||
|
r15: usize,
|
||||||
|
r14: usize,
|
||||||
|
r13: usize,
|
||||||
|
r12: usize,
|
||||||
|
rbp: usize,
|
||||||
|
rbx: usize,
|
||||||
|
rip: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ContextData {
|
||||||
|
fn new(entry: extern fn(usize) -> !, arg0: usize) -> Self {
|
||||||
|
ContextData {
|
||||||
|
rip: entry as usize,
|
||||||
|
rdi: arg0,
|
||||||
|
..ContextData::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct Thread {
|
||||||
|
rsp: usize,
|
||||||
|
stack: [u8; STACK_SIZE],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Thread {
|
||||||
|
unsafe fn init() -> Box<Self> {
|
||||||
|
Box::new(core::mem::uninitialized())
|
||||||
|
}
|
||||||
|
fn new(entry: extern fn(usize) -> !, arg0: usize) -> Box<Self> {
|
||||||
|
let mut thread = unsafe { Thread::init() };
|
||||||
|
let rsp = thread.stack.as_ptr() as usize + STACK_SIZE - core::mem::size_of::<ContextData>();
|
||||||
|
// push a Context at stack top
|
||||||
|
let init_context = ContextData::new(entry, arg0);
|
||||||
|
unsafe { (rsp as *mut ContextData).write(init_context); }
|
||||||
|
thread.rsp = rsp;
|
||||||
|
thread
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement `switch_to` for a thread
|
||||||
|
impl Context for Thread {
|
||||||
|
/// Switch to another thread.
|
||||||
|
unsafe fn switch_to(&mut self, target: &mut Context) {
|
||||||
|
let (to, _): (*mut Thread, usize) = core::mem::transmute(target);
|
||||||
|
inner(self, to);
|
||||||
|
|
||||||
|
#[naked]
|
||||||
|
#[inline(never)]
|
||||||
|
unsafe extern "C" fn inner(_from: *mut Thread, _to: *mut Thread) {
|
||||||
|
asm!(
|
||||||
|
"
|
||||||
|
// push rip (by caller)
|
||||||
|
|
||||||
|
// Save self callee-save registers
|
||||||
|
push rbx
|
||||||
|
push rbp
|
||||||
|
push r12
|
||||||
|
push r13
|
||||||
|
push r14
|
||||||
|
push r15
|
||||||
|
push rdi
|
||||||
|
|
||||||
|
// Switch stacks
|
||||||
|
mov [rdi], rsp // *rdi = from_rsp
|
||||||
|
mov rsp, [rsi] // *rsi = to_rsp
|
||||||
|
|
||||||
|
// Restore target callee-save registers
|
||||||
|
pop rdi
|
||||||
|
pop r15
|
||||||
|
pop r14
|
||||||
|
pop r13
|
||||||
|
pop r12
|
||||||
|
pop rbp
|
||||||
|
pop rbx
|
||||||
|
|
||||||
|
// pop rip
|
||||||
|
ret"
|
||||||
|
: : : : "intel" "volatile" )
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Define global `Processor` for each core.
|
||||||
|
static PROCESSORS: [Processor; MAX_CPU_NUM] = [Processor::new()];
|
||||||
|
|
||||||
|
/// Now we only have one core.
|
||||||
|
fn cpu_id() -> usize { 0 }
|
||||||
|
|
||||||
|
/// Implement dependency for `rcore_thread::std_thread`
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn processor() -> &'static Processor {
|
||||||
|
&PROCESSORS[cpu_id()]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Implement dependency for `rcore_thread::std_thread`
|
||||||
|
#[no_mangle]
|
||||||
|
pub fn new_kernel_context(entry: extern fn(usize) -> !, arg0: usize) -> Box<Context> {
|
||||||
|
Thread::new(entry, arg0)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[panic_handler]
|
||||||
|
fn panic(info: &PanicInfo) -> ! {
|
||||||
|
let location = info.location().unwrap();
|
||||||
|
let message = info.message().unwrap();
|
||||||
|
serial_println!("\nPANIC @ {}\n\t{}", location, message);
|
||||||
|
|
||||||
|
unsafe { exit_qemu(); }
|
||||||
|
loop {}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[global_allocator]
|
||||||
|
static HEAP_ALLOCATOR: LockedHeap = LockedHeap::empty();
|
||||||
|
|
||||||
|
static mut HEAP: [u8; HEAP_SIZE] = [0; HEAP_SIZE];
|
||||||
|
|
||||||
|
#[lang = "oom"]
|
||||||
|
fn oom(_: Layout) -> ! {
|
||||||
|
panic!("out of memory");
|
||||||
|
}
|
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"llvm-target": "x86_64-unknown-none",
|
||||||
|
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
|
||||||
|
"arch": "x86_64",
|
||||||
|
"target-endian": "little",
|
||||||
|
"target-pointer-width": "64",
|
||||||
|
"target-c-int-width": "32",
|
||||||
|
"os": "none",
|
||||||
|
"executables": true,
|
||||||
|
"linker-flavor": "ld.lld",
|
||||||
|
"linker": "rust-lld",
|
||||||
|
"panic-strategy": "abort",
|
||||||
|
"disable-redzone": true,
|
||||||
|
"features": "-mmx,-sse,+soft-float"
|
||||||
|
}
|
@ -1,3 +1,5 @@
|
|||||||
|
//! Enable and disable interrupt for each architecture.
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[cfg(target_arch = "x86_64")]
|
#[cfg(target_arch = "x86_64")]
|
||||||
pub unsafe fn disable_and_store() -> usize {
|
pub unsafe fn disable_and_store() -> usize {
|
@ -0,0 +1,219 @@
|
|||||||
|
use alloc::boxed::Box;
|
||||||
|
use alloc::vec::Vec;
|
||||||
|
use spin::Mutex;
|
||||||
|
use log::*;
|
||||||
|
use crate::scheduler::Scheduler;
|
||||||
|
use crate::timer::Timer;
|
||||||
|
|
||||||
|
struct Thread {
|
||||||
|
status: Status,
|
||||||
|
status_after_stop: Status,
|
||||||
|
context: Option<Box<Context>>,
|
||||||
|
parent: Tid,
|
||||||
|
children: Vec<Tid>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type Tid = usize;
|
||||||
|
type ExitCode = usize;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||||
|
pub enum Status {
|
||||||
|
Ready,
|
||||||
|
Running(usize),
|
||||||
|
Sleeping,
|
||||||
|
Waiting(Tid),
|
||||||
|
/// aka ZOMBIE. Its context was dropped.
|
||||||
|
Exited(ExitCode),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
|
enum Event {
|
||||||
|
Wakeup(Tid),
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Context {
|
||||||
|
unsafe fn switch_to(&mut self, target: &mut Context);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ThreadPool {
|
||||||
|
threads: Vec<Mutex<Option<Thread>>>,
|
||||||
|
scheduler: Mutex<Box<Scheduler>>,
|
||||||
|
timer: Mutex<Timer<Event>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ThreadPool {
|
||||||
|
pub fn new(scheduler: Box<Scheduler>, max_proc_num: usize) -> Self {
|
||||||
|
ThreadPool {
|
||||||
|
threads: new_vec_default(max_proc_num),
|
||||||
|
scheduler: Mutex::new(scheduler),
|
||||||
|
timer: Mutex::new(Timer::new()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn alloc_tid(&self) -> Tid {
|
||||||
|
for (i, proc) in self.threads.iter().enumerate() {
|
||||||
|
if proc.lock().is_none() {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
panic!("Process number exceeded");
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Add a new process
|
||||||
|
pub fn add(&self, context: Box<Context>, parent: Tid) -> Tid {
|
||||||
|
let tid = self.alloc_tid();
|
||||||
|
*(&self.threads[tid]).lock() = Some(Thread {
|
||||||
|
status: Status::Ready,
|
||||||
|
status_after_stop: Status::Ready,
|
||||||
|
context: Some(context),
|
||||||
|
parent,
|
||||||
|
children: Vec::new(),
|
||||||
|
});
|
||||||
|
self.scheduler.lock().insert(tid);
|
||||||
|
self.threads[parent].lock().as_mut().expect("invalid parent proc")
|
||||||
|
.children.push(tid);
|
||||||
|
tid
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make process `tid` time slice -= 1.
|
||||||
|
/// Return true if time slice == 0.
|
||||||
|
/// Called by timer interrupt handler.
|
||||||
|
pub fn tick(&self, tid: Tid) -> bool {
|
||||||
|
let mut timer = self.timer.lock();
|
||||||
|
timer.tick();
|
||||||
|
while let Some(event) = timer.pop() {
|
||||||
|
match event {
|
||||||
|
Event::Wakeup(tid) => self.set_status(tid, Status::Ready),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.scheduler.lock().tick(tid)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set the priority of process `tid`
|
||||||
|
pub fn set_priority(&self, tid: Tid, priority: u8) {
|
||||||
|
self.scheduler.lock().set_priority(tid, priority);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) -> (Tid, Box<Context>) {
|
||||||
|
let mut scheduler = self.scheduler.lock();
|
||||||
|
let tid = scheduler.select()
|
||||||
|
.expect("failed to select a runnable process");
|
||||||
|
scheduler.remove(tid);
|
||||||
|
let mut proc_lock = self.threads[tid].lock();
|
||||||
|
let mut proc = proc_lock.as_mut().expect("process not exist");
|
||||||
|
proc.status = Status::Running(cpu_id);
|
||||||
|
(tid, proc.context.take().expect("context not exist"))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Called by Processor to finish running a process
|
||||||
|
/// and give its context back.
|
||||||
|
pub fn stop(&self, tid: Tid, context: Box<Context>) {
|
||||||
|
let mut proc_lock = self.threads[tid].lock();
|
||||||
|
let mut proc = proc_lock.as_mut().expect("process not exist");
|
||||||
|
proc.status = proc.status_after_stop.clone();
|
||||||
|
proc.status_after_stop = Status::Ready;
|
||||||
|
proc.context = Some(context);
|
||||||
|
match proc.status {
|
||||||
|
Status::Ready => self.scheduler.lock().insert(tid),
|
||||||
|
Status::Exited(_) => self.exit_handler(tid, proc),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Switch the status of a process.
|
||||||
|
/// Insert/Remove it to/from scheduler if necessary.
|
||||||
|
fn set_status(&self, tid: Tid, status: Status) {
|
||||||
|
let mut proc_lock = self.threads[tid].lock();
|
||||||
|
let mut proc = proc_lock.as_mut().expect("process not exist");
|
||||||
|
trace!("process {} {:?} -> {:?}", tid, proc.status, status);
|
||||||
|
match (&proc.status, &status) {
|
||||||
|
(Status::Ready, Status::Ready) => return,
|
||||||
|
(Status::Ready, _) => self.scheduler.lock().remove(tid),
|
||||||
|
(Status::Exited(_), _) => panic!("can not set status for a exited process"),
|
||||||
|
(Status::Sleeping, Status::Exited(_)) => self.timer.lock().stop(Event::Wakeup(tid)),
|
||||||
|
(_, Status::Ready) => self.scheduler.lock().insert(tid),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
match proc.status {
|
||||||
|
Status::Running(_) => proc.status_after_stop = status,
|
||||||
|
_ => proc.status = status,
|
||||||
|
}
|
||||||
|
match proc.status {
|
||||||
|
Status::Exited(_) => self.exit_handler(tid, proc),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_status(&self, tid: Tid) -> Option<Status> {
|
||||||
|
self.threads[tid].lock().as_ref().map(|p| p.status.clone())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Remove an exited proc `tid`.
|
||||||
|
/// Its all children will be set parent to 0.
|
||||||
|
pub fn remove(&self, tid: Tid) {
|
||||||
|
let mut proc_lock = self.threads[tid].lock();
|
||||||
|
let proc = proc_lock.as_ref().expect("process not exist");
|
||||||
|
match proc.status {
|
||||||
|
Status::Exited(_) => {}
|
||||||
|
_ => panic!("can not remove non-exited process"),
|
||||||
|
}
|
||||||
|
// orphan procs
|
||||||
|
for child in proc.children.iter() {
|
||||||
|
(&self.threads[*child]).lock().as_mut().expect("process not exist").parent = 0;
|
||||||
|
}
|
||||||
|
// remove self from parent's children list
|
||||||
|
self.threads[proc.parent].lock().as_mut().expect("process not exist")
|
||||||
|
.children.retain(|&i| i != tid);
|
||||||
|
// release the tid
|
||||||
|
*proc_lock = None;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sleep `tid` for `time` ticks.
|
||||||
|
/// `time` == 0 means sleep forever
|
||||||
|
pub fn sleep(&self, tid: Tid, time: usize) {
|
||||||
|
self.set_status(tid, Status::Sleeping);
|
||||||
|
if time != 0 {
|
||||||
|
self.timer.lock().start(time, Event::Wakeup(tid));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wakeup(&self, tid: Tid) {
|
||||||
|
self.set_status(tid, Status::Ready);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn wait(&self, tid: Tid, target: Tid) {
|
||||||
|
self.set_status(tid, Status::Waiting(target));
|
||||||
|
}
|
||||||
|
pub fn wait_child(&self, tid: Tid) {
|
||||||
|
self.set_status(tid, Status::Waiting(0));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn get_children(&self, tid: Tid) -> Vec<Tid> {
|
||||||
|
self.threads[tid].lock().as_ref().expect("process not exist").children.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exit(&self, tid: Tid, code: ExitCode) {
|
||||||
|
// NOTE: if `tid` is running, status change will be deferred.
|
||||||
|
self.set_status(tid, Status::Exited(code));
|
||||||
|
}
|
||||||
|
/// Called when a process exit
|
||||||
|
fn exit_handler(&self, tid: Tid, proc: &mut Thread) {
|
||||||
|
// wakeup parent if waiting
|
||||||
|
let parent = proc.parent;
|
||||||
|
match self.get_status(parent).expect("process not exist") {
|
||||||
|
Status::Waiting(target) if target == tid || target == 0 => self.wakeup(parent),
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
// drop its context
|
||||||
|
proc.context = None;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_vec_default<T: Default>(size: usize) -> Vec<T> {
|
||||||
|
let mut vec = Vec::new();
|
||||||
|
vec.resize_default(size);
|
||||||
|
vec
|
||||||
|
}
|
@ -0,0 +1,65 @@
|
|||||||
|
//! A simple timer
|
||||||
|
|
||||||
|
use alloc::collections::VecDeque;
|
||||||
|
|
||||||
|
type Time = usize;
|
||||||
|
|
||||||
|
struct Event<T> {
|
||||||
|
time: Time,
|
||||||
|
data: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A simple timer using ordered dequeue
|
||||||
|
pub struct Timer<T> {
|
||||||
|
tick: Time,
|
||||||
|
timers: VecDeque<Event<T>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: PartialEq> Timer<T> {
|
||||||
|
/// Create a new timer.
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Timer {
|
||||||
|
tick: 0,
|
||||||
|
timers: VecDeque::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Called on each tick.
|
||||||
|
pub fn tick(&mut self) {
|
||||||
|
self.tick += 1;
|
||||||
|
}
|
||||||
|
/// Pop an expired timer after `tick`.
|
||||||
|
///
|
||||||
|
/// This must be called after calling `tick`,
|
||||||
|
/// and should be called multiple times until return `None`.
|
||||||
|
pub fn pop(&mut self) -> Option<T> {
|
||||||
|
match self.timers.front() {
|
||||||
|
None => return None,
|
||||||
|
Some(timer) if timer.time != self.tick => return None,
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
self.timers.pop_front().map(|t| t.data)
|
||||||
|
}
|
||||||
|
/// Start a timer with given time interval
|
||||||
|
pub fn start(&mut self, time_after: Time, data: T) {
|
||||||
|
//debug!("{:?} {:?}", self.tick, time_after);
|
||||||
|
let time = self.tick + time_after;
|
||||||
|
let event = Event { time, data };
|
||||||
|
let mut it = self.timers.iter();
|
||||||
|
let mut i : usize = 0;
|
||||||
|
loop {
|
||||||
|
match it.next() {
|
||||||
|
None => break,
|
||||||
|
Some(e) if e.time >= time => break,
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
self.timers.insert(i, event);
|
||||||
|
}
|
||||||
|
/// Stop a timer
|
||||||
|
pub fn stop(&mut self, data: T) {
|
||||||
|
if let Some(i) = self.timers.iter().position(|t| t.data == data) {
|
||||||
|
self.timers.remove(i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue