mirror of
https://github.com/rcore-os/rCore.git
synced 2025-01-19 01:07:05 +04:00
Implement tid/pid/ppid separation
This commit is contained in:
parent
82457be2ec
commit
8cb11b7aa8
@ -23,6 +23,8 @@ pub struct MemoryArea {
|
||||
name: &'static str,
|
||||
}
|
||||
|
||||
unsafe impl Send for MemoryArea { }
|
||||
|
||||
impl MemoryArea {
|
||||
/*
|
||||
** @brief get slice of the content in the memory area
|
||||
|
@ -59,9 +59,9 @@ impl Processor {
|
||||
unsafe {
|
||||
inner.loop_context.switch_to(&mut *inner.proc.as_mut().unwrap().1);
|
||||
}
|
||||
let (pid, context) = inner.proc.take().unwrap();
|
||||
trace!("CPU{} stop running thread {}", inner.id, pid);
|
||||
inner.manager.stop(pid, context);
|
||||
let (tid, context) = inner.proc.take().unwrap();
|
||||
trace!("CPU{} stop running thread {}", inner.id, tid);
|
||||
inner.manager.stop(tid, context);
|
||||
} else {
|
||||
trace!("CPU{} idle", inner.id);
|
||||
unsafe { interrupt::enable_and_wfi(); }
|
||||
|
@ -11,19 +11,19 @@ mod rr;
|
||||
mod stride;
|
||||
mod work_stealing;
|
||||
|
||||
type Pid = usize;
|
||||
type Tid = usize;
|
||||
|
||||
/// The scheduler for a ThreadPool
|
||||
pub trait Scheduler: 'static {
|
||||
/// Push a thread to the back of ready queue.
|
||||
fn push(&self, pid: Pid);
|
||||
fn push(&self, tid: Tid);
|
||||
/// Select a thread to run, pop it from the queue.
|
||||
fn pop(&self, cpu_id: usize) -> Option<Pid>;
|
||||
fn pop(&self, cpu_id: usize) -> Option<Tid>;
|
||||
/// Got a tick from CPU.
|
||||
/// Return true if need reschedule.
|
||||
fn tick(&self, current_pid: Pid) -> bool;
|
||||
fn tick(&self, current_tid: Tid) -> bool;
|
||||
/// Set priority of a thread.
|
||||
fn set_priority(&self, pid: Pid, priority: u8);
|
||||
fn set_priority(&self, tid: Tid, priority: u8);
|
||||
}
|
||||
|
||||
fn expand<T: Default + Clone>(vec: &mut Vec<T>, id: usize) {
|
||||
|
@ -13,21 +13,21 @@ struct RRSchedulerInner {
|
||||
struct RRProcInfo {
|
||||
present: bool,
|
||||
rest_slice: usize,
|
||||
prev: Pid,
|
||||
next: Pid,
|
||||
prev: Tid,
|
||||
next: Tid,
|
||||
}
|
||||
|
||||
impl Scheduler for RRScheduler {
|
||||
fn push(&self, pid: usize) {
|
||||
self.inner.lock().push(pid);
|
||||
fn push(&self, tid: usize) {
|
||||
self.inner.lock().push(tid);
|
||||
}
|
||||
fn pop(&self, _cpu_id: usize) -> Option<usize> {
|
||||
self.inner.lock().pop()
|
||||
}
|
||||
fn tick(&self, current_pid: usize) -> bool {
|
||||
self.inner.lock().tick(current_pid)
|
||||
fn tick(&self, current_tid: usize) -> bool {
|
||||
self.inner.lock().tick(current_tid)
|
||||
}
|
||||
fn set_priority(&self, _pid: usize, _priority: u8) {}
|
||||
fn set_priority(&self, _tid: usize, _priority: u8) {}
|
||||
}
|
||||
|
||||
impl RRScheduler {
|
||||
@ -41,35 +41,35 @@ impl RRScheduler {
|
||||
}
|
||||
|
||||
impl RRSchedulerInner {
|
||||
fn push(&mut self, pid: Pid) {
|
||||
let pid = pid + 1;
|
||||
expand(&mut self.infos, pid);
|
||||
fn push(&mut self, tid: Tid) {
|
||||
let tid = tid + 1;
|
||||
expand(&mut self.infos, tid);
|
||||
{
|
||||
let info = &mut self.infos[pid];
|
||||
let info = &mut self.infos[tid];
|
||||
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!("rr push {}", pid - 1);
|
||||
self._list_add_before(tid, 0);
|
||||
trace!("rr push {}", tid - 1);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<Pid> {
|
||||
fn pop(&mut self) -> Option<Tid> {
|
||||
let ret = match self.infos[0].next {
|
||||
0 => None,
|
||||
pid => {
|
||||
self.infos[pid].present = false;
|
||||
self._list_remove(pid);
|
||||
Some(pid - 1)
|
||||
tid => {
|
||||
self.infos[tid].present = false;
|
||||
self._list_remove(tid);
|
||||
Some(tid - 1)
|
||||
},
|
||||
};
|
||||
trace!("rr pop {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn tick(&mut self, current: Pid) -> bool {
|
||||
fn tick(&mut self, current: Tid) -> bool {
|
||||
let current = current + 1;
|
||||
expand(&mut self.infos, current);
|
||||
assert!(!self.infos[current].present);
|
||||
@ -85,18 +85,18 @@ impl RRSchedulerInner {
|
||||
}
|
||||
|
||||
impl RRSchedulerInner {
|
||||
fn _list_add_before(&mut self, i: Pid, at: Pid) {
|
||||
fn _list_add_before(&mut self, i: Tid, at: Tid) {
|
||||
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_add_after(&mut self, i: Pid, at: Pid) {
|
||||
fn _list_add_after(&mut self, i: Tid, at: Tid) {
|
||||
let next = self.infos[at].next;
|
||||
self._list_add_before(i, next);
|
||||
}
|
||||
fn _list_remove(&mut self, i: Pid) {
|
||||
fn _list_remove(&mut self, i: Tid) {
|
||||
let next = self.infos[i].next;
|
||||
let prev = self.infos[i].prev;
|
||||
self.infos[next].prev = prev;
|
||||
|
@ -7,7 +7,7 @@ pub struct StrideScheduler {
|
||||
pub struct StrideSchedulerInner {
|
||||
max_time_slice: usize,
|
||||
infos: Vec<StrideProcInfo>,
|
||||
queue: BinaryHeap<(Stride, Pid)>, // It's max heap, so pass < 0
|
||||
queue: BinaryHeap<(Stride, Tid)>, // It's max heap, so pass < 0
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
@ -35,17 +35,17 @@ impl StrideProcInfo {
|
||||
type Stride = i32;
|
||||
|
||||
impl Scheduler for StrideScheduler {
|
||||
fn push(&self, pid: usize) {
|
||||
self.inner.lock().push(pid);
|
||||
fn push(&self, tid: usize) {
|
||||
self.inner.lock().push(tid);
|
||||
}
|
||||
fn pop(&self, _cpu_id: usize) -> Option<usize> {
|
||||
self.inner.lock().pop()
|
||||
}
|
||||
fn tick(&self, current_pid: usize) -> bool {
|
||||
self.inner.lock().tick(current_pid)
|
||||
fn tick(&self, current_tid: usize) -> bool {
|
||||
self.inner.lock().tick(current_tid)
|
||||
}
|
||||
fn set_priority(&self, pid: usize, priority: u8) {
|
||||
self.inner.lock().set_priority(pid, priority);
|
||||
fn set_priority(&self, tid: usize, priority: u8) {
|
||||
self.inner.lock().set_priority(tid, priority);
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,31 +61,31 @@ impl StrideScheduler {
|
||||
}
|
||||
|
||||
impl StrideSchedulerInner {
|
||||
fn push(&mut self, pid: Pid) {
|
||||
expand(&mut self.infos, pid);
|
||||
let info = &mut self.infos[pid];
|
||||
fn push(&mut self, tid: Tid) {
|
||||
expand(&mut self.infos, tid);
|
||||
let info = &mut self.infos[tid];
|
||||
assert!(!info.present);
|
||||
info.present = true;
|
||||
if info.rest_slice == 0 {
|
||||
info.rest_slice = self.max_time_slice;
|
||||
}
|
||||
self.queue.push((-info.stride, pid));
|
||||
trace!("stride push {}", pid);
|
||||
self.queue.push((-info.stride, tid));
|
||||
trace!("stride push {}", tid);
|
||||
}
|
||||
|
||||
fn pop(&mut self) -> Option<Pid> {
|
||||
let ret = self.queue.pop().map(|(_, pid)| pid);
|
||||
if let Some(pid) = ret {
|
||||
let old_stride = self.infos[pid].stride;
|
||||
self.infos[pid].pass();
|
||||
let stride = self.infos[pid].stride;
|
||||
trace!("stride {} {:#x} -> {:#x}", pid, old_stride, stride);
|
||||
fn pop(&mut self) -> Option<Tid> {
|
||||
let ret = self.queue.pop().map(|(_, tid)| tid);
|
||||
if let Some(tid) = ret {
|
||||
let old_stride = self.infos[tid].stride;
|
||||
self.infos[tid].pass();
|
||||
let stride = self.infos[tid].stride;
|
||||
trace!("stride {} {:#x} -> {:#x}", tid, old_stride, stride);
|
||||
}
|
||||
trace!("stride pop {:?}", ret);
|
||||
ret
|
||||
}
|
||||
|
||||
fn tick(&mut self, current: Pid) -> bool {
|
||||
fn tick(&mut self, current: Tid) -> bool {
|
||||
expand(&mut self.infos, current);
|
||||
assert!(!self.infos[current].present);
|
||||
|
||||
@ -98,8 +98,8 @@ impl StrideSchedulerInner {
|
||||
*rest == 0
|
||||
}
|
||||
|
||||
fn set_priority(&mut self, pid: Pid, priority: u8) {
|
||||
self.infos[pid].priority = priority;
|
||||
trace!("stride {} priority = {}", pid, priority);
|
||||
fn set_priority(&mut self, tid: Tid, priority: u8) {
|
||||
self.infos[tid].priority = priority;
|
||||
trace!("stride {} priority = {}", tid, priority);
|
||||
}
|
||||
}
|
||||
|
@ -3,9 +3,9 @@ use deque::{self, Stealer, Worker, Stolen};
|
||||
|
||||
pub struct WorkStealingScheduler {
|
||||
/// The ready queue of each processors
|
||||
workers: Vec<Worker<Pid>>,
|
||||
workers: Vec<Worker<Tid>>,
|
||||
/// Stealers to all processors' queue
|
||||
stealers: Vec<Stealer<Pid>>,
|
||||
stealers: Vec<Stealer<Tid>>,
|
||||
}
|
||||
|
||||
impl WorkStealingScheduler {
|
||||
@ -16,17 +16,17 @@ impl WorkStealingScheduler {
|
||||
}
|
||||
|
||||
impl Scheduler for WorkStealingScheduler {
|
||||
fn push(&self, pid: usize) {
|
||||
fn push(&self, tid: usize) {
|
||||
// TODO: push to random queue?
|
||||
// now just push to cpu0
|
||||
self.workers[0].push(pid);
|
||||
trace!("work-stealing: cpu0 push thread {}", pid);
|
||||
self.workers[0].push(tid);
|
||||
trace!("work-stealing: cpu0 push thread {}", tid);
|
||||
}
|
||||
|
||||
fn pop(&self, cpu_id: usize) -> Option<usize> {
|
||||
if let Some(pid) = self.workers[cpu_id].pop() {
|
||||
trace!("work-stealing: cpu{} pop thread {}", cpu_id, pid);
|
||||
return Some(pid);
|
||||
if let Some(tid) = self.workers[cpu_id].pop() {
|
||||
trace!("work-stealing: cpu{} pop thread {}", cpu_id, tid);
|
||||
return Some(tid);
|
||||
}
|
||||
let n = self.workers.len();
|
||||
for i in 1..n {
|
||||
@ -38,9 +38,9 @@ impl Scheduler for WorkStealingScheduler {
|
||||
match self.stealers[other_id].steal() {
|
||||
Stolen::Abort => {} // retry
|
||||
Stolen::Empty => break,
|
||||
Stolen::Data(pid) => {
|
||||
trace!("work-stealing: cpu{} steal thread {} from cpu{}", cpu_id, pid, other_id);
|
||||
return Some(pid);
|
||||
Stolen::Data(tid) => {
|
||||
trace!("work-stealing: cpu{} steal thread {} from cpu{}", cpu_id, tid, other_id);
|
||||
return Some(tid);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -48,9 +48,9 @@ impl Scheduler for WorkStealingScheduler {
|
||||
None
|
||||
}
|
||||
|
||||
fn tick(&self, _current_pid: usize) -> bool {
|
||||
fn tick(&self, _current_tid: usize) -> bool {
|
||||
true
|
||||
}
|
||||
|
||||
fn set_priority(&self, _pid: usize, _priority: u8) {}
|
||||
fn set_priority(&self, _tid: usize, _priority: u8) {}
|
||||
}
|
||||
|
@ -92,17 +92,16 @@ pub fn spawn<F, T>(f: F) -> JoinHandle<T>
|
||||
|
||||
// 在Processor中创建新的线程
|
||||
let context = new_kernel_context(kernel_thread_entry::<F, T>, f as usize);
|
||||
let pid = processor().manager().add(context, 0);
|
||||
let tid = processor().manager().add(context);
|
||||
|
||||
// 接下来看看`JoinHandle::join()`的实现
|
||||
// 了解是如何获取f返回值的
|
||||
return JoinHandle {
|
||||
thread: Thread { pid },
|
||||
mark: PhantomData,
|
||||
};
|
||||
}
|
||||
|
||||
/// Cooperatively gives up a timeslice to the OS scheduler.
|
||||
/// Cooperatively gives up a time slice to the OS scheduler.
|
||||
pub fn yield_now() {
|
||||
trace!("yield:");
|
||||
processor().yield_now();
|
||||
@ -131,119 +130,14 @@ impl Thread {
|
||||
}
|
||||
}
|
||||
|
||||
/// An owned permission to join on a thread (block on its termination).
|
||||
/// An owned permission to join on a process (block on its termination).
|
||||
pub struct JoinHandle<T> {
|
||||
thread: Thread,
|
||||
mark: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> JoinHandle<T> {
|
||||
/// Extracts a handle to the underlying thread.
|
||||
pub fn thread(&self) -> &Thread {
|
||||
&self.thread
|
||||
}
|
||||
/// Waits for the associated thread to finish.
|
||||
pub fn join(self) -> Result<T, ()> {
|
||||
loop {
|
||||
trace!("{} join", self.thread.pid);
|
||||
match processor().manager().get_status(self.thread.pid) {
|
||||
Some(Status::Exited(exit_code)) => {
|
||||
processor().manager().remove(self.thread.pid);
|
||||
// Find return value on the heap from the exit code.
|
||||
return Ok(unsafe { *Box::from_raw(exit_code as *mut T) });
|
||||
}
|
||||
None => return Err(()),
|
||||
_ => {}
|
||||
}
|
||||
processor().manager().wait(current().id(), self.thread.pid);
|
||||
processor().yield_now();
|
||||
}
|
||||
unimplemented!();
|
||||
}
|
||||
/// Force construct a JoinHandle struct
|
||||
pub unsafe fn _of(pid: Tid) -> JoinHandle<T> {
|
||||
JoinHandle {
|
||||
thread: Thread { pid },
|
||||
mark: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//pub struct LocalKey<T: 'static> {
|
||||
// init: fn() -> T,
|
||||
//}
|
||||
//
|
||||
//impl<T: 'static> LocalKey<T> {
|
||||
// pub fn with<F, R>(&'static self, f: F) -> R
|
||||
// where F: FnOnce(&T) -> R
|
||||
// {
|
||||
// let map = unsafe { Self::get_map() };
|
||||
// let key = self as *const _ as usize;
|
||||
// if !map.contains_key(&key) {
|
||||
// map.insert(key, Box::new((self.init)()));
|
||||
// }
|
||||
// let value = map.get(&key).unwrap().downcast_ref::<T>().expect("type error");
|
||||
// f(value)
|
||||
// }
|
||||
// pub const fn new(init: fn() -> T) -> Self {
|
||||
// LocalKey { init }
|
||||
// }
|
||||
// /// Get `BTreeMap<usize, Box<Any>>` at the current kernel stack bottom
|
||||
// /// The stack must be aligned with 0x8000
|
||||
// unsafe fn get_map() -> &'static mut BTreeMap<usize, Box<Any>> {
|
||||
// const STACK_SIZE: usize = 0x8000;
|
||||
// let stack_var = 0usize;
|
||||
// let ptr = (&stack_var as *const _ as usize) / STACK_SIZE * STACK_SIZE;
|
||||
// let map = unsafe { &mut *(ptr as *mut Option<BTreeMap<usize, Box<Any>>>) };
|
||||
// if map.is_none() {
|
||||
// *map = Some(BTreeMap::new());
|
||||
// }
|
||||
// map.as_mut().unwrap()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//pub mod test {
|
||||
// use thread;
|
||||
// use core::cell::RefCell;
|
||||
// use core::time::Duration;
|
||||
//
|
||||
// pub fn unpack() {
|
||||
// let parked_thread = thread::spawn(|| {
|
||||
// println!("Parking thread");
|
||||
// thread::park();
|
||||
// println!("Thread unparked");
|
||||
// 5
|
||||
// });
|
||||
//
|
||||
// // Let some time pass for the thread to be spawned.
|
||||
// thread::sleep(Duration::from_secs(2));
|
||||
//
|
||||
// println!("Unpark the thread");
|
||||
// parked_thread.thread().unpark();
|
||||
//
|
||||
// let ret = parked_thread.join().unwrap();
|
||||
// assert_eq!(ret, 5);
|
||||
// }
|
||||
//
|
||||
// pub fn local_key() {
|
||||
// static FOO: thread::LocalKey<RefCell<usize>> = thread::LocalKey::new(|| RefCell::new(1));
|
||||
//
|
||||
// FOO.with(|f| {
|
||||
// assert_eq!(*f.borrow(), 1);
|
||||
// *f.borrow_mut() = 2;
|
||||
// });
|
||||
//
|
||||
// // each thread starts out with the initial value of 1
|
||||
// thread::spawn(move || {
|
||||
// FOO.with(|f| {
|
||||
// assert_eq!(*f.borrow(), 1);
|
||||
// *f.borrow_mut() = 3;
|
||||
// });
|
||||
// }).join();
|
||||
//
|
||||
// // we retain our original value of 2 despite the child thread
|
||||
// FOO.with(|f| {
|
||||
// assert_eq!(*f.borrow(), 2);
|
||||
// });
|
||||
// println!("local key success");
|
||||
// }
|
||||
//}
|
||||
}
|
@ -1,6 +1,6 @@
|
||||
use alloc::boxed::Box;
|
||||
use alloc::vec::Vec;
|
||||
use spin::Mutex;
|
||||
use spin::{Mutex, MutexGuard};
|
||||
use log::*;
|
||||
use crate::scheduler::Scheduler;
|
||||
use crate::timer::Timer;
|
||||
@ -9,8 +9,6 @@ struct Thread {
|
||||
status: Status,
|
||||
status_after_stop: Status,
|
||||
context: Option<Box<Context>>,
|
||||
parent: Tid,
|
||||
children: Vec<Tid>,
|
||||
}
|
||||
|
||||
pub type Tid = usize;
|
||||
@ -21,7 +19,6 @@ pub enum Status {
|
||||
Ready,
|
||||
Running(usize),
|
||||
Sleeping,
|
||||
Waiting(Tid),
|
||||
/// aka ZOMBIE. Its context was dropped.
|
||||
Exited(ExitCode),
|
||||
}
|
||||
@ -32,7 +29,11 @@ enum Event {
|
||||
}
|
||||
|
||||
pub trait Context {
|
||||
/// Switch to target context
|
||||
unsafe fn switch_to(&mut self, target: &mut Context);
|
||||
|
||||
/// A tid is allocated for this context
|
||||
fn set_tid(&mut self, tid: Tid);
|
||||
}
|
||||
|
||||
pub struct ThreadPool {
|
||||
@ -50,28 +51,27 @@ impl ThreadPool {
|
||||
}
|
||||
}
|
||||
|
||||
fn alloc_tid(&self) -> Tid {
|
||||
fn alloc_tid(&self) -> (Tid, MutexGuard<Option<Thread>>) {
|
||||
for (i, proc) in self.threads.iter().enumerate() {
|
||||
if proc.lock().is_none() {
|
||||
return i;
|
||||
let thread = proc.lock();
|
||||
if thread.is_none() {
|
||||
return (i, thread);
|
||||
}
|
||||
}
|
||||
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 {
|
||||
/// Add a new thread
|
||||
/// Calls action with tid and thread context
|
||||
pub fn add(&self, mut context: Box<Context>) -> Tid {
|
||||
let (tid, mut thread) = self.alloc_tid();
|
||||
context.set_tid(tid);
|
||||
*thread = Some(Thread {
|
||||
status: Status::Ready,
|
||||
status_after_stop: Status::Ready,
|
||||
context: Some(context),
|
||||
parent,
|
||||
children: Vec::new(),
|
||||
});
|
||||
self.scheduler.push(tid);
|
||||
self.threads[parent].lock().as_mut().expect("invalid parent proc")
|
||||
.children.push(tid);
|
||||
tid
|
||||
}
|
||||
|
||||
@ -162,7 +162,6 @@ impl ThreadPool {
|
||||
}
|
||||
|
||||
/// 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");
|
||||
@ -170,13 +169,6 @@ impl ThreadPool {
|
||||
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;
|
||||
}
|
||||
@ -194,32 +186,12 @@ impl ThreadPool {
|
||||
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 get_parent(&self, tid: Tid) -> Tid {
|
||||
self.threads[tid].lock().as_ref().expect("process not exist").parent
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -1,13 +1,13 @@
|
||||
use alloc::{boxed::Box, collections::BTreeMap, string::String, sync::Arc, vec::Vec};
|
||||
use alloc::{boxed::Box, collections::BTreeMap, collections::btree_map::Entry, string::String, sync::Arc, vec::Vec, sync::Weak};
|
||||
use core::fmt;
|
||||
|
||||
use log::*;
|
||||
use rcore_fs::vfs::INode;
|
||||
use spin::Mutex;
|
||||
use spin::{Mutex, RwLock};
|
||||
use xmas_elf::{ElfFile, header, program::{Flags, Type}};
|
||||
use smoltcp::socket::SocketHandle;
|
||||
use smoltcp::wire::IpEndpoint;
|
||||
use rcore_memory::PAGE_SIZE;
|
||||
use rcore_thread::Tid;
|
||||
|
||||
use crate::arch::interrupt::{Context, TrapFrame};
|
||||
use crate::memory::{ByFrame, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet};
|
||||
@ -69,13 +69,67 @@ impl fmt::Debug for FileLike {
|
||||
}
|
||||
}
|
||||
|
||||
/// Pid type
|
||||
/// For strong type separation
|
||||
#[derive(Clone, PartialEq)]
|
||||
pub struct Pid(Option<usize>);
|
||||
|
||||
impl Pid {
|
||||
pub fn uninitialized() -> Self {
|
||||
Pid(None)
|
||||
}
|
||||
|
||||
pub fn no_one() -> Self {
|
||||
Pid(Some(0))
|
||||
}
|
||||
|
||||
/// Return if it was uninitialized before this call
|
||||
/// When returning true, it usually means this is the first thread
|
||||
pub fn set_if_uninitialized(&mut self, tid: Tid) -> bool {
|
||||
if self.0 == None {
|
||||
self.0 = Some(tid as usize);
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(&self) -> usize {
|
||||
self.0.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Pid {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self.0 {
|
||||
Some(pid) => write!(f, "{}", pid),
|
||||
None => write!(f, "None"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Process {
|
||||
pub memory_set: MemorySet,
|
||||
pub files: BTreeMap<usize, FileLike>,
|
||||
pub cwd: String,
|
||||
pub pid: Pid, // i.e. tgid, usually the tid of first thread
|
||||
pub ppid: Pid, // the pid of the parent process
|
||||
pub threads: Vec<Tid>, // threads in the same process
|
||||
pub exit_cond: Condvar, // notified when the whole process is going to terminate
|
||||
pub exit_code: Option<usize>, // only available when last thread exits
|
||||
futexes: BTreeMap<usize, Arc<Condvar>>,
|
||||
}
|
||||
|
||||
/// Records the mapping between pid and Process struct.
|
||||
lazy_static! {
|
||||
pub static ref PROCESSES: RwLock<BTreeMap<usize, Weak<Mutex<Process>>>> = RwLock::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
/// Records the list of child processes
|
||||
lazy_static! {
|
||||
pub static ref CHILD_PROCESSES: RwLock<BTreeMap<usize, Vec<Arc<Mutex<Process>>>>> = RwLock::new(BTreeMap::new());
|
||||
}
|
||||
|
||||
/// 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) {
|
||||
@ -83,6 +137,26 @@ impl rcore_thread::Context for Thread {
|
||||
let (target, _): (&mut Thread, *const ()) = transmute(target);
|
||||
self.context.switch(&mut target.context);
|
||||
}
|
||||
|
||||
fn set_tid(&mut self, tid: Tid) {
|
||||
// set pid=tid if unspecified
|
||||
let mut proc = self.proc.lock();
|
||||
if proc.pid.set_if_uninitialized(tid) {
|
||||
// first thread in the process
|
||||
// link to its ppid
|
||||
match CHILD_PROCESSES.write().entry(proc.ppid.get()) {
|
||||
Entry::Vacant(entry) => {
|
||||
entry.insert(vec![self.proc.clone()]);
|
||||
}
|
||||
Entry::Occupied(mut entry) => {
|
||||
entry.get_mut().push(self.proc.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
// add it to threads
|
||||
proc.threads.push(tid);
|
||||
PROCESSES.write().insert(proc.pid.get(), Arc::downgrade(&self.proc));
|
||||
}
|
||||
}
|
||||
|
||||
impl Thread {
|
||||
@ -97,6 +171,11 @@ impl Thread {
|
||||
files: BTreeMap::default(),
|
||||
cwd: String::from("/"),
|
||||
futexes: BTreeMap::default(),
|
||||
pid: Pid::uninitialized(),
|
||||
ppid: Pid::no_one(),
|
||||
exit_cond: Condvar::new(),
|
||||
threads: Vec::new(),
|
||||
exit_code: None
|
||||
})),
|
||||
})
|
||||
}
|
||||
@ -114,6 +193,11 @@ impl Thread {
|
||||
files: BTreeMap::default(),
|
||||
cwd: String::from("/"),
|
||||
futexes: BTreeMap::default(),
|
||||
pid: Pid::uninitialized(),
|
||||
ppid: Pid::no_one(),
|
||||
exit_cond: Condvar::new(),
|
||||
threads: Vec::new(),
|
||||
exit_code: None
|
||||
})),
|
||||
})
|
||||
}
|
||||
@ -201,6 +285,11 @@ impl Thread {
|
||||
files,
|
||||
cwd: String::from("/"),
|
||||
futexes: BTreeMap::default(),
|
||||
pid: Pid::uninitialized(),
|
||||
ppid: Pid::no_one(),
|
||||
exit_cond: Condvar::new(),
|
||||
threads: Vec::new(),
|
||||
exit_code: None
|
||||
})),
|
||||
})
|
||||
}
|
||||
@ -211,6 +300,7 @@ impl Thread {
|
||||
let memory_set = self.proc.lock().memory_set.clone();
|
||||
let files = self.proc.lock().files.clone();
|
||||
let cwd = self.proc.lock().cwd.clone();
|
||||
let ppid = self.proc.lock().pid.clone();
|
||||
debug!("fork: finish clone MemorySet");
|
||||
|
||||
// MMU: copy data to the new space
|
||||
@ -244,6 +334,11 @@ impl Thread {
|
||||
files,
|
||||
cwd,
|
||||
futexes: BTreeMap::default(),
|
||||
pid: Pid::uninitialized(),
|
||||
ppid,
|
||||
exit_cond: Condvar::new(),
|
||||
threads: Vec::new(),
|
||||
exit_code: None
|
||||
})),
|
||||
})
|
||||
}
|
||||
|
@ -13,9 +13,9 @@ pub fn run_user_shell() {
|
||||
println!("Going to user mode shell.");
|
||||
println!("Use 'ls' to list available programs.");
|
||||
let data = inode.read_as_vec().unwrap();
|
||||
processor().manager().add(Thread::new_user(data.as_slice(), "sh".split(' ')), 0);
|
||||
processor().manager().add(Thread::new_user(data.as_slice(), "sh".split(' ')));
|
||||
} else {
|
||||
processor().manager().add(Thread::new_kernel(shell, 0), 0);
|
||||
processor().manager().add(Thread::new_kernel(shell, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,8 +33,8 @@ pub extern fn shell(_arg: usize) -> ! {
|
||||
let name = cmd.trim().split(' ').next().unwrap();
|
||||
if let Ok(file) = ROOT_INODE.lookup(name) {
|
||||
let data = file.read_as_vec().unwrap();
|
||||
let pid = processor().manager().add(Thread::new_user(data.as_slice(), cmd.split(' ')), thread::current().id());
|
||||
unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap();
|
||||
let pid = processor().manager().add(Thread::new_user(data.as_slice(), cmd.split(' ')));
|
||||
//unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap();
|
||||
} else {
|
||||
println!("Program not exist");
|
||||
}
|
||||
|
@ -14,7 +14,7 @@ impl Condvar {
|
||||
}
|
||||
|
||||
pub fn _wait(&self) {
|
||||
self.wait_queue.lock().push_back(Arc::new(thread::current()));
|
||||
self.add_to_wait_queue();
|
||||
thread::park();
|
||||
}
|
||||
|
||||
@ -26,6 +26,10 @@ impl Condvar {
|
||||
thread::park();
|
||||
}
|
||||
|
||||
pub fn add_to_wait_queue(&self) {
|
||||
self.wait_queue.lock().push_back(Arc::new(thread::current()));
|
||||
}
|
||||
|
||||
pub fn wait<'a, T, S>(&self, guard: MutexGuard<'a, T, S>) -> MutexGuard<'a, T, S>
|
||||
where S: MutexSupport
|
||||
{
|
||||
|
@ -29,9 +29,12 @@ mod misc;
|
||||
|
||||
/// System call dispatcher
|
||||
pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
|
||||
let pid = cpu::id();
|
||||
let cid = cpu::id();
|
||||
let pid = {
|
||||
process().pid.clone()
|
||||
};
|
||||
let tid = processor().tid();
|
||||
debug!("{}:{} syscall id {} begin", pid, tid, id);
|
||||
debug!("{}:{}:{} syscall id {} begin", cid, pid, tid, id);
|
||||
let ret = match id {
|
||||
// file
|
||||
000 => sys_read(args[0], args[1] as *mut u8, args[2]),
|
||||
@ -77,7 +80,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
|
||||
// use fork for vfork
|
||||
058 => sys_fork(tf),
|
||||
059 => sys_exec(args[0] as *const u8, args[1] as *const *const u8, args[2] as *const *const u8, tf),
|
||||
060 => sys_exit(args[0] as isize),
|
||||
060 => sys_exit(args[0] as usize),
|
||||
061 => sys_wait4(args[0] as isize, args[1] as *mut i32), // TODO: wait4
|
||||
062 => sys_kill(args[0]),
|
||||
063 => sys_uname(args[0] as *mut u8),
|
||||
@ -109,6 +112,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
|
||||
204 => sys_sched_getaffinity(args[0], args[1], args[2] as *mut u32),
|
||||
217 => sys_getdents64(args[0], args[1] as *mut LinuxDirent64, args[2]),
|
||||
228 => sys_clock_gettime(args[0], args[1] as *mut TimeSpec),
|
||||
231 => sys_exit_group(args[0]),
|
||||
288 => sys_accept(args[0], args[1] as *mut SockAddr, args[2] as *mut u32), // use accept for accept4
|
||||
// 293 => sys_pipe(),
|
||||
|
||||
@ -185,10 +189,6 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
|
||||
warn!("sys_set_tid_address is unimplemented");
|
||||
Ok(thread::current().id())
|
||||
}
|
||||
231 => {
|
||||
warn!("sys_exit_group is unimplemented");
|
||||
sys_exit(args[0] as isize);
|
||||
}
|
||||
280 => {
|
||||
warn!("sys_utimensat is unimplemented");
|
||||
Ok(0)
|
||||
@ -206,7 +206,7 @@ pub fn syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> isize {
|
||||
crate::trap::error(tf);
|
||||
}
|
||||
};
|
||||
debug!("{}:{} syscall id {} ret with {:x?}", pid, tid, id, ret);
|
||||
debug!("{}:{}:{} syscall id {} ret with {:x?}", cid, pid, tid, id, ret);
|
||||
match ret {
|
||||
Ok(code) => code as isize,
|
||||
Err(err) => -(err as isize),
|
||||
@ -313,6 +313,14 @@ impl fmt::Display for SysError {
|
||||
ENOLCK => "No record locks available",
|
||||
ENOSYS => "Function not implemented",
|
||||
ENOTEMPTY => "Directory not empty",
|
||||
ENOTSOCK => "Socket operation on non-socket",
|
||||
ENOPROTOOPT => "Protocol not available",
|
||||
EPFNOSUPPORT => "Protocol family not supported",
|
||||
EAFNOSUPPORT => "Address family not supported by protocol",
|
||||
ENOBUFS => "No buffer space available",
|
||||
EISCONN => "Transport endpoint is already connected",
|
||||
ENOTCONN => "Transport endpoint is not connected",
|
||||
ECONNREFUSED => "Connection refused",
|
||||
_ => "Unknown error",
|
||||
},
|
||||
)
|
||||
|
@ -1,11 +1,13 @@
|
||||
//! Syscalls for process
|
||||
|
||||
use super::*;
|
||||
use crate::process::{PROCESSES, CHILD_PROCESSES};
|
||||
use crate::sync::Condvar;
|
||||
|
||||
/// Fork the current process. Return the child's PID.
|
||||
pub fn sys_fork(tf: &TrapFrame) -> SysResult {
|
||||
let new_thread = current_thread().fork(tf);
|
||||
let pid = processor().manager().add(new_thread, thread::current().id());
|
||||
let pid = processor().manager().add(new_thread);
|
||||
info!("fork: {} -> {}", thread::current().id(), pid);
|
||||
Ok(pid)
|
||||
}
|
||||
@ -29,7 +31,7 @@ pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut u32, child_tid: *m
|
||||
}
|
||||
let new_thread = current_thread().clone(tf, newsp, newtls, child_tid as usize);
|
||||
// FIXME: parent pid
|
||||
let tid = processor().manager().add(new_thread, thread::current().id());
|
||||
let tid = processor().manager().add(new_thread);
|
||||
info!("clone: {} -> {}", thread::current().id(), tid);
|
||||
unsafe {
|
||||
parent_tid.write(tid as u32);
|
||||
@ -42,6 +44,7 @@ pub fn sys_clone(flags: usize, newsp: usize, parent_tid: *mut u32, child_tid: *m
|
||||
/// Return the PID. Store exit code to `code` if it's not null.
|
||||
pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult {
|
||||
info!("wait4: pid: {}, code: {:?}", pid, wstatus);
|
||||
let cur_pid = process().pid.get();
|
||||
if !wstatus.is_null() {
|
||||
process().memory_set.check_mut_ptr(wstatus)?;
|
||||
}
|
||||
@ -57,13 +60,13 @@ pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult {
|
||||
};
|
||||
loop {
|
||||
use alloc::vec;
|
||||
let all_child: Vec<_> = CHILD_PROCESSES.read().get(&cur_pid).unwrap().clone();
|
||||
let wait_procs = match target {
|
||||
WaitFor::AnyChild => processor().manager().get_children(thread::current().id()),
|
||||
WaitFor::AnyChild => all_child,
|
||||
WaitFor::Pid(pid) => {
|
||||
// check if pid is a child
|
||||
if processor().manager().get_children(thread::current().id()).iter()
|
||||
.find(|&&p| p == pid).is_some() {
|
||||
vec![pid]
|
||||
if let Some(proc) = all_child.iter().find(|p| p.lock().pid.get() == pid) {
|
||||
vec![proc.clone()]
|
||||
} else {
|
||||
vec![]
|
||||
}
|
||||
@ -73,26 +76,25 @@ pub fn sys_wait4(pid: isize, wstatus: *mut i32) -> SysResult {
|
||||
return Err(SysError::ECHILD);
|
||||
}
|
||||
|
||||
for pid in wait_procs {
|
||||
match processor().manager().get_status(pid) {
|
||||
Some(Status::Exited(exit_code)) => {
|
||||
if !wstatus.is_null() {
|
||||
unsafe { wstatus.write(exit_code as i32); }
|
||||
}
|
||||
processor().manager().remove(pid);
|
||||
info!("wait: {} -> {}", thread::current().id(), pid);
|
||||
return Ok(pid);
|
||||
}
|
||||
None => return Err(SysError::ECHILD),
|
||||
_ => {}
|
||||
for proc_lock in wait_procs.iter() {
|
||||
let proc = proc_lock.lock();
|
||||
if let Some(exit_code) = proc.exit_code {
|
||||
// recycle process
|
||||
let pid = proc.pid.get();
|
||||
drop(proc);
|
||||
|
||||
let mut child_processes = CHILD_PROCESSES.write();
|
||||
child_processes.get_mut(&cur_pid).unwrap().retain(|p| p.lock().pid.get() != pid);
|
||||
child_processes.remove(&pid);
|
||||
return Ok(pid);
|
||||
}
|
||||
}
|
||||
info!("wait: {} -> {:?}, sleep", thread::current().id(), target);
|
||||
match target {
|
||||
WaitFor::AnyChild => processor().manager().wait_child(thread::current().id()),
|
||||
WaitFor::Pid(pid) => processor().manager().wait(thread::current().id(), pid),
|
||||
|
||||
for proc in wait_procs.iter() {
|
||||
proc.lock().exit_cond.add_to_wait_queue();
|
||||
}
|
||||
processor().yield_now();
|
||||
thread::park();
|
||||
}
|
||||
}
|
||||
|
||||
@ -163,7 +165,7 @@ pub fn sys_kill(pid: usize) -> SysResult {
|
||||
/// Get the current process id
|
||||
pub fn sys_getpid() -> SysResult {
|
||||
info!("getpid");
|
||||
Ok(thread::current().id())
|
||||
Ok(process().pid.get())
|
||||
}
|
||||
|
||||
/// Get the current thread id
|
||||
@ -175,15 +177,21 @@ pub fn sys_gettid() -> SysResult {
|
||||
|
||||
/// Get the parent process id
|
||||
pub fn sys_getppid() -> SysResult {
|
||||
let pid = thread::current().id();
|
||||
let ppid = processor().manager().get_parent(pid);
|
||||
Ok(ppid)
|
||||
Ok(process().ppid.get())
|
||||
}
|
||||
|
||||
/// Exit the current thread
|
||||
pub fn sys_exit(exit_code: isize) -> ! {
|
||||
let pid = thread::current().id();
|
||||
info!("exit: {}, code: {}", pid, exit_code);
|
||||
pub fn sys_exit(exit_code: usize) -> ! {
|
||||
let tid = thread::current().id();
|
||||
info!("exit: {}, code: {}", tid, exit_code);
|
||||
let mut proc = process();
|
||||
proc.threads.retain(|&id| id != tid);
|
||||
if proc.threads.len() == 0 {
|
||||
// last thread
|
||||
proc.exit_code = Some(exit_code);
|
||||
proc.exit_cond.notify_all();
|
||||
}
|
||||
drop(proc);
|
||||
|
||||
// perform futex wake 1
|
||||
// ref: http://man7.org/linux/man-pages/man2/set_tid_address.2.html
|
||||
@ -196,7 +204,24 @@ pub fn sys_exit(exit_code: isize) -> ! {
|
||||
queue.notify_one();
|
||||
}
|
||||
|
||||
processor().manager().exit(pid, exit_code as usize);
|
||||
processor().manager().exit(tid, exit_code as usize);
|
||||
processor().yield_now();
|
||||
unreachable!();
|
||||
}
|
||||
|
||||
/// Exit the current thread group (i.e. progress)
|
||||
pub fn sys_exit_group(exit_code: usize) -> ! {
|
||||
let mut proc = process();
|
||||
info!("exit_group: {}, code: {}", proc.pid, exit_code);
|
||||
|
||||
// quit all threads
|
||||
for tid in proc.threads.iter() {
|
||||
processor().manager().exit(*tid, exit_code);
|
||||
}
|
||||
proc.exit_code = Some(exit_code);
|
||||
proc.exit_cond.notify_all();
|
||||
drop(proc);
|
||||
|
||||
processor().yield_now();
|
||||
unreachable!();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user