diff --git a/easy-fs-fuse/Cargo.toml b/easy-fs-fuse/Cargo.toml index 5c5e68d51..8f2573847 100644 --- a/easy-fs-fuse/Cargo.toml +++ b/easy-fs-fuse/Cargo.toml @@ -9,7 +9,9 @@ edition = "2018" [dependencies] clap = "2.33.3" easy-fs = { path = "../easy-fs" } -rand = "0.8.0" +zerocopy = "=0.7.35" +rand = "=0.8.4" +ppv-lite86 = "=0.2.17" # [features] # board_qemu = [] diff --git a/easy-fs/src/block_cache.rs b/easy-fs/src/block_cache.rs index 1b3b9697d..d3be7e6c0 100644 --- a/easy-fs/src/block_cache.rs +++ b/easy-fs/src/block_cache.rs @@ -66,6 +66,10 @@ impl BlockCache { self.block_device.write_block(self.block_id, &self.cache); } } + pub fn which_device(&self) -> u64 { + // self.block_device + 0 + } } impl Drop for BlockCache { diff --git a/easy-fs/src/efs.rs b/easy-fs/src/efs.rs index 82e95ae02..da9a1e372 100644 --- a/easy-fs/src/efs.rs +++ b/easy-fs/src/efs.rs @@ -144,4 +144,12 @@ impl EasyFileSystem { (block_id - self.data_area_start_block) as usize, ) } + /// Get inode id from block_id and block_offset + pub fn get_disk_inode_id(&self, block_id: u32, block_offset: usize) -> u32 { + // inode_id = (当前块号 - Inode区起始块号) * 每个块能存的 Inode 数 + 块内偏移索引 + let inodes_per_block = BLOCK_SZ / core::mem::size_of::(); + let relative_block = block_id - self.inode_area_start_block; + (relative_block * inodes_per_block as u32) + + (block_offset / core::mem::size_of::()) as u32 + } } diff --git a/easy-fs/src/layout.rs b/easy-fs/src/layout.rs index 3cad1b2ba..a7a7087e1 100644 --- a/easy-fs/src/layout.rs +++ b/easy-fs/src/layout.rs @@ -58,7 +58,7 @@ impl SuperBlock { } } -#[derive(PartialEq)] +#[derive(PartialEq, Clone)] pub enum DiskInodeType { File, Directory, @@ -68,8 +68,10 @@ type IndirectBlock = [u32; BLOCK_SZ / 4]; type DataBlock = [u8; BLOCK_SZ]; #[repr(C)] +#[derive(PartialEq, Clone)] pub struct DiskInode { pub size: u32, + pub nlink: u32, pub direct: [u32; INODE_DIRECT_COUNT], pub indirect1: u32, pub indirect2: u32, @@ -80,6 +82,7 @@ impl DiskInode { /// indirect1 and indirect2 block are allocated only when they are needed. pub fn initialize(&mut self, type_: DiskInodeType) { self.size = 0; + self.nlink = 1; self.direct.iter_mut().for_each(|v| *v = 0); self.indirect1 = 0; self.indirect2 = 0; diff --git a/easy-fs/src/vfs.rs b/easy-fs/src/vfs.rs index 0de5f78f3..dce028e77 100644 --- a/easy-fs/src/vfs.rs +++ b/easy-fs/src/vfs.rs @@ -183,4 +183,130 @@ impl Inode { }); block_cache_sync_all(); } + + /// Delete entry from name + fn delete_entry_from_name(&self, name: &str, disk_inode: &mut DiskInode) -> Option { + // assert it is a directory + assert!(disk_inode.is_dir()); + let file_count = (disk_inode.size as usize) / DIRENT_SZ; + let mut dirent = DirEntry::empty(); + for i in 0..file_count { + assert_eq!( + disk_inode.read_at(DIRENT_SZ * i, dirent.as_bytes_mut(), &self.block_device,), + DIRENT_SZ, + ); + if dirent.name() == name { + let mut replace_entry = DirEntry::empty(); + if i != file_count - 1 { + assert_eq!( + disk_inode.read_at( + DIRENT_SZ * (file_count - 1), + replace_entry.as_bytes_mut(), + &self.block_device, + ), + DIRENT_SZ, + ); + } + assert_eq!( + disk_inode.write_at( + DIRENT_SZ * i, + replace_entry.as_bytes_mut(), + &self.block_device, + ), + DIRENT_SZ, + ); + disk_inode.size -= DIRENT_SZ as u32; + return Some(dirent.inode_id()); + } + } + None + } + + /// Hard link current inode to another name under root directory + pub fn linkat(&self, name: &str) -> bool { + let mut fs = self.fs.lock(); + let inode_id = fs.get_disk_inode_id(self.block_id as u32, self.block_offset); + get_block_cache(self.block_id as usize, Arc::clone(&self.block_device)) + .lock() + .modify(self.block_offset, |inode: &mut DiskInode| { + inode.nlink += 1; + }); + + let (root_block_id, root_block_offset) = fs.get_disk_inode_pos(0); + let res = get_block_cache(root_block_id as usize, Arc::clone(&self.block_device)) + .lock() + .modify(root_block_offset, |root_node: &mut DiskInode| { + assert!(root_node.is_dir()); + + if self.find_inode_id(name, root_node).is_some() { + return false; + } + + let size = root_node.size; + let new_size = root_node.size + DIRENT_SZ as u32; + self.increase_size(new_size, root_node, &mut fs); + + let dirent = DirEntry::new(name, inode_id); + root_node.write_at(size as usize, dirent.as_bytes(), &self.block_device); + true + }); + block_cache_sync_all(); + res + } + /// Unlink an entry from root with name + pub fn unlinkat(&self, name: &str) -> bool { + let mut fs = self.fs.lock(); + + let (root_block_id, root_block_offset) = fs.get_disk_inode_pos(0); + let target_inode_id = + get_block_cache(root_block_id as usize, Arc::clone(&self.block_device)) + .lock() + .modify(root_block_offset, |root_node: &mut DiskInode| { + assert!(root_node.is_dir()); + + // Find name in root directory + // Replace entry with latest entry + // Decrease root directory + self.delete_entry_from_name(name, root_node) + }); + + // Decrease target disknode nlink + let inode_id = match target_inode_id { + Some(id) => id, + None => return false, + }; + + let (target_block, target_offset) = fs.get_disk_inode_pos(inode_id); + get_block_cache(target_block as usize, Arc::clone(&self.block_device)) + .lock() + .modify(target_offset, |inode: &mut DiskInode| { + inode.nlink -= 1; + if inode.nlink == 0 { + let size = inode.size; + let data_blocks_dealloc = inode.clear_size(&self.block_device); + assert!(data_blocks_dealloc.len() == DiskInode::total_blocks(size) as usize); + for data_block in data_blocks_dealloc.into_iter() { + fs.dealloc_data(data_block); + } + } + }); + + block_cache_sync_all(); + true + } + /// get inode info + pub fn stat(&self) -> (u64, u64, Option, Option) { + let fs = self.fs.lock(); + let inode = fs.get_disk_inode_id(self.block_id as u32, self.block_offset); + let dev = get_block_cache(self.block_id as usize, Arc::clone(&self.block_device)) + .lock() + .which_device(); + let mut is_dir = None; + let mut nlink = None; + self.modify_disk_inode(|disk_inode| { + is_dir = Some(disk_inode.is_dir()); + nlink = Some(disk_inode.nlink); + }); + (dev, inode as u64, is_dir, nlink) + } } diff --git a/os/Cargo.toml b/os/Cargo.toml index 55be8151e..56c5060bf 100644 --- a/os/Cargo.toml +++ b/os/Cargo.toml @@ -14,5 +14,4 @@ log = "0.4" riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] } xmas-elf = "0.7.0" virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers", rev = "4ee80e5" } -easy-fs = { path = "../easy-fs" } - +easy-fs = { path = "../easy-fs" } \ No newline at end of file diff --git a/os/src/config.rs b/os/src/config.rs index 0dade9ab2..cee592e0d 100644 --- a/os/src/config.rs +++ b/os/src/config.rs @@ -23,3 +23,7 @@ pub const CLOCK_FREQ: usize = 12500000; pub const MEMORY_END: usize = 0x88000000; /// The base address of control registers in Virtio_Block device pub const MMIO: &[(usize, usize)] = &[(0x10001000, 0x1000)]; +/// Max syscall num +pub const MAX_SYSCALL_NUM: usize = 500; +/// Prior process manager BigStride +pub const BIG_STRIDE: u8 = 2<<6; diff --git a/os/src/fs/inode.rs b/os/src/fs/inode.rs index 9953942b5..029e0ee79 100644 --- a/os/src/fs/inode.rs +++ b/os/src/fs/inode.rs @@ -5,7 +5,7 @@ //! `UPSafeCell` -> `OSInode`: for static `ROOT_INODE`,we //! need to wrap `OSInodeInner` into `UPSafeCell` use super::File; -use crate::drivers::BLOCK_DEVICE; +use crate::{drivers::BLOCK_DEVICE, fs::StatMode}; use crate::mm::UserBuffer; use crate::sync::UPSafeCell; use alloc::sync::Arc; @@ -13,6 +13,7 @@ use alloc::vec::Vec; use bitflags::*; use easy_fs::{EasyFileSystem, Inode}; use lazy_static::*; +use crate::fs::Stat; /// inode in memory /// A wrapper around a filesystem inode @@ -53,6 +54,14 @@ impl OSInode { } v } + /// link current OSInode to name + pub fn linkat(&self,name:&str)->bool{ + self.inner.exclusive_access().inode.linkat(name) + } + /// unlink name from rootinode + pub fn unlinkat(&self,name:&str)->bool{ + self.inner.exclusive_access().inode.unlinkat(name) + } } lazy_static! { @@ -156,4 +165,18 @@ impl File for OSInode { } total_write_size } + fn stat(&self, st:&mut Stat) { + let(dev,ino,is_dir,nlink)=self.inner.exclusive_access().inode.stat(); + st.dev=dev; + st.ino=ino; + st.mode= match is_dir { + Some(true) => StatMode::DIR, + Some(false) => StatMode::FILE, + None => StatMode::NULL, + }; + st.nlink=match nlink{ + Some(val)=>val, + None=>0, + } + } } diff --git a/os/src/fs/mod.rs b/os/src/fs/mod.rs index c298c1775..2a7754b2f 100644 --- a/os/src/fs/mod.rs +++ b/os/src/fs/mod.rs @@ -16,6 +16,8 @@ pub trait File: Send + Sync { fn read(&self, buf: UserBuffer) -> usize; /// write to the file from buf, return the number of bytes written fn write(&self, buf: UserBuffer) -> usize; + /// provide file state + fn stat(&self, st: &mut Stat); } /// The stat of a inode @@ -44,6 +46,8 @@ bitflags! { const DIR = 0o040000; /// ordinary regular file const FILE = 0o100000; + /// pipe + const PIPE = 0o010000; } } diff --git a/os/src/fs/pipe.rs b/os/src/fs/pipe.rs index 9bd5d773e..3f0273412 100644 --- a/os/src/fs/pipe.rs +++ b/os/src/fs/pipe.rs @@ -1,4 +1,5 @@ use super::File; +use crate::fs::StatMode; use crate::mm::UserBuffer; use crate::sync::UPSafeCell; use alloc::sync::{Arc, Weak}; @@ -173,4 +174,10 @@ impl File for Pipe { } } } + fn stat(&self, st: &mut super::Stat) { + st.dev = 0; + st.ino = 0; + st.mode = StatMode::PIPE; + st.nlink = 1; + } } diff --git a/os/src/fs/stdio.rs b/os/src/fs/stdio.rs index 6075a6546..58f2c8f56 100644 --- a/os/src/fs/stdio.rs +++ b/os/src/fs/stdio.rs @@ -1,5 +1,6 @@ //!Stdin & Stdout use super::File; +use crate::fs::StatMode; use crate::mm::UserBuffer; use crate::sbi::console_getchar; use crate::task::suspend_current_and_run_next; @@ -39,6 +40,12 @@ impl File for Stdin { fn write(&self, _user_buf: UserBuffer) -> usize { panic!("Cannot write to stdin!"); } + fn stat(&self, st:&mut super::Stat) { + st.dev=0; + st.ino=0; + st.mode=StatMode::NULL; + st.nlink=0; + } } impl File for Stdout { @@ -57,4 +64,10 @@ impl File for Stdout { } user_buf.len() } + fn stat(&self, st:&mut super::Stat) { + st.dev=0; + st.ino=0; + st.mode=StatMode::NULL; + st.nlink=0; + } } diff --git a/os/src/mm/address.rs b/os/src/mm/address.rs index 68eb7da6d..d570e141b 100644 --- a/os/src/mm/address.rs +++ b/os/src/mm/address.rs @@ -243,6 +243,14 @@ where pub fn get_end(&self) -> T { self.r } + pub fn is_overlap(&self,l:T,r:T)->Option{ + if l >= r { return None; } + if l < self.r && self.l < r { + Some(true) + } else { + Some(false) + } + } } impl IntoIterator for SimpleRange where diff --git a/os/src/mm/memory_set.rs b/os/src/mm/memory_set.rs index 8f88afad5..96e896096 100644 --- a/os/src/mm/memory_set.rs +++ b/os/src/mm/memory_set.rs @@ -65,6 +65,18 @@ impl MemorySet { None, ); } + /// insert_lazy_frame + pub fn insert_lazy_framed_area( + &mut self, + start_va: VirtAddr, + end_va: VirtAddr, + permission: MapPermission, + ) { + self.push( + MapArea::new(start_va, end_va, MapType::LazyFramed, permission), + None, + ); + } /// remove a area pub fn remove_area_with_start_vpn(&mut self, start_vpn: VirtPageNum) { if let Some((idx, area)) = self @@ -81,7 +93,9 @@ impl MemorySet { /// Assuming that there are no conflicts in the virtual address /// space. fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) { - map_area.map(&mut self.page_table); + if map_area.map_type!=MapType::LazyFramed{ + map_area.map(&mut self.page_table); + } if let Some(data) = data { map_area.copy_data(&mut self.page_table, data); } @@ -317,7 +331,123 @@ impl MemorySet { false } } + + /// Memory map to user-space + #[allow(unused)] + pub fn mmap(&mut self, start_va:VirtAddr, end_va: VirtAddr, permission: MapPermission)->isize{ + let start_vpn=start_va.floor(); + let end_vpn=end_va.ceil(); + + if let Some(area)=self.areas.iter().find(|area| { + let res = area.vpn_range.is_overlap(start_vpn, end_vpn); + !res.is_none()&&res.unwrap() + }){ + -1 + }else{ + self.insert_lazy_framed_area(start_va, end_va, permission); + 0 + } + } + + /// unmap pages, do not contains end_va located pages + #[allow(unused)] + pub fn unmmap(&mut self, start_va:VirtAddr, end_va: VirtAddr)->isize{ + let start_vpn=start_va.floor(); + let end_vpn=end_va.ceil(); + + let mut idx=0; + + while idxbool{ + let vpn=va.floor(); + // 如果已经映射就不处理 + if let Some(pte) = self.page_table.translate(vpn) { + if pte.is_valid() { + return false; + } + } + + if let Some(area) = self.areas.iter_mut().find(|a: &&mut MapArea| { + a.map_type == MapType::LazyFramed + && a.vpn_range.get_start() <= vpn + && vpn < a.vpn_range.get_end() + }) { + area.compensate_map_one(&mut self.page_table, vpn); // 现在才分配物理页 + true + } else { + false + } + } } + /// map area structure, controls a contiguous piece of virtual memory pub struct MapArea { vpn_range: VPNRange, @@ -327,6 +457,7 @@ pub struct MapArea { } impl MapArea { + /// Establish new MapArea pub fn new( start_va: VirtAddr, end_va: VirtAddr, @@ -361,15 +492,41 @@ impl MapArea { ppn = frame.ppn; self.data_frames.insert(vpn, frame); } + MapType::LazyFramed =>{ + ppn=PhysPageNum(0) + } } let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); page_table.map(vpn, ppn, pte_flags); } + /// Map one virtual page into pagetable + pub fn compensate_map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) ->isize{ + let ppn: PhysPageNum; + match self.map_type { + MapType::LazyFramed =>{ + let frame: FrameTracker = frame_alloc().unwrap(); + ppn = frame.ppn; + self.data_frames.insert(vpn, frame); + let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap(); + page_table.map(vpn, ppn, pte_flags); + 0 + } + _=>{-1} + } + } pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) { - if self.map_type == MapType::Framed { + if self.map_type==MapType::LazyFramed{ + let pte=page_table.translate(vpn); + if pte.is_some(){ + self.data_frames.remove(&vpn); + page_table.unmap(vpn); + } + }else if self.map_type == MapType::Framed { self.data_frames.remove(&vpn); + page_table.unmap(vpn); + }else{ + page_table.unmap(vpn); } - page_table.unmap(vpn); } pub fn map(&mut self, page_table: &mut PageTable) { for vpn in self.vpn_range { @@ -388,6 +545,15 @@ impl MapArea { } self.vpn_range = VPNRange::new(self.vpn_range.get_start(), new_end); } + /// Shrink up + #[allow(unused)] + pub fn shrink_up(&mut self, page_table: &mut PageTable, new_start: VirtPageNum) { + for vpn in VPNRange::new(self.vpn_range.get_start(),new_start) { + self.unmap_one(page_table, vpn) + } + self.vpn_range = VPNRange::new(new_start,self.vpn_range.get_end()); + } + /// Append next #[allow(unused)] pub fn append_to(&mut self, page_table: &mut PageTable, new_end: VirtPageNum) { for vpn in VPNRange::new(self.vpn_range.get_end(), new_end) { @@ -422,8 +588,12 @@ impl MapArea { #[derive(Copy, Clone, PartialEq, Debug)] /// map type for memory set: identical or framed pub enum MapType { + /// Identical project Identical, + /// Framed project Framed, + /// Lazy allocate + LazyFramed, } bitflags! { diff --git a/os/src/mm/mod.rs b/os/src/mm/mod.rs index d75c373e1..8ead5fb3d 100644 --- a/os/src/mm/mod.rs +++ b/os/src/mm/mod.rs @@ -19,8 +19,8 @@ pub use memory_set::remap_test; pub use memory_set::{kernel_token, MapPermission, MemorySet, KERNEL_SPACE}; use page_table::PTEFlags; pub use page_table::{ - translated_byte_buffer, translated_ref, translated_refmut, translated_str, PageTable, - PageTableEntry, UserBuffer, UserBufferIterator, + translated_byte_buffer, translated_ref, translated_refmut, translated_str, copy_to_user, + PageTable, PageTableEntry, UserBuffer, UserBufferIterator, }; /// initiate heap allocator, frame allocator and kernel space diff --git a/os/src/mm/page_table.rs b/os/src/mm/page_table.rs index bca347114..96ba0d2c3 100644 --- a/os/src/mm/page_table.rs +++ b/os/src/mm/page_table.rs @@ -1,5 +1,6 @@ //! Implementation of [`PageTableEntry`] and [`PageTable`]. use super::{frame_alloc, FrameTracker, PhysAddr, PhysPageNum, StepByOne, VirtAddr, VirtPageNum}; +use crate::config::PAGE_SIZE; use alloc::string::String; use alloc::vec; use alloc::vec::Vec; @@ -62,6 +63,10 @@ impl PageTableEntry { pub fn executable(&self) -> bool { (self.flags() & PTEFlags::X) != PTEFlags::empty() } + /// The page pointered by page table entry is user-alowed? + pub fn is_user_allowed(&self) -> bool { + (self.flags() & PTEFlags::U) != PTEFlags::empty() + } } /// page table structure @@ -158,6 +163,34 @@ impl PageTable { } } +/// copy src to user-space address dst_va +pub fn copy_to_user(token: usize, src: &[u8], dst_va: usize) -> isize { + let page_table = PageTable::from_token(token); + let start: usize = dst_va; + let len = src.len(); + let mut processed: usize = 0; + + while processed < len { + let start_va = VirtAddr::from(start + processed); + let vpn = start_va.floor(); + let pte = page_table.translate(vpn); + + // vaild and writeable + if pte.is_none() || !pte.unwrap().is_valid() || !pte.unwrap().writable() { + return -1 as isize; + } + + let ppn = pte.unwrap().ppn(); + let page_offset = start_va.page_offset(); + let write_len = (PAGE_SIZE - page_offset).min(len - processed); + + let dst_slice = &mut ppn.get_bytes_array()[page_offset..page_offset + write_len]; + dst_slice.copy_from_slice(&src[processed..processed + write_len]); + processed += write_len; + } + 0 +} + /// Translate&Copy a ptr[u8] array with LENGTH len to a mutable u8 Vec through page table pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<&'static mut [u8]> { let page_table = PageTable::from_token(token); @@ -168,6 +201,7 @@ pub fn translated_byte_buffer(token: usize, ptr: *const u8, len: usize) -> Vec<& let start_va = VirtAddr::from(start); let mut vpn = start_va.floor(); let ppn = page_table.translate(vpn).unwrap().ppn(); + // 可能越界!应当通过检查user权限避免对页顶部进行访问 vpn.step(); let mut end_va: VirtAddr = vpn.into(); end_va = end_va.min(VirtAddr::from(end)); diff --git a/os/src/syscall/fs.rs b/os/src/syscall/fs.rs index 7f497b06a..674d7d8f8 100644 --- a/os/src/syscall/fs.rs +++ b/os/src/syscall/fs.rs @@ -48,7 +48,7 @@ pub fn sys_read(fd: usize, buf: *const u8, len: usize) -> isize { } pub fn sys_open(path: *const u8, flags: u32) -> isize { - trace!("kernel:pid[{}] sys_open", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_open", current_task().unwrap().pid.0); let task = current_task().unwrap(); let token = current_user_token(); let path = translated_str(token, path); @@ -63,7 +63,7 @@ pub fn sys_open(path: *const u8, flags: u32) -> isize { } pub fn sys_close(fd: usize) -> isize { - trace!("kernel:pid[{}] sys_close", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_close", current_task().unwrap().pid.0); let task = current_task().unwrap(); let mut inner = task.inner_exclusive_access(); if fd >= inner.fd_table.len() { @@ -77,7 +77,7 @@ pub fn sys_close(fd: usize) -> isize { } pub fn sys_pipe(pipe: *mut usize) -> isize { - trace!("kernel:pid[{}] sys_pipe", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_pipe", current_task().unwrap().pid.0); let task = current_task().unwrap(); let token = current_user_token(); let mut inner = task.inner_exclusive_access(); @@ -92,7 +92,7 @@ pub fn sys_pipe(pipe: *mut usize) -> isize { } pub fn sys_dup(fd: usize) -> isize { - trace!("kernel:pid[{}] sys_dup", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_dup", current_task().unwrap().pid.0); let task = current_task().unwrap(); let mut inner = task.inner_exclusive_access(); if fd >= inner.fd_table.len() { @@ -106,20 +106,67 @@ pub fn sys_dup(fd: usize) -> isize { new_fd as isize } -/// YOUR JOB: Implement fstat. -pub fn sys_fstat(_fd: usize, _st: *mut Stat) -> isize { - trace!("kernel:pid[{}] sys_fstat NOT IMPLEMENTED", current_task().unwrap().pid.0); +pub fn sys_fstat(fd: usize, st: *mut Stat) -> isize { + trace!( + "kernel:pid[{}] sys_fstat NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + let task = current_task().unwrap(); + let token = current_user_token(); + let path: &mut Stat = translated_refmut(token, st); + let inner = task.inner_exclusive_access(); + if fd >= inner.fd_table.len() { + return -1; + } + if inner.fd_table[fd].is_none() { + return -1; + } + + if let Some(file) = inner.fd_table[fd].as_ref() { + if st.is_null() { + return -1; + } + file.stat(path); + return 0; + } -1 } /// YOUR JOB: Implement linkat. pub fn sys_linkat(_old_name: *const u8, _new_name: *const u8) -> isize { - trace!("kernel:pid[{}] sys_linkat NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_linkat NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + let token = current_user_token(); + let path: alloc::string::String = translated_str(token, _old_name); + let new_path: alloc::string::String = translated_str(token, _new_name); + let inode = match open_file(path.as_str(), OpenFlags::RDONLY) { + Some(inode) => inode, + None => return -1, + }; + + match inode.linkat(new_path.as_str()) { + true => 0, + false => -1, + } } /// YOUR JOB: Implement unlinkat. pub fn sys_unlinkat(_name: *const u8) -> isize { - trace!("kernel:pid[{}] sys_unlinkat NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_unlinkat NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + let token = current_user_token(); + let path: alloc::string::String = translated_str(token, _name); + let inode = match open_file(path.as_str(), OpenFlags::RDONLY) { + Some(inode) => inode, + None => return -1, + }; + + match inode.unlinkat(path.as_str()) { + true => 0, + false => -1, + } } diff --git a/os/src/syscall/mod.rs b/os/src/syscall/mod.rs index 44e5f89ff..a0806cdcb 100644 --- a/os/src/syscall/mod.rs +++ b/os/src/syscall/mod.rs @@ -67,10 +67,12 @@ mod process; use fs::*; use process::*; +use crate::task::cur_syscall_count_inc; use crate::{fs::Stat, task::SignalAction}; /// handle syscall exception with `syscall_id` and other arguments pub fn syscall(syscall_id: usize, args: [usize; 4]) -> isize { + cur_syscall_count_inc(check_syscall(syscall_id)); match syscall_id { SYSCALL_DUP => sys_dup(args[0]), SYSCALL_OPEN => sys_open(args[1] as *const u8, args[2] as u32), diff --git a/os/src/syscall/process.rs b/os/src/syscall/process.rs index c42269f17..ed476ae3a 100644 --- a/os/src/syscall/process.rs +++ b/os/src/syscall/process.rs @@ -1,12 +1,18 @@ //! Process management syscalls use crate::{ + config::{CLOCK_FREQ, MAX_SYSCALL_NUM, PAGE_SIZE}, fs::{open_file, OpenFlags}, - mm::{translated_ref, translated_refmut, translated_str}, + mm::{ + copy_to_user, translated_ref, translated_refmut, translated_str, MapPermission, VirtAddr, + }, task::{ - add_task, current_task, current_user_token, exit_current_and_run_next, pid2task, - suspend_current_and_run_next, SignalAction, SignalFlags, MAX_SIG, + add_task, cur_syscall_count_get, current_mmap, current_munmap, current_task, + current_task_set_prio, current_user_token, exit_current_and_run_next, + handle_cur_page_fault, pid2task, suspend_current_and_run_next, SignalAction, SignalFlags, + MAX_SIG, MIN_PRIORITY, }, + timer::get_time, }; use alloc::{string::String, sync::Arc, vec::Vec}; @@ -17,8 +23,16 @@ pub struct TimeVal { pub usec: usize, } +// return SYSCALL idx in COUNTER_SYSCALL +pub fn check_syscall(syscall_num: usize) -> usize { + match syscall_num { + 0..=MAX_SYSCALL_NUM => syscall_num, + _ => panic!("Too big id for syscall!"), + } +} + pub fn sys_exit(exit_code: i32) -> ! { - trace!("kernel:pid[{}] sys_exit",current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_exit", current_task().unwrap().pid.0); exit_current_and_run_next(exit_code); panic!("Unreachable in sys_exit!"); } @@ -30,12 +44,12 @@ pub fn sys_yield() -> isize { } pub fn sys_getpid() -> isize { - trace!("kernel: sys_getpid pid:{}", current_task().unwrap().pid.0); + trace!("kernel: sys_getpid pid:{}", current_task().unwrap().pid.0); current_task().unwrap().pid.0 as isize } pub fn sys_fork() -> isize { - trace!("kernel:pid[{}] sys_fork", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_fork", current_task().unwrap().pid.0); let current_task = current_task().unwrap(); let new_task = current_task.fork(); let new_pid = new_task.pid.0; @@ -79,7 +93,7 @@ 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 { - //trace!("kernel: sys_waitpid"); + //trace!("kernel: sys_waitpid"); let task = current_task().unwrap(); // find a child process @@ -115,7 +129,7 @@ pub fn sys_waitpid(pid: isize, exit_code_ptr: *mut i32) -> isize { } pub fn sys_kill(pid: usize, signum: i32) -> isize { - trace!("kernel:pid[{}] sys_kill", current_task().unwrap().pid.0); + trace!("kernel:pid[{}] sys_kill", current_task().unwrap().pid.0); if let Some(task) = pid2task(pid) { if let Some(flag) = SignalFlags::from_bits(1 << signum) { // insert the signal if legal @@ -137,20 +151,69 @@ pub fn sys_kill(pid: usize, signum: i32) -> isize { /// HINT: You might reimplement it with virtual memory management. /// HINT: What if [`TimeVal`] is splitted by two pages ? pub fn sys_get_time(_ts: *mut TimeVal, _tz: usize) -> isize { - trace!("kernel:pid[{}] sys_get_time NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_get_time NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + trace!("kernel: sys_get_time"); + let time = TimeVal { + sec: get_time() / CLOCK_FREQ, + usec: (get_time() % CLOCK_FREQ) * 1000000 / CLOCK_FREQ, + }; + + let src = unsafe { + core::slice::from_raw_parts( + &time as *const _ as *const u8, + core::mem::size_of::(), + ) + }; + + copy_to_user(current_user_token(), src, _ts as usize) } /// YOUR JOB: Implement mmap. -pub fn sys_mmap(_start: usize, _len: usize, _port: usize) -> isize { - trace!("kernel:pid[{}] sys_mmap NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 +pub fn sys_mmap(_start: usize, _len: usize, _prot: usize) -> isize { + trace!( + "kernel:pid[{}] sys_mmap NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + // not aligned + if _start & (PAGE_SIZE - 1) != 0 { + return -1; + } + // illegal prot or meaningless prot + if _prot & !0x7 != 0 || _prot & 0x7 == 0 { + return -1; + } + + let start_va = VirtAddr::from(_start); + let end_va = VirtAddr::from(_start + _len); + let mut map_prem = MapPermission::from_bits_truncate((_prot << 1) as u8); + map_prem |= MapPermission::U; + + current_mmap(start_va, end_va, map_prem) } /// YOUR JOB: Implement munmap. pub fn sys_munmap(_start: usize, _len: usize) -> isize { - trace!("kernel:pid[{}] sys_munmap NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_munmap NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + // not aligned + if _start & (PAGE_SIZE - 1) != 0 { + return -1; + } + + // not aligned + if _len & (PAGE_SIZE - 1) != 0 { + return -1; + } + + let start_va = VirtAddr::from(_start); + let end_va = VirtAddr::from(_start + _len); + + current_munmap(start_va, end_va) } /// change data segment size @@ -166,18 +229,69 @@ pub fn sys_sbrk(size: i32) -> isize { /// YOUR JOB: Implement spawn. /// HINT: fork + exec =/= spawn pub fn sys_spawn(_path: *const u8) -> isize { - trace!("kernel:pid[{}] sys_spawn NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_spawn NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + let token = current_user_token(); + let path = translated_str(token, _path); + 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 new_task = task.spawn(all_data.as_slice()); + let new_pid = new_task.pid.0; + // add new task to scheduler + add_task(new_task); + new_pid as isize + } else { + -1 + } } // YOUR JOB: Set task priority. pub fn sys_set_priority(_prio: isize) -> isize { - trace!("kernel:pid[{}] sys_set_priority NOT IMPLEMENTED", current_task().unwrap().pid.0); - -1 + trace!( + "kernel:pid[{}] sys_set_priority NOT IMPLEMENTED", + current_task().unwrap().pid.0 + ); + if _prio < MIN_PRIORITY as isize { + return -1; + } + + current_task_set_prio(_prio as usize); + _prio +} + +#[allow(unused)] +pub fn sys_trace(_trace_request: usize, _id: usize, _data: usize) -> isize { + trace!("kernel: sys_trace"); + let token = current_user_token(); + match _trace_request { + 0 => { + // 如果vaild=1或vaild=0但是不是lazy-allocate,则不处理 + handle_cur_page_fault(_id.into()); + // translated_refmut遇到非法va直接unwrap->panic + *(translated_refmut(token, _id as *mut u8)) as isize + } + 1 => { + let src: [u8; 1] = (_data as u8).to_le_bytes(); + // 如果vaild=1或vaild=0但是不是lazy-allocate,则不处理 + handle_cur_page_fault(_id.into()); + copy_to_user(token, &src, _id) + } + 2 => { + let checked_syscall = check_syscall(_id); + cur_syscall_count_get(checked_syscall) as isize + } + _ => -1, + } } pub fn sys_sigprocmask(mask: u32) -> isize { - trace!("kernel:pid[{}] sys_sigprocmask", current_task().unwrap().pid.0); + trace!( + "kernel:pid[{}] sys_sigprocmask", + current_task().unwrap().pid.0 + ); if let Some(task) = current_task() { let mut inner = task.inner_exclusive_access(); let old_mask = inner.signal_mask; @@ -193,7 +307,10 @@ pub fn sys_sigprocmask(mask: u32) -> isize { } pub fn sys_sigreturn() -> isize { - trace!("kernel:pid[{}] sys_sigreturn", current_task().unwrap().pid.0); + trace!( + "kernel:pid[{}] sys_sigreturn", + current_task().unwrap().pid.0 + ); if let Some(task) = current_task() { let mut inner = task.inner_exclusive_access(); inner.handling_sig = -1; @@ -226,7 +343,10 @@ pub fn sys_sigaction( action: *const SignalAction, old_action: *mut SignalAction, ) -> isize { - trace!("kernel:pid[{}] sys_sigaction", current_task().unwrap().pid.0); + trace!( + "kernel:pid[{}] sys_sigaction", + current_task().unwrap().pid.0 + ); let token = current_user_token(); let task = current_task().unwrap(); let mut inner = task.inner_exclusive_access(); diff --git a/os/src/task/manager.rs b/os/src/task/manager.rs index 57e7ca53d..af76f3810 100644 --- a/os/src/task/manager.rs +++ b/os/src/task/manager.rs @@ -3,7 +3,7 @@ //! It is only used to manage processes and schedule process based on ready queue. //! Other CPU process monitoring functions are in Processor. -use super::TaskControlBlock; +use super::{TaskControlBlock, TaskPriority}; use crate::sync::UPSafeCell; use alloc::collections::{BTreeMap, VecDeque}; use alloc::sync::Arc; @@ -27,7 +27,27 @@ impl TaskManager { } /// Take a process out of the ready queue pub fn fetch(&mut self) -> Option> { - self.ready_queue.pop_front() + if let Some(index) = self.find_candidate_task() { + let task = self.ready_queue.remove(index).unwrap(); + task.inner_exclusive_access().inc_stride(); + Some(task) + } else { + None + } + } + /// Find minial stride task, return idx + pub fn find_candidate_task(&self) -> Option { + self.ready_queue + .iter() + .enumerate() + .min_by(|(_, task_a), (_, task_b)| { + let a_stride = task_a.inner_exclusive_access().get_stride(); + let b_stride = task_b.inner_exclusive_access().get_stride(); + + // 使用之前定义的 Ord 实现(包含 wrapping_sub 逻辑) + TaskPriority::cmp(a_stride, b_stride) + }) + .map(|(idx, _)| idx) } } @@ -42,7 +62,7 @@ lazy_static! { /// Add process to ready queue pub fn add_task(task: Arc) { - //trace!("kernel: TaskManager::add_task"); + //trace!("kernel: TaskManager::add_task"); PID2TCB .exclusive_access() .insert(task.getpid(), Arc::clone(&task)); @@ -51,7 +71,7 @@ pub fn add_task(task: Arc) { /// Take a process out of the ready queue pub fn fetch_task() -> Option> { - //trace!("kernel: TaskManager::fetch_task"); + //trace!("kernel: TaskManager::fetch_task"); TASK_MANAGER.exclusive_access().fetch() } diff --git a/os/src/task/mod.rs b/os/src/task/mod.rs index f82e5e347..f1bbfee72 100644 --- a/os/src/task/mod.rs +++ b/os/src/task/mod.rs @@ -11,8 +11,8 @@ mod action; mod context; -mod manager; mod id; +mod manager; mod processor; mod signal; mod switch; @@ -26,13 +26,15 @@ use lazy_static::*; use manager::fetch_task; use manager::remove_from_pid2task; use switch::__switch; -pub use task::{TaskControlBlock, TaskStatus}; +pub use task::{TaskControlBlock, TaskPriority, TaskStatus, MAX_PRIORITY, MIN_PRIORITY}; pub use action::{SignalAction, SignalActions}; -pub use manager::{add_task, pid2task}; pub use id::{kstack_alloc, pid_alloc, KernelStack, PidHandle}; +pub use manager::{add_task, pid2task}; pub use processor::{ - current_task, current_trap_cx, current_user_token, run_tasks, schedule, take_current_task, + cur_syscall_count_get, cur_syscall_count_inc, current_mmap, current_munmap, current_task, + current_task_set_prio, current_trap_cx, current_user_token, handle_cur_page_fault, run_tasks, + schedule, take_current_task, }; pub use signal::{SignalFlags, MAX_SIG}; diff --git a/os/src/task/processor.rs b/os/src/task/processor.rs index 83a6250a6..bff7a8b75 100644 --- a/os/src/task/processor.rs +++ b/os/src/task/processor.rs @@ -7,6 +7,7 @@ use super::__switch; use super::{fetch_task, TaskStatus}; use super::{TaskContext, TaskControlBlock}; +use crate::mm::{MapPermission, VirtAddr}; use crate::sync::UPSafeCell; use crate::trap::TrapContext; use alloc::sync::Arc; @@ -109,3 +110,54 @@ pub fn schedule(switched_task_cx_ptr: *mut TaskContext) { __switch(switched_task_cx_ptr, idle_task_cx_ptr); } } + +/// Set current task prio +pub fn current_task_set_prio(prio: usize) { + current_task() + .unwrap() + .inner_exclusive_access() + .set_priority(prio); +} + +/// Mmap in current user-space +pub fn current_mmap(start_va: VirtAddr, end_va: VirtAddr, perm: MapPermission) -> isize { + current_task() + .unwrap() + .inner_exclusive_access() + .memory_set + .mmap(start_va, end_va, perm) +} + +/// Unmmap in current user-space +pub fn current_munmap(start_va: VirtAddr, end_va: VirtAddr) -> isize { + current_task() + .unwrap() + .inner_exclusive_access() + .memory_set + .unmmap(start_va, end_va) +} + +/// export pagefault-handler +pub fn handle_cur_page_fault(va: VirtAddr) -> bool { + current_task() + .unwrap() + .inner_exclusive_access() + .memory_set + .handle_page_fault(va) +} + +/// Inc certain syscall count in cur task +pub fn cur_syscall_count_inc(syscall: usize) { + current_task() + .unwrap() + .inner_exclusive_access() + .syscall_count_inc(syscall); +} + +/// Get certain syscall count in cur task +pub fn cur_syscall_count_get(syscall: usize) -> u8 { + current_task() + .unwrap() + .inner_exclusive_access() + .syscall_count_get(syscall) +} diff --git a/os/src/task/task.rs b/os/src/task/task.rs index 9c9d3099e..2b39fcf3c 100644 --- a/os/src/task/task.rs +++ b/os/src/task/task.rs @@ -1,8 +1,10 @@ //! Types related to task management & Functions for completely changing TCB -use super::{kstack_alloc, pid_alloc, KernelStack, PidHandle, SignalActions, SignalFlags, TaskContext}; +use super::{ + kstack_alloc, pid_alloc, KernelStack, PidHandle, SignalActions, SignalFlags, TaskContext, +}; use crate::{ - config::TRAP_CONTEXT_BASE, + config::{BIG_STRIDE, MAX_SYSCALL_NUM, TRAP_CONTEXT_BASE}, fs::{File, Stdin, Stdout}, mm::{translated_refmut, MemorySet, PhysPageNum, VirtAddr, KERNEL_SPACE}, sync::UPSafeCell, @@ -15,6 +17,7 @@ use alloc::{ vec::Vec, }; use core::cell::RefMut; +use core::cmp::Ordering; /// Task control block structure /// @@ -43,6 +46,60 @@ impl TaskControlBlock { } } +type PriorityType = u8; +type StrideType = u16; +/// Max priority +pub const MIN_PRIORITY: PriorityType = 2; +/// Min priority +pub const MAX_PRIORITY: PriorityType = 16; + +#[derive(Clone, Copy)] +/// TaskPriority +pub struct TaskPriority { + priority: PriorityType, + current_stride: StrideType, +} + +impl TaskPriority { + /// StrideType max + pub const MAX: StrideType = StrideType::MAX; + + /// New + pub fn new(prio: PriorityType) -> Self { + Self { + priority: prio, + current_stride: 0, + } + } + + /// Increse cur stride by a pass + pub fn inc_stride(&mut self) { + // 使用 wrapping_add 显式处理溢出(即使 StrideType 改变也安全) + let pass = (BIG_STRIDE as PriorityType / self.priority) as StrideType; + self.current_stride = self.current_stride.wrapping_add(pass); + } + + /// Get current stride + pub fn get_stride(&self) -> StrideType { + self.current_stride + } + + /// Set priority + pub fn set_priority(&mut self, prio: PriorityType) { + self.priority = prio; + } + + /// Compare stride + pub fn cmp(a: StrideType, b: StrideType) -> core::cmp::Ordering { + let diff = a.wrapping_sub(b); + if diff < (StrideType::MAX / 2) { + Ordering::Greater + } else { + Ordering::Less + } + } +} + pub struct TaskControlBlockInner { /// The physical page number of the frame where the trap context is placed pub trap_cx_ppn: PhysPageNum, @@ -87,6 +144,12 @@ pub struct TaskControlBlockInner { /// Program break pub program_brk: usize, + + /// Syscall count + pub task_syscall_counter: [u8; MAX_SYSCALL_NUM], + + /// Priority of current task + pub priorty: TaskPriority, } impl TaskControlBlockInner { @@ -110,6 +173,26 @@ impl TaskControlBlockInner { self.fd_table.len() - 1 } } + pub fn syscall_count_inc(&mut self, syscall: usize) { + self.task_syscall_counter[syscall] += 1; + } + pub fn syscall_count_get(&self, syscall: usize) -> u8 { + self.task_syscall_counter[syscall] + } + pub fn get_stride(&self) -> StrideType { + self.priorty.get_stride() + } + pub fn inc_stride(&mut self) { + self.priorty.inc_stride(); + } + pub fn set_priority(&mut self, prio: usize) { + let safe_prio = if prio > PriorityType::MAX as usize { + PriorityType::MAX + } else { + prio as PriorityType + }; + self.priorty.set_priority(safe_prio); + } } impl TaskControlBlock { @@ -158,6 +241,8 @@ impl TaskControlBlock { trap_ctx_backup: None, heap_bottom: user_sp, program_brk: user_sp, + task_syscall_counter: [0; MAX_SYSCALL_NUM], + priorty: TaskPriority::new(MIN_PRIORITY), }) }, }; @@ -273,6 +358,8 @@ impl TaskControlBlock { trap_ctx_backup: None, heap_bottom: parent_inner.heap_bottom, program_brk: parent_inner.program_brk, + task_syscall_counter: [0; MAX_SYSCALL_NUM], + priorty: TaskPriority::new(MIN_PRIORITY), }) }, }); @@ -288,6 +375,18 @@ impl TaskControlBlock { // ---- release parent PCB } + /// spawn a new process from elf + pub fn spawn(self: &Arc, elf_data: &[u8]) -> Arc { + let mut parent_inner = self.inner_exclusive_access(); + let child = Arc::new(Self::new(elf_data)); + + child.inner_exclusive_access().priorty = TaskPriority::new(MAX_PRIORITY); + + parent_inner.children.push(child.clone()); + child.inner_exclusive_access().parent = Some(Arc::downgrade(self)); + child + } + /// get pid of process pub fn getpid(&self) -> usize { self.pid.0 diff --git a/os/src/trap/mod.rs b/os/src/trap/mod.rs index 8f5473b50..bc84847e3 100644 --- a/os/src/trap/mod.rs +++ b/os/src/trap/mod.rs @@ -18,7 +18,8 @@ use crate::config::{TRAMPOLINE, TRAP_CONTEXT_BASE}; use crate::syscall::syscall; use crate::task::{ check_signals_error_of_current, current_add_signal, current_trap_cx, current_user_token, - exit_current_and_run_next, handle_signals, suspend_current_and_run_next, SignalFlags, + exit_current_and_run_next, handle_cur_page_fault, handle_signals, suspend_current_and_run_next, + SignalFlags, }; use crate::timer::set_next_trigger; use core::arch::{asm, global_asm}; @@ -87,7 +88,16 @@ pub fn trap_handler() -> ! { stval, current_trap_cx().sepc, ); - current_add_signal(SignalFlags::SIGSEGV); + // current_add_signal(SignalFlags::SIGSEGV); + // page fault exit code + if handle_cur_page_fault(stval.into()) { + (); + // 继续执行 + } else { + let cx = current_trap_cx(); + println!("[kernel] PageFault in application, bad addr = {:#x}, bad instruction = {:#x}, kernel killed it.", stval, cx.sepc); + exit_current_and_run_next(-2); + } } Trap::Exception(Exception::IllegalInstruction) => { current_add_signal(SignalFlags::SIGILL); diff --git a/rust-toolchain.toml b/rust-toolchain.toml index e5e44e734..1db334da3 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,4 +2,4 @@ profile = "minimal" # use the nightly version of the last stable toolchain, see channel = "nightly-2024-02-25" -components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy"] +components = ["rust-src", "llvm-tools-preview", "rustfmt", "clippy", "rust-analyzer"]