1
0
mirror of https://github.com/rcore-os/rCore.git synced 2024-11-22 16:16:16 +04:00

delay mapping file

This commit is contained in:
WangRunji 2019-04-30 13:37:31 +08:00
parent 88e1055eed
commit a25c8132fe
9 changed files with 189 additions and 79 deletions

View File

@ -40,9 +40,10 @@ impl<T: FrameAllocator> MemoryHandler for Delay<T> {
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

View File

@ -0,0 +1,108 @@
use super::*;
/// Delay mapping a page to an area of a file.
#[derive(Clone)]
pub struct File<F, T> {
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<F: Read, T: FrameAllocator> MemoryHandler for File<F, T> {
fn box_clone(&self) -> Box<MemoryHandler> {
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<F: Read, T: FrameAllocator> File<F, T> {
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<F, T> Debug for File<F, T> {
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()
}
}

View File

@ -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};

View File

@ -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<INode> {
self.inode.clone()
}
}

View File

@ -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<INode>,
exec_path: &str,
mut args: Vec<String>,
envs: Vec<String>,
) -> (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<INode>,
exec_path: &str,
mut args: Vec<String>,
envs: Vec<String>,
) -> Box<Thread> {
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<INode>) -> 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<INode>) -> 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<INode>);
impl Read for INodeForMap {
fn read_at(&self, offset: usize, buf: &mut [u8]) -> usize {
self.0.read_at(offset, buf).unwrap()
}
}

View File

@ -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");
}

View File

@ -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);
}
}

View File

@ -329,7 +329,7 @@ fn x86_64_syscall(id: usize, args: [usize; 6], tf: &mut TrapFrame) -> Option<Sys
SYS_DUP2 => 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),

View File

@ -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
unsafe {
vm.activate();
}
core::mem::swap(&mut proc.vm, &mut vm);
unsafe {
proc.vm.activate();
}
// Modify the TrapFrame
*tf = TrapFrame::new_user_thread(entry_addr, ustack_top);