Files
rust-based-os-comp2022/chapter4/5kernel-app-spaces.html
2022-06-30 04:46:48 +00:00

931 lines
128 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html class="no-js" lang="zh_CN">
<head><meta charset="utf-8"/>
<meta name="viewport" content="width=device-width,initial-scale=1"/>
<meta name="color-scheme" content="light dark"><link rel="index" title="索引" href="../genindex.html" /><link rel="search" title="搜索" href="../search.html" /><link rel="next" title="基于地址空间的分时多任务" href="6multitasking-based-on-as.html" /><link rel="prev" title="实现 SV39 多级页表机制(下)" href="4sv39-implementation-2.html" />
<meta name="generator" content="sphinx-4.1.2, furo 2021.08.31"/>
<title>内核与应用的地址空间 - Open-Source-OS-Training-Camp-2022 文档</title>
<link rel="stylesheet" type="text/css" href="../_static/pygments.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo.css?digest=c7c65a82b42f6b978e58466c1e9ef2509836d916" />
<link rel="stylesheet" type="text/css" href="../_static/tabs.css" />
<link rel="stylesheet" type="text/css" href="../_static/styles/furo-extensions.css?digest=16fb25fabf47304eee183a5e9af80b1ba98259b1" />
<link rel="stylesheet" type="text/css" href="../_static/my_style.css" />
<style>
body {
--color-code-background: #f8f8f8;
--color-code-foreground: black;
}
body[data-theme="dark"] {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
@media (prefers-color-scheme: dark) {
body:not([data-theme="light"]) {
--color-code-background: #202020;
--color-code-foreground: #d0d0d0;
}
}
</style></head>
<body>
<script>
document.body.dataset.theme = localStorage.getItem("theme") || "auto";
</script>
<svg xmlns="http://www.w3.org/2000/svg" style="display: none;">
<symbol id="svg-toc" viewBox="0 0 24 24">
<title>Contents</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round">
<path stroke="none" d="M0 0h24v24H0z" />
<line x1="4" y1="6" x2="20" y2="6" />
<line x1="10" y1="12" x2="20" y2="12" />
<line x1="6" y1="18" x2="20" y2="18" />
</svg>
</symbol>
<symbol id="svg-menu" viewBox="0 0 24 24">
<title>Menu</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-menu">
<line x1="3" y1="12" x2="21" y2="12"></line>
<line x1="3" y1="6" x2="21" y2="6"></line>
<line x1="3" y1="18" x2="21" y2="18"></line>
</svg>
</symbol>
<symbol id="svg-arrow-right" viewBox="0 0 24 24">
<title>Expand</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather-chevron-right">
<polyline points="9 18 15 12 9 6"></polyline>
</svg>
</symbol>
<symbol id="svg-sun" viewBox="0 0 24 24">
<title>Light mode</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="feather-sun">
<circle cx="12" cy="12" r="5"></circle>
<line x1="12" y1="1" x2="12" y2="3"></line>
<line x1="12" y1="21" x2="12" y2="23"></line>
<line x1="4.22" y1="4.22" x2="5.64" y2="5.64"></line>
<line x1="18.36" y1="18.36" x2="19.78" y2="19.78"></line>
<line x1="1" y1="12" x2="3" y2="12"></line>
<line x1="21" y1="12" x2="23" y2="12"></line>
<line x1="4.22" y1="19.78" x2="5.64" y2="18.36"></line>
<line x1="18.36" y1="5.64" x2="19.78" y2="4.22"></line>
</svg>
</symbol>
<symbol id="svg-moon" viewBox="0 0 24 24">
<title>Dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-moon">
<path stroke="none" d="M0 0h24v24H0z" fill="none" />
<path d="M12 3c.132 0 .263 0 .393 0a7.5 7.5 0 0 0 7.92 12.446a9 9 0 1 1 -8.313 -12.454z" />
</svg>
</symbol>
<symbol id="svg-sun-half" viewBox="0 0 24 24">
<title>Auto light/dark mode</title>
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor"
stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="icon-tabler-shadow">
<path stroke="none" d="M0 0h24v24H0z" fill="none"/>
<circle cx="12" cy="12" r="9" />
<path d="M13 12h5" />
<path d="M13 15h4" />
<path d="M13 18h1" />
<path d="M13 9h4" />
<path d="M13 6h1" />
</svg>
</symbol>
</svg>
<input type="checkbox" class="sidebar-toggle" name="__navigation" id="__navigation">
<input type="checkbox" class="sidebar-toggle" name="__toc" id="__toc">
<label class="overlay sidebar-overlay" for="__navigation">
<div class="visually-hidden">Hide navigation sidebar</div>
</label>
<label class="overlay toc-overlay" for="__toc">
<div class="visually-hidden">Hide table of contents sidebar</div>
</label>
<div class="page">
<header class="mobile-header">
<div class="header-left">
<label class="nav-overlay-icon" for="__navigation">
<div class="visually-hidden">Toggle site navigation sidebar</div>
<i class="icon"><svg><use href="#svg-menu"></use></svg></i>
</label>
</div>
<div class="header-center">
<a href="../index.html"><div class="brand">Open-Source-OS-Training-Camp-2022 文档</div></a>
</div>
<div class="header-right">
<div class="theme-toggle-container theme-toggle-header">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-header-icon" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
</header>
<aside class="sidebar-drawer">
<div class="sidebar-container">
<div class="sidebar-sticky"><a class="sidebar-brand" href="../index.html">
<span class="sidebar-brand-text">Open-Source-OS-Training-Camp-2022 文档</span>
</a><form class="sidebar-search-container" method="get" action="../search.html" role="search">
<input class="sidebar-search" placeholder=搜索 name="q" aria-label="搜索">
<input type="hidden" name="check_keywords" value="yes">
<input type="hidden" name="area" value="default">
</form>
<div id="searchbox"></div><div class="sidebar-scroll"><div class="sidebar-tree">
<p class="caption" role="heading"><span class="caption-text">正文</span></p>
<ul class="current">
<li class="toctree-l1 has-children"><a class="reference internal" href="../0setup-devel-env.html">第零章:实验环境配置</a><input class="toctree-checkbox" id="toctree-checkbox-1" name="toctree-checkbox-1" role="switch" type="checkbox"/><label for="toctree-checkbox-1"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter1/index.html">第一章:应用程序与基本执行环境</a><input class="toctree-checkbox" id="toctree-checkbox-2" name="toctree-checkbox-2" role="switch" type="checkbox"/><label for="toctree-checkbox-2"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter1/0intro.html">引言</a></li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter1/1app-ee-platform.html">应用程序执行环境与平台支持</a><input class="toctree-checkbox" id="toctree-checkbox-3" name="toctree-checkbox-3" role="switch" type="checkbox"/><label for="toctree-checkbox-3"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter1/2remove-std.html">移除标准库依赖</a><input class="toctree-checkbox" id="toctree-checkbox-4" name="toctree-checkbox-4" role="switch" type="checkbox"/><label for="toctree-checkbox-4"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter1/3mini-rt-usrland.html">构建用户态执行环境</a><input class="toctree-checkbox" id="toctree-checkbox-5" name="toctree-checkbox-5" role="switch" type="checkbox"/><label for="toctree-checkbox-5"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter1/4mini-rt-baremetal.html">构建裸机执行环境</a><input class="toctree-checkbox" id="toctree-checkbox-6" name="toctree-checkbox-6" role="switch" type="checkbox"/><label for="toctree-checkbox-6"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a><input class="toctree-checkbox" id="toctree-checkbox-7" name="toctree-checkbox-7" role="switch" type="checkbox"/><label for="toctree-checkbox-7"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter2/0intro.html">引言</a></li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter2/2application.html">实现应用程序</a><input class="toctree-checkbox" id="toctree-checkbox-8" name="toctree-checkbox-8" role="switch" type="checkbox"/><label for="toctree-checkbox-8"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter2/3batch-system.html">实现批处理操作系统</a><input class="toctree-checkbox" id="toctree-checkbox-9" name="toctree-checkbox-9" role="switch" type="checkbox"/><label for="toctree-checkbox-9"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l2 has-children"><a class="reference internal" href="../chapter2/4trap-handling.html">实现特权级的切换</a><input class="toctree-checkbox" id="toctree-checkbox-10" name="toctree-checkbox-10" role="switch" type="checkbox"/><label for="toctree-checkbox-10"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a><input class="toctree-checkbox" id="toctree-checkbox-11" name="toctree-checkbox-11" role="switch" type="checkbox"/><label for="toctree-checkbox-11"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/1multi-loader.html">多道程序放置与加载</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/2task-switching.html">任务切换</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/3multiprogramming.html">管理多道程序</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/4time-sharing-system.html">分时多任务系统</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter3/5exercise.html">chapter3练习</a></li>
</ul>
</li>
<li class="toctree-l1 current has-children"><a class="reference internal" href="index.html">第四章:地址空间</a><input checked="" class="toctree-checkbox" id="toctree-checkbox-12" name="toctree-checkbox-12" role="switch" type="checkbox"/><label for="toctree-checkbox-12"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="3sv39-implementation-1.html">实现 SV39 多级页表机制(上)</a></li>
<li class="toctree-l2"><a class="reference internal" href="4sv39-implementation-2.html">实现 SV39 多级页表机制(下)</a></li>
<li class="toctree-l2 current current-page"><a class="current reference internal" href="#">内核与应用的地址空间</a></li>
<li class="toctree-l2"><a class="reference internal" href="6multitasking-based-on-as.html">基于地址空间的分时多任务</a></li>
<li class="toctree-l2"><a class="reference internal" href="7exercise.html">chapter4练习</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter5/index.html">第五章:进程及进程管理</a><input class="toctree-checkbox" id="toctree-checkbox-13" name="toctree-checkbox-13" role="switch" type="checkbox"/><label for="toctree-checkbox-13"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter5/0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter5/1process.html">与进程有关的重要系统调用</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter5/2core-data-structures.html">进程管理的核心数据结构</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter5/3implement-process-mechanism.html">进程管理机制的设计实现</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter5/4exercise.html">chapter5练习</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter6/index.html">第六章文件系统与I/O重定向</a><input class="toctree-checkbox" id="toctree-checkbox-14" name="toctree-checkbox-14" role="switch" type="checkbox"/><label for="toctree-checkbox-14"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/1file-descriptor.html">文件与文件描述符</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/1fs-interface.html">文件系统接口</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/2fs-implementation-1.html">简易文件系统 easy-fs (上)</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/2fs-implementation-2.html">简易文件系统 easy-fs (下)</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/3using-easy-fs-in-kernel.html">在内核中使用 easy-fs</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter6/4exercise.html">chapter6练习</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter7/index.html">第七章:进程间通信</a><input class="toctree-checkbox" id="toctree-checkbox-15" name="toctree-checkbox-15" role="switch" type="checkbox"/><label for="toctree-checkbox-15"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter7/0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter7/1pipe.html">管道</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter7/2cmdargs-and-redirection.html">命令行参数与标准 I/O 重定向</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter7/3exercise.html">chapter7练习</a></li>
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../chapter8/index.html">第八章:并发</a><input class="toctree-checkbox" id="toctree-checkbox-16" name="toctree-checkbox-16" role="switch" type="checkbox"/><label for="toctree-checkbox-16"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/0intro.html">引言</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/1thread-kernel.html">内核态的线程管理</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/2lock.html">锁机制</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/3semaphore.html">信号量机制</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/4condition-variable.html">条件变量机制</a></li>
<li class="toctree-l2"><a class="reference internal" href="../chapter8/5exercise.html">chapter8 练习</a></li>
</ul>
</li>
</ul>
<p class="caption" role="heading"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../appendix-a/index.html">附录 ARust 系统编程资料</a><input class="toctree-checkbox" id="toctree-checkbox-17" name="toctree-checkbox-17" role="switch" type="checkbox"/><label for="toctree-checkbox-17"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../appendix-b/index.html">附录 B常见工具的使用方法</a><input class="toctree-checkbox" id="toctree-checkbox-18" name="toctree-checkbox-18" role="switch" type="checkbox"/><label for="toctree-checkbox-18"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../appendix-c/index.html">附录 C深入机器模式RustSBI</a><input class="toctree-checkbox" id="toctree-checkbox-19" name="toctree-checkbox-19" role="switch" type="checkbox"/><label for="toctree-checkbox-19"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-d/index.html">附录 DRISC-V相关信息</a></li>
</ul>
<p class="caption" role="heading"><span class="caption-text">开发注记</span></p>
<ul>
<li class="toctree-l1 has-children"><a class="reference internal" href="../setup-sphinx.html">修改和构建本项目</a><input class="toctree-checkbox" id="toctree-checkbox-20" name="toctree-checkbox-20" role="switch" type="checkbox"/><label for="toctree-checkbox-20"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
<li class="toctree-l1 has-children"><a class="reference internal" href="../rest-example.html">reStructuredText 基本语法</a><input class="toctree-checkbox" id="toctree-checkbox-21" name="toctree-checkbox-21" role="switch" type="checkbox"/><label for="toctree-checkbox-21"><div class="visually-hidden">Toggle child pages in navigation</div><i class="icon"><svg><use href="#svg-arrow-right"></use></svg></i></label><ul class="simple">
</ul>
</li>
</ul>
</div>
</div>
</div>
</div>
</aside>
<div class="main">
<div class="content">
<article role="main">
<div class="content-icon-container">
<div class="theme-toggle-container theme-toggle-content">
<button class="theme-toggle">
<div class="visually-hidden">Toggle Light / Dark / Auto color theme</div>
<svg class="theme-icon-when-auto"><use href="#svg-sun-half"></use></svg>
<svg class="theme-icon-when-dark"><use href="#svg-moon"></use></svg>
<svg class="theme-icon-when-light"><use href="#svg-sun"></use></svg>
</button>
</div>
<label class="toc-overlay-icon toc-content-icon" for="__toc">
<div class="visually-hidden">Toggle table of contents sidebar</div>
<i class="icon"><svg><use href="#svg-toc"></use></svg></i>
</label>
</div>
<div class="section" id="id1">
<h1>内核与应用的地址空间<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<p>本节我们就在内核中通过基于页表的各种数据结构实现地址空间的抽象。</p>
<div class="section" id="id2">
<h2>实现地址空间抽象<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
<div class="section" id="id3">
<h3>逻辑段:一段连续地址的虚拟内存<a class="headerlink" href="#id3" title="永久链接至标题"></a></h3>
<p>我们以逻辑段 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code> 为单位描述一段连续地址的虚拟内存。所谓逻辑段,就是指地址区间中的一段实际可用(即 MMU 通过查多级页表
可以正确完成地址转换)的地址连续的虚拟地址区间,该区间内包含的所有虚拟页面都以一种相同的方式映射到物理页帧,具有可读/可写/可执行等属性。</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MapArea</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">vpn_range</span>: <span class="nc">VPNRange</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">data_frames</span>: <span class="nc">BTreeMap</span><span class="o">&lt;</span><span class="n">VirtPageNum</span><span class="p">,</span><span class="w"> </span><span class="n">FrameTracker</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">map_type</span>: <span class="nc">MapType</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">map_perm</span>: <span class="nc">MapPermission</span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>其中 <code class="docutils literal notranslate"><span class="pre">VPNRange</span></code> 描述一段虚拟页号的连续区间,表示该逻辑段在地址区间中的位置和长度。它是一个迭代器,可以使用 Rust
的语法糖 for-loop 进行迭代。有兴趣的读者可以参考 <code class="docutils literal notranslate"><span class="pre">os/src/mm/address.rs</span></code> 中它的实现。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p><strong>Rust 语法卡片:迭代器 Iterator</strong></p>
<p>Rust编程的迭代器模式允许你对一个序列的项进行某些处理。迭代器iterator是负责遍历序列中的每一项和决定序列何时结束的控制逻辑。
对于如何使用迭代器处理元素序列和如何实现 Iterator trait 来创建自定义迭代器的内容,
可以参考 <a class="reference external" href="https://kaisery.github.io/trpl-zh-cn/ch13-02-iterators.html">Rust 程序设计语言-中文版第十三章第二节</a></p>
</div>
<p><code class="docutils literal notranslate"><span class="pre">MapType</span></code> 描述该逻辑段内的所有虚拟页面映射到物理页帧的同一种方式,它是一个枚举类型,在内核当前的实现中支持两种方式:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="cp">#[derive(Copy, Clone, PartialEq, Debug)]</span><span class="w"></span>
<span class="k">pub</span><span class="w"> </span><span class="k">enum</span> <span class="nc">MapType</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">Framed</span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>其中 <code class="docutils literal notranslate"><span class="pre">Identical</span></code> 表示之前也有提到的恒等映射,用于在启用多级页表之后仍能够访问一个特定的物理地址指向的物理内存;而
<code class="docutils literal notranslate"><span class="pre">Framed</span></code> 则表示对于每个虚拟页面都需要映射到一个新分配的物理页帧。</p>
<p>当逻辑段采用 <code class="docutils literal notranslate"><span class="pre">MapType::Framed</span></code> 方式映射到物理内存的时候, <code class="docutils literal notranslate"><span class="pre">data_frames</span></code> 是一个保存了该逻辑段内的每个虚拟页面
和它被映射到的物理页帧 <code class="docutils literal notranslate"><span class="pre">FrameTracker</span></code> 的一个键值对容器 <code class="docutils literal notranslate"><span class="pre">BTreeMap</span></code> 中,这些物理页帧被用来存放实际内存数据而不是
作为多级页表中的中间节点。和之前的 <code class="docutils literal notranslate"><span class="pre">PageTable</span></code> 一样,这也用到了 RAII 的思想,将这些物理页帧的生命周期绑定到它所在的逻辑段
<code class="docutils literal notranslate"><span class="pre">MapArea</span></code> 下,当逻辑段被回收之后这些之前分配的物理页帧也会自动地同时被回收。</p>
<p><code class="docutils literal notranslate"><span class="pre">MapPermission</span></code> 表示控制该逻辑段的访问方式,它是页表项标志位 <code class="docutils literal notranslate"><span class="pre">PTEFlags</span></code> 的一个子集,仅保留 U/R/W/X
四个标志位,因为其他的标志位仅与硬件的地址转换机制细节相关,这样的设计能避免引入错误的标志位。</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="n">bitflags</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MapPermission</span>: <span class="kt">u8</span> <span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">R</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">1</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">W</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">X</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">3</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">U</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">1</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">4</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
</div>
<div class="section" id="id4">
<h3>地址空间:一系列有关联的逻辑段<a class="headerlink" href="#id4" title="永久链接至标题"></a></h3>
<p>地址空间是一系列有关联的逻辑段,这种关联一般是指这些逻辑段属于一个运行的程序(目前把一个运行的程序称为任务,后续会称为进程)。
用来表明正在运行的应用所在执行环境中的可访问内存空间,在这个内存空间中,包含了一系列的不一定连续的逻辑段。
这样我们就有任务的地址空间、内核的地址空间等说法了。地址空间使用 <code class="docutils literal notranslate"><span class="pre">MemorySet</span></code> 类型来表示:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">MemorySet</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">page_table</span>: <span class="nc">PageTable</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">areas</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">MapArea</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>它包含了该地址空间的多级页表 <code class="docutils literal notranslate"><span class="pre">page_table</span></code> 和一个逻辑段 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code> 的向量 <code class="docutils literal notranslate"><span class="pre">areas</span></code> 。注意 <code class="docutils literal notranslate"><span class="pre">PageTable</span></code>
挂着所有多级页表的节点所在的物理页帧,而每个 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code> 下则挂着对应逻辑段中的数据所在的物理页帧,这两部分
合在一起构成了一个地址空间所需的所有物理页帧。这同样是一种 RAII 风格,当一个地址空间 <code class="docutils literal notranslate"><span class="pre">MemorySet</span></code> 生命周期结束后,
这些物理页帧都会被回收。</p>
<p>地址空间 <code class="docutils literal notranslate"><span class="pre">MemorySet</span></code> 的方法如下:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">impl</span><span class="w"> </span><span class="n">MemorySet</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new_bare</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="bp">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="n">page_table</span>: <span class="nc">PageTable</span>::<span class="n">new</span><span class="p">(),</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="n">areas</span>: <span class="nb">Vec</span>::<span class="n">new</span><span class="p">(),</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">10</span><span class="w"> </span><span class="k">fn</span> <span class="nf">push</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">map_area</span>: <span class="nc">MapArea</span><span class="p">,</span><span class="w"> </span><span class="n">data</span>: <span class="nb">Option</span><span class="o">&lt;&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">]</span><span class="o">&gt;</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="n">map_area</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">page_table</span><span class="p">);</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="n">data</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">data</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="n">map_area</span><span class="p">.</span><span class="n">copy_data</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">page_table</span><span class="p">,</span><span class="w"> </span><span class="n">data</span><span class="p">);</span><span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">15</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">areas</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">map_area</span><span class="p">);</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="sd">/// Assume that no conflicts.</span>
<span class="linenos">18</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">insert_framed_area</span><span class="p">(</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="n">start_va</span>: <span class="nc">VirtAddr</span><span class="p">,</span><span class="w"> </span><span class="n">end_va</span>: <span class="nc">VirtAddr</span><span class="p">,</span><span class="w"> </span><span class="n">permission</span>: <span class="nc">MapPermission</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="n">start_va</span><span class="p">,</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="n">end_va</span><span class="p">,</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="p">,</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="n">permission</span><span class="p">,</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">28</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">29</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new_kernel</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="p">;</span><span class="w"></span>
<span class="linenos">30</span><span class="w"> </span><span class="sd">/// Include sections in elf and trampoline and TrapContext and user stack,</span>
<span class="linenos">31</span><span class="w"> </span><span class="sd">/// also returns user_sp and entry point.</span>
<span class="linenos">32</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_elf</span><span class="p">(</span><span class="n">elf_data</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="kt">usize</span><span class="p">);</span><span class="w"></span>
<span class="linenos">33</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul class="simple">
<li><p>第 4 行, <code class="docutils literal notranslate"><span class="pre">new_bare</span></code> 方法可以新建一个空的地址空间;</p></li>
<li><p>第 10 行, <code class="docutils literal notranslate"><span class="pre">push</span></code> 方法可以在当前地址空间插入一个新的逻辑段 <code class="docutils literal notranslate"><span class="pre">map_area</span></code> ,如果它是以 <code class="docutils literal notranslate"><span class="pre">Framed</span></code> 方式映射到
物理内存,还可以可选地在那些被映射到的物理页帧上写入一些初始化数据 <code class="docutils literal notranslate"><span class="pre">data</span></code> </p></li>
<li><p>第 18 行, <code class="docutils literal notranslate"><span class="pre">insert_framed_area</span></code> 方法调用 <code class="docutils literal notranslate"><span class="pre">push</span></code> ,可以在当前地址空间插入一个 <code class="docutils literal notranslate"><span class="pre">Framed</span></code> 方式映射到
物理内存的逻辑段。注意该方法的调用者要保证同一地址空间内的任意两个逻辑段不能存在交集,从后面即将分别介绍的内核和
应用的地址空间布局可以看出这一要求得到了保证;</p></li>
<li><p>第 29 行, <code class="docutils literal notranslate"><span class="pre">new_kernel</span></code> 可以生成内核的地址空间,而第 32 行的 <code class="docutils literal notranslate"><span class="pre">from_elf</span></code> 则可以应用的 ELF 格式可执行文件
解析出各数据段并对应生成应用的地址空间。它们的实现我们将在后面讨论。</p></li>
</ul>
<p>在实现 <code class="docutils literal notranslate"><span class="pre">push</span></code> 方法在地址空间中插入一个逻辑段 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code> 的时候,需要同时维护地址空间的多级页表 <code class="docutils literal notranslate"><span class="pre">page_table</span></code>
记录的虚拟页号到页表项的映射关系,也需要用到这个映射关系来找到向哪些物理页帧上拷贝初始数据。这用到了 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code>
提供的另外几个方法:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">impl</span><span class="w"> </span><span class="n">MapArea</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="n">start_va</span>: <span class="nc">VirtAddr</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="n">end_va</span>: <span class="nc">VirtAddr</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="n">map_type</span>: <span class="nc">MapType</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="n">map_perm</span>: <span class="nc">MapPermission</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">10</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">start_vpn</span>: <span class="nc">VirtPageNum</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">start_va</span><span class="p">.</span><span class="n">floor</span><span class="p">();</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">end_vpn</span>: <span class="nc">VirtPageNum</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">end_va</span><span class="p">.</span><span class="n">ceil</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="bp">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="n">vpn_range</span>: <span class="nc">VPNRange</span>::<span class="n">new</span><span class="p">(</span><span class="n">start_vpn</span><span class="p">,</span><span class="w"> </span><span class="n">end_vpn</span><span class="p">),</span><span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="n">data_frames</span>: <span class="nc">BTreeMap</span>::<span class="n">new</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">15</span><span class="w"> </span><span class="n">map_type</span><span class="p">,</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="n">map_perm</span><span class="p">,</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">18</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">map</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">page_table</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">PageTable</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">vpn</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">vpn_range</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">map_one</span><span class="p">(</span><span class="n">page_table</span><span class="p">,</span><span class="w"> </span><span class="n">vpn</span><span class="p">);</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">unmap</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">page_table</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">PageTable</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">vpn</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">vpn_range</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">unmap_one</span><span class="p">(</span><span class="n">page_table</span><span class="p">,</span><span class="w"> </span><span class="n">vpn</span><span class="p">);</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">28</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">29</span><span class="w"> </span><span class="sd">/// data: start-aligned but maybe with shorter length</span>
<span class="linenos">30</span><span class="w"> </span><span class="sd">/// assume that all frames were cleared before</span>
<span class="linenos">31</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">copy_data</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">page_table</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">PageTable</span><span class="p">,</span><span class="w"> </span><span class="n">data</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">32</span><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">map_type</span><span class="p">,</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="p">);</span><span class="w"></span>
<span class="linenos">33</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">start</span>: <span class="kt">usize</span> <span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span>
<span class="linenos">34</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">current_vpn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">vpn_range</span><span class="p">.</span><span class="n">get_start</span><span class="p">();</span><span class="w"></span>
<span class="linenos">35</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">data</span><span class="p">.</span><span class="n">len</span><span class="p">();</span><span class="w"></span>
<span class="linenos">36</span><span class="w"> </span><span class="k">loop</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">37</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">src</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="n">data</span><span class="p">[</span><span class="n">start</span><span class="o">..</span><span class="n">len</span><span class="p">.</span><span class="n">min</span><span class="p">(</span><span class="n">start</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">)];</span><span class="w"></span>
<span class="linenos">38</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">dst</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="n">page_table</span><span class="w"></span>
<span class="linenos">39</span><span class="w"> </span><span class="p">.</span><span class="n">translate</span><span class="p">(</span><span class="n">current_vpn</span><span class="p">)</span><span class="w"></span>
<span class="linenos">40</span><span class="w"> </span><span class="p">.</span><span class="n">unwrap</span><span class="p">()</span><span class="w"></span>
<span class="linenos">41</span><span class="w"> </span><span class="p">.</span><span class="n">ppn</span><span class="p">()</span><span class="w"></span>
<span class="linenos">42</span><span class="w"> </span><span class="p">.</span><span class="n">get_bytes_array</span><span class="p">()[</span><span class="o">..</span><span class="n">src</span><span class="p">.</span><span class="n">len</span><span class="p">()];</span><span class="w"></span>
<span class="linenos">43</span><span class="w"> </span><span class="n">dst</span><span class="p">.</span><span class="n">copy_from_slice</span><span class="p">(</span><span class="n">src</span><span class="p">);</span><span class="w"></span>
<span class="linenos">44</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">;</span><span class="w"></span>
<span class="linenos">45</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">&gt;=</span><span class="w"> </span><span class="n">len</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">46</span><span class="w"> </span><span class="k">break</span><span class="p">;</span><span class="w"></span>
<span class="linenos">47</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">48</span><span class="w"> </span><span class="n">current_vpn</span><span class="p">.</span><span class="n">step</span><span class="p">();</span><span class="w"></span>
<span class="linenos">49</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">50</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">51</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul>
<li><p>第 4 行的 <code class="docutils literal notranslate"><span class="pre">new</span></code> 方法可以新建一个逻辑段结构体,注意传入的起始/终止虚拟地址会分别被下取整/上取整为虚拟页号并传入
迭代器 <code class="docutils literal notranslate"><span class="pre">vpn_range</span></code> 中;</p></li>
<li><p>第 19 行的 <code class="docutils literal notranslate"><span class="pre">map</span></code> 和第 24 行的 <code class="docutils literal notranslate"><span class="pre">unmap</span></code> 可以将当前逻辑段到物理内存的映射从传入的该逻辑段所属的地址空间的
多级页表中加入或删除。可以看到它们的实现是遍历逻辑段中的所有虚拟页面,并以每个虚拟页面为单位依次在多级页表中进行
键值对的插入或删除,分别对应 <code class="docutils literal notranslate"><span class="pre">MapArea</span></code><code class="docutils literal notranslate"><span class="pre">map_one</span></code><code class="docutils literal notranslate"><span class="pre">unmap_one</span></code> 方法,我们后面将介绍它们的实现;</p></li>
<li><p>第 31 行的 <code class="docutils literal notranslate"><span class="pre">copy_data</span></code> 方法将切片 <code class="docutils literal notranslate"><span class="pre">data</span></code> 中的数据拷贝到当前逻辑段实际被内核放置在的各物理页帧上,从而
在地址空间中通过该逻辑段就能访问这些数据。调用它的时候需要满足:切片 <code class="docutils literal notranslate"><span class="pre">data</span></code> 中的数据大小不超过当前逻辑段的
总大小,且切片中的数据会被对齐到逻辑段的开头,然后逐页拷贝到实际的物理页帧。</p>
<p>从第 36 行开始的循环会遍历每一个需要拷贝数据的虚拟页面,在数据拷贝完成后会在第 48 行通过调用 <code class="docutils literal notranslate"><span class="pre">step</span></code> 方法,该
方法来自于 <code class="docutils literal notranslate"><span class="pre">os/src/mm/address.rs</span></code> 中为 <code class="docutils literal notranslate"><span class="pre">VirtPageNum</span></code> 实现的 <code class="docutils literal notranslate"><span class="pre">StepOne</span></code> Trait感兴趣的读者可以阅读
代码确认其实现。</p>
<p>每个页面的数据拷贝需要确定源 <code class="docutils literal notranslate"><span class="pre">src</span></code> 和目标 <code class="docutils literal notranslate"><span class="pre">dst</span></code> 两个切片并直接使用 <code class="docutils literal notranslate"><span class="pre">copy_from_slice</span></code> 完成复制。当确定
目标切片 <code class="docutils literal notranslate"><span class="pre">dst</span></code> 的时候,第 <code class="docutils literal notranslate"><span class="pre">39</span></code> 行从传入的当前逻辑段所属的地址空间的多级页表中手动查找迭代到的虚拟页号被映射
到的物理页帧,并通过 <code class="docutils literal notranslate"><span class="pre">get_bytes_array</span></code> 方法获取能够真正改写该物理页帧上内容的字节数组型可变引用,最后再获取它
的切片用于数据拷贝。</p>
</li>
</ul>
<p>接下来介绍对逻辑段中的单个虚拟页面进行映射/解映射的方法 <code class="docutils literal notranslate"><span class="pre">map_one</span></code><code class="docutils literal notranslate"><span class="pre">unmap_one</span></code> 。显然它们的实现取决于当前
逻辑段被映射到物理内存的方式:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">impl</span><span class="w"> </span><span class="n">MemoryArea</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">map_one</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">page_table</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">PageTable</span><span class="p">,</span><span class="w"> </span><span class="n">vpn</span>: <span class="nc">VirtPageNum</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ppn</span>: <span class="nc">PhysPageNum</span><span class="p">;</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">map_type</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="n">ppn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PhysPageNum</span><span class="p">(</span><span class="n">vpn</span><span class="p">.</span><span class="mi">0</span><span class="p">);</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">10</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">frame</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frame_alloc</span><span class="p">().</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="n">ppn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">frame</span><span class="p">.</span><span class="n">ppn</span><span class="p">;</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">data_frames</span><span class="p">.</span><span class="n">insert</span><span class="p">(</span><span class="n">vpn</span><span class="p">,</span><span class="w"> </span><span class="n">frame</span><span class="p">);</span><span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">15</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">pte_flags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PTEFlags</span>::<span class="n">from_bits</span><span class="p">(</span><span class="bp">self</span><span class="p">.</span><span class="n">map_perm</span><span class="p">.</span><span class="n">bits</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="n">page_table</span><span class="p">.</span><span class="n">map</span><span class="p">(</span><span class="n">vpn</span><span class="p">,</span><span class="w"> </span><span class="n">ppn</span><span class="p">,</span><span class="w"> </span><span class="n">pte_flags</span><span class="p">);</span><span class="w"></span>
<span class="linenos">18</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">unmap_one</span><span class="p">(</span><span class="o">&amp;</span><span class="k">mut</span><span class="w"> </span><span class="bp">self</span><span class="p">,</span><span class="w"> </span><span class="n">page_table</span>: <span class="kp">&amp;</span><span class="nc">mut</span><span class="w"> </span><span class="n">PageTable</span><span class="p">,</span><span class="w"> </span><span class="n">vpn</span>: <span class="nc">VirtPageNum</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">map_type</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">data_frames</span><span class="p">.</span><span class="n">remove</span><span class="p">(</span><span class="o">&amp;</span><span class="n">vpn</span><span class="p">);</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="n">_</span><span class="w"> </span><span class="o">=&gt;</span><span class="w"> </span><span class="p">{}</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="n">page_table</span><span class="p">.</span><span class="n">unmap</span><span class="p">(</span><span class="n">vpn</span><span class="p">);</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">28</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul>
<li><p>对于第 4 行的 <code class="docutils literal notranslate"><span class="pre">map_one</span></code> 来说,在虚拟页号 <code class="docutils literal notranslate"><span class="pre">vpn</span></code> 已经确定的情况下,它需要知道要将一个怎么样的页表项插入多级页表。
页表项的标志位来源于当前逻辑段的类型为 <code class="docutils literal notranslate"><span class="pre">MapPermission</span></code> 的统一配置,只需将其转换为 <code class="docutils literal notranslate"><span class="pre">PTEFlags</span></code> ;而页表项的
物理页号则取决于当前逻辑段映射到物理内存的方式:</p>
<ul class="simple">
<li><p>当以恒等映射 <code class="docutils literal notranslate"><span class="pre">Identical</span></code> 方式映射的时候,物理页号就等于虚拟页号;</p></li>
<li><p>当以 <code class="docutils literal notranslate"><span class="pre">Framed</span></code> 方式映射的时候,需要分配一个物理页帧让当前的虚拟页面可以映射过去,此时页表项中的物理页号自然就是
这个被分配的物理页帧的物理页号。此时还需要将这个物理页帧挂在逻辑段的 <code class="docutils literal notranslate"><span class="pre">data_frames</span></code> 字段下。</p></li>
</ul>
<p>当确定了页表项的标志位和物理页号之后,即可调用多级页表 <code class="docutils literal notranslate"><span class="pre">PageTable</span></code><code class="docutils literal notranslate"><span class="pre">map</span></code> 接口来插入键值对。</p>
</li>
<li><p>对于第 19 行的 <code class="docutils literal notranslate"><span class="pre">unmap_one</span></code> 来说,基本上就是调用 <code class="docutils literal notranslate"><span class="pre">PageTable</span></code><code class="docutils literal notranslate"><span class="pre">unmap</span></code> 接口删除以传入的虚拟页号为键的
键值对即可。然而,当以 <code class="docutils literal notranslate"><span class="pre">Framed</span></code> 映射的时候,不要忘记同时将虚拟页面被映射到的物理页帧 <code class="docutils literal notranslate"><span class="pre">FrameTracker</span></code>
<code class="docutils literal notranslate"><span class="pre">data_frames</span></code> 中移除,这样这个物理页帧才能立即被回收以备后续分配。</p></li>
</ul>
</div>
</div>
<div class="section" id="id5">
<h2>内核地址空间<a class="headerlink" href="#id5" title="永久链接至标题"></a></h2>
<p id="term-isolation">在本章之前,内核和应用代码的访存地址都被视为一个物理地址直接访问物理内存,而在分页模式开启之后,它们都需要通过 MMU 的
地址转换变成物理地址再交给 CPU 的访存单元去访问物理内存。地址空间抽象的重要意义在于 <strong>隔离</strong> (Isolation) ,当我们
在执行每个应用的代码的时候,内核需要控制 MMU 使用这个应用地址空间的多级页表进行地址转换。由于每个应用地址空间在创建
的时候也顺带设置好了多级页表使得只有那些存放了它的数据的物理页帧能够通过该多级页表被映射到,这样它就只能访问自己的数据
而无法触及其他应用或是内核的数据。</p>
<p id="term-trampoline">启用分页模式下,内核代码的访存地址也会被视为一个虚拟地址并需要经过 MMU 的地址转换,因此我们也需要为内核对应构造一个
地址空间,它除了仍然需要允许内核的各数据段能够被正常访问之后,还需要包含所有应用的内核栈以及一个
<strong>跳板</strong> (Trampoline) 。我们会在本章的最后一节再深入介绍跳板的机制。</p>
<p>下图是软件看到的 64 位地址空间在 SV39 分页模式下实际可能通过 MMU 检查的最高 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span> (之前在
<span class="xref std std-ref">这里</span> 中解释过最高和最低 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span> 的问题):</p>
<a class="reference internal image-reference" href="../_images/kernel-as-high.png" id="kernel-as-high"><img alt="../_images/kernel-as-high.png" class="align-center" id="kernel-as-high" src="../_images/kernel-as-high.png" style="height: 400px;"/></a>
<p>可以看到,跳板放在最高的一个虚拟页面中。接下来则是从高到低放置每个应用的内核栈,内核栈的大小由 <code class="docutils literal notranslate"><span class="pre">config</span></code> 子模块的
<code class="docutils literal notranslate"><span class="pre">KERNEL_STACK_SIZE</span></code> 给出。它们的映射方式为 <code class="docutils literal notranslate"><span class="pre">MapPermission</span></code> 中的 rw 两个标志位,意味着这个逻辑段仅允许
CPU 处于内核态访问,且只能读或写。</p>
<p id="term-guard-page">注意相邻两个内核栈之间会预留一个 <strong>保护页面</strong> (Guard Page) ,它是内核地址空间中的空洞,多级页表中并不存在与它相关的映射。
它的意义在于当内核栈空间不足(如调用层数过多或死递归)的时候,代码会尝试访问
空洞区域内的虚拟地址,然而它无法在多级页表中找到映射,便会触发异常,此时控制权会交给 trap handler 对这种情况进行
处理。由于编译器会对访存顺序和局部变量在栈帧中的位置进行优化,我们难以确定一个已经溢出的栈帧中的哪些位置会先被访问,
但总的来说,空洞区域被设置的越大,我们就能越早捕获到这一错误并避免它覆盖其他重要数据。由于我们的内核非常简单且内核栈
的大小设置比较宽裕,在当前的设计中我们仅将空洞区域的大小设置为单个页面。</p>
<p>下面则给出了内核地址空间的低 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span> 的布局:</p>
<a class="reference internal image-reference" href="../_images/kernel-as-low.png"><img alt="../_images/kernel-as-low.png" class="align-center" src="../_images/kernel-as-low.png" style="height: 400px;"/></a>
<p>四个逻辑段 <code class="docutils literal notranslate"><span class="pre">.text/.rodata/.data/.bss</span></code> 被恒等映射到物理内存,这使得我们在无需调整内核内存布局 <code class="docutils literal notranslate"><span class="pre">os/src/linker.ld</span></code>
的情况下就仍能和启用页表机制之前那样访问内核的各数据段。注意我们借用页表机制对这些逻辑段的访问方式做出了限制,这都是为了
在硬件的帮助下能够尽可能发现内核中的 bug ,在这里:</p>
<ul class="simple">
<li><p>四个逻辑段的 U 标志位均未被设置,使得 CPU 只能在处于 S 特权级(或以上)时访问它们;</p></li>
<li><p>代码段 <code class="docutils literal notranslate"><span class="pre">.text</span></code> 不允许被修改;</p></li>
<li><p>只读数据段 <code class="docutils literal notranslate"><span class="pre">.rodata</span></code> 不允许被修改,也不允许从它上面取指;</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">.data/.bss</span></code> 均允许被读写,但是不允许从它上面取指。</p></li>
</ul>
<p>此外, <a class="reference internal" href="4sv39-implementation-2.html#modify-page-table"><span class="std std-ref">之前</span></a> 提到过内核地址空间中需要存在一个恒等映射到内核数据段之外的可用物理
页帧的逻辑段,这样才能在启用页表机制之后,内核仍能以纯软件的方式读写这些物理页帧。它们的标志位仅包含 rw ,意味着该
逻辑段只能在 S 特权级以上访问,并且只能读写。</p>
<p>下面我们给出创建内核地址空间的方法 <code class="docutils literal notranslate"><span class="pre">new_kernel</span></code> </p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">extern</span><span class="w"> </span><span class="s">"C"</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="k">fn</span> <span class="nf">stext</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="k">fn</span> <span class="nf">etext</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="k">fn</span> <span class="nf">srodata</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="k">fn</span> <span class="nf">erodata</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="k">fn</span> <span class="nf">sdata</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="k">fn</span> <span class="nf">edata</span><span class="p">();</span><span class="w"></span>
<span class="linenos">10</span><span class="w"> </span><span class="k">fn</span> <span class="nf">sbss_with_stack</span><span class="p">();</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="k">fn</span> <span class="nf">ebss</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="k">fn</span> <span class="nf">ekernel</span><span class="p">();</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="k">fn</span> <span class="nf">strampoline</span><span class="p">();</span><span class="w"></span>
<span class="linenos">14</span><span class="p">}</span><span class="w"></span>
<span class="linenos">15</span>
<span class="linenos">16</span><span class="k">impl</span><span class="w"> </span><span class="n">MemorySet</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="sd">/// Without kernel stacks.</span>
<span class="linenos">18</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">new_kernel</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="nc">Self</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">memory_set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">Self</span>::<span class="n">new_bare</span><span class="p">();</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="c1">// map trampoline</span>
<span class="linenos">21</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">map_trampoline</span><span class="p">();</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="c1">// map kernel sections</span>
<span class="linenos">23</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">".text [{:#x}, {:#x})"</span><span class="p">,</span><span class="w"> </span><span class="n">stext</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">etext</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">);</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">".rodata [{:#x}, {:#x})"</span><span class="p">,</span><span class="w"> </span><span class="n">srodata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">erodata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">);</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">".data [{:#x}, {:#x})"</span><span class="p">,</span><span class="w"> </span><span class="n">sdata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">edata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">);</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">".bss [{:#x}, {:#x})"</span><span class="p">,</span><span class="w"> </span><span class="n">sbss_with_stack</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">ebss</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">);</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"mapping .text section"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">28</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">29</span><span class="w"> </span><span class="p">(</span><span class="n">stext</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">30</span><span class="w"> </span><span class="p">(</span><span class="n">etext</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">31</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="linenos">32</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">X</span><span class="p">,</span><span class="w"></span>
<span class="linenos">33</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">34</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"mapping .rodata section"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">35</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">36</span><span class="w"> </span><span class="p">(</span><span class="n">srodata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">37</span><span class="w"> </span><span class="p">(</span><span class="n">erodata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">38</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="linenos">39</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="p">,</span><span class="w"></span>
<span class="linenos">40</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">41</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"mapping .data section"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">42</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">43</span><span class="w"> </span><span class="p">(</span><span class="n">sdata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">44</span><span class="w"> </span><span class="p">(</span><span class="n">edata</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">45</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="linenos">46</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="p">,</span><span class="w"></span>
<span class="linenos">47</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">48</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"mapping .bss section"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">49</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">50</span><span class="w"> </span><span class="p">(</span><span class="n">sbss_with_stack</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">51</span><span class="w"> </span><span class="p">(</span><span class="n">ebss</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">52</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="linenos">53</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="p">,</span><span class="w"></span>
<span class="linenos">54</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">55</span><span class="w"> </span><span class="fm">println!</span><span class="p">(</span><span class="s">"mapping physical memory"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">56</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">57</span><span class="w"> </span><span class="p">(</span><span class="n">ekernel</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">58</span><span class="w"> </span><span class="n">MEMORY_END</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">59</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Identical</span><span class="p">,</span><span class="w"></span>
<span class="linenos">60</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="p">,</span><span class="w"></span>
<span class="linenos">61</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">62</span><span class="w"> </span><span class="n">memory_set</span><span class="w"></span>
<span class="linenos">63</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">64</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">new_kernel</span></code> 将映射跳板和地址空间中最低 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span> 中的所有的逻辑段。第 3 行开始,我们从
<code class="docutils literal notranslate"><span class="pre">os/src/linker.ld</span></code> 中引用了很多表示了各个段位置的符号,而后在 <code class="docutils literal notranslate"><span class="pre">new_kernel</span></code> 中,我们从低地址到高地址
依次创建 5 个逻辑段并通过 <code class="docutils literal notranslate"><span class="pre">push</span></code> 方法将它们插入到内核地址空间中,上面我们已经详细介绍过这 5 个逻辑段。跳板
是通过 <code class="docutils literal notranslate"><span class="pre">map_trampoline</span></code> 方法来映射的,我们也将在本章最后一节进行讲解。</p>
</div>
<div class="section" id="id6">
<h2>应用地址空间<a class="headerlink" href="#id6" title="永久链接至标题"></a></h2>
<p>现在我们来介绍如何创建应用的地址空间。在前面的章节中,我们直接将丢弃所有符号的应用二进制镜像链接到内核,在初始化的时候
内核仅需将他们加载到正确的初始物理地址就能使它们正确执行。但本章中,我们希望效仿内核地址空间的设计,同样借助页表机制
使得应用地址空间的各个逻辑段也可以有不同的访问方式限制,这样可以提早检测出应用的错误并及时将其终止以最小化它对系统带来的
恶劣影响。</p>
<p>在第三章中,每个应用链接脚本中的起始地址被要求是不同的,这样它们的代码和数据存放的位置才不会产生冲突。但是这是一种对于应用开发者
极其不友好的设计。现在,借助地址空间的抽象,我们终于可以让所有应用程序都使用同样的起始地址,这也意味着所有应用可以使用同一个链接脚本了:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="o">/*</span> <span class="n">user</span><span class="o">/</span><span class="n">src</span><span class="o">/</span><span class="n">linker</span><span class="o">.</span><span class="n">ld</span> <span class="o">*/</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="n">OUTPUT_ARCH</span><span class="p">(</span><span class="n">riscv</span><span class="p">)</span>
<span class="linenos"> 4</span><span class="n">ENTRY</span><span class="p">(</span><span class="n">_start</span><span class="p">)</span>
<span class="linenos"> 5</span>
<span class="linenos"> 6</span><span class="n">BASE_ADDRESS</span> <span class="o">=</span> <span class="mh">0x0</span><span class="p">;</span>
<span class="linenos"> 7</span>
<span class="linenos"> 8</span><span class="n">SECTIONS</span>
<span class="linenos"> 9</span><span class="p">{</span>
<span class="linenos">10</span> <span class="o">.</span> <span class="o">=</span> <span class="n">BASE_ADDRESS</span><span class="p">;</span>
<span class="linenos">11</span> <span class="o">.</span><span class="n">text</span> <span class="p">:</span> <span class="p">{</span>
<span class="linenos">12</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">text</span><span class="o">.</span><span class="n">entry</span><span class="p">)</span>
<span class="linenos">13</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">text</span> <span class="o">.</span><span class="n">text</span><span class="o">.*</span><span class="p">)</span>
<span class="linenos">14</span> <span class="p">}</span>
<span class="linenos">15</span> <span class="o">.</span> <span class="o">=</span> <span class="n">ALIGN</span><span class="p">(</span><span class="mi">4</span><span class="n">K</span><span class="p">);</span>
<span class="linenos">16</span> <span class="o">.</span><span class="n">rodata</span> <span class="p">:</span> <span class="p">{</span>
<span class="linenos">17</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">rodata</span> <span class="o">.</span><span class="n">rodata</span><span class="o">.*</span><span class="p">)</span>
<span class="linenos">18</span> <span class="p">}</span>
<span class="linenos">19</span> <span class="o">.</span> <span class="o">=</span> <span class="n">ALIGN</span><span class="p">(</span><span class="mi">4</span><span class="n">K</span><span class="p">);</span>
<span class="linenos">20</span> <span class="o">.</span><span class="n">data</span> <span class="p">:</span> <span class="p">{</span>
<span class="linenos">21</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">data</span> <span class="o">.</span><span class="n">data</span><span class="o">.*</span><span class="p">)</span>
<span class="linenos">22</span> <span class="p">}</span>
<span class="linenos">23</span> <span class="o">.</span><span class="n">bss</span> <span class="p">:</span> <span class="p">{</span>
<span class="linenos">24</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">bss</span> <span class="o">.</span><span class="n">bss</span><span class="o">.*</span><span class="p">)</span>
<span class="linenos">25</span> <span class="p">}</span>
<span class="linenos">26</span> <span class="o">/</span><span class="n">DISCARD</span><span class="o">/</span> <span class="p">:</span> <span class="p">{</span>
<span class="linenos">27</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">eh_frame</span><span class="p">)</span>
<span class="linenos">28</span> <span class="o">*</span><span class="p">(</span><span class="o">.</span><span class="n">debug</span><span class="o">*</span><span class="p">)</span>
<span class="linenos">29</span> <span class="p">}</span>
<span class="linenos">30</span><span class="p">}</span>
</pre></div>
</div>
<p>我们将起始地址 <code class="docutils literal notranslate"><span class="pre">BASE_ADDRESS</span></code> 设置为 <span class="math notranslate nohighlight">\(\text{0x0}\)</span> ,显然它只能是一个地址空间中的虚拟地址而非物理地址。
事实上由于我们将入口汇编代码段放在最低的地方,这也是整个应用的入口点。
我们只需清楚这一事实即可,而无需像之前一样将其硬编码到代码中。此外,在 <code class="docutils literal notranslate"><span class="pre">.text</span></code><code class="docutils literal notranslate"><span class="pre">.rodata</span></code> 中间以及 <code class="docutils literal notranslate"><span class="pre">.rodata</span></code>
<code class="docutils literal notranslate"><span class="pre">.data</span></code> 中间我们进行了页面对齐,因为前后两个逻辑段的访问方式限制是不同的,由于我们只能以页为单位对这个限制进行设置,
因此就只能将下一个逻辑段对齐到下一个页面开始放置。相对的, <code class="docutils literal notranslate"><span class="pre">.data</span></code><code class="docutils literal notranslate"><span class="pre">.bss</span></code> 两个逻辑段由于限制相同,它们中间
则无需进行页面对齐。</p>
<p>下图展示了应用地址空间的布局:</p>
<a class="reference internal image-reference" href="../_images/app-as-full.png"><img alt="../_images/app-as-full.png" class="align-center" src="../_images/app-as-full.png" style="height: 400px;"/></a>
<p>左侧给出了应用地址空间最低 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span> 的布局:从 <span class="math notranslate nohighlight">\(\text{0x0}\)</span> 开始向高地址放置应用内存布局中的
各个逻辑段,最后放置带有一个保护页面的用户栈。这些逻辑段都是以 <code class="docutils literal notranslate"><span class="pre">Framed</span></code> 方式映射到物理内存的,从访问方式上来说都加上
了 U 标志位代表 CPU 可以在 U 特权级也就是执行应用代码的时候访问它们。右侧则给出了最高的 <span class="math notranslate nohighlight">\(256\text{GiB}\)</span>
可以看出它只是和内核地址空间一样将跳板放置在最高页,还将 Trap 上下文放置在次高页中。这两个虚拟页面虽然位于应用地址空间,
但是它们并不包含 U 标志位,事实上它们在地址空间切换的时候才会发挥作用,请同样参考本章的最后一节。</p>
<p><code class="docutils literal notranslate"><span class="pre">os/src/build.rs</span></code> 中,我们不再将丢弃了所有符号的应用二进制镜像链接进内核,而是直接使用 ELF 格式的可执行文件,
因为在前者中内存布局中各个逻辑段的位置和访问限制等信息都被裁剪掉了。而 <code class="docutils literal notranslate"><span class="pre">loader</span></code> 子模块也变得极其精简:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/loader.rs</span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_num_app</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="kt">usize</span> <span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">"C"</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">fn</span> <span class="nf">_num_app</span><span class="p">();</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="p">(</span><span class="n">_num_app</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">read_volatile</span><span class="p">()</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">get_app_data</span><span class="p">(</span><span class="n">app_id</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kp">&amp;</span><span class="o">'</span><span class="nb">static</span> <span class="p">[</span><span class="kt">u8</span><span class="p">]</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">extern</span><span class="w"> </span><span class="s">"C"</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">fn</span> <span class="nf">_num_app</span><span class="p">();</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num_app_ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">_num_app</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">usize</span><span class="p">;</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">num_app</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">get_num_app</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">app_start</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">core</span>::<span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="n">num_app_ptr</span><span class="p">.</span><span class="n">add</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span><span class="w"> </span><span class="n">num_app</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="w"> </span><span class="fm">assert!</span><span class="p">(</span><span class="n">app_id</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">num_app</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">core</span>::<span class="n">slice</span>::<span class="n">from_raw_parts</span><span class="p">(</span><span class="w"></span>
<span class="w"> </span><span class="n">app_start</span><span class="p">[</span><span class="n">app_id</span><span class="p">]</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="o">*</span><span class="k">const</span><span class="w"> </span><span class="kt">u8</span><span class="p">,</span><span class="w"></span>
<span class="w"> </span><span class="n">app_start</span><span class="p">[</span><span class="n">app_id</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">]</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">app_start</span><span class="p">[</span><span class="n">app_id</span><span class="p">]</span><span class="w"></span>
<span class="w"> </span><span class="p">)</span><span class="w"></span>
<span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>它仅需要提供两个函数: <code class="docutils literal notranslate"><span class="pre">get_num_app</span></code> 获取链接到内核内的应用的数目,而 <code class="docutils literal notranslate"><span class="pre">get_app_data</span></code> 则根据传入的应用编号
取出对应应用的 ELF 格式可执行文件数据。它们和之前一样仍是基于 <code class="docutils literal notranslate"><span class="pre">build.rs</span></code> 生成的 <code class="docutils literal notranslate"><span class="pre">link_app.S</span></code> 给出的符号来
确定其位置,并实际放在内核的数据段中。
<code class="docutils literal notranslate"><span class="pre">loader</span></code> 模块中原有的内核和用户栈则分别作为逻辑段放在内核和用户地址空间中,我们无需再去专门为其定义一种类型。</p>
<p>在创建应用地址空间的时候,我们需要对 <code class="docutils literal notranslate"><span class="pre">get_app_data</span></code> 得到的 ELF 格式数据进行解析,找到各个逻辑段所在位置和访问
限制并插入进来,最终得到一个完整的应用地址空间:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">impl</span><span class="w"> </span><span class="n">MemorySet</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="sd">/// Include sections in elf and trampoline and TrapContext and user stack,</span>
<span class="linenos"> 5</span><span class="w"> </span><span class="sd">/// also returns user_sp and entry point.</span>
<span class="linenos"> 6</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">from_elf</span><span class="p">(</span><span class="n">elf_data</span>: <span class="kp">&amp;</span><span class="p">[</span><span class="kt">u8</span><span class="p">])</span><span class="w"> </span>-&gt; <span class="p">(</span><span class="bp">Self</span><span class="p">,</span><span class="w"> </span><span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">memory_set</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="bp">Self</span>::<span class="n">new_bare</span><span class="p">();</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="c1">// map trampoline</span>
<span class="linenos"> 9</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">map_trampoline</span><span class="p">();</span><span class="w"></span>
<span class="linenos">10</span><span class="w"> </span><span class="c1">// map program headers of elf, with U flag</span>
<span class="linenos">11</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">elf</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">xmas_elf</span>::<span class="n">ElfFile</span>::<span class="n">new</span><span class="p">(</span><span class="n">elf_data</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">elf_header</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">elf</span><span class="p">.</span><span class="n">header</span><span class="p">;</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">magic</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">elf_header</span><span class="p">.</span><span class="n">pt1</span><span class="p">.</span><span class="n">magic</span><span class="p">;</span><span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="fm">assert_eq!</span><span class="p">(</span><span class="n">magic</span><span class="p">,</span><span class="w"> </span><span class="p">[</span><span class="mh">0x7f</span><span class="p">,</span><span class="w"> </span><span class="mh">0x45</span><span class="p">,</span><span class="w"> </span><span class="mh">0x4c</span><span class="p">,</span><span class="w"> </span><span class="mh">0x46</span><span class="p">],</span><span class="w"> </span><span class="s">"invalid elf!"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">15</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ph_count</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">elf_header</span><span class="p">.</span><span class="n">pt2</span><span class="p">.</span><span class="n">ph_count</span><span class="p">();</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">max_end_vpn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">VirtPageNum</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="mi">0</span><span class="o">..</span><span class="n">ph_count</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">18</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ph</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">elf</span><span class="p">.</span><span class="n">program_header</span><span class="p">(</span><span class="n">i</span><span class="p">).</span><span class="n">unwrap</span><span class="p">();</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">ph</span><span class="p">.</span><span class="n">get_type</span><span class="p">().</span><span class="n">unwrap</span><span class="p">()</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="n">xmas_elf</span>::<span class="n">program</span>::<span class="n">Type</span>::<span class="n">Load</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">start_va</span>: <span class="nc">VirtAddr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">ph</span><span class="p">.</span><span class="n">virtual_addr</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">end_va</span>: <span class="nc">VirtAddr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">((</span><span class="n">ph</span><span class="p">.</span><span class="n">virtual_addr</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ph</span><span class="p">.</span><span class="n">mem_size</span><span class="p">())</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">).</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">map_perm</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">U</span><span class="p">;</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">ph_flags</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ph</span><span class="p">.</span><span class="n">flags</span><span class="p">();</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">ph_flags</span><span class="p">.</span><span class="n">is_read</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">map_perm</span><span class="w"> </span><span class="o">|=</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">ph_flags</span><span class="p">.</span><span class="n">is_write</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">map_perm</span><span class="w"> </span><span class="o">|=</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="n">ph_flags</span><span class="p">.</span><span class="n">is_execute</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="n">map_perm</span><span class="w"> </span><span class="o">|=</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">X</span><span class="p">;</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">map_area</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">28</span><span class="w"> </span><span class="n">start_va</span><span class="p">,</span><span class="w"></span>
<span class="linenos">29</span><span class="w"> </span><span class="n">end_va</span><span class="p">,</span><span class="w"></span>
<span class="linenos">30</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="p">,</span><span class="w"></span>
<span class="linenos">31</span><span class="w"> </span><span class="n">map_perm</span><span class="p">,</span><span class="w"></span>
<span class="linenos">32</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="linenos">33</span><span class="w"> </span><span class="n">max_end_vpn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">map_area</span><span class="p">.</span><span class="n">vpn_range</span><span class="p">.</span><span class="n">get_end</span><span class="p">();</span><span class="w"></span>
<span class="linenos">34</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="w"></span>
<span class="linenos">35</span><span class="w"> </span><span class="n">map_area</span><span class="p">,</span><span class="w"></span>
<span class="linenos">36</span><span class="w"> </span><span class="nb">Some</span><span class="p">(</span><span class="o">&amp;</span><span class="n">elf</span><span class="p">.</span><span class="n">input</span><span class="p">[</span><span class="n">ph</span><span class="p">.</span><span class="n">offset</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="o">..</span><span class="p">(</span><span class="n">ph</span><span class="p">.</span><span class="n">offset</span><span class="p">()</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">ph</span><span class="p">.</span><span class="n">file_size</span><span class="p">())</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">])</span><span class="w"></span>
<span class="linenos">37</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="linenos">38</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">39</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">40</span><span class="w"> </span><span class="c1">// map user stack with U flags</span>
<span class="linenos">41</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">max_end_va</span>: <span class="nc">VirtAddr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">max_end_vpn</span><span class="p">.</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="linenos">42</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="k">mut</span><span class="w"> </span><span class="n">user_stack_bottom</span>: <span class="kt">usize</span> <span class="o">=</span><span class="w"> </span><span class="n">max_end_va</span><span class="p">.</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="linenos">43</span><span class="w"> </span><span class="c1">// guard page</span>
<span class="linenos">44</span><span class="w"> </span><span class="n">user_stack_bottom</span><span class="w"> </span><span class="o">+=</span><span class="w"> </span><span class="n">PAGE_SIZE</span><span class="p">;</span><span class="w"></span>
<span class="linenos">45</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">user_stack_top</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">user_stack_bottom</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="n">USER_STACK_SIZE</span><span class="p">;</span><span class="w"></span>
<span class="linenos">46</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">47</span><span class="w"> </span><span class="n">user_stack_bottom</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">48</span><span class="w"> </span><span class="n">user_stack_top</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">49</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="p">,</span><span class="w"></span>
<span class="linenos">50</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">U</span><span class="p">,</span><span class="w"></span>
<span class="linenos">51</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">52</span><span class="w"> </span><span class="c1">// map TrapContext</span>
<span class="linenos">53</span><span class="w"> </span><span class="n">memory_set</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">MapArea</span>::<span class="n">new</span><span class="p">(</span><span class="w"></span>
<span class="linenos">54</span><span class="w"> </span><span class="n">TRAP_CONTEXT</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">55</span><span class="w"> </span><span class="n">TRAMPOLINE</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">56</span><span class="w"> </span><span class="n">MapType</span>::<span class="n">Framed</span><span class="p">,</span><span class="w"></span>
<span class="linenos">57</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">MapPermission</span>::<span class="n">W</span><span class="p">,</span><span class="w"></span>
<span class="linenos">58</span><span class="w"> </span><span class="p">),</span><span class="w"> </span><span class="nb">None</span><span class="p">);</span><span class="w"></span>
<span class="linenos">59</span><span class="w"> </span><span class="p">(</span><span class="n">memory_set</span><span class="p">,</span><span class="w"> </span><span class="n">user_stack_top</span><span class="p">,</span><span class="w"> </span><span class="n">elf</span><span class="p">.</span><span class="n">header</span><span class="p">.</span><span class="n">pt2</span><span class="p">.</span><span class="n">entry_point</span><span class="p">()</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">usize</span><span class="p">)</span><span class="w"></span>
<span class="linenos">60</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">61</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul>
<li><p>第 9 行,我们将跳板插入到应用地址空间;</p></li>
<li><p>第 11 行,我们使用外部 crate <code class="docutils literal notranslate"><span class="pre">xmas_elf</span></code> 来解析传入的应用 ELF 数据并可以轻松取出各个部分。
<a class="reference internal" href="../appendix-b/index.html#term-elf"><span class="std std-ref">此前</span></a> 我们简要介绍过 ELF 格式的布局。第 14 行,我们取出 ELF 的魔数来判断
它是不是一个合法的 ELF 。</p>
<p>第 15 行,我们可以直接得到 program header 的数目,然后遍历所有的 program header 并将合适的区域加入
到应用地址空间中。这一过程的主体在第 17~39 行之间。第 19 行我们确认 program header 的类型是 <code class="docutils literal notranslate"><span class="pre">LOAD</span></code>
这表明它有被内核加载的必要,此时不必理会其他类型的 program header 。接着通过 <code class="docutils literal notranslate"><span class="pre">ph.virtual_addr()</span></code>
<code class="docutils literal notranslate"><span class="pre">ph.mem_size()</span></code> 来计算这一区域在应用地址空间中的位置,通过 <code class="docutils literal notranslate"><span class="pre">ph.flags()</span></code> 来确认这一区域访问方式的
限制并将其转换为 <code class="docutils literal notranslate"><span class="pre">MapPermission</span></code> 类型(注意它默认包含 U 标志位)。最后我们在第 27 行创建逻辑段
<code class="docutils literal notranslate"><span class="pre">map_area</span></code> 并在第 34 行 <code class="docutils literal notranslate"><span class="pre">push</span></code> 到应用地址空间。在 <code class="docutils literal notranslate"><span class="pre">push</span></code> 的时候我们需要完成数据拷贝,当前
program header 数据被存放的位置可以通过 <code class="docutils literal notranslate"><span class="pre">ph.offset()</span></code><code class="docutils literal notranslate"><span class="pre">ph.file_size()</span></code> 来找到。 注意当
存在一部分零初始化的时候, <code class="docutils literal notranslate"><span class="pre">ph.file_size()</span></code> 将会小于 <code class="docutils literal notranslate"><span class="pre">ph.mem_size()</span></code> ,因为这些零出于缩减可执行
文件大小的原因不应该实际出现在 ELF 数据中。</p>
</li>
<li><p>我们从第 40 行开始处理用户栈。注意在前面加载各个 program header 的时候,我们就已经维护了 <code class="docutils literal notranslate"><span class="pre">max_end_vpn</span></code>
记录目前涉及到的最大的虚拟页号,只需紧接着在它上面再放置一个保护页面和用户栈即可。</p></li>
<li><p>第 53 行则在应用地址空间中映射次高页面来存放 Trap 上下文。</p></li>
<li><p>第 59 行返回的时候,我们不仅返回应用地址空间 <code class="docutils literal notranslate"><span class="pre">memory_set</span></code> ,也同时返回用户栈虚拟地址 <code class="docutils literal notranslate"><span class="pre">user_stack_top</span></code>
以及从解析 ELF 得到的该应用入口点地址,它们将被我们用来创建应用的任务控制块。</p></li>
</ul>
</div>
</div>
</article>
<footer>
<div class="related-pages">
<a class="next-page" href="6multitasking-based-on-as.html">
<div class="page-info">
<div class="context">
<span>Next</span>
</div>
<div class="title">基于地址空间的分时多任务</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="4sv39-implementation-2.html">
<svg><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
</div>
<div class="title">实现 SV39 多级页表机制(下)</div>
</div>
</a>
</div>
<div class="related-information">
Copyright &#169; OS2022Summer
|
Built with <a href="https://www.sphinx-doc.org/">Sphinx</a>
and
<a class="muted-link" href="https://pradyunsg.me">@pradyunsg</a>'s
<a href="https://github.com/pradyunsg/furo">Furo theme</a>.
|
<a class="muted-link" href="../_sources/chapter4/5kernel-app-spaces.rst.txt"
rel="nofollow">
显示源代码
</a>
</div>
</footer>
</div>
<aside class="toc-drawer">
<div class="toc-sticky toc-scroll">
<div class="toc-title-container">
<span class="toc-title">
目录
</span>
</div>
<div class="toc-tree-container">
<div class="toc-tree">
<ul>
<li><a class="reference internal" href="#">内核与应用的地址空间</a><ul>
<li><a class="reference internal" href="#id2">实现地址空间抽象</a><ul>
<li><a class="reference internal" href="#id3">逻辑段:一段连续地址的虚拟内存</a></li>
<li><a class="reference internal" href="#id4">地址空间:一系列有关联的逻辑段</a></li>
</ul>
</li>
<li><a class="reference internal" href="#id5">内核地址空间</a></li>
<li><a class="reference internal" href="#id6">应用地址空间</a></li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</aside>
</div>
</div><script data-url_root="../" id="documentation_options" src="../_static/documentation_options.js"></script>
<script src="../_static/jquery.js"></script>
<script src="../_static/underscore.js"></script>
<script src="../_static/doctools.js"></script>
<script src="../_static/scripts/main.js"></script>
<script kind="utterances">
var commentsRunWhenDOMLoaded = cb => {
if (document.readyState != 'loading') {
cb()
} else if (document.addEventListener) {
document.addEventListener('DOMContentLoaded', cb)
} else {
document.attachEvent('onreadystatechange', function() {
if (document.readyState == 'complete') cb()
})
}
}
var addUtterances = () => {
var script = document.createElement("script");
script.type = "text/javascript";
script.src = "https://utteranc.es/client.js";
script.async = "async";
script.setAttribute("repo", "LearningOS/rust-based-os-comp2022");
script.setAttribute("issue-term", "pathname");
script.setAttribute("theme", "github-light");
script.setAttribute("label", "comments");
script.setAttribute("crossorigin", "anonymous");
sections = document.querySelectorAll("div.section");
if (sections !== null) {
section = sections[sections.length-1];
section.appendChild(script);
}
}
commentsRunWhenDOMLoaded(addUtterances);
</script>
<script src="../_static/translations.js"></script>
<script async="async" src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>
</body>
</html>