From a25c8132fe1c7f86d6acb1324c9eb54eda34ec6e Mon Sep 17 00:00:00 2001 From: WangRunji Date: Tue, 30 Apr 2019 13:37:31 +0800 Subject: [PATCH] delay mapping file --- crate/memory/src/memory_set/handler/delay.rs | 3 +- crate/memory/src/memory_set/handler/file.rs | 108 +++++++++++++++++++ crate/memory/src/memory_set/handler/mod.rs | 2 + kernel/src/fs/file.rs | 4 + kernel/src/process/structs.rs | 94 ++++++++-------- kernel/src/shell.rs | 14 +-- kernel/src/syscall/mem.rs | 28 ++--- kernel/src/syscall/mod.rs | 2 +- kernel/src/syscall/proc.rs | 11 +- 9 files changed, 188 insertions(+), 78 deletions(-) create mode 100644 crate/memory/src/memory_set/handler/file.rs diff --git a/crate/memory/src/memory_set/handler/delay.rs b/crate/memory/src/memory_set/handler/delay.rs index 6195091..ac4e856 100644 --- a/crate/memory/src/memory_set/handler/delay.rs +++ b/crate/memory/src/memory_set/handler/delay.rs @@ -40,9 +40,10 @@ impl MemoryHandler for Delay { let data = Vec::from(pt.get_page_slice_mut(addr)); with(&mut || { let target = self.allocator.alloc().expect("failed to alloc frame"); + let target_data = pt.get_page_slice_mut(addr); let entry = pt.map(addr, target); + target_data.copy_from_slice(&data); attr.apply(entry); - pt.get_page_slice_mut(addr).copy_from_slice(&data); }); } else { // delay map diff --git a/crate/memory/src/memory_set/handler/file.rs b/crate/memory/src/memory_set/handler/file.rs new file mode 100644 index 0000000..09f0acb --- /dev/null +++ b/crate/memory/src/memory_set/handler/file.rs @@ -0,0 +1,108 @@ +use super::*; + +/// Delay mapping a page to an area of a file. +#[derive(Clone)] +pub struct File { + pub file: F, + pub mem_start: usize, + pub file_start: usize, + pub file_end: usize, + pub allocator: T, +} + +pub trait Read: Clone + Send + Sync + 'static { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize; +} + +impl MemoryHandler for File { + fn box_clone(&self) -> Box { + Box::new(self.clone()) + } + + fn map(&self, pt: &mut PageTable, addr: usize, attr: &MemoryAttr) { + let entry = pt.map(addr, 0); + entry.set_present(false); + attr.apply(entry); + } + + fn unmap(&self, pt: &mut PageTable, addr: usize) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + self.allocator.dealloc(entry.target()); + } + + // PageTable::unmap requires page to be present + entry.set_present(true); + pt.unmap(addr); + } + + fn clone_map( + &self, + pt: &mut PageTable, + with: &Fn(&mut FnMut()), + addr: usize, + attr: &MemoryAttr, + ) { + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() && !attr.readonly { + // eager map and copy data + let data = Vec::from(pt.get_page_slice_mut(addr)); + with(&mut || { + let target = self.allocator.alloc().expect("failed to alloc frame"); + let target_data = pt.get_page_slice_mut(addr); + let entry = pt.map(addr, target); + target_data.copy_from_slice(&data); + attr.apply(entry); + }); + } else { + // delay map + with(&mut || self.map(pt, addr, attr)); + } + } + + fn handle_page_fault(&self, pt: &mut PageTable, addr: usize) -> bool { + let addr = addr & !(PAGE_SIZE - 1); + let entry = pt.get_entry(addr).expect("failed to get entry"); + if entry.present() { + return false; + } + let frame = self.allocator.alloc().expect("failed to alloc frame"); + entry.set_target(frame); + entry.set_present(true); + let writable = entry.writable(); + entry.set_writable(true); + entry.update(); + + self.fill_data(pt, addr); + + let entry = pt.get_entry(addr).expect("failed to get entry"); + entry.set_writable(writable); + entry.update(); + + true + } +} + +impl File { + fn fill_data(&self, pt: &mut PageTable, addr: VirtAddr) { + let data = pt.get_page_slice_mut(addr); + let file_offset = addr + self.file_start - self.mem_start; + let read_size = (self.file_end as isize - file_offset as isize) + .min(PAGE_SIZE as isize) + .max(0) as usize; + let read_size = self.file.read_at(file_offset, &mut data[..read_size]); + if read_size != PAGE_SIZE { + data[read_size..].iter_mut().for_each(|x| *x = 0); + } + } +} + +impl Debug for File { + fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error> { + f.debug_struct("FileHandler") + .field("mem_start", &self.mem_start) + .field("file_start", &self.file_start) + .field("file_end", &self.file_end) + .finish() + } +} diff --git a/crate/memory/src/memory_set/handler/mod.rs b/crate/memory/src/memory_set/handler/mod.rs index 32d6c54..6154e96 100644 --- a/crate/memory/src/memory_set/handler/mod.rs +++ b/crate/memory/src/memory_set/handler/mod.rs @@ -42,8 +42,10 @@ pub trait FrameAllocator: Debug + Clone + Send + Sync + 'static { mod byframe; mod delay; mod linear; +mod file; //mod swap; pub use self::byframe::ByFrame; pub use self::delay::Delay; pub use self::linear::Linear; +pub use self::file::{File, Read}; diff --git a/kernel/src/fs/file.rs b/kernel/src/fs/file.rs index 5e1b84f..3979c4d 100644 --- a/kernel/src/fs/file.rs +++ b/kernel/src/fs/file.rs @@ -116,4 +116,8 @@ impl FileHandle { pub fn io_control(&self, cmd: u32, arg: usize) -> Result<()> { self.inode.io_control(cmd, arg) } + + pub fn inode(&self) -> Arc { + self.inode.clone() + } } diff --git a/kernel/src/process/structs.rs b/kernel/src/process/structs.rs index 3e08ff2..251203c 100644 --- a/kernel/src/process/structs.rs +++ b/kernel/src/process/structs.rs @@ -14,10 +14,13 @@ use xmas_elf::{ use crate::arch::interrupt::{Context, TrapFrame}; use crate::fs::{FileHandle, FileLike, INodeExt, OpenOptions, FOLLOW_MAX_DEPTH}; -use crate::memory::{ByFrame, Delay, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet}; +use crate::memory::{ + ByFrame, Delay, File, GlobalFrameAlloc, KernelStack, MemoryAttr, MemorySet, Read, +}; use crate::sync::{Condvar, SpinNoIrqLock as Mutex}; use super::abi::{self, ProcInitInfo}; +use rcore_fs::vfs::INode; // TODO: avoid pub pub struct Thread { @@ -128,19 +131,25 @@ impl Thread { /// Construct virtual memory of a new user process from ELF `data`. /// Return `(MemorySet, entry_point, ustack_top)` pub fn new_user_vm( - data: &[u8], + inode: &Arc, exec_path: &str, mut args: Vec, envs: Vec, - ) -> (MemorySet, usize, usize) { + ) -> Result<(MemorySet, usize, usize), &'static str> { + // Read data + use crate::fs::INodeExt; + let data = inode + .read_as_vec() + .map_err(|_| "failed to read from INode")?; + // Parse ELF - let elf = ElfFile::new(data).expect("failed to read elf"); + let elf = ElfFile::new(&data)?; // Check ELF type match elf.header.pt2.type_().as_type() { header::Type::Executable => {} header::Type::SharedObject => {} - _ => panic!("ELF is not executable or shared object"), + _ => return Err("ELF is not executable or shared object"), } // Check ELF arch @@ -153,7 +162,7 @@ impl Thread { header::Machine::Other(243) => {} #[cfg(target_arch = "mips")] header::Machine::Mips => {} - machine @ _ => panic!("invalid elf arch: {:?}", machine), + machine @ _ => return Err("invalid ELF arch"), } // Check interpreter (for dynamic link) @@ -161,18 +170,17 @@ impl Thread { // assuming absolute path let inode = crate::fs::ROOT_INODE .lookup_follow(loader_path, FOLLOW_MAX_DEPTH) - .expect("interpreter not found"); - let buf = inode.read_as_vec().expect("failed to load interpreter"); + .map_err(|_| "interpreter not found")?; // modify args for loader args[0] = exec_path.into(); args.insert(0, loader_path.into()); // Elf loader should not have INTERP // No infinite loop - return Thread::new_user_vm(buf.as_slice(), exec_path, args, envs); + return Thread::new_user_vm(&inode, exec_path, args, envs); } // Make page table - let mut vm = elf.make_memory_set(); + let mut vm = elf.make_memory_set(inode); // User stack use crate::consts::{USER_STACK_OFFSET, USER_STACK_SIZE}; @@ -219,17 +227,17 @@ impl Thread { trace!("{:#x?}", vm); let entry_addr = elf.header.pt2.entry_point() as usize; - (vm, entry_addr, ustack_top) + Ok((vm, entry_addr, ustack_top)) } /// Make a new user process from ELF `data` pub fn new_user( - data: &[u8], + inode: &Arc, exec_path: &str, mut args: Vec, envs: Vec, ) -> Box { - let (vm, entry_addr, ustack_top) = Self::new_user_vm(data, exec_path, args, envs); + let (vm, entry_addr, ustack_top) = Self::new_user_vm(inode, exec_path, args, envs).unwrap(); let kstack = KernelStack::new(); @@ -307,7 +315,8 @@ impl Thread { threads: Vec::new(), child_exit: Arc::new(Condvar::new()), child_exit_code: BTreeMap::new(), - }.add_to_table(); + } + .add_to_table(); // link to parent proc.children.push(Arc::downgrade(&new_proc)); @@ -382,10 +391,12 @@ trait ToMemoryAttr { impl ToMemoryAttr for Flags { fn to_attr(&self) -> MemoryAttr { let mut flags = MemoryAttr::default().user(); - // FIXME: handle readonly if self.is_execute() { flags = flags.execute(); } + if !self.is_write() { + flags = flags.readonly(); + } flags } } @@ -393,7 +404,7 @@ impl ToMemoryAttr for Flags { /// Helper functions to process ELF file trait ElfExt { /// Generate a MemorySet according to the ELF file. - fn make_memory_set(&self) -> MemorySet; + fn make_memory_set(&self, inode: &Arc) -> MemorySet; /// Get interpreter string if it has. fn get_interpreter(&self) -> Result<&str, &str>; @@ -403,7 +414,7 @@ trait ElfExt { } impl ElfExt for ElfFile<'_> { - fn make_memory_set(&self) -> MemorySet { + fn make_memory_set(&self, inode: &Arc) -> MemorySet { debug!("creating MemorySet from ELF"); let mut ms = MemorySet::new(); @@ -411,33 +422,19 @@ impl ElfExt for ElfFile<'_> { if ph.get_type() != Ok(Type::Load) { continue; } - let virt_addr = ph.virtual_addr() as usize; - let mem_size = ph.mem_size() as usize; - let data = match ph.get_data(self).unwrap() { - SegmentData::Undefined(data) => data, - _ => unreachable!(), - }; - - // Get target slice - let target = { - ms.push( - virt_addr, - virt_addr + mem_size, - ph.flags().to_attr(), - ByFrame::new(GlobalFrameAlloc), - "elf", - ); - unsafe { ::core::slice::from_raw_parts_mut(virt_addr as *mut u8, mem_size) } - }; - // Copy data - unsafe { - ms.with(|| { - if data.len() != 0 { - target[..data.len()].copy_from_slice(data); - } - target[data.len()..].iter_mut().for_each(|x| *x = 0); - }); - } + ms.push( + ph.virtual_addr() as usize, + ph.virtual_addr() as usize + ph.mem_size() as usize, + ph.flags().to_attr(), + File { + file: INodeForMap(inode.clone()), + mem_start: ph.virtual_addr() as usize, + file_start: ph.offset() as usize, + file_end: ph.offset() as usize + ph.file_size() as usize, + allocator: GlobalFrameAlloc, + }, + "elf", + ); } ms } @@ -479,3 +476,12 @@ impl ElfExt for ElfFile<'_> { } } } + +#[derive(Clone)] +pub struct INodeForMap(pub Arc); + +impl Read for INodeForMap { + fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize { + self.0.read_at(offset, buf).unwrap() + } +} diff --git a/kernel/src/shell.rs b/kernel/src/shell.rs index 61431e8..57bf14c 100644 --- a/kernel/src/shell.rs +++ b/kernel/src/shell.rs @@ -30,9 +30,8 @@ pub fn add_user_shell() { let init_args = vec!["busybox".into(), "ash".into()]; if let Ok(inode) = ROOT_INODE.lookup(init_shell) { - let data = inode.read_as_vec().unwrap(); processor().manager().add(Thread::new_user( - data.as_slice(), + &inode, init_shell, init_args, init_envs, @@ -46,9 +45,8 @@ pub fn add_user_shell() { pub fn add_user_shell() { let cmdline = CMDLINE.read(); let inode = ROOT_INODE.lookup(&cmdline).unwrap(); - let data = inode.read_as_vec().unwrap(); processor().manager().add(Thread::new_user( - data.as_slice(), + &inode, cmdline.split(' ').map(|s| s.into()).collect(), Vec::new(), )); @@ -66,16 +64,14 @@ pub extern "C" fn shell(_arg: usize) -> ! { continue; } let name = cmd.trim().split(' ').next().unwrap(); - if let Ok(file) = ROOT_INODE.lookup(name) { - let data = file.read_as_vec().unwrap(); - let _pid = processor().manager().add(Thread::new_user( - data.as_slice(), + if let Ok(inode) = ROOT_INODE.lookup(name) { + let _tid = processor().manager().add(Thread::new_user( + &inode, &cmd, cmd.split(' ').map(|s| s.into()).collect(), Vec::new(), )); // TODO: wait until process exits, or use user land shell completely - //unsafe { thread::JoinHandle::<()>::_of(pid) }.join().unwrap(); } else { println!("Program not exist"); } diff --git a/kernel/src/syscall/mem.rs b/kernel/src/syscall/mem.rs index dd2b3e2..d108a7c 100644 --- a/kernel/src/syscall/mem.rs +++ b/kernel/src/syscall/mem.rs @@ -1,4 +1,4 @@ -use rcore_memory::memory_set::handler::{ByFrame, Delay}; +use rcore_memory::memory_set::handler::{ByFrame, Delay, File}; use rcore_memory::memory_set::MemoryAttr; use rcore_memory::paging::PageTable; use rcore_memory::Page; @@ -46,35 +46,25 @@ pub fn sys_mmap( addr, addr + len, prot.to_attr(), - // ByFrame::new(GlobalFrameAlloc), //eagle mmap mode Delay::new(GlobalFrameAlloc), "mmap_anon", ); - //init with zero for eagle mmap mode - // let data = unsafe { slice::from_raw_parts_mut(addr as *mut u8, len) }; - // for x in data { - // *x = 0; - // } return Ok(addr); } else { - // only check - let _ = proc.get_file(fd)?; - - // TODO: delay mmap file + let inode = proc.get_file(fd)?.inode(); proc.vm.push( addr, addr + len, prot.to_attr(), - ByFrame::new(GlobalFrameAlloc), + File { + file: INodeForMap(inode), + mem_start: addr, + file_start: offset, + file_end: offset + len, + allocator: GlobalFrameAlloc, + }, "mmap_file", ); - let data = unsafe { slice::from_raw_parts_mut(addr as *mut u8, len) }; - let file = proc.get_file(fd)?; - let read_len = file.read_at(offset, data)?; - if read_len != data.len() { - // use count() to consume the iterator - data[read_len..].iter_mut().map(|x| *x = 0).count(); - } return Ok(addr); } } diff --git a/kernel/src/syscall/mod.rs b/kernel/src/syscall/mod.rs index 7b7483c..c247f15 100644 --- a/kernel/src/syscall/mod.rs +++ b/kernel/src/syscall/mod.rs @@ -329,7 +329,7 @@ fn x86_64_syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> Option sys_dup2(args[0], args[1]), SYS_ALARM => unimplemented("alarm", Ok(0)), SYS_FORK => sys_fork(tf), - SYS_VFORK => sys_fork(tf), // use fork for vfork + SYS_VFORK => sys_vfork(tf), SYS_RENAME => sys_rename(args[0] as *const u8, args[1] as *const u8), SYS_MKDIR => sys_mkdir(args[0] as *const u8, args[1]), SYS_RMDIR => sys_rmdir(args[0] as *const u8), diff --git a/kernel/src/syscall/proc.rs b/kernel/src/syscall/proc.rs index 8114b06..7fc1f47 100644 --- a/kernel/src/syscall/proc.rs +++ b/kernel/src/syscall/proc.rs @@ -13,6 +13,10 @@ pub fn sys_fork(tf: &TrapFrame) -> SysResult { Ok(pid) } +pub fn sys_vfork(tf: &TrapFrame) -> SysResult { + sys_fork(tf) +} + /// Create a new thread in the current process. /// The new thread's stack pointer will be set to `newsp`, /// and thread pointer will be set to `newtls`. @@ -168,16 +172,15 @@ pub fn sys_exec( // Read program file let inode = proc.lookup_inode(&path)?; - let data = inode.read_as_vec()?; // Make new Thread - let (mut vm, entry_addr, ustack_top) = Thread::new_user_vm(data.as_slice(), &path, args, envs); + let (mut vm, entry_addr, ustack_top) = Thread::new_user_vm(&inode, &path, args, envs).unwrap(); // Activate new page table + core::mem::swap(&mut proc.vm, &mut vm); unsafe { - vm.activate(); + proc.vm.activate(); } - core::mem::swap(&mut proc.vm, &mut vm); // Modify the TrapFrame *tf = TrapFrame::new_user_thread(entry_addr, ustack_top);