From 8cb11b7aa84550c4a4c527d2b039967a690c7f9e Mon Sep 17 00:00:00 2001 From: Jiajie Chen Date: Sun, 10 Mar 2019 15:23:15 +0800 Subject: [PATCH] Implement tid/pid/ppid separation --- crate/memory/src/memory_set/mod.rs | 2 + crate/thread/src/processor.rs | 6 +- crate/thread/src/scheduler/mod.rs | 10 +- crate/thread/src/scheduler/rr.rs | 44 ++++---- crate/thread/src/scheduler/stride.rs | 46 ++++---- crate/thread/src/scheduler/work_stealing.rs | 26 ++--- crate/thread/src/std_thread.rs | 116 +------------------- crate/thread/src/thread_pool.rs | 58 +++------- kernel/src/process/structs.rs | 101 ++++++++++++++++- kernel/src/shell.rs | 8 +- kernel/src/sync/condvar.rs | 6 +- kernel/src/syscall/mod.rs | 24 ++-- kernel/src/syscall/proc.rs | 85 +++++++++----- 13 files changed, 266 insertions(+), 266 deletions(-) diff --git a/crate/memory/src/memory_set/mod.rs b/crate/memory/src/memory_set/mod.rs index 221fdc5..46c2ae9 100644 --- a/crate/memory/src/memory_set/mod.rs +++ b/crate/memory/src/memory_set/mod.rs @@ -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 diff --git a/crate/thread/src/processor.rs b/crate/thread/src/processor.rs index f46d62e..9f3d506 100644 --- a/crate/thread/src/processor.rs +++ b/crate/thread/src/processor.rs @@ -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(); } diff --git a/crate/thread/src/scheduler/mod.rs b/crate/thread/src/scheduler/mod.rs index 9974f51..40ff440 100644 --- a/crate/thread/src/scheduler/mod.rs +++ b/crate/thread/src/scheduler/mod.rs @@ -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; + fn pop(&self, cpu_id: usize) -> Option; /// 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(vec: &mut Vec, id: usize) { diff --git a/crate/thread/src/scheduler/rr.rs b/crate/thread/src/scheduler/rr.rs index b404191..acbc375 100644 --- a/crate/thread/src/scheduler/rr.rs +++ b/crate/thread/src/scheduler/rr.rs @@ -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 { 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 { + fn pop(&mut self) -> Option { 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; diff --git a/crate/thread/src/scheduler/stride.rs b/crate/thread/src/scheduler/stride.rs index 262d64d..e399c19 100644 --- a/crate/thread/src/scheduler/stride.rs +++ b/crate/thread/src/scheduler/stride.rs @@ -7,7 +7,7 @@ pub struct StrideScheduler { pub struct StrideSchedulerInner { max_time_slice: usize, infos: Vec, - 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 { 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 { - 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 { + 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); } } diff --git a/crate/thread/src/scheduler/work_stealing.rs b/crate/thread/src/scheduler/work_stealing.rs index d085633..acb2044 100644 --- a/crate/thread/src/scheduler/work_stealing.rs +++ b/crate/thread/src/scheduler/work_stealing.rs @@ -3,9 +3,9 @@ use deque::{self, Stealer, Worker, Stolen}; pub struct WorkStealingScheduler { /// The ready queue of each processors - workers: Vec>, + workers: Vec>, /// Stealers to all processors' queue - stealers: Vec>, + stealers: Vec>, } 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 { - 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) {} } diff --git a/crate/thread/src/std_thread.rs b/crate/thread/src/std_thread.rs index e59a559..5cbceb0 100644 --- a/crate/thread/src/std_thread.rs +++ b/crate/thread/src/std_thread.rs @@ -92,17 +92,16 @@ pub fn spawn(f: F) -> JoinHandle // 在Processor中创建新的线程 let context = new_kernel_context(kernel_thread_entry::, 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 { - thread: Thread, mark: PhantomData, } impl JoinHandle { - /// 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 { - 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(); - } - } - /// Force construct a JoinHandle struct - pub unsafe fn _of(pid: Tid) -> JoinHandle { - JoinHandle { - thread: Thread { pid }, - mark: PhantomData, - } + unimplemented!(); } -} - -//pub struct LocalKey { -// init: fn() -> T, -//} -// -//impl LocalKey { -// pub fn with(&'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::().expect("type error"); -// f(value) -// } -// pub const fn new(init: fn() -> T) -> Self { -// LocalKey { init } -// } -// /// Get `BTreeMap>` at the current kernel stack bottom -// /// The stack must be aligned with 0x8000 -// unsafe fn get_map() -> &'static mut BTreeMap> { -// 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>>) }; -// 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> = 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"); -// } -//} +} \ No newline at end of file diff --git a/crate/thread/src/thread_pool.rs b/crate/thread/src/thread_pool.rs index 8a8e5b0..87fa627 100644 --- a/crate/thread/src/thread_pool.rs +++ b/crate/thread/src/thread_pool.rs @@ -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>, - parent: Tid, - children: Vec, } 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>) { 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, 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) -> 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 { - 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; } diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs index fb25f89..6a92710 100644 --- a/kernel/src/process/structs.rs +++ b/kernel/src/process/structs.rs @@ -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); + +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, 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, // threads in the same process + pub exit_cond: Condvar, // notified when the whole process is going to terminate + pub exit_code: Option, // only available when last thread exits futexes: BTreeMap>, } +/// Records the mapping between pid and Process struct. +lazy_static! { + pub static ref PROCESSES: RwLock>>> = RwLock::new(BTreeMap::new()); +} + +/// Records the list of child processes +lazy_static! { + pub static ref CHILD_PROCESSES: RwLock>>>> = 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 })), }) } diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs index e98848d..02ce244 100644 --- a/kernel/src/shell.rs +++ b/kernel/src/shell.rs @@ -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"); } diff --git a/kernel/src/sync/condvar.rs b/kernel/src/sync/condvar.rs index 92767b1..e6f3f68 100644 --- a/kernel/src/sync/condvar.rs +++ b/kernel/src/sync/condvar.rs @@ -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 { diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 86b324c..7969b4e 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -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", }, ) diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs index a79c72e..3d5c330 100644 --- a/kernel/src/syscall/proc.rs +++ b/kernel/src/syscall/proc.rs @@ -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!(); }