Clear memory crate, redefine PageTable interface, reimplement MockPageTable.

master
WangRunji 7 years ago
parent d2d9affddf
commit aa109498f1

@ -3,10 +3,9 @@
extern crate alloc; extern crate alloc;
pub mod physical;
pub mod paging; pub mod paging;
pub mod memory_set; //pub mod swap;
pub mod swap;
type VirtAddr = usize; type VirtAddr = usize;
type PhysAddr = usize;
const PAGE_SIZE: usize = 4096; const PAGE_SIZE: usize = 4096;

@ -1,89 +0,0 @@
use alloc::vec::Vec;
type Addr = usize;
/// 一片连续内存空间,有相同的访问权限
/// 对应ucore中 `vma_struct`
#[derive(Debug, Eq, PartialEq)]
pub struct MemoryArea {
pub start_addr: Addr,
pub end_addr: Addr,
pub flags: u32,
pub name: &'static str,
}
impl MemoryArea {
pub fn contains(&self, addr: Addr) -> bool {
addr >= self.start_addr && addr < self.end_addr
}
fn is_overlap_with(&self, other: &MemoryArea) -> bool {
!(self.end_addr <= other.start_addr || self.start_addr >= other.end_addr)
}
}
/// 内存空间集合,包含若干段连续空间
/// 对应ucore中 `mm_struct`
#[derive(Debug)]
pub struct MemorySet {
areas: Vec<MemoryArea>,
}
impl MemorySet {
pub fn new() -> Self {
MemorySet { areas: Vec::<MemoryArea>::new() }
}
pub fn find_area(&self, addr: Addr) -> Option<&MemoryArea> {
self.areas.iter().find(|area| area.contains(addr))
}
pub fn push(&mut self, area: MemoryArea) {
assert!(area.start_addr <= area.end_addr, "invalid memory area");
if self.areas.iter()
.find(|other| area.is_overlap_with(other))
.is_some() {
panic!("memory area overlap");
}
self.areas.push(area);
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn push_and_find() {
let mut ms = MemorySet::new();
ms.push(MemoryArea {
start_addr: 0x0,
end_addr: 0x8,
flags: 0x0,
name: "code",
});
ms.push(MemoryArea {
start_addr: 0x8,
end_addr: 0x10,
flags: 0x1,
name: "data",
});
assert_eq!(ms.find_area(0x6).unwrap().name, "code");
assert_eq!(ms.find_area(0x11), None);
}
#[test]
#[should_panic]
fn push_overlap() {
let mut ms = MemorySet::new();
ms.push(MemoryArea {
start_addr: 0x0,
end_addr: 0x8,
flags: 0x0,
name: "code",
});
ms.push(MemoryArea {
start_addr: 0x4,
end_addr: 0x10,
flags: 0x1,
name: "data",
});
}
}

@ -0,0 +1,146 @@
use alloc::boxed::Box;
use super::*;
const PAGE_COUNT: usize = 16;
const PAGE_SIZE: usize = 4096;
pub struct MockPageTable {
entries: [MockEntry; PAGE_COUNT],
data: [u8; PAGE_SIZE * PAGE_COUNT],
page_fault_handler: Option<PageFaultHandler>,
}
#[derive(Default, Copy, Clone)]
pub struct MockEntry {
target: PhysAddr,
present: bool,
writable: bool,
accessed: bool,
dirty: bool,
}
impl Entry for MockEntry {
fn accessed(&self) -> bool { self.accessed }
fn dirty(&self) -> bool { self.dirty }
fn writable(&self) -> bool { self.writable }
fn present(&self) -> bool { self.present }
fn clear_accessed(&mut self) { self.accessed = false; }
fn clear_dirty(&mut self) { self.dirty = false; }
fn set_writable(&mut self, value: bool) { self.writable = value; }
fn set_present(&mut self, value: bool) { self.present = value; }
fn target(&self) -> usize { self.target }
}
type PageFaultHandler = Box<FnMut(&mut MockPageTable, VirtAddr)>;
impl PageTable for MockPageTable {
type Entry = MockEntry;
/// Map a page, return false if no more space
fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry {
let entry = &mut self.entries[addr / PAGE_SIZE];
assert!(!entry.present);
entry.present = true;
entry.target = target & !(PAGE_SIZE - 1);
entry
}
fn unmap(&mut self, addr: VirtAddr) {
let entry = &mut self.entries[addr / PAGE_SIZE];
assert!(entry.present);
entry.present = false;
}
fn get_entry(&mut self, addr: VirtAddr) -> &mut <Self as PageTable>::Entry {
&mut self.entries[addr / PAGE_SIZE]
}
}
impl MockPageTable {
pub fn new() -> Self {
use core::mem::uninitialized;
MockPageTable {
entries: [MockEntry::default(); PAGE_COUNT],
data: unsafe { uninitialized() },
page_fault_handler: None,
}
}
pub fn set_handler(&mut self, page_fault_handler: PageFaultHandler) {
self.page_fault_handler = Some(page_fault_handler);
}
fn trigger_page_fault(&mut self, addr: VirtAddr) {
// In order to call the handler with &mut self as an argument
// We have to first take the handler out of self, finally put it back
let mut handler = self.page_fault_handler.take().unwrap();
handler(self, addr);
self.page_fault_handler = Some(handler);
}
fn translate(&self, addr: VirtAddr) -> PhysAddr {
let entry = &self.entries[addr / PAGE_SIZE];
assert!(entry.present);
(entry.target & !(PAGE_SIZE - 1)) | (addr & (PAGE_SIZE - 1))
}
/// Read memory, mark accessed, trigger page fault if not present
pub fn read(&mut self, addr: VirtAddr) -> u8 {
while !self.entries[addr / PAGE_SIZE].present {
self.trigger_page_fault(addr);
}
self.entries[addr / PAGE_SIZE].accessed = true;
self.data[self.translate(addr)]
}
/// Write memory, mark accessed and dirty, trigger page fault if not present
pub fn write(&mut self, addr: VirtAddr, data: u8) {
while !(self.entries[addr / PAGE_SIZE].present && self.entries[addr / PAGE_SIZE].writable) {
self.trigger_page_fault(addr);
}
self.entries[addr / PAGE_SIZE].accessed = true;
self.entries[addr / PAGE_SIZE].dirty = true;
self.data[self.translate(addr)] = data;
}
}
#[cfg(test)]
mod test {
use super::*;
use alloc::arc::Arc;
use core::cell::RefCell;
#[test]
fn test() {
let page_fault_count = Arc::new(RefCell::new(0usize));
let mut pt = MockPageTable::new();
pt.set_handler(Box::new({
let page_fault_count1 = page_fault_count.clone();
move |pt: &mut MockPageTable, addr: VirtAddr| {
*page_fault_count1.borrow_mut() += 1;
pt.map(addr, addr).set_writable(true);
}
}));
pt.map(0, 0);
pt.read(0);
assert_eq!(*page_fault_count.borrow(), 0);
assert!(pt.get_entry(0).accessed());
assert!(!pt.get_entry(0).dirty());
pt.get_entry(0).clear_accessed();
assert!(!pt.get_entry(0).accessed());
pt.read(1);
assert_eq!(*page_fault_count.borrow(), 0);
assert!(pt.get_entry(0).accessed());
pt.write(0x1000, 0xff);
assert_eq!(*page_fault_count.borrow(), 1);
assert!(pt.get_entry(0x1000).accessed());
assert!(pt.get_entry(0x1000).dirty());
assert_eq!(pt.read(0x1000), 0xff);
pt.get_entry(0x1000).clear_dirty();
assert!(!pt.get_entry(0x1000).dirty());
pt.unmap(0);
pt.read(0);
assert_eq!(*page_fault_count.borrow(), 2);
}
}

@ -1,4 +1,28 @@
pub use self::page_table::*;
use super::*; use super::*;
pub use self::mock_page_table::MockPageTable;
mod page_table; mod mock_page_table;
pub trait PageTable {
type Entry: Entry;
fn map(&mut self, addr: VirtAddr, target: PhysAddr) -> &mut Self::Entry;
fn unmap(&mut self, addr: VirtAddr);
fn get_entry(&mut self, addr: VirtAddr) -> &mut Self::Entry;
}
pub trait Entry {
fn accessed(&self) -> bool;
// Will be set when accessed
fn dirty(&self) -> bool;
// Will be set when written
fn writable(&self) -> bool;
// Will PageFault when try to write page where writable=0
fn present(&self) -> bool; // Will PageFault when try to access page where present=0
fn clear_accessed(&mut self);
fn clear_dirty(&mut self);
fn set_writable(&mut self, value: bool);
fn set_present(&mut self, value: bool);
fn target(&self) -> PhysAddr;
}

@ -1,130 +0,0 @@
use alloc::boxed::Box;
use super::*;
const PAGE_COUNT: usize = 16;
const PAGE_SIZE: usize = 4096;
pub struct MockPageTable {
mapped: [bool; PAGE_COUNT],
accessed: [bool; PAGE_COUNT],
dirty: [bool; PAGE_COUNT],
data: [u8; PAGE_SIZE * PAGE_COUNT],
page_fault_handler: Option<PageFaultHandler>,
capacity: usize,
}
type PageFaultHandler = Box<FnMut(&mut MockPageTable, VirtAddr)>;
impl PageTable for MockPageTable {
fn accessed(&self, addr: VirtAddr) -> bool {
self.accessed[addr / PAGE_SIZE]
}
fn dirty(&self, addr: VirtAddr) -> bool {
self.dirty[addr / PAGE_SIZE]
}
fn clear_accessed(&mut self, addr: usize) {
self.accessed[addr / PAGE_SIZE] = false;
}
fn clear_dirty(&mut self, addr: usize) {
self.dirty[addr / PAGE_SIZE] = false;
}
/// Map a page, return false if no more space
fn map(&mut self, addr: VirtAddr) -> bool {
if self.mapped.iter().filter(|&&b| b).count() == self.capacity {
return false;
}
self.mapped[addr / PAGE_SIZE] = true;
true
}
fn unmap(&mut self, addr: VirtAddr) {
self.mapped[addr / PAGE_SIZE] = false;
}
}
impl MockPageTable {
pub fn new(capacity: usize) -> Self {
use core::mem::uninitialized;
MockPageTable {
mapped: [false; PAGE_COUNT],
accessed: [false; PAGE_COUNT],
dirty: [false; PAGE_COUNT],
data: unsafe{ uninitialized() },
page_fault_handler: None,
capacity,
}
}
pub fn set_handler(&mut self, page_fault_handler: PageFaultHandler) {
self.page_fault_handler = Some(page_fault_handler);
}
fn trigger_page_fault_if_not_present(&mut self, addr: VirtAddr) {
let page_id = addr / PAGE_SIZE;
while !self.mapped[page_id] {
let self_mut = unsafe{ &mut *(self as *mut Self) };
(self.page_fault_handler.as_mut().unwrap())(self_mut, addr);
}
}
/// Read memory, mark accessed, trigger page fault if not present
pub fn read(&mut self, addr: VirtAddr) -> u8 {
let page_id = addr / PAGE_SIZE;
self.trigger_page_fault_if_not_present(addr);
self.accessed[page_id] = true;
self.data[addr]
}
/// Write memory, mark accessed and dirty, trigger page fault if not present
pub fn write(&mut self, addr: VirtAddr, data: u8) {
let page_id = addr / PAGE_SIZE;
self.trigger_page_fault_if_not_present(addr);
self.accessed[page_id] = true;
self.dirty[page_id] = true;
self.data[addr] = data;
}
}
#[cfg(test)]
mod test {
use super::*;
use alloc::arc::Arc;
use core::cell::RefCell;
#[test]
fn test() {
let page_fault_count = Arc::new(RefCell::new(0usize));
let mut pt = MockPageTable::new(2);
pt.set_handler(Box::new({
let page_fault_count1 = page_fault_count.clone();
move |pt: &mut MockPageTable, addr: VirtAddr| {
*page_fault_count1.borrow_mut() += 1;
pt.map(addr);
}
}));
pt.map(0);
pt.read(0);
assert_eq!(*page_fault_count.borrow(), 0);
assert!(pt.accessed(0));
assert!(!pt.dirty(0));
pt.clear_accessed(0);
assert!(!pt.accessed(0));
pt.read(1);
assert_eq!(*page_fault_count.borrow(), 0);
assert!(pt.accessed(0));
pt.write(0x1000, 0xff);
assert_eq!(*page_fault_count.borrow(), 1);
assert!(pt.accessed(0x1000));
assert!(pt.dirty(0x1000));
assert_eq!(pt.read(0x1000), 0xff);
pt.clear_dirty(0x1000);
assert!(!pt.dirty(0x1000));
assert_eq!(pt.map(0x2000), false);
pt.unmap(0);
pt.read(0);
assert_eq!(*page_fault_count.borrow(), 2);
}
}

@ -1,13 +0,0 @@
use super::*;
pub use self::mock_page_table::MockPageTable;
mod mock_page_table;
pub trait PageTable {
fn accessed(&self, addr: VirtAddr) -> bool;
fn dirty(&self, addr: VirtAddr) -> bool;
fn clear_accessed(&mut self, addr: VirtAddr);
fn clear_dirty(&mut self, addr: VirtAddr);
fn map(&mut self, addr: VirtAddr) -> bool;
fn unmap(&mut self, addr: VirtAddr);
}

@ -1,33 +0,0 @@
use super::*;
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
pub struct Frame {
number: usize,
}
impl Frame {
pub fn containing_address(address: PhysAddr) -> Frame {
Frame{ number: address.get() as usize / PAGE_SIZE }
}
//TODO: Set private
pub fn start_address(&self) -> PhysAddr {
PhysAddr::new((self.number * PAGE_SIZE) as u64)
}
pub fn clone(&self) -> Frame {
Frame { number: self.number }
}
//TODO: Set private
// pub fn range_inclusive(start: Frame, end: Frame) -> FrameIter {
// FrameIter {
// start: start,
// end: end,
// }
// }
}
impl Drop for Frame {
fn drop(&mut self) {
panic!("frame must be deallocate");
}
}

@ -1,11 +0,0 @@
use super::*;
pub trait FrameAllocator {
fn allocate_frame(&mut self) -> Option<Frame>;
fn deallocate_frame(&mut self, frame: Frame);
}
pub trait MemoryArea {
fn begin(&self) -> PhysAddr;
fn end(&self) -> PhysAddr;
}

@ -1,9 +0,0 @@
pub use self::physaddr::PhysAddr;
pub use self::frame::Frame;
pub use self::frame_allocator::FrameAllocator;
use super::*;
mod frame;
mod physaddr;
mod frame_allocator;

@ -1,50 +0,0 @@
use core::fmt;
/// Represents a physical memory address
#[derive(Copy, Clone, Eq, Ord, PartialEq, PartialOrd)]
pub struct PhysAddr(u64);
impl PhysAddr {
pub fn new(addr: u64) -> PhysAddr {
PhysAddr(addr)
}
pub fn get(&self) -> u64 {
self.0
}
}
impl fmt::Debug for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:#x}", self.0)
}
}
impl fmt::Binary for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Display for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::LowerHex for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::Octal for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
impl fmt::UpperHex for PhysAddr {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}

@ -29,17 +29,15 @@ impl<T: 'static + SwappablePageTable> SwapManager for EnhancedClockSwapManager<T
fn pop(&mut self) -> Option<usize> { fn pop(&mut self) -> Option<usize> {
loop { loop {
let addr = self.deque[self.clock_ptr]; let addr = self.deque[self.clock_ptr];
let accessed = self.page_table.accessed(addr); let entry = self.page_table.get_entry(addr);
let dirty = self.page_table.dirty(addr);
match (accessed, dirty) { match (entry.accessed(), entry.dirty()) {
(true, _) => { (true, _) => {
self.page_table.clear_accessed(addr); entry.clear_accessed();
}, },
(false, true) => { (false, true) => {
if self.page_table.swap_out(addr).is_ok() { if self.page_table.swap_out(addr).is_ok() {
self.page_table.clear_dirty(addr); entry.clear_dirty();
} }
}, },
_ => { _ => {

Loading…
Cancel
Save