Pass uCore `forktest`

master
WangRunji 7 years ago
parent 731d6319e4
commit d798c8084a

@ -23,10 +23,11 @@ pub fn init() {
idt[T_SWITCH_TOU].set_handler_fn(to_user);
idt[T_SWITCH_TOK].set_handler_fn(to_kernel)
.set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
// TODO: Enable interrupt during syscall
idt[T_SYSCALL].set_handler_fn(syscall)
.set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::TRAP);
.set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
idt[0x80].set_handler_fn(syscall32)
.set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::TRAP);
.set_flags(IdtFlags::PRESENT | IdtFlags::RING_3 | IdtFlags::INTERRUPT);
unsafe {
idt[T_DBLFLT].set_handler_fn(double_fault)

@ -84,7 +84,7 @@ interrupt_switch!(timer, stack, rsp, {
if tick % 100 == 0 {
println!("\nInterupt: Timer\ntick = {}", tick);
use process;
process::schedule(rsp);
process::schedule(&mut rsp);
}
ack(IRQ_TIMER);
});
@ -110,13 +110,13 @@ interrupt_stack_p!(to_kernel, stack, {
interrupt_switch!(syscall, stack, rsp, {
println!("\nInterupt: Syscall {:#x?}", stack.scratch.rax);
use syscall::syscall;
let ret = syscall(stack, rsp, false);
let ret = syscall(stack, &mut rsp, false);
stack.scratch.rax = ret as usize;
});
interrupt_switch!(syscall32, stack, rsp, {
// println!("\nInterupt: Syscall {:#x?}", stack.scratch.rax);
use syscall::syscall;
let ret = syscall(stack, rsp, true);
let ret = syscall(stack, &mut rsp, true);
stack.scratch.rax = ret as usize;
});

@ -318,9 +318,21 @@ macro_rules! interrupt_switch {
($name:ident, $stack: ident, $rsp: ident, $func:block) => {
#[naked]
pub unsafe extern fn $name () {
// WARNING: Don't do anything outside the inner function.
// rbp is not pointing to kernel stack!
#[inline(never)]
unsafe fn inner($stack: &mut InterruptStackP, $rsp: &mut usize) {
unsafe fn inner($stack: &mut InterruptStackP) -> usize {
let mut $rsp = $stack as *const _ as usize;
$func
// Set return rsp if to user
use arch::gdt;
use core::mem::size_of;
let tf = &mut *($rsp as *mut TrapFrame);
if tf.iret.cs & 0x3 == 3 {
gdt::set_ring0_rsp($rsp + size_of::<TrapFrame>());
}
$rsp
}
// Push scratch registers
@ -329,20 +341,11 @@ macro_rules! interrupt_switch {
fs_push!();
// Get reference to stack variables
let mut rsp: usize;
let rsp: usize;
asm!("" : "={rsp}"(rsp) : : : "intel", "volatile");
// Call inner rust function
inner(&mut *(rsp as *mut InterruptStackP), &mut rsp);
// Set return rsp if to user
use arch::gdt;
use core::mem::size_of;
let tf = &mut *(rsp as *mut TrapFrame);
if tf.iret.cs & 0x3 == 3 {
gdt::set_ring0_rsp(rsp + size_of::<TrapFrame>());
}
let rsp = inner(&mut *(rsp as *mut InterruptStackP));
asm!("" : : "{rsp}"(rsp) : : "intel", "volatile");
// Pop scratch registers and return

@ -37,14 +37,15 @@ pub fn load_sfs() {
let files = root.borrow().list().unwrap();
debug!("Loading programs: {:?}", files);
for name in files.iter().filter(|&f| f != "." && f != "..") {
// for name in files.iter().filter(|&f| f != "." && f != "..") {
for name in files.iter().filter(|&f| f == "forktest") {
static mut BUF: [u8; 64 << 12] = [0; 64 << 12];
let file = root.borrow().lookup(name.as_str()).unwrap();
let len = file.borrow().read_at(0, unsafe { &mut BUF }).unwrap();
process::add_user_process(name.as_str(), unsafe { &BUF[..len] });
process::add_user_process(name, unsafe { &BUF[..len] });
}
process::add_user_process("forktest", unsafe { MemBuf::new(_binary_user_forktest_start, _binary_user_forktest_end).0 });
// process::add_user_process("forktest", unsafe { MemBuf::new(_binary_user_forktest_start, _binary_user_forktest_end).0 });
process::print();
}

@ -66,7 +66,7 @@ pub fn init(boot_info: BootInformation) -> MemoryController {
let stack_allocator = {
let stack_alloc_range = Page::range_of(KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE,
KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE + 0x100000);
KERNEL_HEAP_OFFSET + KERNEL_HEAP_SIZE + 0x1000000);
stack_allocator::StackAllocator::new(stack_alloc_range)
};

@ -84,6 +84,6 @@ impl Stack {
impl Drop for Stack {
fn drop(&mut self) {
panic!("stack leak: {:#x?}", self);
debug!("WARNING: stack leak: {:#x?}", self);
}
}

@ -48,13 +48,32 @@ extern fn idle_thread() {
}
}
/// Fork the current process
/// Fork the current process. Return the child's PID.
pub fn sys_fork(tf: &TrapFrame) -> i32 {
let mut processor = PROCESSOR.try().unwrap().lock();
let mut mc = MC.try().unwrap().lock();
let new = processor.current().fork(tf, &mut mc);
processor.add(new);
0
let pid = processor.add(new);
debug!("fork: {}", pid);
pid as i32
}
/// Wait the process exit. Return the exit code.
pub fn sys_wait(rsp: &mut usize, pid: usize) -> i32 {
debug!("wait: {}", pid);
let mut processor = PROCESSOR.try().unwrap().lock();
let target = match pid {
0 => WaitTarget::AnyChild,
_ => WaitTarget::Proc(pid),
};
match processor.current_wait_for(target) {
WaitResult::Ok(error_code) => error_code as i32,
WaitResult::Blocked => {
processor.schedule(rsp);
0 /* unused */
},
WaitResult::NotExist => { -1 }
}
}
/// Kill the process
@ -69,7 +88,7 @@ pub fn sys_getpid() -> i32 {
}
/// Exit the current process
pub fn sys_exit(rsp: &mut usize, error_code: usize) -> i32 {
pub fn sys_exit(rsp: &mut usize, error_code: ErrorCode) -> i32 {
let mut processor = PROCESSOR.try().unwrap().lock();
let pid = processor.current().pid;
processor.schedule(rsp);
@ -77,11 +96,11 @@ pub fn sys_exit(rsp: &mut usize, error_code: usize) -> i32 {
0
}
pub fn add_user_process(name: &str, data: &[u8]) {
pub fn add_user_process(name: impl AsRef<str>, data: &[u8]) {
let mut processor = PROCESSOR.try().unwrap().lock();
let mut mc = MC.try().unwrap().lock();
let mut new = Process::new_user(data, &mut mc);
new.name = String::from(name);
new.name = String::from(name.as_ref());
processor.add(new);
}

@ -7,6 +7,7 @@ use alloc::{rc::Rc, String};
#[derive(Debug)]
pub struct Process {
pub(in process) pid: Pid,
pub(in process) parent: Pid,
pub(in process) name: String,
kstack: Stack,
pub(in process) memory_set: Option<MemorySet>,
@ -17,10 +18,14 @@ pub struct Process {
}
pub type Pid = usize;
pub type ErrorCode = usize;
#[derive(Debug, Eq, PartialEq)]
pub enum Status {
Ready, Running, Sleeping(usize), Exited
Ready,
Running,
Sleeping(Pid),
Exited(ErrorCode),
}
impl Process {
@ -32,6 +37,7 @@ impl Process {
Process {
pid: 0,
parent: 0,
name: String::from(name),
kstack,
memory_set: None,
@ -48,6 +54,7 @@ impl Process {
assert_has_not_been_called!();
Process {
pid: 0,
parent: 0,
name: String::from("init"),
kstack: mc.kernel_stack.take().unwrap(),
memory_set: None,
@ -72,7 +79,7 @@ impl Process {
// User stack
use consts::{USER_STACK_OFFSET, USER_STACK_SIZE, USER_TCB_OFFSET};
let (user_stack_buttom, mut user_stack_top) = match is32 {
let (user_stack_buttom, user_stack_top) = match is32 {
true => (USER_TCB_OFFSET, USER_TCB_OFFSET + USER_STACK_SIZE),
false => (USER_STACK_OFFSET, USER_STACK_OFFSET + USER_STACK_SIZE),
};
@ -104,7 +111,6 @@ impl Process {
// TODO: full argc & argv
*(user_stack_top as *mut u32).offset(-1) = 0; // argv
*(user_stack_top as *mut u32).offset(-2) = 0; // argc
user_stack_top -= 8;
}
}
});
@ -112,12 +118,13 @@ impl Process {
// Allocate kernel stack and push trap frame
let kstack = mc.alloc_stack(7).unwrap();
let tf = TrapFrame::new_user_thread(entry_addr, user_stack_top, is32);
let tf = TrapFrame::new_user_thread(entry_addr, user_stack_top - 8, is32);
let rsp = kstack.push_at_top(tf);
// debug!("rsp = {:#x}", rsp);
Process {
pid: 0,
parent: 0,
name: String::new(),
kstack,
memory_set: Some(memory_set),
@ -152,11 +159,12 @@ impl Process {
// Allocate kernel stack and push trap frame
let kstack = mc.alloc_stack(7).unwrap();
let mut tf = tf.clone();
tf.scratch.rax = 1;
tf.scratch.rax = 0; // sys_fork return 0 for child
let rsp = kstack.push_at_top(tf);
Process {
pid: 0,
parent: self.pid,
name: self.name.clone() + "_fork",
kstack,
memory_set: Some(memory_set),
@ -166,6 +174,17 @@ impl Process {
is_user: true,
}
}
pub fn set_return_value(&self, value: usize) {
let tf = unsafe { &mut *(self.rsp as *mut TrapFrame) };
tf.scratch.rax = value;
}
pub fn exit_code(&self) -> Option<ErrorCode> {
match self.status {
Status::Exited(code) => Some(code),
_ => None,
}
}
}
use memory::{MemorySet, MemoryArea, PhysAddr, FromToVirtualAddress, EntryFlags};

@ -29,10 +29,11 @@ impl Processor {
return next;
}
pub fn add(&mut self, mut process: Process) {
pub fn add(&mut self, mut process: Process) -> Pid {
let pid = self.alloc_pid();
process.pid = pid;
self.procs.insert(pid, process);
pid
}
pub fn schedule(&mut self, rsp: &mut usize) {
@ -43,10 +44,13 @@ impl Processor {
fn find_next(&self) -> Pid {
*self.procs.keys()
.find(|&&i| i > self.current_pid
&& self.get(i).status != Status::Exited)
.unwrap_or(self.procs.keys().nth(0).unwrap())
&& self.get(i).exit_code().is_none())
.unwrap_or(self.procs.keys().next().unwrap())
}
/// Switch process to `pid`, switch page table if necessary.
/// Store `rsp` and point it to target kernel stack.
/// The current status will be set to `Ready` if it is `Running` now.
fn switch_to(&mut self, pid: Pid, rsp: &mut usize) {
// for debug print
let pid0 = self.current_pid;
@ -55,44 +59,102 @@ impl Processor {
if pid == self.current_pid {
return;
}
{
let current = self.procs.get_mut(&self.current_pid).unwrap();
current.status = Status::Ready;
current.rsp = *rsp;
self.current_pid = pid;
let (from, to) = self.get_mut2(pid0, pid);
// set `from`
if from.status == Status::Running {
from.status = Status::Ready;
}
{
let process = self.procs.get_mut(&pid).unwrap();
process.status = Status::Running;
*rsp = process.rsp;
// switch page table
if let Some(page_table) = process.page_table.take() {
let mut active_table = unsafe { ActivePageTable::new() };
let old_table = active_table.switch(page_table);
process.page_table = Some(old_table);
}
from.rsp = *rsp;
// set `to`
assert_eq!(to.status, Status::Ready);
to.status = Status::Running;
*rsp = to.rsp;
// switch page table
if let Some(page_table) = to.page_table.take() {
let mut active_table = unsafe { ActivePageTable::new() };
let old_table = active_table.switch(page_table);
from.page_table = Some(old_table);
}
self.current_pid = pid;
debug!("Processor: switch from {} to {}\n rsp: {:#x} -> {:#x}", pid0, pid, rsp0, rsp);
}
fn get(&self, pid: Pid) -> &Process {
self.procs.get(&pid).unwrap()
}
fn get_mut(&mut self, pid: Pid) -> &mut Process {
self.procs.get_mut(&pid).unwrap()
}
fn get_mut2(&mut self, pid1: Pid, pid2: Pid) -> (&mut Process, &mut Process) {
assert_ne!(pid1, pid2);
let procs1 = &mut self.procs as *mut BTreeMap<_, _>;
let procs2 = procs1;
let p1 = unsafe { &mut *procs1 }.get_mut(&pid1).unwrap();
let p2 = unsafe { &mut *procs2 }.get_mut(&pid2).unwrap();
(p1, p2)
}
pub fn current(&self) -> &Process {
self.get(self.current_pid)
}
pub fn kill(&mut self, pid: Pid) {
let process = self.procs.get_mut(&pid).unwrap();
process.status = Status::Exited;
// TODO: Remove process from set
self.exit(pid, 0x1000); // TODO: error code for killed
}
pub fn exit(&mut self, pid: Pid, error_code: usize) {
pub fn exit(&mut self, pid: Pid, error_code: ErrorCode) {
assert_ne!(pid, self.current_pid);
debug!("Processor: {} exit, code: {}", pid, error_code);
self.kill(pid);
self.get_mut(pid).status = Status::Exited(error_code);
if let Some(waiter) = self.find_waiter(pid) {
{
let p = self.get_mut(waiter);
p.status = Status::Ready;
p.set_return_value(error_code);
}
debug!("Processor: remove {}", pid);
self.procs.remove(&pid);
}
}
/// Let current process wait for another
pub fn current_wait_for(&mut self, target: WaitTarget) -> WaitResult {
// Find one target process and it's exit code
let (pid, exit_code) = match target {
WaitTarget::AnyChild => {
let childs = self.procs.values()
.filter(|&p| p.parent == self.current_pid);
if childs.clone().next().is_none() {
return WaitResult::NotExist;
}
childs.clone()
.find(|&p| p.exit_code().is_some())
.map(|p| (p.pid, p.exit_code()))
.unwrap_or((0, None))
}
WaitTarget::Proc(pid) => (pid, self.get(pid).exit_code()),
};
if let Some(exit_code) = exit_code {
debug!("Processor: remove {}", pid);
self.procs.remove(&pid);
WaitResult::Ok(exit_code)
} else {
debug!("Processor: {} wait for {}", self.current_pid, pid);
let current_pid = self.current_pid;
self.get_mut(current_pid).status = Status::Sleeping(pid);
WaitResult::Blocked
}
}
fn find_waiter(&self, pid: Pid) -> Option<Pid> {
self.procs.values().find(|&p| {
p.status == Status::Sleeping(pid) ||
(p.status == Status::Sleeping(0) && self.get(pid).parent == p.pid)
}).map(|ref p| p.pid)
}
}
@ -102,4 +164,19 @@ impl Debug for Processor {
.entries(self.procs.iter().map(|(pid, proc0)| { (pid, &proc0.name) }))
.finish()
}
}
pub enum WaitTarget {
AnyChild,
Proc(Pid),
}
pub enum WaitResult {
/// The target process is still running.
/// The waiter's status will be set to `Sleeping`.
Blocked,
/// The target process is exited with `ErrorCode`.
Ok(ErrorCode),
/// The target process is not exist.
NotExist,
}

@ -21,6 +21,8 @@ pub unsafe fn syscall(tf: &TrapFrame, rsp: &mut usize, is32: bool) -> i32 {
io::open(args[0] as *const u8, args[1]),
Syscall::Xv6(SYS_CLOSE) | Syscall::Ucore(UCORE_SYS_CLOSE) =>
io::close(args[0]),
Syscall::Xv6(SYS_WAIT) | Syscall::Ucore(UCORE_SYS_WAIT) =>
process::sys_wait(rsp, args[0]),
Syscall::Xv6(SYS_FORK) | Syscall::Ucore(UCORE_SYS_FORK) =>
process::sys_fork(tf),
Syscall::Xv6(SYS_KILL) | Syscall::Ucore(UCORE_SYS_KILL) =>

Loading…
Cancel
Save