|
|
<!DOCTYPE HTML>
|
|
|
<html lang="cn" class="sidebar-visible no-js light">
|
|
|
<head>
|
|
|
<!-- Book generated using mdBook -->
|
|
|
<meta charset="UTF-8">
|
|
|
<title>🚧 物理内存:VMO 对象 - 简明 zCore 教程</title>
|
|
|
|
|
|
|
|
|
<!-- Custom HTML head -->
|
|
|
|
|
|
|
|
|
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
|
|
<meta name="description" content="">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
|
<meta name="theme-color" content="#ffffff" />
|
|
|
|
|
|
<link rel="icon" href="favicon.svg">
|
|
|
<link rel="shortcut icon" href="favicon.png">
|
|
|
<link rel="stylesheet" href="css/variables.css">
|
|
|
<link rel="stylesheet" href="css/general.css">
|
|
|
<link rel="stylesheet" href="css/chrome.css">
|
|
|
<link rel="stylesheet" href="css/print.css" media="print">
|
|
|
|
|
|
<!-- Fonts -->
|
|
|
<link rel="stylesheet" href="FontAwesome/css/font-awesome.css">
|
|
|
<link rel="stylesheet" href="fonts/fonts.css">
|
|
|
|
|
|
<!-- Highlight.js Stylesheets -->
|
|
|
<link rel="stylesheet" href="highlight.css">
|
|
|
<link rel="stylesheet" href="tomorrow-night.css">
|
|
|
<link rel="stylesheet" href="ayu-highlight.css">
|
|
|
|
|
|
<!-- Custom theme stylesheets -->
|
|
|
|
|
|
</head>
|
|
|
<body>
|
|
|
<!-- Provide site root to javascript -->
|
|
|
<script type="text/javascript">
|
|
|
var path_to_root = "";
|
|
|
var default_theme = window.matchMedia("(prefers-color-scheme: dark)").matches ? "navy" : "light";
|
|
|
</script>
|
|
|
|
|
|
<!-- Work around some values being stored in localStorage wrapped in quotes -->
|
|
|
<script type="text/javascript">
|
|
|
try {
|
|
|
var theme = localStorage.getItem('mdbook-theme');
|
|
|
var sidebar = localStorage.getItem('mdbook-sidebar');
|
|
|
|
|
|
if (theme.startsWith('"') && theme.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-theme', theme.slice(1, theme.length - 1));
|
|
|
}
|
|
|
|
|
|
if (sidebar.startsWith('"') && sidebar.endsWith('"')) {
|
|
|
localStorage.setItem('mdbook-sidebar', sidebar.slice(1, sidebar.length - 1));
|
|
|
}
|
|
|
} catch (e) { }
|
|
|
</script>
|
|
|
|
|
|
<!-- Set the theme before any content is loaded, prevents flash -->
|
|
|
<script type="text/javascript">
|
|
|
var theme;
|
|
|
try { theme = localStorage.getItem('mdbook-theme'); } catch(e) { }
|
|
|
if (theme === null || theme === undefined) { theme = default_theme; }
|
|
|
var html = document.querySelector('html');
|
|
|
html.classList.remove('no-js')
|
|
|
html.classList.remove('light')
|
|
|
html.classList.add(theme);
|
|
|
html.classList.add('js');
|
|
|
</script>
|
|
|
|
|
|
<!-- Hide / unhide sidebar before it is displayed -->
|
|
|
<script type="text/javascript">
|
|
|
var html = document.querySelector('html');
|
|
|
var sidebar = 'hidden';
|
|
|
if (document.body.clientWidth >= 1080) {
|
|
|
try { sidebar = localStorage.getItem('mdbook-sidebar'); } catch(e) { }
|
|
|
sidebar = sidebar || 'visible';
|
|
|
}
|
|
|
html.classList.remove('sidebar-visible');
|
|
|
html.classList.add("sidebar-" + sidebar);
|
|
|
</script>
|
|
|
|
|
|
<nav id="sidebar" class="sidebar" aria-label="Table of contents">
|
|
|
<div class="sidebar-scrollbox">
|
|
|
<ol class="chapter"><li class="chapter-item expanded affix "><a href="index.html">简明 zCore 教程</a></li><li class="chapter-item expanded affix "><a href="zcore-intro.html">🚧 zCore 整体结构和设计模式</a></li><li class="chapter-item expanded affix "><a href="fuchsia.html">🚧 Fuchsia OS 和 Zircon 微内核</a></li><li class="chapter-item expanded "><a href="ch01-00-object.html"><strong aria-hidden="true">1.</strong> 内核对象</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch01-01-kernel-object.html"><strong aria-hidden="true">1.1.</strong> ✅ 初识内核对象</a></li><li class="chapter-item expanded "><a href="ch01-02-process-object.html"><strong aria-hidden="true">1.2.</strong> 🚧 对象管理器:Process 对象</a></li><li class="chapter-item expanded "><a href="ch01-03-channel-object.html"><strong aria-hidden="true">1.3.</strong> 🚧 对象传送器:Channel 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch02-00-task.html"><strong aria-hidden="true">2.</strong> 任务管理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch02-01-zircon-task.html"><strong aria-hidden="true">2.1.</strong> 🚧 Zircon 任务管理体系</a></li><li class="chapter-item expanded "><a href="ch02-02-process-job-object.html"><strong aria-hidden="true">2.2.</strong> 🚧 进程管理:Process 与 Job 对象</a></li><li class="chapter-item expanded "><a href="ch02-03-thread-object.html"><strong aria-hidden="true">2.3.</strong> 🚧 线程管理:Thread 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch03-00-memory.html"><strong aria-hidden="true">3.</strong> 内存管理</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch03-01-zircon-memory.html"><strong aria-hidden="true">3.1.</strong> 🚧 Zircon 内存管理模型</a></li><li class="chapter-item expanded "><a href="ch03-02-vmo.html" class="active"><strong aria-hidden="true">3.2.</strong> 🚧 物理内存:VMO 对象</a></li><li class="chapter-item expanded "><a href="ch03-03-vmo-paged.html"><strong aria-hidden="true">3.3.</strong> 🚧 物理内存:按页分配的 VMO</a></li><li class="chapter-item expanded "><a href="ch03-04-vmar.html"><strong aria-hidden="true">3.4.</strong> 🚧 虚拟内存:VMAR 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch04-00-userspace.html"><strong aria-hidden="true">4.</strong> 用户程序</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch04-01-user-program.html"><strong aria-hidden="true">4.1.</strong> 🚧 Zircon 用户程序</a></li><li class="chapter-item expanded "><a href="ch04-02-context-switch.html"><strong aria-hidden="true">4.2.</strong> 🚧 上下文切换</a></li><li class="chapter-item expanded "><a href="ch04-03-syscall.html"><strong aria-hidden="true">4.3.</strong> 🚧 系统调用</a></li></ol></li><li class="chapter-item expanded "><a href="ch05-00-signal-and-waiting.html"><strong aria-hidden="true">5.</strong> 信号和等待</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch05-01-wait-signal.html"><strong aria-hidden="true">5.1.</strong> 🚧 等待内核对象的信号</a></li><li class="chapter-item expanded "><a href="ch05-02-port-object.html"><strong aria-hidden="true">5.2.</strong> 🚧 同时等待多个信号:Port 对象</a></li><li class="chapter-item expanded "><a href="ch05-03-more-signal-objects.html"><strong aria-hidden="true">5.3.</strong> 🚧 实现更多:EventPair, Timer 对象</a></li><li class="chapter-item expanded "><a href="ch05-04-futex-object.html"><strong aria-hidden="true">5.4.</strong> 🚧 用户态同步互斥:Futex 对象</a></li></ol></li><li class="chapter-item expanded "><a href="ch06-00-hal.html"><strong aria-hidden="true">6.</strong> 硬件抽象层</a></li><li><ol class="section"><li class="chapter-item expanded "><a href="ch06-01-zcore-hal-unix.html"><strong aria-hidden="true">6.1.</strong> ✅ UNIX硬件抽象层</a></li></ol></li></ol> </div>
|
|
|
<div id="sidebar-resize-handle" class="sidebar-resize-handle"></div>
|
|
|
</nav>
|
|
|
|
|
|
<div id="page-wrapper" class="page-wrapper">
|
|
|
|
|
|
<div class="page">
|
|
|
|
|
|
<div id="menu-bar-hover-placeholder"></div>
|
|
|
<div id="menu-bar" class="menu-bar sticky bordered">
|
|
|
<div class="left-buttons">
|
|
|
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
|
|
|
<i class="fa fa-bars"></i>
|
|
|
</button>
|
|
|
<button id="theme-toggle" class="icon-button" type="button" title="Change theme" aria-label="Change theme" aria-haspopup="true" aria-expanded="false" aria-controls="theme-list">
|
|
|
<i class="fa fa-paint-brush"></i>
|
|
|
</button>
|
|
|
<ul id="theme-list" class="theme-popup" aria-label="Themes" role="menu">
|
|
|
<li role="none"><button role="menuitem" class="theme" id="light">Light (default)</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="rust">Rust</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="coal">Coal</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="navy">Navy</button></li>
|
|
|
<li role="none"><button role="menuitem" class="theme" id="ayu">Ayu</button></li>
|
|
|
</ul>
|
|
|
<button id="search-toggle" class="icon-button" type="button" title="Search. (Shortkey: s)" aria-label="Toggle Searchbar" aria-expanded="false" aria-keyshortcuts="S" aria-controls="searchbar">
|
|
|
<i class="fa fa-search"></i>
|
|
|
</button>
|
|
|
</div>
|
|
|
|
|
|
<h1 class="menu-title">简明 zCore 教程</h1>
|
|
|
|
|
|
<div class="right-buttons">
|
|
|
<a href="print.html" title="Print this book" aria-label="Print this book">
|
|
|
<i id="print-button" class="fa fa-print"></i>
|
|
|
</a>
|
|
|
<a href="https://github.com/rcore-os/zCore-Tutorial" title="Git repository" aria-label="Git repository">
|
|
|
<i id="git-repository-button" class="fa fa-github"></i>
|
|
|
</a>
|
|
|
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<div id="search-wrapper" class="hidden">
|
|
|
<form id="searchbar-outer" class="searchbar-outer">
|
|
|
<input type="search" id="searchbar" name="searchbar" placeholder="Search this book ..." aria-controls="searchresults-outer" aria-describedby="searchresults-header">
|
|
|
</form>
|
|
|
<div id="searchresults-outer" class="searchresults-outer hidden">
|
|
|
<div id="searchresults-header" class="searchresults-header"></div>
|
|
|
<ul id="searchresults">
|
|
|
</ul>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<!-- Apply ARIA attributes after the sidebar and the sidebar toggle button are added to the DOM -->
|
|
|
<script type="text/javascript">
|
|
|
document.getElementById('sidebar-toggle').setAttribute('aria-expanded', sidebar === 'visible');
|
|
|
document.getElementById('sidebar').setAttribute('aria-hidden', sidebar !== 'visible');
|
|
|
Array.from(document.querySelectorAll('#sidebar a')).forEach(function(link) {
|
|
|
link.setAttribute('tabIndex', sidebar === 'visible' ? 0 : -1);
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
<div id="content" class="content">
|
|
|
<main>
|
|
|
<h1 id="物理内存vmo-对象"><a class="header" href="#物理内存vmo-对象">物理内存:VMO 对象</a></h1>
|
|
|
<h2 id="vmo-简介"><a class="header" href="#vmo-简介">VMO 简介</a></h2>
|
|
|
<blockquote>
|
|
|
<p>根据文档梳理 VMO 的主要特性</p>
|
|
|
</blockquote>
|
|
|
<p>虚拟拟内存对象(Virtual Memory Objects, VMO)代表一组物理内存页面,或 潜在的页面(将根据需要延迟创建/填充)。</p>
|
|
|
<p>它们可以通过 <a href="https://fuchsia.dev/docs/reference/syscalls/vmar_map"><code>zx_vmar_map()</code></a>被映射到一个进程(Process)的地址空间,也可通过 <a href="https://fuchsia.dev/docs/reference/syscalls/vmar_unmap"><code>zx_vmar_unmap()</code></a>来解除映射。可以使用<a href="https://fuchsia.dev/docs/reference/syscalls/vmar_protect"><code>zx_vmar_protect()</code></a>来调整映射页面的权限。</p>
|
|
|
<p>也可以直接使用<a href="https://fuchsia.dev/docs/reference/syscalls/vmo_read"><code>zx_vmo_read()</code></a>来读取VMO和通过使用 <a href="https://fuchsia.dev/docs/reference/syscalls/vmo_write"><code>zx_vmo_write()</code></a>来写入 VMO。因此,通过诸如“创建 VMO,将数据集写入其中,然后将其交给另一个进程使用”等一次性(one-shot )操作,可以避免将它们映射到地址空间的开销。</p>
|
|
|
<h2 id="实现-vmo-对象框架"><a class="header" href="#实现-vmo-对象框架">实现 VMO 对象框架</a></h2>
|
|
|
<blockquote>
|
|
|
<p>实现 VmObject 结构,其中定义 VmObjectTrait 接口,并提供三个具体实现 Paged, Physical, Slice</p>
|
|
|
</blockquote>
|
|
|
<p>VmObject 结构体</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>// vm/vmo/mod.rs
|
|
|
pub struct VmObject {
|
|
|
base: KObjectBase,
|
|
|
resizable: bool,
|
|
|
trait_: Arc<dyn VMObjectTrait>,
|
|
|
inner: Mutex<VmObjectInner>,
|
|
|
}
|
|
|
|
|
|
impl_kobject!(VmObject);
|
|
|
|
|
|
#[derive(Default)]
|
|
|
struct VmObjectInner {
|
|
|
parent: Weak<VmObject>,
|
|
|
children: Vec<Weak<VmObject>>,
|
|
|
mapping_count: usize,
|
|
|
content_size: usize,
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p><code>trait_</code> 指向实现了 VMObjectTrait 的对象,它由三个具体实现,分别是 VMObjectPage, VMObjectPhysical, VMObjectSlice。VMObjectPaged 是按页分配内存,VMObjectSlice 主要用于共享内存,VMObjectPhysical 在 zCore-Tutorial 中暂时不会使用到。<br />
|
|
|
<code>mapping_count</code> 表示这个 VmObject 被 map 到几个 VMAR 中。<br />
|
|
|
<code>content_size</code> 是分配的物理内存的大小。<br />
|
|
|
VmObjectTrait 定义了一组 VMObject* 共有的方法</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>pub trait VMObjectTrait: Sync + Send {
|
|
|
/// Read memory to `buf` from VMO at `offset`.
|
|
|
fn read(&self, offset: usize, buf: &mut [u8]) -> ZxResult;
|
|
|
|
|
|
/// Write memory from `buf` to VMO at `offset`.
|
|
|
fn write(&self, offset: usize, buf: &[u8]) -> ZxResult;
|
|
|
|
|
|
/// Resets the range of bytes in the VMO from `offset` to `offset+len` to 0.
|
|
|
fn zero(&self, offset: usize, len: usize) -> ZxResult;
|
|
|
|
|
|
/// Get the length of VMO.
|
|
|
fn len(&self) -> usize;
|
|
|
|
|
|
/// Set the length of VMO.
|
|
|
fn set_len(&self, len: usize) -> ZxResult;
|
|
|
|
|
|
/// Commit a page.
|
|
|
fn commit_page(&self, page_idx: usize, flags: MMUFlags) -> ZxResult<PhysAddr>;
|
|
|
|
|
|
/// Commit pages with an external function f.
|
|
|
/// the vmo is internally locked before it calls f,
|
|
|
/// allowing `VmMapping` to avoid deadlock
|
|
|
fn commit_pages_with(
|
|
|
&self,
|
|
|
f: &mut dyn FnMut(&mut dyn FnMut(usize, MMUFlags) -> ZxResult<PhysAddr>) -> ZxResult,
|
|
|
) -> ZxResult;
|
|
|
|
|
|
/// Commit allocating physical memory.
|
|
|
fn commit(&self, offset: usize, len: usize) -> ZxResult;
|
|
|
|
|
|
/// Decommit allocated physical memory.
|
|
|
fn decommit(&self, offset: usize, len: usize) -> ZxResult;
|
|
|
|
|
|
/// Create a child VMO.
|
|
|
fn create_child(&self, offset: usize, len: usize) -> ZxResult<Arc<dyn VMObjectTrait>>;
|
|
|
|
|
|
/// Append a mapping to the VMO's mapping list.
|
|
|
fn append_mapping(&self, _mapping: Weak<VmMapping>) {}
|
|
|
|
|
|
/// Remove a mapping from the VMO's mapping list.
|
|
|
fn remove_mapping(&self, _mapping: Weak<VmMapping>) {}
|
|
|
|
|
|
/// Complete the VmoInfo.
|
|
|
fn complete_info(&self, info: &mut VmoInfo);
|
|
|
|
|
|
/// Get the cache policy.
|
|
|
fn cache_policy(&self) -> CachePolicy;
|
|
|
|
|
|
/// Set the cache policy.
|
|
|
fn set_cache_policy(&self, policy: CachePolicy) -> ZxResult;
|
|
|
|
|
|
/// Count committed pages of the VMO.
|
|
|
fn committed_pages_in_range(&self, start_idx: usize, end_idx: usize) -> usize;
|
|
|
|
|
|
/// Pin the given range of the VMO.
|
|
|
fn pin(&self, _offset: usize, _len: usize) -> ZxResult {
|
|
|
Err(ZxError::NOT_SUPPORTED)
|
|
|
}
|
|
|
|
|
|
/// Unpin the given range of the VMO.
|
|
|
fn unpin(&self, _offset: usize, _len: usize) -> ZxResult {
|
|
|
Err(ZxError::NOT_SUPPORTED)
|
|
|
}
|
|
|
|
|
|
/// Returns true if the object is backed by a contiguous range of physical memory.
|
|
|
fn is_contiguous(&self) -> bool {
|
|
|
false
|
|
|
}
|
|
|
|
|
|
/// Returns true if the object is backed by RAM.
|
|
|
fn is_paged(&self) -> bool {
|
|
|
false
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p><code>read()</code> 和 <code>write()</code> 用于读和写,<code>zero()</code> 用于清空一段内存。<br />
|
|
|
比较特别的是:<code>fn commit_page(&self, page_idx: usize, flags: MMUFlags) -> ZxResult<PhysAddr>;</code>,<code>fn commit(&self, offset: usize, len: usize) -> ZxResult;</code> 和 <code>fn commit(&self, offset: usize, len: usize) -> ZxResult;</code> 主要用于分配物理内存,因为一些内存分配策略,物理内存并不一定是马上分配的,所以需要 commit 来分配一块内存。<br />
|
|
|
<code>pin</code> 和 <code>unpin</code> 在这里主要用于增加和减少引用计数。<br />
|
|
|
VmObject 实现了不同的 new 方法,它们之间的差别在于实现 trait_ 的对象不同。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>impl VmObject {
|
|
|
/// Create a new VMO backing on physical memory allocated in pages.
|
|
|
pub fn new_paged(pages: usize) -> Arc<Self> {
|
|
|
Self::new_paged_with_resizable(false, pages)
|
|
|
}
|
|
|
|
|
|
/// Create a new VMO, which can be resizable, backing on physical memory allocated in pages.
|
|
|
pub fn new_paged_with_resizable(resizable: bool, pages: usize) -> Arc<Self> {
|
|
|
let base = KObjectBase::new();
|
|
|
Arc::new(VmObject {
|
|
|
resizable,
|
|
|
trait_: VMObjectPaged::new(pages),
|
|
|
inner: Mutex::new(VmObjectInner::default()),
|
|
|
base,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
/// Create a new VMO representing a piece of contiguous physical memory.
|
|
|
pub fn new_physical(paddr: PhysAddr, pages: usize) -> Arc<Self> {
|
|
|
Arc::new(VmObject {
|
|
|
base: KObjectBase::new(),
|
|
|
resizable: false,
|
|
|
trait_: VMObjectPhysical::new(paddr, pages),
|
|
|
inner: Mutex::new(VmObjectInner::default()),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
/// Create a VM object referring to a specific contiguous range of physical frame.
|
|
|
pub fn new_contiguous(pages: usize, align_log2: usize) -> ZxResult<Arc<Self>> {
|
|
|
let vmo = Arc::new(VmObject {
|
|
|
base: KObjectBase::new(),
|
|
|
resizable: false,
|
|
|
trait_: VMObjectPaged::new_contiguous(pages, align_log2)?,
|
|
|
inner: Mutex::new(VmObjectInner::default()),
|
|
|
});
|
|
|
Ok(vmo)
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p>通过 <code>pub fn create_child(self: &Arc<Self>, resizable: bool, offset: usize, len: usize)</code> 可以创建一个 VMObject 的快照副本。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>impl VmObject {
|
|
|
/// Create a child VMO.
|
|
|
pub fn create_child(
|
|
|
self: &Arc<Self>,
|
|
|
resizable: bool,
|
|
|
offset: usize,
|
|
|
len: usize,
|
|
|
) -> ZxResult<Arc<Self>> {
|
|
|
// Create child VmObject
|
|
|
let base = KObjectBase::with_name(&self.base.name());
|
|
|
let trait_ = self.trait_.create_child(offset, len)?;
|
|
|
let child = Arc::new(VmObject {
|
|
|
base,
|
|
|
resizable,
|
|
|
trait_,
|
|
|
inner: Mutex::new(VmObjectInner {
|
|
|
parent: Arc::downgrade(self),
|
|
|
..VmObjectInner::default()
|
|
|
}),
|
|
|
});
|
|
|
// Add child VmObject to this VmObject
|
|
|
self.add_child(&child);
|
|
|
Ok(child)
|
|
|
}
|
|
|
|
|
|
/// Add child to the list
|
|
|
fn add_child(&self, child: &Arc<VmObject>) {
|
|
|
let mut inner = self.inner.lock();
|
|
|
// 判断这个 child VmObject 是否还是存在,通过获取子对象的强引用数来判断
|
|
|
inner.children.retain(|x| x.strong_count() != 0);
|
|
|
// downgrade 将 Arc 转为 Weak
|
|
|
inner.children.push(Arc::downgrade(child));
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<h2 id="hal用文件模拟物理内存"><a class="header" href="#hal用文件模拟物理内存">HAL:用文件模拟物理内存</a></h2>
|
|
|
<blockquote>
|
|
|
<p>初步介绍 mmap,引出用文件模拟物理内存的思想</p>
|
|
|
<p>创建文件并用 mmap 线性映射到进程地址空间</p>
|
|
|
<p>实现 pmem_read, pmem_write</p>
|
|
|
</blockquote>
|
|
|
<h3 id="mmap"><a class="header" href="#mmap">mmap</a></h3>
|
|
|
<p>mmap是一种内存映射文件的方法,将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘地址和进程虚拟地址空间中一段虚拟地址一一对应的关系。实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。因此,新建一个文件,然后调用 mmap,其实就相当于分配了一块物理内存,因此我们可以用文件来模拟物理内存。
|
|
|
<img src="img/mmap.png" alt="mmap.png" /></p>
|
|
|
<h3 id="分配地址空间"><a class="header" href="#分配地址空间">分配地址空间</a></h3>
|
|
|
<p>创建一个文件用于 mmap 系统调用。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>fn create_pmem_file() -> File {
|
|
|
let dir = tempdir().expect("failed to create pmem dir");
|
|
|
let path = dir.path().join("pmem");
|
|
|
|
|
|
// workaround on macOS to avoid permission denied.
|
|
|
// see https://jiege.ch/software/2020/02/07/macos-mmap-exec/ for analysis on this problem.
|
|
|
#[cfg(target_os = "macos")]
|
|
|
std::mem::forget(dir);
|
|
|
|
|
|
let file = OpenOptions::new()
|
|
|
.read(true)
|
|
|
.write(true)
|
|
|
.create(true)
|
|
|
.open(&path)
|
|
|
.expect("failed to create pmem file");
|
|
|
file.set_len(PMEM_SIZE as u64)
|
|
|
.expect("failed to resize file");
|
|
|
trace!("create pmem file: path={:?}, size={:#x}", path, PMEM_SIZE);
|
|
|
let prot = libc::PROT_READ | libc::PROT_WRITE;
|
|
|
// 调用 mmap (这个不是系统调用)进行文件和内存之间的双向映射
|
|
|
mmap(file.as_raw_fd(), 0, PMEM_SIZE, phys_to_virt(0), prot);
|
|
|
file
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p>mmap:</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>/// Mmap frame file `fd` to `vaddr`.
|
|
|
fn mmap(fd: libc::c_int, offset: usize, len: usize, vaddr: VirtAddr, prot: libc::c_int) {
|
|
|
// 根据不同的操作系统去修改权限
|
|
|
// workaround on macOS to write text section.
|
|
|
#[cfg(target_os = "macos")]
|
|
|
let prot = if prot & libc::PROT_EXEC != 0 {
|
|
|
prot | libc::PROT_WRITE
|
|
|
} else {
|
|
|
prot
|
|
|
};
|
|
|
|
|
|
// 调用 mmap 系统调用,ret 为返回值
|
|
|
let ret = unsafe {
|
|
|
let flags = libc::MAP_SHARED | libc::MAP_FIXED;
|
|
|
libc::mmap(vaddr as _, len, prot, flags, fd, offset as _)
|
|
|
} as usize;
|
|
|
trace!(
|
|
|
"mmap file: fd={}, offset={:#x}, len={:#x}, vaddr={:#x}, prot={:#b}",
|
|
|
fd,
|
|
|
offset,
|
|
|
len,
|
|
|
vaddr,
|
|
|
prot,
|
|
|
);
|
|
|
assert_eq!(ret, vaddr, "failed to mmap: {:?}", Error::last_os_error());
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p>最后创建一个全局变量保存这个分配的内存</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>lazy_static! {
|
|
|
static ref FRAME_FILE: File = create_pmem_file();
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<h3 id="pmem_read-和-pmem_write"><a class="header" href="#pmem_read-和-pmem_write">pmem_read 和 pmem_write</a></h3>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>/// Read physical memory from `paddr` to `buf`.
|
|
|
#[export_name = "hal_pmem_read"]
|
|
|
pub fn pmem_read(paddr: PhysAddr, buf: &mut [u8]) {
|
|
|
trace!("pmem read: paddr={:#x}, len={:#x}", paddr, buf.len());
|
|
|
assert!(paddr + buf.len() <= PMEM_SIZE);
|
|
|
ensure_mmap_pmem();
|
|
|
unsafe {
|
|
|
(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Write physical memory to `paddr` from `buf`.
|
|
|
#[export_name = "hal_pmem_write"]
|
|
|
pub fn pmem_write(paddr: PhysAddr, buf: &[u8]) {
|
|
|
trace!("pmem write: paddr={:#x}, len={:#x}", paddr, buf.len());
|
|
|
assert!(paddr + buf.len() <= PMEM_SIZE);
|
|
|
ensure_mmap_pmem();
|
|
|
unsafe {
|
|
|
buf.as_ptr()
|
|
|
.copy_to_nonoverlapping(phys_to_virt(paddr) as _, buf.len());
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/// Ensure physical memory are mmapped and accessible.
|
|
|
fn ensure_mmap_pmem() {
|
|
|
FRAME_FILE.as_raw_fd();
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p><code>ensure_mmap_pmem()</code> 确保物理内存已经映射<br />
|
|
|
<code>copy_to_nonoverlapping(self, dst *mut T, count: usize)</code> 将 self 的字节序列拷贝到 dst 中,source 和 destination 是不互相重叠的。<code>(phys_to_virt(paddr) as *const u8).copy_to_nonoverlapping(buf.as_mut_ptr(), buf.len());</code> 通过 <code>phys_to_virt(paddr)</code> 将 paddr 加上 PMEM_BASE 转为虚拟地址,然后将里面的字节拷贝到 buf 里面。</p>
|
|
|
<h2 id="实现物理内存-vmo"><a class="header" href="#实现物理内存-vmo">实现物理内存 VMO</a></h2>
|
|
|
<blockquote>
|
|
|
<p>用 HAL 实现 VmObjectPhysical 的方法,并做单元测试
|
|
|
物理内存 VMO 结构体:</p>
|
|
|
</blockquote>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>pub struct VMObjectPhysical {
|
|
|
paddr: PhysAddr,
|
|
|
pages: usize,
|
|
|
/// Lock this when access physical memory.
|
|
|
data_lock: Mutex<()>,
|
|
|
inner: Mutex<VMObjectPhysicalInner>,
|
|
|
}
|
|
|
|
|
|
struct VMObjectPhysicalInner {
|
|
|
cache_policy: CachePolicy,
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p>这里比较奇怪的是 data_lock 这个字段,这个字段里 Mutex 的泛型类型是一个 unit type,其实相当于它是没有“值”的,它只是起到一个锁的作用。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>impl VMObjectTrait for VMObjectPhysical {
|
|
|
fn read(&self, offset: usize, buf: &mut [u8]) -> ZxResult {
|
|
|
let _ = self.data_lock.lock(); // 先获取锁
|
|
|
assert!(offset + buf.len() <= self.len());
|
|
|
kernel_hal::pmem_read(self.paddr + offset, buf); // 对一块物理内存进行读
|
|
|
Ok(())
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<h2 id="实现切片-vmo"><a class="header" href="#实现切片-vmo">实现切片 VMO</a></h2>
|
|
|
<blockquote>
|
|
|
<p>实现 VmObjectSlice,并做单元测试
|
|
|
VMObjectSlice 中的 parent 用于指向一个实际的 VMO 对象,比如:VMObjectPaged,这样通过 VMObjectSlice 就可以实现对 VMObjectPaged 的共享。</p>
|
|
|
</blockquote>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>pub struct VMObjectSlice {
|
|
|
/// Parent node.
|
|
|
parent: Arc<dyn VMObjectTrait>,
|
|
|
/// The offset from parent.
|
|
|
offset: usize,
|
|
|
/// The size in bytes.
|
|
|
size: usize,
|
|
|
}
|
|
|
|
|
|
impl VMObjectSlice {
|
|
|
pub fn new(parent: Arc<dyn VMObjectTrait>, offset: usize, size: usize) -> Arc<Self> {
|
|
|
Arc::new(VMObjectSlice {
|
|
|
parent,
|
|
|
offset,
|
|
|
size,
|
|
|
})
|
|
|
}
|
|
|
|
|
|
fn check_range(&self, offset: usize, len: usize) -> ZxResult {
|
|
|
if offset + len >= self.size {
|
|
|
return Err(ZxError::OUT_OF_RANGE);
|
|
|
}
|
|
|
Ok(())
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
<p>VMObjectSlice 实现的读写,第一步是 <code>check_range</code> ,第二步是调用 parent 中的读写方法。</p>
|
|
|
<pre><pre class="playground"><code class="language-rust edition2018">
|
|
|
<span class="boring">#![allow(unused)]
|
|
|
</span><span class="boring">fn main() {
|
|
|
</span>impl VMObjectTrait for VMObjectSlice {
|
|
|
fn read(&self, offset: usize, buf: &mut [u8]) -> ZxResult {
|
|
|
self.check_range(offset, buf.len())?;
|
|
|
self.parent.read(offset + self.offset, buf)
|
|
|
}
|
|
|
}
|
|
|
<span class="boring">}
|
|
|
</span></code></pre></pre>
|
|
|
|
|
|
</main>
|
|
|
|
|
|
<nav class="nav-wrapper" aria-label="Page navigation">
|
|
|
<!-- Mobile navigation buttons -->
|
|
|
<a rel="prev" href="ch03-01-zircon-memory.html" class="mobile-nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next" href="ch03-03-vmo-paged.html" class="mobile-nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
|
|
|
<div style="clear: both"></div>
|
|
|
</nav>
|
|
|
</div>
|
|
|
</div>
|
|
|
|
|
|
<nav class="nav-wide-wrapper" aria-label="Page navigation">
|
|
|
<a rel="prev" href="ch03-01-zircon-memory.html" class="nav-chapters previous" title="Previous chapter" aria-label="Previous chapter" aria-keyshortcuts="Left">
|
|
|
<i class="fa fa-angle-left"></i>
|
|
|
</a>
|
|
|
|
|
|
<a rel="next" href="ch03-03-vmo-paged.html" class="nav-chapters next" title="Next chapter" aria-label="Next chapter" aria-keyshortcuts="Right">
|
|
|
<i class="fa fa-angle-right"></i>
|
|
|
</a>
|
|
|
</nav>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<script type="text/javascript">
|
|
|
window.playground_copyable = true;
|
|
|
</script>
|
|
|
|
|
|
<script src="ace.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="editor.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="mode-rust.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="theme-dawn.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="theme-tomorrow_night.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script src="elasticlunr.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="mark.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="searcher.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<script src="clipboard.min.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="highlight.js" type="text/javascript" charset="utf-8"></script>
|
|
|
<script src="book.js" type="text/javascript" charset="utf-8"></script>
|
|
|
|
|
|
<!-- Custom JS scripts -->
|
|
|
|
|
|
|
|
|
</body>
|
|
|
</html>
|