Merge branch 'thread' into biscuit

# Conflicts:
#	kernel/src/process/mod.rs
#	kernel/src/process/structs.rs
#	kernel/src/syscall.rs
master
WangRunji 6 years ago
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]
name = "rcore-process"
name = "rcore-thread"
version = "0.1.0"
authors = ["WangRunji <wangrunji0408@163.com>"]
description = "Bare-metal thread scheduler and executor"
edition = "2018"
[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)]
#[cfg(target_arch = "x86_64")]
pub unsafe fn disable_and_store() -> usize {

@ -9,12 +9,12 @@
extern crate alloc;
mod process_manager;
mod thread_pool;
mod processor;
pub mod scheduler;
pub mod thread;
mod event_hub;
pub mod std_thread;
mod timer;
mod interrupt;
pub use crate::process_manager::*;
pub use crate::thread_pool::*;
pub use crate::processor::Processor;

@ -2,10 +2,10 @@ use alloc::boxed::Box;
use alloc::sync::Arc;
use log::*;
use core::cell::UnsafeCell;
use crate::process_manager::*;
use crate::thread_pool::*;
use crate::interrupt;
/// Process executor
/// Thread executor
///
/// Per-CPU struct. Defined at global.
/// Only accessed by associated CPU with interrupt disabled.
@ -18,9 +18,9 @@ unsafe impl Sync for Processor {}
struct ProcessorInner {
id: usize,
proc: Option<(Pid, Box<Context>)>,
proc: Option<(Tid, Box<Context>)>,
loop_context: Box<Context>,
manager: Arc<ProcessManager>,
manager: Arc<ThreadPool>,
}
impl Processor {
@ -28,7 +28,7 @@ impl Processor {
Processor { inner: UnsafeCell::new(None) }
}
pub unsafe fn init(&self, id: usize, context: Box<Context>, manager: Arc<ProcessManager>) {
pub unsafe fn init(&self, id: usize, context: Box<Context>, manager: Arc<ThreadPool>) {
*self.inner.get() = Some(ProcessorInner {
id,
proc: None,
@ -76,7 +76,7 @@ impl Processor {
}
}
pub fn pid(&self) -> Pid {
pub fn tid(&self) -> Tid {
self.inner().proc.as_ref().unwrap().0
}
@ -84,13 +84,13 @@ impl Processor {
&*self.inner().proc.as_ref().unwrap().1
}
pub fn manager(&self) -> &ProcessManager {
pub fn manager(&self) -> &ThreadPool {
&*self.inner().manager
}
pub fn tick(&self) {
let flags = unsafe { interrupt::disable_and_store() };
let need_reschedule = self.manager().tick(self.pid());
let need_reschedule = self.manager().tick(self.tid());
unsafe { interrupt::restore(flags); }
if need_reschedule {

@ -1,4 +1,4 @@
//! Thread std-like interface
//! `std::thread`-like interface
//!
//! Based on Processor. Used in kernel.
//!
@ -11,7 +11,7 @@ use core::marker::PhantomData;
use core::time::Duration;
use log::*;
use crate::processor::*;
use crate::process_manager::*;
use crate::thread_pool::*;
#[linkage = "weak"]
#[no_mangle]
@ -30,7 +30,7 @@ fn new_kernel_context(_entry: extern fn(usize) -> !, _arg: usize) -> Box<Context
/// Gets a handle to the thread that invokes it.
pub fn current() -> Thread {
Thread { pid: processor().pid() }
Thread { pid: processor().tid() }
}
/// Puts the current thread to sleep for the specified amount of time.
@ -160,7 +160,7 @@ impl<T> JoinHandle<T> {
}
}
/// Force construct a JoinHandle struct
pub unsafe fn _of(pid: Pid) -> JoinHandle<T> {
pub unsafe fn _of(pid: Tid) -> JoinHandle<T> {
JoinHandle {
thread: Thread { pid },
mark: PhantomData,

@ -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);
}
}
}

@ -1,6 +1,6 @@
# 上下文切换
平台无关的代码位于 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs) 中,而平台相关(aarch64)的代码位于 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs) 中。
平台无关的代码位于 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs) 中,而平台相关(aarch64)的代码位于 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs) 中。
## 相关数据结构
@ -56,7 +56,7 @@
}
```
每个进程控制块 `Process` ([kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L13)) 都会维护一个平台相关的 `Context` 对象,在 AArch64 中包含下列信息:
每个进程控制块 `Process` ([kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L13)) 都会维护一个平台相关的 `Context` 对象,在 AArch64 中包含下列信息:
1. `stack_top`:内核栈顶地址
2. `ttbr`:页表基址
@ -64,7 +64,7 @@
## 切换流程
在 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L22) 里,`switch_to()` 是平台无关的切换函数,最终会调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L129) 里平台相关的切换函数 `Context::switch()`
在 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L22) 里,`switch_to()` 是平台无关的切换函数,最终会调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L129) 里平台相关的切换函数 `Context::switch()`
```rust
pub unsafe fn switch(&mut self, target: &mut Self) {
@ -190,7 +190,7 @@ ret
2. 创建新的**用户线程**:解析 ELF 文件。
3. 从一个线程 **fork** 出一个新线程:通过 `fork` 系统调用。
三种线程的平台无关创建流程实现在 [kernel/src/process/context.rs](../../../kernel/src/process/context.rs#L40) 里,最终会分别调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L146) 里的 `new_kernel_thread()`、`new_user_thread()` 和 `new_fork()` 这三个函数创建平台相关的 `Context` 结构。
三种线程的平台无关创建流程实现在 [kernel/src/process/context.rs](../../../kernel/src/process/structs.rs#L40) 里,最终会分别调用 [kernel/src/arch/aarch64/interrupt/context.rs](../../../kernel/src/arch/aarch64/interrupt/context.rs#L146) 里的 `new_kernel_thread()`、`new_user_thread()` 和 `new_fork()` 这三个函数创建平台相关的 `Context` 结构。
在这三个函数里,会构造 `ContextData``TrapFrame` 结构,构成一个 `InitStack`,并向新线程的内核栈压入 `InitStack` 结构,最后将新内核栈顶地址、页表基址等信息构成 `Context` 结构返回。这两个结构的构造方式如下:

4
kernel/Cargo.lock generated

@ -256,7 +256,7 @@ dependencies = [
"pc-keyboard 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)",
"raw-cpuid 6.1.0 (registry+https://github.com/rust-lang/crates.io-index)",
"rcore-memory 0.1.0",
"rcore-process 0.1.0",
"rcore-thread 0.1.0",
"riscv 0.3.0 (git+https://github.com/riscv-and-rust-and-decaf/riscv)",
"simple-filesystem 0.1.0 (git+https://github.com/wangrunji0408/SimpleFileSystem-Rust)",
"smoltcp 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)",
@ -275,7 +275,7 @@ dependencies = [
]
[[package]]
name = "rcore-process"
name = "rcore-thread"
version = "0.1.0"
dependencies = [
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",

@ -54,7 +54,7 @@ lazy_static = { version = "1.2", features = ["spin_no_std"] }
smoltcp = { version = "0.5.0", default-features = false, features = ["alloc", "log", "proto-ipv4", "proto-igmp", "socket-icmp", "socket-udp", "socket-tcp"] }
bit-allocator = { path = "../crate/bit-allocator" }
rcore-memory = { path = "../crate/memory" }
rcore-process = { path = "../crate/process" }
rcore-thread = { path = "../crate/thread" }
simple-filesystem = { git = "https://github.com/wangrunji0408/SimpleFileSystem-Rust" }
[target.'cfg(target_arch = "x86_64")'.dependencies]

@ -13,7 +13,7 @@
extern crate alloc;
pub use crate::process::{processor, new_kernel_context};
use rcore_process::thread;
use rcore_thread::std_thread as thread;
use linked_list_allocator::LockedHeap;
#[macro_use] // print!

@ -1,21 +1,22 @@
pub use self::context::Process;
pub use rcore_process::*;
pub use self::structs::*;
pub use rcore_thread::*;
use crate::consts::{MAX_CPU_NUM, MAX_PROCESS_NUM};
use crate::arch::cpu;
use alloc::{boxed::Box, sync::Arc};
use spin::MutexGuard;
use log::*;
pub mod context;
pub mod structs;
mod abi;
pub fn init() {
// NOTE: max_time_slice <= 5 to ensure 'priority' test pass
let scheduler = Box::new(scheduler::RRScheduler::new(5));
let manager = Arc::new(ProcessManager::new(scheduler, MAX_PROCESS_NUM));
let manager = Arc::new(ThreadPool::new(scheduler, MAX_PROCESS_NUM));
unsafe {
for cpu_id in 0..MAX_CPU_NUM {
PROCESSORS[cpu_id].init(cpu_id, Process::new_init(), manager.clone());
PROCESSORS[cpu_id].init(cpu_id, Thread::new_init(), manager.clone());
}
}
@ -26,7 +27,7 @@ pub fn init() {
use core::str::FromStr;
let cores = usize::from_str(env!("SMP")).unwrap();
for i in 0..cores {
manager.add(Process::new_kernel(idle, i), 0);
manager.add(Thread::new_kernel(idle, i), 0);
}
crate::shell::run_user_shell();
@ -35,12 +36,17 @@ pub fn init() {
static PROCESSORS: [Processor; MAX_CPU_NUM] = [Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new(), Processor::new()];
/// Get current thread struct
/// Get current process
pub fn process() -> MutexGuard<'static, Process> {
current_thread().proc.lock()
}
/// Get current thread
///
/// FIXME: It's obviously unsafe to get &mut !
pub fn process() -> &'static mut Process {
pub fn current_thread() -> &'static mut Thread {
use core::mem::transmute;
let (process, _): (&mut Process, *const ()) = unsafe {
let (process, _): (&mut Thread, *const ()) = unsafe {
transmute(processor().context())
};
process
@ -56,5 +62,5 @@ pub fn processor() -> &'static Processor {
#[no_mangle]
pub fn new_kernel_context(entry: extern fn(usize) -> !, arg: usize) -> Box<Context> {
Process::new_kernel(entry, arg)
Thread::new_kernel(entry, arg)
}

@ -3,56 +3,66 @@ use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::V
use log::*;
use simple_filesystem::{file::File, INode};
use spin::Mutex;
use rcore_process::Context;
use xmas_elf::{ElfFile, header, program::{Flags, Type}};
use crate::arch::interrupt::{Context as ArchContext, TrapFrame};
use crate::arch::interrupt::{Context, TrapFrame};
use crate::memory::{ByFrame, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet};
use super::abi::ProcInitInfo;
// TODO: avoid pub
pub struct Thread {
pub context: Context,
pub kstack: KernelStack,
pub proc: Arc<Mutex<Process>>,
}
pub struct Process {
pub arch: ArchContext,
pub memory_set: MemorySet,
pub kstack: KernelStack,
pub files: BTreeMap<usize, Arc<Mutex<File>>>, // FIXME: => Box<File>
pub cwd: String,
}
impl Context for Process {
unsafe fn switch_to(&mut self, target: &mut Context) {
/// Let `rcore_thread` can switch between our `Thread`
impl rcore_thread::Context for Thread {
unsafe fn switch_to(&mut self, target: &mut rcore_thread::Context) {
use core::mem::transmute;
let (target, _): (&mut Process, *const ()) = transmute(target);
self.arch.switch(&mut target.arch);
let (target, _): (&mut Thread, *const ()) = transmute(target);
self.context.switch(&mut target.context);
}
}
impl Process {
pub unsafe fn new_init() -> Box<Context> {
Box::new(Process {
arch: ArchContext::null(),
memory_set: MemorySet::new(),
impl Thread {
/// Make a struct for the init thread
pub unsafe fn new_init() -> Box<Thread> {
Box::new(Thread {
context: Context::null(),
kstack: KernelStack::new(),
proc: Arc::new(Mutex::new(Process {
memory_set: MemorySet::new(),
files: BTreeMap::default(),
cwd: String::new(),
})),
})
}
pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Box<Context> {
/// Make a new kernel thread starting from `entry` with `arg`
pub fn new_kernel(entry: extern fn(usize) -> !, arg: usize) -> Box<Thread> {
let memory_set = MemorySet::new();
let kstack = KernelStack::new();
Box::new(Process {
arch: unsafe { ArchContext::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) },
memory_set,
Box::new(Thread {
context: unsafe { Context::new_kernel_thread(entry, arg, kstack.top(), memory_set.token()) },
kstack,
proc: Arc::new(Mutex::new(Process {
memory_set,
files: BTreeMap::default(),
cwd: String::new(),
})),
})
}
/// Make a new user thread from ELF data
pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box<Process>
/// Make a new user process from ELF `data`
pub fn new_user<'a, Iter>(data: &[u8], args: Iter) -> Box<Thread>
where Iter: Iterator<Item=&'a str>
{
// Parse elf
@ -105,23 +115,25 @@ impl Process {
files.insert(1, Arc::new(Mutex::new(File::new(crate::fs::STDOUT.clone(), false, true))));
files.insert(2, Arc::new(Mutex::new(File::new(crate::fs::STDOUT.clone(), false, true))));
Box::new(Process {
arch: unsafe {
ArchContext::new_user_thread(
Box::new(Thread {
context: unsafe {
Context::new_user_thread(
entry_addr, ustack_top, kstack.top(), is32, memory_set.token())
},
memory_set,
kstack,
proc: Arc::new(Mutex::new(Process {
memory_set,
files,
cwd: String::new(),
})),
})
}
/// Fork
pub fn fork(&self, tf: &TrapFrame) -> Box<Context> {
/// Fork a new process from current one
pub fn fork(&self, tf: &TrapFrame) -> Box<Thread> {
info!("COME into fork!");
// Clone memory set, make a new page table
let memory_set = self.memory_set.clone();
let memory_set = self.proc.lock().memory_set.clone();
info!("finish mmset clone in fork!");
// MMU: copy data to the new space
@ -137,12 +149,14 @@ impl Process {
info!("temporary copy data!");
let kstack = KernelStack::new();
Box::new(Process {
arch: unsafe { ArchContext::new_fork(tf, kstack.top(), memory_set.token()) },
memory_set,
Box::new(Thread {
context: unsafe { Context::new_fork(tf, kstack.top(), memory_set.token()) },
kstack,
files: self.files.clone(),
proc: Arc::new(Mutex::new(Process {
memory_set,
files: self.proc.lock().files.clone(),
cwd: String::new(),
})),
})
}
}

@ -3,15 +3,16 @@
use alloc::string::String;
use crate::fs::{ROOT_INODE, INodeExt};
use crate::process::*;
use crate::thread;
pub fn run_user_shell() {
if let Ok(inode) = ROOT_INODE.lookup("sh") {
println!("Going to user mode shell.");
println!("Use 'ls' to list available programs.");
let data = inode.read_as_vec().unwrap();
processor().manager().add(Process::new_user(data.as_slice(), "sh".split(' ')), 0);
processor().manager().add(Thread::new_user(data.as_slice(), "sh".split(' ')), 0);
} else {
processor().manager().add(Process::new_kernel(shell, 0), 0);
processor().manager().add(Thread::new_kernel(shell, 0), 0);
}
}
@ -28,7 +29,7 @@ pub extern fn shell(_arg: usize) -> ! {
let name = cmd.split(' ').next().unwrap();
if let Ok(file) = ROOT_INODE.lookup(name) {
let data = file.read_as_vec().unwrap();
let pid = processor().manager().add(Process::new_user(data.as_slice(), cmd.split(' ')), thread::current().id());
let pid = processor().manager().add(Thread::new_user(data.as_slice(), cmd.split(' ')), thread::current().id());
unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap();
} else {
println!("Program not exist");

@ -3,7 +3,7 @@
use simple_filesystem::{INode, file::File, FileInfo, FileType, FsError};
use core::{slice, str};
use alloc::{sync::Arc, vec::Vec, string::String};
use spin::Mutex;
use spin::{Mutex, MutexGuard};
use log::*;
use bitflags::bitflags;
use crate::arch::interrupt::TrapFrame;
@ -98,7 +98,8 @@ fn sys_read(fd: usize, base: *mut u8, len: usize) -> SysResult {
// TODO: check ptr
info!("read: fd: {}, base: {:?}, len: {:#x}", fd, base, len);
let slice = unsafe { slice::from_raw_parts_mut(base, len) };
let len = get_file(fd)?.lock().read(slice)?;
let proc = process();
let len = get_file(&proc, fd)?.lock().read(slice)?;
Ok(len as isize)
}
@ -106,7 +107,8 @@ fn sys_write(fd: usize, base: *const u8, len: usize) -> SysResult {
// TODO: check ptr
info!("write: fd: {}, base: {:?}, len: {:#x}", fd, base, len);
let slice = unsafe { slice::from_raw_parts(base, len) };
let len = get_file(fd)?.lock().write(slice)?;
let proc = process();
let len = get_file(&proc, fd)?.lock().write(slice)?;
Ok(len as isize)
}
@ -140,7 +142,8 @@ fn sys_close(fd: usize) -> SysResult {
fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult {
// TODO: check ptr
info!("fstat: {}", fd);
let file = get_file(fd)?;
let proc = process();
let file = get_file(&proc, fd)?;
let stat = Stat::from(file.lock().info()?);
unsafe { stat_ptr.write(stat); }
Ok(0)
@ -152,7 +155,8 @@ fn sys_fstat(fd: usize, stat_ptr: *mut Stat) -> SysResult {
fn sys_getdirentry(fd: usize, dentry_ptr: *mut DirEntry) -> SysResult {
// TODO: check ptr
info!("getdirentry: {}", fd);
let file = get_file(fd)?;
let proc = process();
let file = get_file(&proc, fd)?;
let dentry = unsafe { &mut *dentry_ptr };
if !dentry.check() {
return Err(SysError::Inval);
@ -168,7 +172,8 @@ fn sys_getdirentry(fd: usize, dentry_ptr: *mut DirEntry) -> SysResult {
fn sys_dup2(fd1: usize, fd2: usize) -> SysResult {
info!("dup2: {} {}", fd1, fd2);
let file = get_file(fd1)?;
let proc = process();
let file = get_file(&proc, fd1)?;
if process().files.contains_key(&fd2) {
return Err(SysError::Inval);
}
@ -178,7 +183,7 @@ fn sys_dup2(fd1: usize, fd2: usize) -> SysResult {
/// Fork the current process. Return the child's PID.
fn sys_fork(tf: &TrapFrame) -> SysResult {
let context = process().fork(tf);
let context = current_thread().fork(tf);
let pid = processor().manager().add(context, thread::current().id());
info!("fork: {} -> {}", thread::current().id(), pid);
Ok(pid as isize)
@ -244,19 +249,19 @@ fn sys_exec(name: *const u8, argc: usize, argv: *const *const u8, tf: &mut TrapF
unsafe { buf.set_len(size); }
inode.read_at(0, buf.as_mut_slice())?;
// Make new Context
// Make new Thread
let iter = args.iter().map(|s| s.as_str());
let mut context = Process::new_user(buf.as_slice(), iter);
let mut thread = Thread::new_user(buf.as_slice(), iter);
// Activate new page table
unsafe { context.memory_set.activate(); }
unsafe { thread.proc.lock().memory_set.activate(); }
// Modify the TrapFrame
*tf = unsafe { context.arch.get_init_tf() };
*tf = unsafe { thread.context.get_init_tf() };
// Swap Context but keep KStack
::core::mem::swap(&mut process().kstack, &mut context.kstack);
::core::mem::swap(process(), &mut *context);
::core::mem::swap(&mut current_thread().kstack, &mut thread.kstack);
::core::mem::swap(current_thread(), &mut *thread);
Ok(0)
}
@ -315,8 +320,8 @@ fn sys_putc(c: char) -> SysResult {
Ok(0)
}
fn get_file(fd: usize) -> Result<&'static Arc<Mutex<File>>, SysError> {
process().files.get(&fd).ok_or(SysError::Inval)
fn get_file<'a>(proc: &'a MutexGuard<'static, Process>, fd: usize) -> Result<&'a Arc<Mutex<File>>, SysError> {
proc.files.get(&fd).ok_or(SysError::Inval)
}
pub type SysResult = Result<isize, SysError>;

@ -14,7 +14,7 @@ pub fn timer() {
pub fn error(tf: &TrapFrame) -> ! {
error!("{:#x?}", tf);
let pid = processor().pid();
let pid = processor().tid();
error!("On CPU{} Process {}", cpu::id(), pid);
processor().manager().exit(pid, 0x100);

Loading…
Cancel
Save