Merge branch 'ch8' into main

This commit is contained in:
Yu Chen 2021-11-20 15:01:58 +08:00
commit fbacac0585
65 changed files with 1998 additions and 660 deletions

View file

@ -8,7 +8,7 @@ pub const PAGE_SIZE: usize = 0x1000;
pub const PAGE_SIZE_BITS: usize = 0xc;
pub const TRAMPOLINE: usize = usize::MAX - PAGE_SIZE + 1;
pub const TRAP_CONTEXT: usize = TRAMPOLINE - PAGE_SIZE;
pub const TRAP_CONTEXT_BASE: usize = TRAMPOLINE - PAGE_SIZE;
#[cfg(feature = "board_k210")]
pub const CLOCK_FREQ: usize = 403000000 / 62;
@ -39,4 +39,4 @@ pub const MMIO: &[(usize, usize)] = &[
(0x5200_0000, 0x1000), /* SPI0 */
(0x5300_0000, 0x1000), /* SPI1 */
(0x5400_0000, 0x1000), /* SPI2 */
];
];

View file

@ -19,14 +19,14 @@ pub fn print(args: fmt::Arguments) {
#[macro_export]
macro_rules! print {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!($fmt $(, $($arg)+)?));
$crate::console::print(format_args!($fmt $(, $($arg)+)?))
}
}
#[macro_export]
macro_rules! println {
($fmt: literal $(, $($arg: tt)+)?) => {
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?));
$crate::console::print(format_args!(concat!($fmt, "\n") $(, $($arg)+)?))
}
}

View file

@ -13,7 +13,7 @@ use k210_soc::{
sysctl,
sleep::usleep,
};
use spin::Mutex;
use crate::sync::UPSafeCell;
use lazy_static::*;
use super::BlockDevice;
use core::convert::TryInto;
@ -711,7 +711,9 @@ fn io_init() {
}
lazy_static! {
static ref PERIPHERALS: Mutex<Peripherals> = Mutex::new(Peripherals::take().unwrap());
static ref PERIPHERALS: UPSafeCell<Peripherals> = unsafe {
UPSafeCell::new(Peripherals::take().unwrap())
};
}
fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
@ -735,19 +737,19 @@ fn init_sdcard() -> SDCard<SPIImpl<SPI0>> {
sd
}
pub struct SDCardWrapper(Mutex<SDCard<SPIImpl<SPI0>>>);
pub struct SDCardWrapper(UPSafeCell<SDCard<SPIImpl<SPI0>>>);
impl SDCardWrapper {
pub fn new() -> Self {
Self(Mutex::new(init_sdcard()))
unsafe { Self(UPSafeCell::new(init_sdcard())) }
}
}
impl BlockDevice for SDCardWrapper {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
self.0.lock().read_sector(buf,block_id as u32).unwrap();
self.0.exclusive_access().read_sector(buf,block_id as u32).unwrap();
}
fn write_block(&self, block_id: usize, buf: &[u8]) {
self.0.lock().write_sector(buf,block_id as u32).unwrap();
self.0.exclusive_access().write_sector(buf,block_id as u32).unwrap();
}
}

View file

@ -12,34 +12,42 @@ use crate::mm::{
kernel_token,
};
use super::BlockDevice;
use spin::Mutex;
use crate::sync::UPSafeCell;
use alloc::vec::Vec;
use lazy_static::*;
#[allow(unused)]
const VIRTIO0: usize = 0x10001000;
pub struct VirtIOBlock(Mutex<VirtIOBlk<'static>>);
pub struct VirtIOBlock(UPSafeCell<VirtIOBlk<'static>>);
lazy_static! {
static ref QUEUE_FRAMES: Mutex<Vec<FrameTracker>> = Mutex::new(Vec::new());
static ref QUEUE_FRAMES: UPSafeCell<Vec<FrameTracker>> = unsafe {
UPSafeCell::new(Vec::new())
};
}
impl BlockDevice for VirtIOBlock {
fn read_block(&self, block_id: usize, buf: &mut [u8]) {
self.0.lock().read_block(block_id, buf).expect("Error when reading VirtIOBlk");
self.0.exclusive_access()
.read_block(block_id, buf)
.expect("Error when reading VirtIOBlk");
}
fn write_block(&self, block_id: usize, buf: &[u8]) {
self.0.lock().write_block(block_id, buf).expect("Error when writing VirtIOBlk");
self.0.exclusive_access()
.write_block(block_id, buf)
.expect("Error when writing VirtIOBlk");
}
}
impl VirtIOBlock {
#[allow(unused)]
pub fn new() -> Self {
Self(Mutex::new(VirtIOBlk::new(
unsafe { &mut *(VIRTIO0 as *mut VirtIOHeader) }
).unwrap()))
unsafe {
Self(UPSafeCell::new(VirtIOBlk::new(
&mut *(VIRTIO0 as *mut VirtIOHeader)
).unwrap()))
}
}
}
@ -50,7 +58,7 @@ pub extern "C" fn virtio_dma_alloc(pages: usize) -> PhysAddr {
let frame = frame_alloc().unwrap();
if i == 0 { ppn_base = frame.ppn; }
assert_eq!(frame.ppn.0, ppn_base.0 + i);
QUEUE_FRAMES.lock().push(frame);
QUEUE_FRAMES.exclusive_access().push(frame);
}
ppn_base.into()
}

View file

@ -9,4 +9,4 @@ _start:
boot_stack:
.space 4096 * 16
.globl boot_stack_top
boot_stack_top:
boot_stack_top:

View file

@ -3,18 +3,18 @@ use easy_fs::{
Inode,
};
use crate::drivers::BLOCK_DEVICE;
use crate::sync::UPSafeCell;
use alloc::sync::Arc;
use lazy_static::*;
use bitflags::*;
use alloc::vec::Vec;
use spin::Mutex;
use super::File;
use crate::mm::UserBuffer;
pub struct OSInode {
readable: bool,
writable: bool,
inner: Mutex<OSInodeInner>,
inner: UPSafeCell<OSInodeInner>,
}
pub struct OSInodeInner {
@ -31,14 +31,14 @@ impl OSInode {
Self {
readable,
writable,
inner: Mutex::new(OSInodeInner {
inner: unsafe { UPSafeCell::new(OSInodeInner {
offset: 0,
inode,
}),
})},
}
}
pub fn read_all(&self) -> Vec<u8> {
let mut inner = self.inner.lock();
let mut inner = self.inner.exclusive_access();
let mut buffer = [0u8; 512];
let mut v: Vec<u8> = Vec::new();
loop {
@ -133,7 +133,7 @@ impl File for OSInode {
fn readable(&self) -> bool { self.readable }
fn writable(&self) -> bool { self.writable }
fn read(&self, mut buf: UserBuffer) -> usize {
let mut inner = self.inner.lock();
let mut inner = self.inner.exclusive_access();
let mut total_read_size = 0usize;
for slice in buf.buffers.iter_mut() {
let read_size = inner.inode.read_at(inner.offset, *slice);
@ -146,7 +146,7 @@ impl File for OSInode {
total_read_size
}
fn write(&self, buf: UserBuffer) -> usize {
let mut inner = self.inner.lock();
let mut inner = self.inner.exclusive_access();
let mut total_write_size = 0usize;
for slice in buf.buffers.iter() {
let write_size = inner.inode.write_at(inner.offset, *slice);

View file

@ -1,26 +1,25 @@
use super::File;
use alloc::sync::{Arc, Weak};
use spin::Mutex;
use crate::mm::{
UserBuffer,
};
use crate::sync::UPSafeCell;
use crate::mm::UserBuffer;
use crate::task::suspend_current_and_run_next;
pub struct Pipe {
readable: bool,
writable: bool,
buffer: Arc<Mutex<PipeRingBuffer>>,
buffer: Arc<UPSafeCell<PipeRingBuffer>>,
}
impl Pipe {
pub fn read_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> Self {
pub fn read_end_with_buffer(buffer: Arc<UPSafeCell<PipeRingBuffer>>) -> Self {
Self {
readable: true,
writable: false,
buffer,
}
}
pub fn write_end_with_buffer(buffer: Arc<Mutex<PipeRingBuffer>>) -> Self {
pub fn write_end_with_buffer(buffer: Arc<UPSafeCell<PipeRingBuffer>>) -> Self {
Self {
readable: false,
writable: true,
@ -101,14 +100,16 @@ impl PipeRingBuffer {
/// Return (read_end, write_end)
pub fn make_pipe() -> (Arc<Pipe>, Arc<Pipe>) {
let buffer = Arc::new(Mutex::new(PipeRingBuffer::new()));
let buffer = Arc::new(unsafe {
UPSafeCell::new(PipeRingBuffer::new())
});
let read_end = Arc::new(
Pipe::read_end_with_buffer(buffer.clone())
);
let write_end = Arc::new(
Pipe::write_end_with_buffer(buffer.clone())
);
buffer.lock().set_write_end(&write_end);
buffer.exclusive_access().set_write_end(&write_end);
(read_end, write_end)
}
@ -120,7 +121,7 @@ impl File for Pipe {
let mut buf_iter = buf.into_iter();
let mut read_size = 0usize;
loop {
let mut ring_buffer = self.buffer.lock();
let mut ring_buffer = self.buffer.exclusive_access();
let loop_read = ring_buffer.available_read();
if loop_read == 0 {
if ring_buffer.all_write_ends_closed() {
@ -146,7 +147,7 @@ impl File for Pipe {
let mut buf_iter = buf.into_iter();
let mut write_size = 0usize;
loop {
let mut ring_buffer = self.buffer.lock();
let mut ring_buffer = self.buffer.exclusive_access();
let loop_write = ring_buffer.available_write();
if loop_write == 0 {
drop(ring_buffer);

View file

@ -1,5 +1,6 @@
use core::panic::PanicInfo;
use crate::sbi::shutdown;
use crate::task::current_kstack_top;
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
@ -14,5 +15,19 @@ fn panic(info: &PanicInfo) -> ! {
}
None => println!("[kernel] panicked at '{}'", info.message().unwrap())
}
unsafe { backtrace(); }
shutdown()
}
unsafe fn backtrace() {
let mut fp: usize;
let stop = current_kstack_top();
asm!("mv {}, s0", out(reg) fp);
println!("---START BACKTRACE---");
for i in 0..10 {
if fp == stop { break; }
println!("#{}:ra={:#x}", i, *((fp-8) as *const usize));
fp = *((fp-16) as *const usize);
}
println!("---END BACKTRACE---");
}

62
os/src/loader.rs Normal file
View file

@ -0,0 +1,62 @@
use alloc::vec::Vec;
use lazy_static::*;
pub fn get_num_app() -> usize {
extern "C" { fn _num_app(); }
unsafe { (_num_app as usize as *const usize).read_volatile() }
}
pub fn get_app_data(app_id: usize) -> &'static [u8] {
extern "C" { fn _num_app(); }
let num_app_ptr = _num_app as usize as *const usize;
let num_app = get_num_app();
let app_start = unsafe {
core::slice::from_raw_parts(num_app_ptr.add(1), num_app + 1)
};
assert!(app_id < num_app);
unsafe {
core::slice::from_raw_parts(
app_start[app_id] as *const u8,
app_start[app_id + 1] - app_start[app_id]
)
}
}
lazy_static! {
static ref APP_NAMES: Vec<&'static str> = {
let num_app = get_num_app();
extern "C" { fn _app_names(); }
let mut start = _app_names as usize as *const u8;
let mut v = Vec::new();
unsafe {
for _ in 0..num_app {
let mut end = start;
while end.read_volatile() != '\0' as u8 {
end = end.add(1);
}
let slice = core::slice::from_raw_parts(start, end as usize - start as usize);
let str = core::str::from_utf8(slice).unwrap();
v.push(str);
start = end.add(1);
}
}
v
};
}
#[allow(unused)]
pub fn get_app_data_by_name(name: &str) -> Option<&'static [u8]> {
let num_app = get_num_app();
(0..num_app)
.find(|&i| APP_NAMES[i] == name)
.map(|i| get_app_data(i))
}
pub fn list_apps() {
println!("/**** APPS ****");
for app in APP_NAMES.iter() {
println!("{}", app);
}
println!("**************/");
}

View file

@ -1,9 +1,8 @@
#![no_std]
#![no_main]
#![feature(global_asm)]
#![feature(llvm_asm)]
#![feature(asm)]
#![feature(panic_info_message)]
#![feature(const_in_array_repeat_expressions)]
#![feature(alloc_error_handler)]
extern crate alloc;
@ -20,6 +19,7 @@ mod trap;
mod config;
mod task;
mod timer;
mod sync;
mod mm;
mod fs;
mod drivers;
@ -31,9 +31,12 @@ fn clear_bss() {
fn sbss();
fn ebss();
}
(sbss as usize..ebss as usize).for_each(|a| {
unsafe { (a as *mut u8).write_volatile(0) }
});
unsafe {
core::slice::from_raw_parts_mut(
sbss as usize as *mut u8,
ebss as usize - sbss as usize,
).fill(0);
}
}
#[no_mangle]
@ -49,4 +52,4 @@ pub fn rust_main() -> ! {
task::add_initproc();
task::run_tasks();
panic!("Unreachable in rust_main!");
}
}

View file

@ -2,6 +2,11 @@ use crate::config::{PAGE_SIZE, PAGE_SIZE_BITS};
use super::PageTableEntry;
use core::fmt::{self, Debug, Formatter};
const PA_WIDTH_SV39: usize = 56;
const VA_WIDTH_SV39: usize = 39;
const PPN_WIDTH_SV39: usize = PA_WIDTH_SV39 - PAGE_SIZE_BITS;
const VPN_WIDTH_SV39: usize = VA_WIDTH_SV39 - PAGE_SIZE_BITS;
/// Definitions
#[repr(C)]
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
@ -47,16 +52,16 @@ impl Debug for PhysPageNum {
/// usize -> T: usize.into()
impl From<usize> for PhysAddr {
fn from(v: usize) -> Self { Self(v) }
fn from(v: usize) -> Self { Self(v & ( (1 << PA_WIDTH_SV39) - 1 )) }
}
impl From<usize> for PhysPageNum {
fn from(v: usize) -> Self { Self(v) }
fn from(v: usize) -> Self { Self(v & ( (1 << PPN_WIDTH_SV39) - 1 )) }
}
impl From<usize> for VirtAddr {
fn from(v: usize) -> Self { Self(v) }
fn from(v: usize) -> Self { Self(v & ( (1 << VA_WIDTH_SV39) - 1 )) }
}
impl From<usize> for VirtPageNum {
fn from(v: usize) -> Self { Self(v) }
fn from(v: usize) -> Self { Self(v & ( (1 << VPN_WIDTH_SV39) - 1 )) }
}
impl From<PhysAddr> for usize {
fn from(v: PhysAddr) -> Self { v.0 }
@ -206,4 +211,4 @@ impl<T> Iterator for SimpleRangeIterator<T> where
}
}
}
pub type VPNRange = SimpleRange<VirtPageNum>;
pub type VPNRange = SimpleRange<VirtPageNum>;

View file

@ -1,6 +1,6 @@
use super::{PhysAddr, PhysPageNum};
use alloc::vec::Vec;
use spin::Mutex;
use crate::sync::UPSafeCell;
use crate::config::MEMORY_END;
use lazy_static::*;
use core::fmt::{self, Debug, Formatter};
@ -88,8 +88,9 @@ impl FrameAllocator for StackFrameAllocator {
type FrameAllocatorImpl = StackFrameAllocator;
lazy_static! {
pub static ref FRAME_ALLOCATOR: Mutex<FrameAllocatorImpl> =
Mutex::new(FrameAllocatorImpl::new());
pub static ref FRAME_ALLOCATOR: UPSafeCell<FrameAllocatorImpl> = unsafe {
UPSafeCell::new(FrameAllocatorImpl::new())
};
}
pub fn init_frame_allocator() {
@ -97,20 +98,20 @@ pub fn init_frame_allocator() {
fn ekernel();
}
FRAME_ALLOCATOR
.lock()
.exclusive_access()
.init(PhysAddr::from(ekernel as usize).ceil(), PhysAddr::from(MEMORY_END).floor());
}
pub fn frame_alloc() -> Option<FrameTracker> {
FRAME_ALLOCATOR
.lock()
.exclusive_access()
.alloc()
.map(|ppn| FrameTracker::new(ppn))
}
pub fn frame_dealloc(ppn: PhysPageNum) {
FRAME_ALLOCATOR
.lock()
.exclusive_access()
.dealloc(ppn);
}

View file

@ -7,13 +7,11 @@ use alloc::vec::Vec;
use riscv::register::satp;
use alloc::sync::Arc;
use lazy_static::*;
use spin::Mutex;
use crate::sync::UPSafeCell;
use crate::config::{
MEMORY_END,
PAGE_SIZE,
TRAMPOLINE,
TRAP_CONTEXT,
USER_STACK_SIZE,
MMIO,
};
@ -31,13 +29,13 @@ extern "C" {
}
lazy_static! {
pub static ref KERNEL_SPACE: Arc<Mutex<MemorySet>> = Arc::new(Mutex::new(
MemorySet::new_kernel()
));
pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> = Arc::new(unsafe {
UPSafeCell::new(MemorySet::new_kernel())
});
}
pub fn kernel_token() -> usize {
KERNEL_SPACE.lock().token()
KERNEL_SPACE.exclusive_access().token()
}
pub struct MemorySet {
@ -142,8 +140,8 @@ impl MemorySet {
}
memory_set
}
/// Include sections in elf and trampoline and TrapContext and user stack,
/// also returns user_sp and entry point.
/// Include sections in elf and trampoline,
/// also returns user_sp_base and entry point.
pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
let mut memory_set = Self::new_bare();
// map trampoline
@ -178,26 +176,10 @@ impl MemorySet {
);
}
}
// map user stack with U flags
let max_end_va: VirtAddr = max_end_vpn.into();
let mut user_stack_bottom: usize = max_end_va.into();
// guard page
user_stack_bottom += PAGE_SIZE;
let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
memory_set.push(MapArea::new(
user_stack_bottom.into(),
user_stack_top.into(),
MapType::Framed,
MapPermission::R | MapPermission::W | MapPermission::U,
), None);
// map TrapContext
memory_set.push(MapArea::new(
TRAP_CONTEXT.into(),
TRAMPOLINE.into(),
MapType::Framed,
MapPermission::R | MapPermission::W,
), None);
(memory_set, user_stack_top, elf.header.pt2.entry_point() as usize)
let mut user_stack_base: usize = max_end_va.into();
user_stack_base += PAGE_SIZE;
(memory_set, user_stack_base, elf.header.pt2.entry_point() as usize)
}
pub fn from_existed_user(user_space: &MemorySet) -> MemorySet {
let mut memory_set = Self::new_bare();
@ -220,7 +202,7 @@ impl MemorySet {
let satp = self.page_table.token();
unsafe {
satp::write(satp);
llvm_asm!("sfence.vma" :::: "volatile");
asm!("sfence.vma");
}
}
pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
@ -338,7 +320,7 @@ bitflags! {
#[allow(unused)]
pub fn remap_test() {
let mut kernel_space = KERNEL_SPACE.lock();
let mut kernel_space = KERNEL_SPACE.exclusive_access();
let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into();
let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into();
let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into();
@ -355,4 +337,4 @@ pub fn remap_test() {
false,
);
println!("remap_test passed!");
}
}

View file

@ -24,5 +24,5 @@ pub use memory_set::remap_test;
pub fn init() {
heap_allocator::init_heap();
frame_allocator::init_frame_allocator();
KERNEL_SPACE.lock().activate();
KERNEL_SPACE.exclusive_access().activate();
}

View file

@ -252,4 +252,4 @@ impl Iterator for UserBufferIterator {
Some(r)
}
}
}
}

View file

@ -14,11 +14,12 @@ const SBI_SHUTDOWN: usize = 8;
fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
let mut ret;
unsafe {
llvm_asm!("ecall"
: "={x10}" (ret)
: "{x10}" (arg0), "{x11}" (arg1), "{x12}" (arg2), "{x17}" (which)
: "memory"
: "volatile"
asm!(
"ecall",
inlateout("x10") arg0 => ret,
in("x11") arg1,
in("x12") arg2,
in("x17") which,
);
}
ret

7
os/src/sync/mod.rs Normal file
View file

@ -0,0 +1,7 @@
mod up;
mod mutex;
mod semaphore;
pub use up::UPSafeCell;
pub use mutex::{Mutex, MutexSpin, MutexBlocking};
pub use semaphore::Semaphore;

87
os/src/sync/mutex.rs Normal file
View file

@ -0,0 +1,87 @@
use super::UPSafeCell;
use crate::task::{block_current_and_run_next, suspend_current_and_run_next};
use crate::task::TaskControlBlock;
use crate::task::{add_task, current_task};
use alloc::{sync::Arc, collections::VecDeque};
pub trait Mutex: Sync + Send {
fn lock(&self);
fn unlock(&self);
}
pub struct MutexSpin {
locked: UPSafeCell<bool>,
}
impl MutexSpin {
pub fn new() -> Self {
Self {
locked: unsafe { UPSafeCell::new(false) },
}
}
}
impl Mutex for MutexSpin {
fn lock(&self) {
loop {
let mut locked = self.locked.exclusive_access();
if *locked {
drop(locked);
suspend_current_and_run_next();
continue;
} else {
*locked = true;
return;
}
}
}
fn unlock(&self) {
let mut locked = self.locked.exclusive_access();
*locked = false;
}
}
pub struct MutexBlocking {
inner: UPSafeCell<MutexBlockingInner>,
}
pub struct MutexBlockingInner {
locked: bool,
wait_queue: VecDeque<Arc<TaskControlBlock>>,
}
impl MutexBlocking {
pub fn new() -> Self {
Self {
inner: unsafe {
UPSafeCell::new(MutexBlockingInner {
locked: false,
wait_queue: VecDeque::new(),
})
},
}
}
}
impl Mutex for MutexBlocking {
fn lock(&self) {
let mut mutex_inner = self.inner.exclusive_access();
if mutex_inner.locked {
mutex_inner.wait_queue.push_back(current_task().unwrap());
drop(mutex_inner);
block_current_and_run_next();
} else {
mutex_inner.locked = true;
}
}
fn unlock(&self) {
let mut mutex_inner = self.inner.exclusive_access();
assert_eq!(mutex_inner.locked, true);
mutex_inner.locked = false;
if let Some(waking_task) = mutex_inner.wait_queue.pop_front() {
add_task(waking_task);
}
}
}

45
os/src/sync/semaphore.rs Normal file
View file

@ -0,0 +1,45 @@
use alloc::{sync::Arc, collections::VecDeque};
use crate::task::{add_task, TaskControlBlock, current_task, block_current_and_run_next};
use crate::sync::UPSafeCell;
pub struct Semaphore {
pub inner: UPSafeCell<SemaphoreInner>,
}
pub struct SemaphoreInner {
pub count: isize,
pub wait_queue: VecDeque<Arc<TaskControlBlock>>,
}
impl Semaphore {
pub fn new(res_count: usize) -> Self {
Self {
inner: unsafe { UPSafeCell::new(
SemaphoreInner {
count: res_count as isize,
wait_queue: VecDeque::new(),
}
)},
}
}
pub fn up(&self) {
let mut inner = self.inner.exclusive_access();
inner.count += 1;
if inner.count <= 0 {
if let Some(task) = inner.wait_queue.pop_front() {
add_task(task);
}
}
}
pub fn down(&self) {
let mut inner = self.inner.exclusive_access();
inner.count -= 1;
if inner.count < 0 {
inner.wait_queue.push_back(current_task().unwrap());
drop(inner);
block_current_and_run_next();
}
}
}

27
os/src/sync/up.rs Normal file
View file

@ -0,0 +1,27 @@
use core::cell::{RefCell, RefMut};
/// Wrap a static data structure inside it so that we are
/// able to access it without any `unsafe`.
///
/// We should only use it in uniprocessor.
///
/// In order to get mutable reference of inner data, call
/// `exclusive_access`.
pub struct UPSafeCell<T> {
/// inner data
inner: RefCell<T>,
}
unsafe impl<T> Sync for UPSafeCell<T> {}
impl<T> UPSafeCell<T> {
/// User is responsible to guarantee that inner struct is only used in
/// uniprocessor.
pub unsafe fn new(value: T) -> Self {
Self { inner: RefCell::new(value) }
}
/// Panic if the data has been borrowed.
pub fn exclusive_access(&self) -> RefMut<'_, T> {
self.inner.borrow_mut()
}
}

View file

@ -4,14 +4,14 @@ use crate::mm::{
translated_refmut,
translated_str,
};
use crate::task::{current_user_token, current_task};
use crate::task::{current_user_token, current_process};
use crate::fs::{make_pipe, OpenFlags, open_file};
use alloc::sync::Arc;
pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
let token = current_user_token();
let task = current_task().unwrap();
let inner = task.acquire_inner_lock();
let process = current_process();
let inner = process.inner_exclusive_access();
if fd >= inner.fd_table.len() {
return -1;
}
@ -20,7 +20,7 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
return -1;
}
let file = file.clone();
// release Task lock manually to avoid deadlock
// release current task TCB manually to avoid multi-borrow
drop(inner);
file.write(
UserBuffer::new(translated_byte_buffer(token, buf, len))
@ -32,8 +32,8 @@ pub fn sys_write(fd: usize, buf: *const u8, len: usize) -> isize {
pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
let token = current_user_token();
let task = current_task().unwrap();
let inner = task.acquire_inner_lock();
let process = current_process();
let inner = process.inner_exclusive_access();
if fd >= inner.fd_table.len() {
return -1;
}
@ -42,7 +42,7 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
if !file.readable() {
return -1;
}
// release Task lock manually to avoid deadlock
// release current task TCB manually to avoid multi-borrow
drop(inner);
file.read(
UserBuffer::new(translated_byte_buffer(token, buf, len))
@ -53,14 +53,14 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize {
}
pub fn sys_open(path: *const u8, flags: u32) -> isize {
let task = current_task().unwrap();
let process = current_process();
let token = current_user_token();
let path = translated_str(token, path);
if let Some(inode) = open_file(
path.as_str(),
OpenFlags::from_bits(flags).unwrap()
) {
let mut inner = task.acquire_inner_lock();
let mut inner = process.inner_exclusive_access();
let fd = inner.alloc_fd();
inner.fd_table[fd] = Some(inode);
fd as isize
@ -70,8 +70,8 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize {
}
pub fn sys_close(fd: usize) -> isize {
let task = current_task().unwrap();
let mut inner = task.acquire_inner_lock();
let process = current_process();
let mut inner = process.inner_exclusive_access();
if fd >= inner.fd_table.len() {
return -1;
}
@ -83,9 +83,9 @@ pub fn sys_close(fd: usize) -> isize {
}
pub fn sys_pipe(pipe: *mut usize) -> isize {
let task = current_task().unwrap();
let process = current_process();
let token = current_user_token();
let mut inner = task.acquire_inner_lock();
let mut inner = process.inner_exclusive_access();
let (pipe_read, pipe_write) = make_pipe();
let read_fd = inner.alloc_fd();
inner.fd_table[read_fd] = Some(pipe_read);
@ -97,8 +97,8 @@ pub fn sys_pipe(pipe: *mut usize) -> isize {
}
pub fn sys_dup(fd: usize) -> isize {
let task = current_task().unwrap();
let mut inner = task.acquire_inner_lock();
let process = current_process();
let mut inner = process.inner_exclusive_access();
if fd >= inner.fd_table.len() {
return -1;
}
@ -108,4 +108,4 @@ pub fn sys_dup(fd: usize) -> isize {
let new_fd = inner.alloc_fd();
inner.fd_table[new_fd] = Some(Arc::clone(inner.fd_table[fd].as_ref().unwrap()));
new_fd as isize
}
}

View file

@ -5,18 +5,32 @@ const SYSCALL_PIPE: usize = 59;
const SYSCALL_READ: usize = 63;
const SYSCALL_WRITE: usize = 64;
const SYSCALL_EXIT: usize = 93;
const SYSCALL_SLEEP: usize = 101;
const SYSCALL_YIELD: usize = 124;
const SYSCALL_GET_TIME: usize = 169;
const SYSCALL_GETPID: usize = 172;
const SYSCALL_FORK: usize = 220;
const SYSCALL_EXEC: usize = 221;
const SYSCALL_WAITPID: usize = 260;
const SYSCALL_THREAD_CREATE: usize = 1000;
const SYSCALL_GETTID: usize = 1001;
const SYSCALL_WAITTID: usize = 1002;
const SYSCALL_MUTEX_CREATE: usize = 1010;
const SYSCALL_MUTEX_LOCK: usize = 1011;
const SYSCALL_MUTEX_UNLOCK: usize = 1012;
const SYSCALL_SEMAPHORE_CREATE: usize = 1020;
const SYSCALL_SEMAPHORE_UP: usize = 1021;
const SYSCALL_SEMAPHORE_DOWN: usize = 1022;
mod fs;
mod process;
mod thread;
mod sync;
use fs::*;
use process::*;
use thread::*;
use sync::*;
pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
match syscall_id {
@ -27,12 +41,22 @@ pub fn syscall(syscall_id: usize, args: [usize; 3]) -> isize {
SYSCALL_READ => sys_read(args[0], args[1] as *const u8, args[2]),
SYSCALL_WRITE => sys_write(args[0], args[1] as *const u8, args[2]),
SYSCALL_EXIT => sys_exit(args[0] as i32),
SYSCALL_SLEEP => sys_sleep(args[0]),
SYSCALL_YIELD => sys_yield(),
SYSCALL_GET_TIME => sys_get_time(),
SYSCALL_GETPID => sys_getpid(),
SYSCALL_FORK => sys_fork(),
SYSCALL_EXEC => sys_exec(args[0] as *const u8, args[1] as *const usize),
SYSCALL_WAITPID => sys_waitpid(args[0] as isize, args[1] as *mut i32),
SYSCALL_THREAD_CREATE => sys_thread_create(args[0], args[1]),
SYSCALL_GETTID => sys_gettid(),
SYSCALL_WAITTID => sys_waittid(args[0]) as isize,
SYSCALL_MUTEX_CREATE => sys_mutex_create(args[0] == 1),
SYSCALL_MUTEX_LOCK => sys_mutex_lock(args[0]),
SYSCALL_MUTEX_UNLOCK => sys_mutex_unlock(args[0]),
SYSCALL_SEMAPHORE_CREATE => sys_semaphore_creare(args[0]),
SYSCALL_SEMAPHORE_UP => sys_semaphore_up(args[0]),
SYSCALL_SEMAPHORE_DOWN => sys_semaphore_down(args[0]),
_ => panic!("Unsupported syscall_id: {}", syscall_id),
}
}

View file

@ -2,8 +2,8 @@ use crate::task::{
suspend_current_and_run_next,
exit_current_and_run_next,
current_task,
current_process,
current_user_token,
add_task,
};
use crate::timer::get_time_ms;
use crate::mm::{
@ -34,20 +34,20 @@ pub fn sys_get_time() -> isize {
}
pub fn sys_getpid() -> isize {
current_task().unwrap().pid.0 as isize
current_task().unwrap().process.upgrade().unwrap().getpid() as isize
}
pub fn sys_fork() -> isize {
let current_task = current_task().unwrap();
let new_task = current_task.fork();
let new_pid = new_task.pid.0;
let current_process = current_process();
let new_process = current_process.fork();
let new_pid = new_process.getpid();
// modify trap context of new_task, because it returns immediately after switching
let trap_cx = new_task.acquire_inner_lock().get_trap_cx();
let new_process_inner = new_process.inner_exclusive_access();
let task = new_process_inner.tasks[0].as_ref().unwrap();
let trap_cx = task.inner_exclusive_access().get_trap_cx();
// we do not have to move to next instruction since we have done it before
// for child process, fork returns 0
trap_cx.x[10] = 0;
// add new task to scheduler
add_task(new_task);
new_pid as isize
}
@ -65,9 +65,9 @@ pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize {
}
if let Some(app_inode) = open_file(path.as_str(), OpenFlags::RDONLY) {
let all_data = app_inode.read_all();
let task = current_task().unwrap();
let process = current_process();
let argc = args_vec.len();
task.exec(all_data.as_slice(), args_vec);
process.exec(all_data.as_slice(), args_vec);
// return argc because cx.x[10] will be covered with it later
argc as isize
} else {
@ -78,38 +78,37 @@ pub fn sys_exec(path: *const u8, mut args: *const usize) -> isize {
/// If there is not a child process whose pid is same as given, return -1.
/// Else if there is a child process but it is still running, return -2.
pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize {
let task = current_task().unwrap();
let process = current_process();
// find a child process
// ---- hold current PCB lock
let mut inner = task.acquire_inner_lock();
let mut inner = process.inner_exclusive_access();
if inner.children
.iter()
.find(|p| {pid == -1 || pid as usize == p.getpid()})
.is_none() {
return -1;
// ---- release current PCB lock
// ---- release current PCB
}
let pair = inner.children
.iter()
.enumerate()
.find(|(_, p)| {
// ++++ temporarily hold child PCB lock
p.acquire_inner_lock().is_zombie() && (pid == -1 || pid as usize == p.getpid())
// ++++ release child PCB lock
// ++++ temporarily access child PCB exclusively
p.inner_exclusive_access().is_zombie && (pid == -1 || pid as usize == p.getpid())
// ++++ release child PCB
});
if let Some((idx, _)) = pair {
let child = inner.children.remove(idx);
// confirm that child will be deallocated after being removed from children list
assert_eq!(Arc::strong_count(&child), 1);
let found_pid = child.getpid();
// ++++ temporarily hold child lock
let exit_code = child.acquire_inner_lock().exit_code;
// ++++ release child PCB lock
// ++++ temporarily access child PCB exclusively
let exit_code = child.inner_exclusive_access().exit_code;
// ++++ release child PCB
*translated_refmut(inner.memory_set.token(), exit_code_ptr) = exit_code;
found_pid as isize
} else {
-2
}
// ---- release current PCB lock automatically
}
// ---- release current PCB automatically
}

89
os/src/syscall/sync.rs Normal file
View file

@ -0,0 +1,89 @@
use crate::task::{current_task, current_process, block_current_and_run_next};
use crate::sync::{MutexSpin, MutexBlocking, Semaphore};
use crate::timer::{get_time_ms, add_timer};
use alloc::sync::Arc;
pub fn sys_sleep(ms: usize) -> isize {
let expire_ms = get_time_ms() + ms;
let task = current_task().unwrap();
add_timer(expire_ms, task);
block_current_and_run_next();
0
}
pub fn sys_mutex_create(blocking: bool) -> isize {
let process = current_process();
let mut process_inner = process.inner_exclusive_access();
if let Some(id) = process_inner
.mutex_list
.iter()
.enumerate()
.find(|(_, item)| item.is_none())
.map(|(id, _)| id) {
process_inner.mutex_list[id] = if !blocking {
Some(Arc::new(MutexSpin::new()))
} else {
Some(Arc::new(MutexBlocking::new()))
};
id as isize
} else {
process_inner.mutex_list.push(Some(Arc::new(MutexSpin::new())));
process_inner.mutex_list.len() as isize - 1
}
}
pub fn sys_mutex_lock(mutex_id: usize) -> isize {
let process = current_process();
let process_inner = process.inner_exclusive_access();
let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap());
drop(process_inner);
drop(process);
mutex.lock();
0
}
pub fn sys_mutex_unlock(mutex_id: usize) -> isize {
let process = current_process();
let process_inner = process.inner_exclusive_access();
let mutex = Arc::clone(process_inner.mutex_list[mutex_id].as_ref().unwrap());
drop(process_inner);
drop(process);
mutex.unlock();
0
}
pub fn sys_semaphore_creare(res_count: usize) -> isize {
let process = current_process();
let mut process_inner = process.inner_exclusive_access();
let id = if let Some(id) = process_inner
.semaphore_list
.iter()
.enumerate()
.find(|(_, item)| item.is_none())
.map(|(id, _)| id) {
process_inner.semaphore_list[id] = Some(Arc::new(Semaphore::new(res_count)));
id
} else {
process_inner.semaphore_list.push(Some(Arc::new(Semaphore::new(res_count))));
process_inner.semaphore_list.len() - 1
};
id as isize
}
pub fn sys_semaphore_up(sem_id: usize) -> isize {
let process = current_process();
let process_inner = process.inner_exclusive_access();
let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap());
drop(process_inner);
sem.up();
0
}
pub fn sys_semaphore_down(sem_id: usize) -> isize {
let process = current_process();
let process_inner = process.inner_exclusive_access();
let sem = Arc::clone(process_inner.semaphore_list[sem_id].as_ref().unwrap());
drop(process_inner);
sem.down();
0
}

71
os/src/syscall/thread.rs Normal file
View file

@ -0,0 +1,71 @@
use alloc::sync::Arc;
use crate::{mm::kernel_token, task::{TaskControlBlock, add_task, current_task}, trap::{TrapContext, trap_handler}};
pub fn sys_thread_create(entry: usize, arg: usize) -> isize {
let task = current_task().unwrap();
let process = task.process.upgrade().unwrap();
// create a new thread
let new_task = Arc::new(TaskControlBlock::new(
Arc::clone(&process),
task.inner_exclusive_access().res.as_ref().unwrap().ustack_base,
true,
));
// add new task to scheduler
add_task(Arc::clone(&new_task));
let new_task_inner = new_task.inner_exclusive_access();
let new_task_res = new_task_inner.res.as_ref().unwrap();
let new_task_tid = new_task_res.tid;
let mut process_inner = process.inner_exclusive_access();
// add new thread to current process
let tasks = &mut process_inner.tasks;
while tasks.len() < new_task_tid + 1 {
tasks.push(None);
}
tasks[new_task_tid] = Some(Arc::clone(&new_task));
let new_task_trap_cx = new_task_inner.get_trap_cx();
*new_task_trap_cx = TrapContext::app_init_context(
entry,
new_task_res.ustack_top(),
kernel_token(),
new_task.kstack.get_top(),
trap_handler as usize,
);
(*new_task_trap_cx).x[10] = arg;
new_task_tid as isize
}
pub fn sys_gettid() -> isize {
current_task().unwrap().inner_exclusive_access().res.as_ref().unwrap().tid as isize
}
/// thread does not exist, return -1
/// thread has not exited yet, return -2
/// otherwise, return thread's exit code
pub fn sys_waittid(tid: usize) -> i32 {
let task = current_task().unwrap();
let process = task.process.upgrade().unwrap();
let task_inner = task.inner_exclusive_access();
let mut process_inner = process.inner_exclusive_access();
// a thread cannot wait for itself
if task_inner.res.as_ref().unwrap().tid == tid {
return -1;
}
let mut exit_code: Option<i32> = None;
let waited_task = process_inner.tasks[tid].as_ref();
if let Some(waited_task) = waited_task {
if let Some(waited_exit_code) = waited_task.inner_exclusive_access().exit_code {
exit_code = Some(waited_exit_code);
}
} else {
// waited thread does not exist
return -1;
}
if let Some(exit_code) = exit_code {
// dealloc the exited thread
process_inner.tasks[tid] = None;
exit_code
} else {
// waited thread has not exited
-2
}
}

View file

@ -3,13 +3,22 @@ use crate::trap::trap_return;
#[repr(C)]
pub struct TaskContext {
ra: usize,
sp: usize,
s: [usize; 12],
}
impl TaskContext {
pub fn goto_trap_return() -> Self {
pub fn zero_init() -> Self {
Self {
ra: 0,
sp: 0,
s: [0; 12],
}
}
pub fn goto_trap_return(kstack_ptr: usize) -> Self {
Self {
ra: trap_return as usize,
sp: kstack_ptr,
s: [0; 12],
}
}

215
os/src/task/id.rs Normal file
View file

@ -0,0 +1,215 @@
use alloc::{vec::Vec, sync::{Arc, Weak}};
use lazy_static::*;
use crate::sync::UPSafeCell;
use crate::mm::{KERNEL_SPACE, MapPermission, PhysPageNum, VirtAddr};
use crate::config::{KERNEL_STACK_SIZE, PAGE_SIZE, TRAMPOLINE, TRAP_CONTEXT_BASE, USER_STACK_SIZE};
use super::ProcessControlBlock;
pub struct RecycleAllocator {
current: usize,
recycled: Vec<usize>,
}
impl RecycleAllocator {
pub fn new() -> Self {
RecycleAllocator {
current: 0,
recycled: Vec::new(),
}
}
pub fn alloc(&mut self) -> usize {
if let Some(id) = self.recycled.pop() {
id
} else {
self.current += 1;
self.current - 1
}
}
pub fn dealloc(&mut self, id: usize) {
assert!(id < self.current);
assert!(
self.recycled.iter().find(|i| **i == id).is_none(),
"id {} has been deallocated!", id
);
self.recycled.push(id);
}
}
lazy_static! {
static ref PID_ALLOCATOR: UPSafeCell<RecycleAllocator> = unsafe {
UPSafeCell::new(RecycleAllocator::new())
};
static ref KSTACK_ALLOCATOR: UPSafeCell<RecycleAllocator> = unsafe {
UPSafeCell::new(RecycleAllocator::new())
};
}
pub struct PidHandle(pub usize);
pub fn pid_alloc() -> PidHandle {
PidHandle(PID_ALLOCATOR.exclusive_access().alloc())
}
impl Drop for PidHandle {
fn drop(&mut self) {
PID_ALLOCATOR.exclusive_access().dealloc(self.0);
}
}
/// Return (bottom, top) of a kernel stack in kernel space.
pub fn kernel_stack_position(kstack_id: usize) -> (usize, usize) {
let top = TRAMPOLINE - kstack_id * (KERNEL_STACK_SIZE + PAGE_SIZE);
let bottom = top - KERNEL_STACK_SIZE;
(bottom, top)
}
pub struct KernelStack(pub usize);
pub fn kstack_alloc() -> KernelStack {
let kstack_id = KSTACK_ALLOCATOR.exclusive_access().alloc();
let (kstack_bottom, kstack_top) = kernel_stack_position(kstack_id);
KERNEL_SPACE
.exclusive_access()
.insert_framed_area(
kstack_bottom.into(),
kstack_top.into(),
MapPermission::R | MapPermission::W,
);
KernelStack(kstack_id)
}
impl Drop for KernelStack {
fn drop(&mut self) {
let (kernel_stack_bottom, _) = kernel_stack_position(self.0);
let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
KERNEL_SPACE
.exclusive_access()
.remove_area_with_start_vpn(kernel_stack_bottom_va.into());
}
}
impl KernelStack {
#[allow(unused)]
pub fn push_on_top<T>(&self, value: T) -> *mut T where
T: Sized, {
let kernel_stack_top = self.get_top();
let ptr_mut = (kernel_stack_top - core::mem::size_of::<T>()) as *mut T;
unsafe { *ptr_mut = value; }
ptr_mut
}
pub fn get_top(&self) -> usize {
let (_, kernel_stack_top) = kernel_stack_position(self.0);
kernel_stack_top
}
}
pub struct TaskUserRes {
pub tid: usize,
pub ustack_base: usize,
pub process: Weak<ProcessControlBlock>,
}
fn trap_cx_bottom_from_tid(tid: usize) -> usize {
TRAP_CONTEXT_BASE - tid * PAGE_SIZE
}
fn ustack_bottom_from_tid(ustack_base: usize, tid: usize) -> usize {
ustack_base + tid * (PAGE_SIZE + USER_STACK_SIZE)
}
impl TaskUserRes {
pub fn new(
process: Arc<ProcessControlBlock>,
ustack_base: usize,
alloc_user_res: bool,
) -> Self {
let tid = process.inner_exclusive_access().alloc_tid();
let task_user_res = Self {
tid,
ustack_base,
process: Arc::downgrade(&process),
};
if alloc_user_res {
task_user_res.alloc_user_res();
}
task_user_res
}
pub fn alloc_user_res(&self) {
let process = self.process.upgrade().unwrap();
let mut process_inner = process.inner_exclusive_access();
// alloc user stack
let ustack_bottom = ustack_bottom_from_tid(self.ustack_base, self.tid);
let ustack_top = ustack_bottom + USER_STACK_SIZE;
process_inner
.memory_set
.insert_framed_area(
ustack_bottom.into(),
ustack_top.into(),
MapPermission::R | MapPermission::W | MapPermission::U,
);
// alloc trap_cx
let trap_cx_bottom = trap_cx_bottom_from_tid(self.tid);
let trap_cx_top = trap_cx_bottom + PAGE_SIZE;
process_inner
.memory_set
.insert_framed_area(
trap_cx_bottom.into(),
trap_cx_top.into(),
MapPermission::R | MapPermission::W,
);
}
fn dealloc_user_res(&self) {
// dealloc tid
let process = self.process.upgrade().unwrap();
let mut process_inner = process.inner_exclusive_access();
// dealloc ustack manually
let ustack_bottom_va: VirtAddr = ustack_bottom_from_tid(self.ustack_base, self.tid).into();
process_inner.memory_set.remove_area_with_start_vpn(ustack_bottom_va.into());
// dealloc trap_cx manually
let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into();
process_inner.memory_set.remove_area_with_start_vpn(trap_cx_bottom_va.into());
}
#[allow(unused)]
pub fn alloc_tid(&mut self) {
self.tid = self
.process
.upgrade()
.unwrap()
.inner_exclusive_access()
.alloc_tid();
}
pub fn dealloc_tid(&self) {
let process = self.process.upgrade().unwrap();
let mut process_inner = process.inner_exclusive_access();
process_inner.dealloc_tid(self.tid);
}
pub fn trap_cx_user_va(&self) -> usize {
trap_cx_bottom_from_tid(self.tid)
}
pub fn trap_cx_ppn(&self) -> PhysPageNum {
let process = self.process.upgrade().unwrap();
let process_inner = process.inner_exclusive_access();
let trap_cx_bottom_va: VirtAddr = trap_cx_bottom_from_tid(self.tid).into();
process_inner.memory_set.translate(trap_cx_bottom_va.into()).unwrap().ppn()
}
pub fn ustack_base(&self) -> usize { self.ustack_base }
pub fn ustack_top(&self) -> usize {
ustack_bottom_from_tid(self.ustack_base, self.tid) + USER_STACK_SIZE
}
}
impl Drop for TaskUserRes {
fn drop(&mut self) {
self.dealloc_tid();
self.dealloc_user_res();
}
}

View file

@ -1,7 +1,7 @@
use crate::sync::UPSafeCell;
use super::TaskControlBlock;
use alloc::collections::VecDeque;
use alloc::sync::Arc;
use spin::Mutex;
use lazy_static::*;
pub struct TaskManager {
@ -22,13 +22,15 @@ impl TaskManager {
}
lazy_static! {
pub static ref TASK_MANAGER: Mutex<TaskManager> = Mutex::new(TaskManager::new());
pub static ref TASK_MANAGER: UPSafeCell<TaskManager> = unsafe {
UPSafeCell::new(TaskManager::new())
};
}
pub fn add_task(task: Arc<TaskControlBlock>) {
TASK_MANAGER.lock().add(task);
TASK_MANAGER.exclusive_access().add(task);
}
pub fn fetch_task() -> Option<Arc<TaskControlBlock>> {
TASK_MANAGER.lock().fetch()
}
TASK_MANAGER.exclusive_access().fetch()
}

View file

@ -3,86 +3,121 @@ mod switch;
mod task;
mod manager;
mod processor;
mod pid;
mod id;
mod process;
use crate::fs::{open_file, OpenFlags};
use switch::__switch;
use task::{TaskControlBlock, TaskStatus};
use alloc::sync::Arc;
use manager::fetch_task;
use lazy_static::*;
pub use context::TaskContext;
use process::ProcessControlBlock;
pub use context::TaskContext;
pub use processor::{
run_tasks,
current_task,
current_process,
current_user_token,
current_trap_cx_user_va,
current_trap_cx,
current_kstack_top,
take_current_task,
schedule,
};
pub use task::{TaskControlBlock, TaskStatus};
pub use manager::add_task;
pub use pid::{PidHandle, pid_alloc, KernelStack};
pub use id::{
PidHandle,
pid_alloc,
KernelStack,
kstack_alloc,
};
pub fn suspend_current_and_run_next() {
// There must be an application running.
let task = take_current_task().unwrap();
// ---- hold current PCB lock
let mut task_inner = task.acquire_inner_lock();
let task_cx_ptr2 = task_inner.get_task_cx_ptr2();
// ---- access current TCB exclusively
let mut task_inner = task.inner_exclusive_access();
let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
// Change status to Ready
task_inner.task_status = TaskStatus::Ready;
drop(task_inner);
// ---- release current PCB lock
// ---- release current TCB
// push back to ready queue.
add_task(task);
// jump to scheduling cycle
schedule(task_cx_ptr2);
schedule(task_cx_ptr);
}
pub fn block_current_and_run_next() {
let task = take_current_task().unwrap();
let mut task_inner = task.inner_exclusive_access();
let task_cx_ptr = &mut task_inner.task_cx as *mut TaskContext;
task_inner.task_status = TaskStatus::Blocking;
drop(task_inner);
schedule(task_cx_ptr);
}
pub fn exit_current_and_run_next(exit_code: i32) {
// take from Processor
let task = take_current_task().unwrap();
// **** hold current PCB lock
let mut inner = task.acquire_inner_lock();
// Change status to Zombie
inner.task_status = TaskStatus::Zombie;
// Record exit code
inner.exit_code = exit_code;
// do not move to its parent but under initproc
// ++++++ hold initproc PCB lock here
{
let mut initproc_inner = INITPROC.acquire_inner_lock();
for child in inner.children.iter() {
child.acquire_inner_lock().parent = Some(Arc::downgrade(&INITPROC));
initproc_inner.children.push(child.clone());
}
}
// ++++++ release parent PCB lock here
inner.children.clear();
// deallocate user space
inner.memory_set.recycle_data_pages();
drop(inner);
// **** release current PCB lock
// drop task manually to maintain rc correctly
let mut task_inner = task.inner_exclusive_access();
let process = task.process.upgrade().unwrap();
let tid = task_inner.res.as_ref().unwrap().tid;
// record exit code
task_inner.exit_code = Some(exit_code);
task_inner.res = None;
// here we do not remove the thread since we are still using the kstack
// it will be deallocated when sys_waittid is called
drop(task_inner);
drop(task);
// however, if this is the main thread of current process
// the process should terminate at once
if tid == 0 {
let mut process_inner = process.inner_exclusive_access();
// mark this process as a zombie process
process_inner.is_zombie = true;
// record exit code of main process
process_inner.exit_code = exit_code;
{
// move all child processes under init process
let mut initproc_inner = INITPROC.inner_exclusive_access();
for child in process_inner.children.iter() {
child.inner_exclusive_access().parent = Some(Arc::downgrade(&INITPROC));
initproc_inner.children.push(child.clone());
}
}
// deallocate user res (including tid/trap_cx/ustack) of all threads
// it has to be done before we dealloc the whole memory_set
// otherwise they will be deallocated twice
for task in process_inner.tasks.iter().filter(|t| t.is_some()) {
let task = task.as_ref().unwrap();
let mut task_inner = task.inner_exclusive_access();
task_inner.res = None;
}
process_inner.children.clear();
// deallocate other data in user space i.e. program code/data section
process_inner.memory_set.recycle_data_pages();
}
drop(process);
// we do not have to save task context
let _unused: usize = 0;
schedule(&_unused as *const _);
let mut _unused = TaskContext::zero_init();
schedule(&mut _unused as *mut _);
}
lazy_static! {
pub static ref INITPROC: Arc<TaskControlBlock> = Arc::new({
pub static ref INITPROC: Arc<ProcessControlBlock> = {
let inode = open_file("initproc", OpenFlags::RDONLY).unwrap();
let v = inode.read_all();
TaskControlBlock::new(v.as_slice())
});
ProcessControlBlock::new(v.as_slice())
};
}
pub fn add_initproc() {
add_task(INITPROC.clone());
let _initproc = INITPROC.clone();
}

View file

@ -1,105 +0,0 @@
use alloc::vec::Vec;
use lazy_static::*;
use spin::Mutex;
use crate::mm::{KERNEL_SPACE, MapPermission, VirtAddr};
use crate::config::{
PAGE_SIZE,
TRAMPOLINE,
KERNEL_STACK_SIZE,
};
struct PidAllocator {
current: usize,
recycled: Vec<usize>,
}
impl PidAllocator {
pub fn new() -> Self {
PidAllocator {
current: 0,
recycled: Vec::new(),
}
}
pub fn alloc(&mut self) -> PidHandle {
if let Some(pid) = self.recycled.pop() {
PidHandle(pid)
} else {
self.current += 1;
PidHandle(self.current - 1)
}
}
pub fn dealloc(&mut self, pid: usize) {
assert!(pid < self.current);
assert!(
self.recycled.iter().find(|ppid| **ppid == pid).is_none(),
"pid {} has been deallocated!", pid
);
self.recycled.push(pid);
}
}
lazy_static! {
static ref PID_ALLOCATOR : Mutex<PidAllocator> = Mutex::new(PidAllocator::new());
}
pub struct PidHandle(pub usize);
impl Drop for PidHandle {
fn drop(&mut self) {
//println!("drop pid {}", self.0);
PID_ALLOCATOR.lock().dealloc(self.0);
}
}
pub fn pid_alloc() -> PidHandle {
PID_ALLOCATOR.lock().alloc()
}
/// Return (bottom, top) of a kernel stack in kernel space.
pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE);
let bottom = top - KERNEL_STACK_SIZE;
(bottom, top)
}
pub struct KernelStack {
pid: usize,
}
impl KernelStack {
pub fn new(pid_handle: &PidHandle) -> Self {
let pid = pid_handle.0;
let (kernel_stack_bottom, kernel_stack_top) = kernel_stack_position(pid);
KERNEL_SPACE
.lock()
.insert_framed_area(
kernel_stack_bottom.into(),
kernel_stack_top.into(),
MapPermission::R | MapPermission::W,
);
KernelStack {
pid: pid_handle.0,
}
}
pub fn push_on_top<T>(&self, value: T) -> *mut T where
T: Sized, {
let kernel_stack_top = self.get_top();
let ptr_mut = (kernel_stack_top - core::mem::size_of::<T>()) as *mut T;
unsafe { *ptr_mut = value; }
ptr_mut
}
pub fn get_top(&self) -> usize {
let (_, kernel_stack_top) = kernel_stack_position(self.pid);
kernel_stack_top
}
}
impl Drop for KernelStack {
fn drop(&mut self) {
let (kernel_stack_bottom, _) = kernel_stack_position(self.pid);
let kernel_stack_bottom_va: VirtAddr = kernel_stack_bottom.into();
KERNEL_SPACE
.lock()
.remove_area_with_start_vpn(kernel_stack_bottom_va.into());
}
}

246
os/src/task/process.rs Normal file
View file

@ -0,0 +1,246 @@
use crate::mm::{
MemorySet,
KERNEL_SPACE,
translated_refmut,
};
use crate::trap::{TrapContext, trap_handler};
use crate::sync::{UPSafeCell, Mutex, Semaphore};
use core::cell::RefMut;
use super::id::RecycleAllocator;
use super::TaskControlBlock;
use super::{PidHandle, pid_alloc};
use super::add_task;
use alloc::sync::{Weak, Arc};
use alloc::vec;
use alloc::vec::Vec;
use alloc::string::String;
use crate::fs::{File, Stdin, Stdout};
pub struct ProcessControlBlock {
// immutable
pub pid: PidHandle,
// mutable
inner: UPSafeCell<ProcessControlBlockInner>,
}
pub struct ProcessControlBlockInner {
pub is_zombie: bool,
pub memory_set: MemorySet,
pub parent: Option<Weak<ProcessControlBlock>>,
pub children: Vec<Arc<ProcessControlBlock>>,
pub exit_code: i32,
pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
pub tasks: Vec<Option<Arc<TaskControlBlock>>>,
pub task_res_allocator: RecycleAllocator,
pub mutex_list: Vec<Option<Arc<dyn Mutex>>>,
pub semaphore_list: Vec<Option<Arc<Semaphore>>>,
}
impl ProcessControlBlockInner {
#[allow(unused)]
pub fn get_user_token(&self) -> usize {
self.memory_set.token()
}
pub fn alloc_fd(&mut self) -> usize {
if let Some(fd) = (0..self.fd_table.len())
.find(|fd| self.fd_table[*fd].is_none()) {
fd
} else {
self.fd_table.push(None);
self.fd_table.len() - 1
}
}
pub fn alloc_tid(&mut self) -> usize {
self.task_res_allocator.alloc()
}
pub fn dealloc_tid(&mut self, tid: usize){
self.task_res_allocator.dealloc(tid)
}
pub fn thread_count(&self) -> usize {
self.tasks.len()
}
pub fn get_task(&self, tid: usize) -> Arc<TaskControlBlock> {
self.tasks[tid].as_ref().unwrap().clone()
}
}
impl ProcessControlBlock {
pub fn inner_exclusive_access(&self) -> RefMut<'_, ProcessControlBlockInner> {
self.inner.exclusive_access()
}
pub fn new(elf_data: &[u8]) -> Arc<Self> {
// memory_set with elf program headers/trampoline/trap context/user stack
let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data);
// allocate a pid
let pid_handle = pid_alloc();
let process = Arc::new(Self {
pid: pid_handle,
inner: unsafe { UPSafeCell::new(ProcessControlBlockInner {
is_zombie: false,
memory_set,
parent: None,
children: Vec::new(),
exit_code: 0,
fd_table: vec![
// 0 -> stdin
Some(Arc::new(Stdin)),
// 1 -> stdout
Some(Arc::new(Stdout)),
// 2 -> stderr
Some(Arc::new(Stdout)),
],
tasks: Vec::new(),
task_res_allocator: RecycleAllocator::new(),
mutex_list: Vec::new(),
semaphore_list: Vec::new(),
})}
});
// create a main thread, we should allocate ustack and trap_cx here
let task = Arc::new(TaskControlBlock::new(
Arc::clone(&process),
ustack_base,
true,
));
// prepare trap_cx of main thread
let task_inner = task.inner_exclusive_access();
let trap_cx = task_inner.get_trap_cx();
let ustack_top = task_inner.res.as_ref().unwrap().ustack_top();
let kstack_top = task.kstack.get_top();
drop(task_inner);
*trap_cx = TrapContext::app_init_context(
entry_point,
ustack_top,
KERNEL_SPACE.exclusive_access().token(),
kstack_top,
trap_handler as usize,
);
// add main thread to the process
let mut process_inner = process.inner_exclusive_access();
process_inner.tasks.push(Some(Arc::clone(&task)));
drop(process_inner);
// add main thread to scheduler
add_task(task);
process
}
/// Only support processes with a single thread.
pub fn exec(self: &Arc<Self>, elf_data: &[u8], args: Vec<String>) {
assert_eq!(self.inner_exclusive_access().thread_count(), 1);
// memory_set with elf program headers/trampoline/trap context/user stack
let (memory_set, ustack_base, entry_point) = MemorySet::from_elf(elf_data);
let new_token = memory_set.token();
// substitute memory_set
self.inner_exclusive_access().memory_set = memory_set;
// then we alloc user resource for main thread again
// since memory_set has been changed
let task = self.inner_exclusive_access().get_task(0);
let mut task_inner = task.inner_exclusive_access();
task_inner.res.as_mut().unwrap().ustack_base = ustack_base;
task_inner.res.as_mut().unwrap().alloc_user_res();
task_inner.trap_cx_ppn = task_inner.res.as_mut().unwrap().trap_cx_ppn();
// push arguments on user stack
let mut user_sp = task_inner.res.as_mut().unwrap().ustack_top();
user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
let argv_base = user_sp;
let mut argv: Vec<_> = (0..=args.len())
.map(|arg| {
translated_refmut(
new_token,
(argv_base + arg * core::mem::size_of::<usize>()) as *mut usize
)
})
.collect();
*argv[args.len()] = 0;
for i in 0..args.len() {
user_sp -= args[i].len() + 1;
*argv[i] = user_sp;
let mut p = user_sp;
for c in args[i].as_bytes() {
*translated_refmut(new_token, p as *mut u8) = *c;
p += 1;
}
*translated_refmut(new_token, p as *mut u8) = 0;
}
// make the user_sp aligned to 8B for k210 platform
user_sp -= user_sp % core::mem::size_of::<usize>();
// initialize trap_cx
let mut trap_cx = TrapContext::app_init_context(
entry_point,
user_sp,
KERNEL_SPACE.exclusive_access().token(),
task.kstack.get_top(),
trap_handler as usize,
);
trap_cx.x[10] = args.len();
trap_cx.x[11] = argv_base;
*task_inner.get_trap_cx() = trap_cx;
}
/// Only support processes with a single thread.
pub fn fork(self: &Arc<Self>) -> Arc<Self> {
let mut parent = self.inner_exclusive_access();
assert_eq!(parent.thread_count(), 1);
// clone parent's memory_set completely including trampoline/ustacks/trap_cxs
let memory_set = MemorySet::from_existed_user(&parent.memory_set);
// alloc a pid
let pid = pid_alloc();
// copy fd table
let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
for fd in parent.fd_table.iter() {
if let Some(file) = fd {
new_fd_table.push(Some(file.clone()));
} else {
new_fd_table.push(None);
}
}
// create child process pcb
let child = Arc::new(Self {
pid,
inner: unsafe { UPSafeCell::new(ProcessControlBlockInner {
is_zombie: false,
memory_set,
parent: Some(Arc::downgrade(self)),
children: Vec::new(),
exit_code: 0,
fd_table: new_fd_table,
tasks: Vec::new(),
task_res_allocator: RecycleAllocator::new(),
mutex_list: Vec::new(),
semaphore_list: Vec::new(),
})}
});
// add child
parent.children.push(Arc::clone(&child));
// create main thread of child process
let task = Arc::new(TaskControlBlock::new(
Arc::clone(&child),
parent.get_task(0).inner_exclusive_access().res.as_ref().unwrap().ustack_base(),
// here we do not allocate trap_cx or ustack again
// but mention that we allocate a new kstack here
false,
));
// attach task to child process
let mut child_inner = child.inner_exclusive_access();
child_inner.tasks.push(Some(Arc::clone(&task)));
drop(child_inner);
// modify kstack_top in trap_cx of this thread
let task_inner = task.inner_exclusive_access();
let trap_cx = task_inner.get_trap_cx();
trap_cx.kernel_sp = task.kstack.get_top();
drop(task_inner);
// add this thread to scheduler
add_task(task);
child
}
pub fn getpid(&self) -> usize {
self.pid.0
}
}

View file

@ -1,95 +1,113 @@
use super::TaskControlBlock;
use super::{TaskContext, TaskControlBlock, ProcessControlBlock};
use alloc::sync::Arc;
use core::cell::RefCell;
use lazy_static::*;
use super::{fetch_task, TaskStatus};
use super::__switch;
use crate::trap::TrapContext;
use crate::sync::UPSafeCell;
pub struct Processor {
inner: RefCell<ProcessorInner>,
}
unsafe impl Sync for Processor {}
struct ProcessorInner {
current: Option<Arc<TaskControlBlock>>,
idle_task_cx_ptr: usize,
idle_task_cx: TaskContext,
}
impl Processor {
pub fn new() -> Self {
Self {
inner: RefCell::new(ProcessorInner {
current: None,
idle_task_cx_ptr: 0,
}),
current: None,
idle_task_cx: TaskContext::zero_init(),
}
}
fn get_idle_task_cx_ptr2(&self) -> *const usize {
let inner = self.inner.borrow();
&inner.idle_task_cx_ptr as *const usize
fn get_idle_task_cx_ptr(&mut self) -> *mut TaskContext {
&mut self.idle_task_cx as *mut _
}
pub fn run(&self) {
loop {
if let Some(task) = fetch_task() {
let idle_task_cx_ptr2 = self.get_idle_task_cx_ptr2();
// acquire
let mut task_inner = task.acquire_inner_lock();
let next_task_cx_ptr2 = task_inner.get_task_cx_ptr2();
task_inner.task_status = TaskStatus::Running;
drop(task_inner);
// release
self.inner.borrow_mut().current = Some(task);
unsafe {
__switch(
idle_task_cx_ptr2,
next_task_cx_ptr2,
);
}
}
}
}
pub fn take_current(&self) -> Option<Arc<TaskControlBlock>> {
self.inner.borrow_mut().current.take()
pub fn take_current(&mut self) -> Option<Arc<TaskControlBlock>> {
self.current.take()
}
pub fn current(&self) -> Option<Arc<TaskControlBlock>> {
self.inner.borrow().current.as_ref().map(|task| Arc::clone(task))
self.current.as_ref().map(|task| Arc::clone(task))
}
}
lazy_static! {
pub static ref PROCESSOR: Processor = Processor::new();
pub static ref PROCESSOR: UPSafeCell<Processor> = unsafe {
UPSafeCell::new(Processor::new())
};
}
pub fn run_tasks() {
PROCESSOR.run();
loop {
let mut processor = PROCESSOR.exclusive_access();
if let Some(task) = fetch_task() {
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
// access coming task TCB exclusively
let mut task_inner = task.inner_exclusive_access();
let next_task_cx_ptr = &task_inner.task_cx as *const TaskContext;
task_inner.task_status = TaskStatus::Running;
drop(task_inner);
// release coming task TCB manually
processor.current = Some(task);
// release processor manually
drop(processor);
unsafe {
__switch(
idle_task_cx_ptr,
next_task_cx_ptr,
);
}
} else {
println!("no tasks available in run_tasks");
}
}
}
pub fn take_current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.take_current()
PROCESSOR.exclusive_access().take_current()
}
pub fn current_task() -> Option<Arc<TaskControlBlock>> {
PROCESSOR.current()
PROCESSOR.exclusive_access().current()
}
pub fn current_process() -> Arc<ProcessControlBlock> {
current_task().unwrap().process.upgrade().unwrap()
}
pub fn current_user_token() -> usize {
let task = current_task().unwrap();
let token = task.acquire_inner_lock().get_user_token();
let token = task.get_user_token();
token
}
pub fn current_trap_cx() -> &'static mut TrapContext {
current_task().unwrap().acquire_inner_lock().get_trap_cx()
current_task().unwrap().inner_exclusive_access().get_trap_cx()
}
pub fn schedule(switched_task_cx_ptr2: *const usize) {
let idle_task_cx_ptr2 = PROCESSOR.get_idle_task_cx_ptr2();
pub fn current_trap_cx_user_va() -> usize {
current_task()
.unwrap()
.inner_exclusive_access()
.res
.as_ref()
.unwrap()
.trap_cx_user_va()
}
pub fn current_kstack_top() -> usize {
current_task()
.unwrap()
.kstack
.get_top()
}
pub fn schedule(switched_task_cx_ptr: *mut TaskContext) {
let mut processor = PROCESSOR.exclusive_access();
let idle_task_cx_ptr = processor.get_idle_task_cx_ptr();
drop(processor);
unsafe {
__switch(
switched_task_cx_ptr2,
idle_task_cx_ptr2,
switched_task_cx_ptr,
idle_task_cx_ptr,
);
}
}

View file

@ -1,37 +1,34 @@
.altmacro
.macro SAVE_SN n
sd s\n, (\n+1)*8(sp)
sd s\n, (\n+2)*8(a0)
.endm
.macro LOAD_SN n
ld s\n, (\n+1)*8(sp)
ld s\n, (\n+2)*8(a1)
.endm
.section .text
.globl __switch
__switch:
# __switch(
# current_task_cx_ptr2: &*const TaskContext,
# next_task_cx_ptr2: &*const TaskContext
# current_task_cx_ptr: *mut TaskContext,
# next_task_cx_ptr: *const TaskContext
# )
# push TaskContext to current sp and save its address to where a0 points to
addi sp, sp, -13*8
sd sp, 0(a0)
# fill TaskContext with ra & s0-s11
sd ra, 0(sp)
# save kernel stack of current task
sd sp, 8(a0)
# save ra & s0~s11 of current execution
sd ra, 0(a0)
.set n, 0
.rept 12
SAVE_SN %n
.set n, n + 1
.endr
# ready for loading TaskContext a1 points to
ld sp, 0(a1)
# load registers in the TaskContext
ld ra, 0(sp)
# restore ra & s0~s11 of next execution
ld ra, 0(a1)
.set n, 0
.rept 12
LOAD_SN %n
.set n, n + 1
.endr
# pop TaskContext
addi sp, sp, 13*8
# restore kernel stack of next task
ld sp, 8(a1)
ret

View file

@ -1,8 +1,10 @@
global_asm!(include_str!("switch.S"));
use super::TaskContext;
extern "C" {
pub fn __switch(
current_task_cx_ptr2: *const usize,
next_task_cx_ptr2: *const usize
current_task_cx_ptr: *mut TaskContext,
next_task_cx_ptr: *const TaskContext
);
}

View file

@ -1,230 +1,78 @@
use crate::mm::{
MemorySet,
PhysPageNum,
KERNEL_SPACE,
VirtAddr,
translated_refmut,
};
use crate::trap::{TrapContext, trap_handler};
use crate::config::{TRAP_CONTEXT};
use super::TaskContext;
use super::{PidHandle, pid_alloc, KernelStack};
use alloc::sync::{Weak, Arc};
use alloc::vec;
use alloc::vec::Vec;
use alloc::string::String;
use spin::{Mutex, MutexGuard};
use crate::fs::{File, Stdin, Stdout};
use alloc::sync::{Arc, Weak};
use crate::{mm::PhysPageNum, sync::UPSafeCell};
use crate::trap::TrapContext;
use super::id::TaskUserRes;
use super::{KernelStack, ProcessControlBlock, TaskContext, kstack_alloc};
use core::cell::RefMut;
pub struct TaskControlBlock {
// immutable
pub pid: PidHandle,
pub kernel_stack: KernelStack,
pub process: Weak<ProcessControlBlock>,
pub kstack: KernelStack,
// mutable
inner: Mutex<TaskControlBlockInner>,
inner: UPSafeCell<TaskControlBlockInner>,
}
impl TaskControlBlock {
pub fn inner_exclusive_access(&self) -> RefMut<'_, TaskControlBlockInner> {
self.inner.exclusive_access()
}
pub fn get_user_token(&self) -> usize {
let process = self.process.upgrade().unwrap();
let inner = process.inner_exclusive_access();
inner.memory_set.token()
}
}
pub struct TaskControlBlockInner {
pub res: Option<TaskUserRes>,
pub trap_cx_ppn: PhysPageNum,
pub base_size: usize,
pub task_cx_ptr: usize,
pub task_cx: TaskContext,
pub task_status: TaskStatus,
pub memory_set: MemorySet,
pub parent: Option<Weak<TaskControlBlock>>,
pub children: Vec<Arc<TaskControlBlock>>,
pub exit_code: i32,
pub fd_table: Vec<Option<Arc<dyn File + Send + Sync>>>,
pub exit_code: Option<i32>,
}
impl TaskControlBlockInner {
pub fn get_task_cx_ptr2(&self) -> *const usize {
&self.task_cx_ptr as *const usize
}
pub fn get_trap_cx(&self) -> &'static mut TrapContext {
self.trap_cx_ppn.get_mut()
}
pub fn get_user_token(&self) -> usize {
self.memory_set.token()
}
#[allow(unused)]
fn get_status(&self) -> TaskStatus {
self.task_status
}
pub fn is_zombie(&self) -> bool {
self.get_status() == TaskStatus::Zombie
}
pub fn alloc_fd(&mut self) -> usize {
if let Some(fd) = (0..self.fd_table.len())
.find(|fd| self.fd_table[*fd].is_none()) {
fd
} else {
self.fd_table.push(None);
self.fd_table.len() - 1
}
}
}
impl TaskControlBlock {
pub fn acquire_inner_lock(&self) -> MutexGuard<TaskControlBlockInner> {
self.inner.lock()
}
pub fn new(elf_data: &[u8]) -> Self {
// memory_set with elf program headers/trampoline/trap context/user stack
let (memory_set, user_sp, entry_point) = MemorySet::from_elf(elf_data);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT).into())
.unwrap()
.ppn();
// alloc a pid and a kernel stack in kernel space
let pid_handle = pid_alloc();
let kernel_stack = KernelStack::new(&pid_handle);
let kernel_stack_top = kernel_stack.get_top();
// push a task context which goes to trap_return to the top of kernel stack
let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
let task_control_block = Self {
pid: pid_handle,
kernel_stack,
inner: Mutex::new(TaskControlBlockInner {
trap_cx_ppn,
base_size: user_sp,
task_cx_ptr: task_cx_ptr as usize,
task_status: TaskStatus::Ready,
memory_set,
parent: None,
children: Vec::new(),
exit_code: 0,
fd_table: vec![
// 0 -> stdin
Some(Arc::new(Stdin)),
// 1 -> stdout
Some(Arc::new(Stdout)),
// 2 -> stderr
Some(Arc::new(Stdout)),
],
}),
};
// prepare TrapContext in user space
let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
*trap_cx = TrapContext::app_init_context(
entry_point,
user_sp,
KERNEL_SPACE.lock().token(),
kernel_stack_top,
trap_handler as usize,
);
task_control_block
}
pub fn exec(&self, elf_data: &[u8], args: Vec<String>) {
// memory_set with elf program headers/trampoline/trap context/user stack
let (memory_set, mut user_sp, entry_point) = MemorySet::from_elf(elf_data);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT).into())
.unwrap()
.ppn();
// push arguments on user stack
user_sp -= (args.len() + 1) * core::mem::size_of::<usize>();
let argv_base = user_sp;
let mut argv: Vec<_> = (0..=args.len())
.map(|arg| {
translated_refmut(
memory_set.token(),
(argv_base + arg * core::mem::size_of::<usize>()) as *mut usize
)
})
.collect();
*argv[args.len()] = 0;
for i in 0..args.len() {
user_sp -= args[i].len() + 1;
*argv[i] = user_sp;
let mut p = user_sp;
for c in args[i].as_bytes() {
*translated_refmut(memory_set.token(), p as *mut u8) = *c;
p += 1;
}
*translated_refmut(memory_set.token(), p as *mut u8) = 0;
pub fn new(
process: Arc<ProcessControlBlock>,
ustack_base: usize,
alloc_user_res: bool
) -> Self {
let res = TaskUserRes::new(Arc::clone(&process), ustack_base, alloc_user_res);
let trap_cx_ppn = res.trap_cx_ppn();
let kstack = kstack_alloc();
let kstack_top = kstack.get_top();
Self {
process: Arc::downgrade(&process),
kstack,
inner: unsafe { UPSafeCell::new(
TaskControlBlockInner {
res: Some(res),
trap_cx_ppn,
task_cx: TaskContext::goto_trap_return(kstack_top),
task_status: TaskStatus::Ready,
exit_code: None,
}
)},
}
// make the user_sp aligned to 8B for k210 platform
user_sp -= user_sp % core::mem::size_of::<usize>();
// **** hold current PCB lock
let mut inner = self.acquire_inner_lock();
// substitute memory_set
inner.memory_set = memory_set;
// update trap_cx ppn
inner.trap_cx_ppn = trap_cx_ppn;
// initialize trap_cx
let mut trap_cx = TrapContext::app_init_context(
entry_point,
user_sp,
KERNEL_SPACE.lock().token(),
self.kernel_stack.get_top(),
trap_handler as usize,
);
trap_cx.x[10] = args.len();
trap_cx.x[11] = argv_base;
*inner.get_trap_cx() = trap_cx;
// **** release current PCB lock
}
pub fn fork(self: &Arc<TaskControlBlock>) -> Arc<TaskControlBlock> {
// ---- hold parent PCB lock
let mut parent_inner = self.acquire_inner_lock();
// copy user space(include trap context)
let memory_set = MemorySet::from_existed_user(
&parent_inner.memory_set
);
let trap_cx_ppn = memory_set
.translate(VirtAddr::from(TRAP_CONTEXT).into())
.unwrap()
.ppn();
// alloc a pid and a kernel stack in kernel space
let pid_handle = pid_alloc();
let kernel_stack = KernelStack::new(&pid_handle);
let kernel_stack_top = kernel_stack.get_top();
// push a goto_trap_return task_cx on the top of kernel stack
let task_cx_ptr = kernel_stack.push_on_top(TaskContext::goto_trap_return());
// copy fd table
let mut new_fd_table: Vec<Option<Arc<dyn File + Send + Sync>>> = Vec::new();
for fd in parent_inner.fd_table.iter() {
if let Some(file) = fd {
new_fd_table.push(Some(file.clone()));
} else {
new_fd_table.push(None);
}
}
let task_control_block = Arc::new(TaskControlBlock {
pid: pid_handle,
kernel_stack,
inner: Mutex::new(TaskControlBlockInner {
trap_cx_ppn,
base_size: parent_inner.base_size,
task_cx_ptr: task_cx_ptr as usize,
task_status: TaskStatus::Ready,
memory_set,
parent: Some(Arc::downgrade(self)),
children: Vec::new(),
exit_code: 0,
fd_table: new_fd_table,
}),
});
// add child
parent_inner.children.push(task_control_block.clone());
// modify kernel_sp in trap_cx
// **** acquire child PCB lock
let trap_cx = task_control_block.acquire_inner_lock().get_trap_cx();
// **** release child PCB lock
trap_cx.kernel_sp = kernel_stack_top;
// return
task_control_block
// ---- release parent PCB lock
}
pub fn getpid(&self) -> usize {
self.pid.0
}
}
#[derive(Copy, Clone, PartialEq)]
pub enum TaskStatus {
Ready,
Running,
Zombie,
}
Blocking,
}

View file

@ -1,6 +1,13 @@
use core::cmp::Ordering;
use riscv::register::time;
use crate::sbi::set_timer;
use crate::config::CLOCK_FREQ;
use crate::task::{TaskControlBlock, add_task};
use crate::sync::UPSafeCell;
use alloc::collections::BinaryHeap;
use alloc::sync::Arc;
use lazy_static::*;
const TICKS_PER_SEC: usize = 100;
const MSEC_PER_SEC: usize = 1000;
@ -15,4 +22,55 @@ pub fn get_time_ms() -> usize {
pub fn set_next_trigger() {
set_timer(get_time() + CLOCK_FREQ / TICKS_PER_SEC);
}
}
pub struct TimerCondVar {
pub expire_ms: usize,
pub task: Arc<TaskControlBlock>,
}
impl PartialEq for TimerCondVar {
fn eq(&self, other: &Self) -> bool {
self.expire_ms == other.expire_ms
}
}
impl Eq for TimerCondVar {}
impl PartialOrd for TimerCondVar {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
let a = -(self.expire_ms as isize);
let b = -(other.expire_ms as isize);
Some(a.cmp(&b))
}
}
impl Ord for TimerCondVar {
fn cmp(&self, other: &Self) -> Ordering {
self.partial_cmp(other).unwrap()
}
}
lazy_static! {
static ref TIMERS: UPSafeCell<BinaryHeap<TimerCondVar>> = unsafe { UPSafeCell::new(
BinaryHeap::<TimerCondVar>::new()
)};
}
pub fn add_timer(expire_ms: usize, task: Arc<TaskControlBlock>) {
let mut timers = TIMERS.exclusive_access();
timers.push(TimerCondVar {
expire_ms,
task,
});
}
pub fn check_timer() {
let current_ms = get_time_ms();
let mut timers = TIMERS.exclusive_access();
while let Some(timer) = timers.peek() {
if timer.expire_ms <= current_ms {
add_task(Arc::clone(&timer.task));
drop(timer);
timers.pop();
} else { break; }
}
}

View file

@ -18,9 +18,10 @@ use crate::task::{
suspend_current_and_run_next,
current_user_token,
current_trap_cx,
current_trap_cx_user_va,
};
use crate::timer::set_next_trigger;
use crate::config::{TRAP_CONTEXT, TRAMPOLINE};
use crate::timer::{set_next_trigger, check_timer};
use crate::config::TRAMPOLINE;
global_asm!(include_str!("trap.S"));
@ -82,6 +83,7 @@ pub fn trap_handler() -> ! {
}
Trap::Interrupt(Interrupt::SupervisorTimer) => {
set_next_trigger();
check_timer();
suspend_current_and_run_next();
}
_ => {
@ -95,7 +97,7 @@ pub fn trap_handler() -> ! {
#[no_mangle]
pub fn trap_return() -> ! {
set_user_trap_entry();
let trap_cx_ptr = TRAP_CONTEXT;
let trap_cx_user_va = current_trap_cx_user_va();
let user_satp = current_user_token();
extern "C" {
fn __alltraps();
@ -103,15 +105,22 @@ pub fn trap_return() -> ! {
}
let restore_va = __restore as usize - __alltraps as usize + TRAMPOLINE;
unsafe {
llvm_asm!("fence.i" :::: "volatile");
llvm_asm!("jr $0" :: "r"(restore_va), "{a0}"(trap_cx_ptr), "{a1}"(user_satp) :: "volatile");
asm!(
"fence.i",
"jr {restore_va}",
restore_va = in(reg) restore_va,
in("a0") trap_cx_user_va,
in("a1") user_satp,
options(noreturn)
);
}
panic!("Unreachable in back_to_user!");
}
#[no_mangle]
pub fn trap_from_kernel() -> ! {
use riscv::register::sepc;
println!("stval = {:#x}, sepc = {:#x}", stval::read(), sepc::read());
panic!("a trap {:?} from kernel!", scause::read().cause());
}
pub use context::{TrapContext};
pub use context::TrapContext;