Files
rust-based-os-comp2022/chapter4/6multitasking-based-on-as.html
2022-06-30 04:46:48 +00:00

1015 lines
126 KiB
HTML
Raw 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="chapter4练习" href="7exercise.html" /><link rel="prev" title="内核与应用的地址空间" href="5kernel-app-spaces.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"><a class="reference internal" href="5kernel-app-spaces.html">内核与应用的地址空间</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="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>
<p>当 SBI 实现(本项目中基于 RustSBI初始化完成后 CPU 将跳转到内核入口点并在 S 特权级上执行,此时还并没有开启分页模式
,内核的每一次访存仍被视为一个物理地址直接访问物理内存。而在开启分页模式之后,内核的代码在访存的时候只能看到内核地址空间,
此时每次访存将被视为一个虚拟地址且需要通过 MMU 基于内核地址空间的多级页表的地址转换。这两种模式之间的过渡在内核初始化期间
完成。</p>
<div class="section" id="id3">
<h3>创建内核地址空间<a class="headerlink" href="#id3" title="永久链接至标题"></a></h3>
<p>我们创建内核地址空间的全局实例:</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">lazy_static</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">static</span><span class="w"> </span><span class="k">ref</span><span class="w"> </span><span class="n">KERNEL_SPACE</span>: <span class="nc">Arc</span><span class="o">&lt;</span><span class="n">UPSafeCell</span><span class="o">&lt;</span><span class="n">MemorySet</span><span class="o">&gt;&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Arc</span>::<span class="n">new</span><span class="p">(</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">UPSafeCell</span>::<span class="n">new</span><span class="p">(</span><span class="n">MemorySet</span>::<span class="n">new_kernel</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">lazy_static!</span></code> 宏的介绍可知, <code class="docutils literal notranslate"><span class="pre">KERNEL_SPACE</span></code> 在运行期间它第一次被用到时才会实际进行初始化,而它所
占据的空间则是编译期被放在全局数据段中。 <code class="docutils literal notranslate"><span class="pre">Arc&lt;UPSafeCell&lt;_&gt;&gt;</span></code> 同时带来 <code class="docutils literal notranslate"><span class="pre">Arc&lt;T&gt;</span></code> 提供的共享
引用,和 <code class="docutils literal notranslate"><span class="pre">UPSafeCell&lt;T&gt;</span></code> 提供的互斥访问。</p>
<p><code class="docutils literal notranslate"><span class="pre">rust_main</span></code> 函数中,我们首先调用 <code class="docutils literal notranslate"><span class="pre">mm::init</span></code> 进行内存管理子系统的初始化:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/mm/mod.rs</span>
<span class="k">pub</span><span class="w"> </span><span class="k">use</span><span class="w"> </span><span class="n">memory_set</span>::<span class="n">KERNEL_SPACE</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">init</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">heap_allocator</span>::<span class="n">init_heap</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="n">frame_allocator</span>::<span class="n">init_frame_allocator</span><span class="p">();</span><span class="w"></span>
<span class="w"> </span><span class="n">KERNEL_SPACE</span><span class="p">.</span><span class="n">exclusive_access</span><span class="p">().</span><span class="n">activate</span><span class="p">();</span><span class="w"></span>
<span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>可以看到,我们最先进行了全局动态内存分配器的初始化,因为接下来马上就要用到 Rust 的堆数据结构。接下来我们初始化物理页帧
管理器(内含堆数据结构 <code class="docutils literal notranslate"><span class="pre">Vec&lt;T&gt;</span></code> )使能可用物理页帧的分配和回收能力。最后我们创建内核地址空间并让 CPU 开启分页模式,
MMU 在地址转换的时候使用内核的多级页表,这一切均在一行之内做到:</p>
<ul>
<li><p>首先,我们引用 <code class="docutils literal notranslate"><span class="pre">KERNEL_SPACE</span></code> ,这是它第一次被使用,就在此时它会被初始化,调用 <code class="docutils literal notranslate"><span class="pre">MemorySet::new_kernel</span></code>
创建一个内核地址空间并使用 <code class="docutils literal notranslate"><span class="pre">Arc&lt;UPSafeCell&lt;T&gt;&gt;</span></code> 包裹起来;</p></li>
<li><p>最然后,我们调用 <code class="docutils literal notranslate"><span class="pre">MemorySet::activate</span></code> </p>
<blockquote>
<div><div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/mm/page_table.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">token</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</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="linenos"> 4</span><span class="w"> </span><span class="mi">8</span><span class="k">usize</span><span class="w"> </span><span class="o">&lt;&lt;</span><span class="w"> </span><span class="mi">60</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">root_ppn</span><span class="p">.</span><span class="mi">0</span><span class="w"></span>
<span class="linenos"> 5</span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 6</span>
<span class="linenos"> 7</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 8</span>
<span class="linenos"> 9</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">10</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">activate</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</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="kd">let</span><span class="w"> </span><span class="n">satp</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">page_table</span><span class="p">.</span><span class="n">token</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="k">unsafe</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">satp</span>::<span class="n">write</span><span class="p">(</span><span class="n">satp</span><span class="p">);</span><span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="n">core</span>::<span class="n">arch</span>::<span class="fm">asm!</span><span class="p">(</span><span class="s">"sfence.vma"</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="p">}</span><span class="w"></span>
<span class="linenos">17</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
</div></blockquote>
<p><code class="docutils literal notranslate"><span class="pre">PageTable::token</span></code> 会按照 <a class="reference internal" href="3sv39-implementation-1.html#satp-layout"><span class="std std-ref">satp CSR 格式要求</span></a> 构造一个无符号 64 位无符号整数,使得其
分页模式为 SV39 ,且将当前多级页表的根节点所在的物理页号填充进去。在 <code class="docutils literal notranslate"><span class="pre">activate</span></code> 中,我们将这个值写入当前 CPU 的
satp CSR ,从这一刻开始 SV39 分页模式就被启用了,而且 MMU 会使用内核地址空间的多级页表进行地址转换。</p>
<p>我们必须注意切换 satp CSR 是否是一个 <em>平滑</em> 的过渡:其含义是指,切换 satp 的指令及其下一条指令这两条相邻的指令的
虚拟地址是相邻的(由于切换 satp 的指令并不是一条跳转指令, pc 只是简单的自增当前指令的字长),
而它们所在的物理地址一般情况下也是相邻的,但是它们所经过的地址转换流程却是不同的——切换 satp 导致 MMU 查的多级页表
是不同的。这就要求前后两个地址空间在切换 satp 的指令 <em>附近</em> 的映射满足某种意义上的连续性。</p>
<p>幸运的是,我们做到了这一点。这条写入 satp 的指令及其下一条指令都在内核内存布局的代码段中,在切换之后是一个恒等映射,
而在切换之前是视为物理地址直接取指,也可以将其看成一个恒等映射。这完全符合我们的期待:即使切换了地址空间,指令仍应该
能够被连续的执行。</p>
</li>
</ul>
<p>注意到在 <code class="docutils literal notranslate"><span class="pre">activate</span></code> 的最后,我们插入了一条汇编指令 <code class="docutils literal notranslate"><span class="pre">sfence.vma</span></code> ,它又起到什么作用呢?</p>
<p>让我们再来回顾一下多级页表:它相比线性表虽然大量节约了内存占用,但是却需要 MMU 进行更多的隐式访存。如果是一个线性表,
MMU 仅需单次访存就能找到页表项并完成地址转换,而多级页表(以 SV39 为例,不考虑大页)最顺利的情况下也需要三次访存。这些
额外的访存和真正访问数据的那些访存在空间上并不相邻,加大了多级缓存的压力,一旦缓存缺失将带来巨大的性能惩罚。如果采用
多级页表实现,这个问题会变得更为严重,使得地址空间抽象的性能开销过大。</p>
<p id="term-tlb">为了解决性能问题,一种常见的做法是在 CPU 中利用部分硬件资源额外加入一个 <strong>快表</strong>
(TLB, Translation Lookaside Buffer) 它维护了部分虚拟页号到页表项的键值对。当 MMU 进行地址转换的时候,首先
会到快表中看看是否匹配,如果匹配的话直接取出页表项完成地址转换而无需访存;否则再去查页表并将键值对保存在快表中。一旦
我们修改了 satp 切换了地址空间,快表中的键值对就会失效,因为它还表示着上个地址空间的映射关系。为了 MMU 的地址转换
能够及时与 satp 的修改同步,我们可以选择立即使用 <code class="docutils literal notranslate"><span class="pre">sfence.vma</span></code> 指令将快表清空,这样 MMU 就不会看到快表中已经
过期的键值对了。</p>
</div>
</div>
<div class="section" id="term-trampoline">
<span id="id4"></span><h2>跳板的实现<a class="headerlink" href="#term-trampoline" title="永久链接至标题"></a></h2>
<p>上一小节我们看到无论是内核还是应用的地址空间,最高的虚拟页面都是一个跳板。同时应用地址空间的次高虚拟页面还被设置为用来
存放应用的 Trap 上下文。那么跳板究竟起什么作用呢?为何不直接把 Trap 上下文仍放到应用的内核栈中呢?</p>
<p>回忆曾在第二章介绍过的,当一个应用 Trap 到内核的时候,
<code class="docutils literal notranslate"><span class="pre">sscratch</span></code> 已经指出了该应用内核栈的栈顶,我们用一条指令即可从用户栈切换到内核栈,然后直接将 Trap 上下文压入内核栈
栈顶。当 Trap 处理完毕返回用户态的时候,将 Trap 上下文中的内容恢复到寄存器上,最后将保存着应用用户栈顶的 <code class="docutils literal notranslate"><span class="pre">sscratch</span></code>
与 sp 进行交换,也就从内核栈切换回了用户栈。在这个过程中, <code class="docutils literal notranslate"><span class="pre">sscratch</span></code> 起到了非常关键的作用,它使得我们可以在不破坏
任何通用寄存器的情况下完成用户栈和内核栈顶的 Trap 上下文这两个工作区域之间的切换。</p>
<p>然而,一旦使能了分页机制,一切就并没有这么简单了,我们必须在这个过程中同时完成地址空间的切换。
具体来说,当 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 保存 Trap 上下文的时候,我们必须通过修改 satp 从应用地址空间切换到内核地址空间,
因为 trap handler 只有在内核地址空间中才能访问;
同理,在 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 恢复 Trap 上下文的时候,我们也必须从内核地址空间切换回应用地址空间,因为应用的代码和
数据只能在它自己的地址空间中才能访问,内核地址空间是看不到的。
进而,地址空间的切换不能影响指令的连续执行,这就要求应用和内核地址空间在切换地址空间指令附近是平滑的。</p>
<div class="admonition note" id="term-meltdown">
<p class="admonition-title">注解</p>
<p><strong>内核与应用地址空间的隔离</strong></p>
<p>目前我们的设计是有一个唯一的内核地址空间存放内核的代码、数据,同时对于每个应用维护一个它们自己的地址空间,因此在
Trap 的时候就需要进行地址空间切换,而在任务切换的时候无需进行(因为这个过程全程在内核内完成)。而教程前两版以及
<span class="math notranslate nohighlight">\(\mu\)</span> core 中的设计是每个应用都有一个地址空间,可以将其中的逻辑段分为内核和用户两部分,分别映射到内核和
用户的数据和代码,且分别在 CPU 处于 S/U 特权级时访问。此设计中并不存在一个单独的内核地址空间。</p>
<p>之前设计方式的优点在于: Trap 的时候无需切换地址空间,而在任务切换的时候才需要切换地址空间。由于后者比前者更容易
实现,这降低了实现的复杂度。而且在应用高频进行系统调用的时候能够避免地址空间切换的开销,这通常源于快表或 cache
的失效问题。但是这种设计方式也有缺点:即内核的逻辑段需要在每个应用的地址空间内都映射一次,这会带来一些无法忽略的
内存占用开销,并显著限制了嵌入式平台的任务并发数。此外,这种做法无法应对处理器的 <a class="reference external" href="https://cacm.acm.org/magazines/2020/6/245161-meltdown/fulltext">熔断
(Meltdown) 漏洞</a>
使得恶意应用能够以某种方式看到它本来无权访问的地址空间中内核部分的数据。将内核与地址空间隔离便是修复此漏洞的一种方法。</p>
<p>经过权衡,在本教程中我们参考 MIT 的教学 OS <a class="reference external" href="https://github.com/mit-pdos/xv6-riscv">xv6</a>
采用内核和应用地址空间隔离的设计。</p>
</div>
<p>我们为何将应用的 Trap 上下文放到应用地址空间的次高页面而不是内核地址空间中的内核栈中呢?原因在于,假如我们将其放在内核栈
中,在保存 Trap 上下文之前我们必须先切换到内核地址空间,这就需要我们将内核地址空间的 token 写入 satp 寄存器,之后我们
还需要有一个通用寄存器保存内核栈栈顶的位置,这样才能以它为基址保存 Trap 上下文。在保存 Trap 上下文之前我们必须完成这
两项工作。然而,我们无法在不破坏任何一个通用寄存器的情况下做到这一点。因为事实上我们需要用到内核的两条信息:内核地址空间
的 token 还有应用内核栈顶的位置,硬件却只提供一个 <code class="docutils literal notranslate"><span class="pre">sscratch</span></code> 可以用来进行周转。所以,我们不得不将 Trap 上下文保存在
应用地址空间的一个虚拟页面中以避免切换到内核地址空间才能保存。</p>
<p>为了方便实现,我们在 Trap 上下文中包含更多内容(和我们关于上下文的定义有些不同,它们在初始化之后便只会被读取而不会被写入
,并不是每次都需要保存/恢复):</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/trap/context.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="cp">#[repr(C)]</span><span class="w"></span>
<span class="linenos"> 4</span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">TrapContext</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">x</span>: <span class="p">[</span><span class="kt">usize</span><span class="p">;</span><span class="w"> </span><span class="mi">32</span><span class="p">],</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">sstatus</span>: <span class="nc">Sstatus</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">sepc</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
<span class="hll"><span class="linenos"> 8</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">kernel_satp</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos"> 9</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">kernel_sp</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">10</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">trap_handler</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="linenos">11</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>在多出的三个字段中:</p>
<ul class="simple">
<li><p><code class="docutils literal notranslate"><span class="pre">kernel_satp</span></code> 表示内核地址空间的 token </p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">kernel_sp</span></code> 表示当前应用在内核地址空间中的内核栈栈顶的虚拟地址;</p></li>
<li><p><code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 表示内核中 trap handler 入口点的虚拟地址。</p></li>
</ul>
<p>它们在应用初始化的时候由内核写入应用地址空间中的 TrapContext 的相应位置,此后就不再被修改。</p>
<p>让我们来看一下现在的 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code><code class="docutils literal notranslate"><span class="pre">__restore</span></code> 各是如何在保存和恢复 Trap 上下文的同时也切换地址空间的:</p>
<div class="highlight-riscv notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c"># os/src/trap/trap.S</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="w"> </span><span class="nf">.section</span><span class="w"> </span><span class="nf">.text.trampoline</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="nf">.globl</span><span class="w"> </span><span class="nb">__alltraps</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="nf">.globl</span><span class="w"> </span><span class="nb">__restore</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="nf">.align</span><span class="w"> </span><span class="m">2</span><span class="w"></span>
<span class="linenos"> 7</span><span class="nb">__alltraps:</span><span class="w"></span>
<span class="linenos"> 8</span><span class="w"> </span><span class="nd">csrrw</span><span class="w"> </span><span class="na">sp</span>,<span class="w"> </span><span class="no">sscratch</span>,<span class="w"> </span><span class="na">sp</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="c"># now sp-&gt;*TrapContext in user space, sscratch-&gt;user stack</span>
<span class="linenos">10</span><span class="w"> </span><span class="c"># save other general purpose registers</span>
<span class="linenos">11</span><span class="w"> </span><span class="nd">sd</span><span class="w"> </span><span class="na">x1</span>,<span class="w"> </span><span class="m">1</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="c"># skip sp(x2), we will save it later</span>
<span class="linenos">13</span><span class="w"> </span><span class="nd">sd</span><span class="w"> </span><span class="na">x3</span>,<span class="w"> </span><span class="m">3</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">14</span><span class="w"> </span><span class="c"># skip tp(x4), application does not use it</span>
<span class="linenos">15</span><span class="w"> </span><span class="c"># save x5~x31</span>
<span class="linenos">16</span><span class="w"> </span><span class="nf">.set</span><span class="w"> </span><span class="ni">n</span>,<span class="w"> </span><span class="m">5</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="nf">.rept</span><span class="w"> </span><span class="m">27</span><span class="w"></span>
<span class="linenos">18</span><span class="w"> </span><span class="nb">SAVE_GP</span><span class="w"> </span>%<span class="ni">n</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="nf">.set</span><span class="w"> </span><span class="ni">n</span>,<span class="w"> </span><span class="ni">n</span>+<span class="m">1</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="nf">.endr</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="c"># we can use t0/t1/t2 freely, because they have been saved in TrapContext</span>
<span class="linenos">22</span><span class="w"> </span><span class="nd">csrr</span><span class="w"> </span><span class="na">t0</span>,<span class="w"> </span><span class="no">sstatus</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="nd">csrr</span><span class="w"> </span><span class="na">t1</span>,<span class="w"> </span><span class="no">sepc</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="nd">sd</span><span class="w"> </span><span class="na">t0</span>,<span class="w"> </span><span class="m">32</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="nd">sd</span><span class="w"> </span><span class="na">t1</span>,<span class="w"> </span><span class="m">33</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="c"># read user stack from sscratch and save it in TrapContext</span>
<span class="linenos">27</span><span class="w"> </span><span class="nd">csrr</span><span class="w"> </span><span class="na">t2</span>,<span class="w"> </span><span class="no">sscratch</span><span class="w"></span>
<span class="linenos">28</span><span class="w"> </span><span class="nd">sd</span><span class="w"> </span><span class="na">t2</span>,<span class="w"> </span><span class="m">2</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">29</span><span class="w"> </span><span class="c"># load kernel_satp into t0</span>
<span class="linenos">30</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">t0</span>,<span class="w"> </span><span class="m">34</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">31</span><span class="w"> </span><span class="c"># load trap_handler into t1</span>
<span class="linenos">32</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">t1</span>,<span class="w"> </span><span class="m">36</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">33</span><span class="w"> </span><span class="c"># move to kernel_sp</span>
<span class="linenos">34</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">sp</span>,<span class="w"> </span><span class="m">35</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">35</span><span class="w"> </span><span class="c"># switch to kernel space</span>
<span class="linenos">36</span><span class="w"> </span><span class="nd">csrw</span><span class="w"> </span><span class="no">satp</span>,<span class="w"> </span><span class="na">t0</span><span class="w"></span>
<span class="linenos">37</span><span class="w"> </span><span class="nd">sfence.vma</span><span class="w"></span>
<span class="linenos">38</span><span class="w"> </span><span class="c"># jump to trap_handler</span>
<span class="linenos">39</span><span class="w"> </span><span class="nd">jr</span><span class="w"> </span><span class="na">t1</span><span class="w"></span>
<span class="linenos">40</span>
<span class="linenos">41</span><span class="nb">__restore:</span><span class="w"></span>
<span class="linenos">42</span><span class="w"> </span><span class="c"># a0: *TrapContext in user space(Constant); a1: user space token</span>
<span class="linenos">43</span><span class="w"> </span><span class="c"># switch to user space</span>
<span class="linenos">44</span><span class="w"> </span><span class="nd">csrw</span><span class="w"> </span><span class="no">satp</span>,<span class="w"> </span><span class="na">a1</span><span class="w"></span>
<span class="linenos">45</span><span class="w"> </span><span class="nd">sfence.vma</span><span class="w"></span>
<span class="linenos">46</span><span class="w"> </span><span class="nd">csrw</span><span class="w"> </span><span class="no">sscratch</span>,<span class="w"> </span><span class="na">a0</span><span class="w"></span>
<span class="linenos">47</span><span class="w"> </span><span class="nd">mv</span><span class="w"> </span><span class="na">sp</span>,<span class="w"> </span><span class="na">a0</span><span class="w"></span>
<span class="linenos">48</span><span class="w"> </span><span class="c"># now sp points to TrapContext in user space, start restoring based on it</span>
<span class="linenos">49</span><span class="w"> </span><span class="c"># restore sstatus/sepc</span>
<span class="linenos">50</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">t0</span>,<span class="w"> </span><span class="m">32</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">51</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">t1</span>,<span class="w"> </span><span class="m">33</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">52</span><span class="w"> </span><span class="nd">csrw</span><span class="w"> </span><span class="no">sstatus</span>,<span class="w"> </span><span class="na">t0</span><span class="w"></span>
<span class="linenos">53</span><span class="w"> </span><span class="nd">csrw</span><span class="w"> </span><span class="no">sepc</span>,<span class="w"> </span><span class="na">t1</span><span class="w"></span>
<span class="linenos">54</span><span class="w"> </span><span class="c"># restore general purpose registers except x0/sp/tp</span>
<span class="linenos">55</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">x1</span>,<span class="w"> </span><span class="m">1</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">56</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">x3</span>,<span class="w"> </span><span class="m">3</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">57</span><span class="w"> </span><span class="nf">.set</span><span class="w"> </span><span class="ni">n</span>,<span class="w"> </span><span class="m">5</span><span class="w"></span>
<span class="linenos">58</span><span class="w"> </span><span class="nf">.rept</span><span class="w"> </span><span class="m">27</span><span class="w"></span>
<span class="linenos">59</span><span class="w"> </span><span class="nb">LOAD_GP</span><span class="w"> </span>%<span class="ni">n</span><span class="w"></span>
<span class="linenos">60</span><span class="w"> </span><span class="nf">.set</span><span class="w"> </span><span class="ni">n</span>,<span class="w"> </span><span class="ni">n</span>+<span class="m">1</span><span class="w"></span>
<span class="linenos">61</span><span class="w"> </span><span class="nf">.endr</span><span class="w"></span>
<span class="linenos">62</span><span class="w"> </span><span class="c"># back to user stack</span>
<span class="linenos">63</span><span class="w"> </span><span class="nd">ld</span><span class="w"> </span><span class="na">sp</span>,<span class="w"> </span><span class="m">2</span>*<span class="m">8</span>(<span class="na">sp</span>)<span class="w"></span>
<span class="linenos">64</span><span class="w"> </span><span class="nd">sret</span><span class="w"></span>
</pre></div>
</div>
<ul class="simple">
<li><p>当应用 Trap 进入内核的时候,硬件会设置一些 CSR 并在 S 特权级下跳转到 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 保存 Trap 上下文。此时
sp 寄存器仍指向用户栈,但 <code class="docutils literal notranslate"><span class="pre">sscratch</span></code> 则被设置为指向应用地址空间中存放 Trap 上下文的位置,实际在次高页面。
随后,就像之前一样,我们 <code class="docutils literal notranslate"><span class="pre">csrrw</span></code> 交换 sp 和 <code class="docutils literal notranslate"><span class="pre">sscratch</span></code> ,并基于指向 Trap 上下文位置的 sp 开始保存通用
寄存器和一些 CSR ,这个过程在第 28 行结束。到这里,我们就全程在应用地址空间中完成了保存 Trap 上下文的工作。</p></li>
<li><p>接下来该考虑切换到内核地址空间并跳转到 trap handler 了。第 30 行我们将内核地址空间的 token 载入到 t0 寄存器中,
第 32 行我们将 trap handler 入口点的虚拟地址载入到 t1 寄存器中,第 34 行我们直接将 sp 修改为应用内核栈顶的地址。
这三条信息均是内核在初始化该应用的时候就已经设置好的。第 36~37 行我们将 satp 修改为内核地址空间的 token 并使用
<code class="docutils literal notranslate"><span class="pre">sfence.vma</span></code> 刷新快表,这就切换到了内核地址空间。最后在第 39 行我们通过 <code class="docutils literal notranslate"><span class="pre">jr</span></code> 指令跳转到 t1 寄存器所保存的
trap handler 入口点的地址。注意这里我们不能像之前的章节那样直接 <code class="docutils literal notranslate"><span class="pre">call</span> <span class="pre">trap_handler</span></code> ,原因稍后解释。</p></li>
<li><p>当内核将 Trap 处理完毕准备返回用户态的时候会 <em>调用</em> <code class="docutils literal notranslate"><span class="pre">__restore</span></code> ,它有两个参数:第一个是 Trap 上下文在应用
地址空间中的位置,这个对于所有的应用来说都是相同的,由调用规范在 a0 寄存器中传递;第二个则是即将回到的应用的地址空间
的 token ,在 a1 寄存器中传递。由于 Trap 上下文是保存在应用地址空间中的,第 44~45 行我们先切换回应用地址空间。第
46 行我们将传入的 Trap 上下文位置保存在 <code class="docutils literal notranslate"><span class="pre">sscratch</span></code> 寄存器中,这样 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 中才能基于它将 Trap 上下文
保存到正确的位置。第 47 行我们将 sp 修改为 Trap 上下文的位置,后面基于它恢复各通用寄存器和 CSR。最后在第 64 行,
我们通过 <code class="docutils literal notranslate"><span class="pre">sret</span></code> 指令返回用户态。</p></li>
</ul>
<p>接下来还需要考虑切换地址空间前后指令能否仍能连续执行。可以看到我们将 <code class="docutils literal notranslate"><span class="pre">trap.S</span></code> 中的整段汇编代码放置在
<code class="docutils literal notranslate"><span class="pre">.text.trampoline</span></code> 段,并在调整内存布局的时候将它对齐到代码段的一个页面中:</p>
<div class="highlight-diff notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span># os/src/linker.ld
<span class="linenos"> 2</span>
<span class="linenos"> 3</span> stext = .;
<span class="linenos"> 4</span> .text : {
<span class="linenos"> 5</span> *(.text.entry)
<span class="linenos"> 6</span><span class="gi">+ . = ALIGN(4K);</span>
<span class="linenos"> 7</span><span class="gi">+ strampoline = .;</span>
<span class="linenos"> 8</span><span class="gi">+ *(.text.trampoline);</span>
<span class="linenos"> 9</span><span class="gi">+ . = ALIGN(4K);</span>
<span class="linenos">10</span> *(.text .text.*)
<span class="linenos">11</span> }
</pre></div>
</div>
<p>这样,这段汇编代码放在一个物理页帧中,且 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 恰好位于这个物理页帧的开头,其物理地址被外部符号
<code class="docutils literal notranslate"><span class="pre">strampoline</span></code> 标记。在开启分页模式之后,内核和应用代码都只能看到各自的虚拟地址空间,而在它们的视角中,这段汇编代码
被放在它们地址空间的最高虚拟页面上,由于这段汇编代码在执行的时候涉及到地址空间切换,故而被称为跳板页面。</p>
<p>那么在产生trap前后的一小段时间内会有一个比较 <strong>极端</strong> 的情况即刚产生trap时CPU已经进入了内核态即Supervisor Mode
但此时执行代码和访问数据还是在应用程序所处的用户态虚拟地址空间中而不是我们通常理解的内核虚拟地址空间。在这段特殊的时间内CPU指令
为什么能够被连续执行呢?这里需要注意:无论是内核还是应用的地址空间,跳板的虚拟页均位于同样位置,且它们也将会映射到同一个实际存放这段
汇编代码的物理页帧。也就是说,在执行 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code><code class="docutils literal notranslate"><span class="pre">__restore</span></code> 函数进行地址空间切换的时候,
应用的用户态虚拟地址空间和操作系统内核的内核态虚拟地址空间对切换地址空间的指令所在页的映射方式均是相同的,
这就说明了这段切换地址空间的指令控制流仍是可以连续执行的。</p>
<p>现在可以说明我们在创建用户/内核地址空间中用到的 <code class="docutils literal notranslate"><span class="pre">map_trampoline</span></code> 是如何实现的了:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/config.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">pub</span><span class="w"> </span><span class="k">const</span><span class="w"> </span><span class="n">TRAMPOLINE</span>: <span class="kt">usize</span> <span class="o">=</span><span class="w"> </span><span class="kt">usize</span>::<span class="n">MAX</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">PAGE_SIZE</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="linenos"> 4</span>
<span class="linenos"> 5</span><span class="c1">// os/src/mm/memory_set.rs</span>
<span class="linenos"> 6</span>
<span class="linenos"> 7</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"> 8</span><span class="w"> </span><span class="sd">/// Mention that trampoline is not collected by areas.</span>
<span class="linenos"> 9</span><span class="w"> </span><span class="k">fn</span> <span class="nf">map_trampoline</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="p">{</span><span class="w"></span>
<span class="linenos">10</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="n">map</span><span class="p">(</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="n">VirtAddr</span>::<span class="n">from</span><span class="p">(</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">12</span><span class="w"> </span><span class="n">PhysAddr</span>::<span class="n">from</span><span class="p">(</span><span class="n">strampoline</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">13</span><span class="w"> </span><span class="n">PTEFlags</span>::<span class="n">R</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">PTEFlags</span>::<span class="n">X</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="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>这里我们为了实现方便并没有新增逻辑段 <code class="docutils literal notranslate"><span class="pre">MemoryArea</span></code> 而是直接在多级页表中插入一个从地址空间的最高虚拟页面映射到
跳板汇编代码所在的物理页帧的键值对,访问方式限制与代码段相同,即 RX 。</p>
<p>最后可以解释为何我们在 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 中需要借助寄存器 <code class="docutils literal notranslate"><span class="pre">jr</span></code> 而不能直接 <code class="docutils literal notranslate"><span class="pre">call</span> <span class="pre">trap_handler</span></code> 了。因为在
内存布局中,这条 <code class="docutils literal notranslate"><span class="pre">.text.trampoline</span></code> 段中的跳转指令和 <code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 都在代码段之内汇编器Assembler
和链接器Linker会根据 <code class="docutils literal notranslate"><span class="pre">linker.ld</span></code> 的地址布局描述,设定电子指令的地址,并计算二者地址偏移量
并让跳转指令的实际效果为当前 pc 自增这个偏移量。但实际上我们知道由于我们设计的缘故,这条跳转指令在被执行的时候,
它的虚拟地址被操作系统内核设置在地址空间中的最高页面之内,加上这个偏移量并不能正确的得到 <code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 的入口地址。</p>
<p><strong>问题的本质可以概括为:跳转指令实际被执行时的虚拟地址和在编译器/汇编器/链接器进行后端代码生成和链接形成最终机器码时设置此指令的地址是不同的。</strong></p>
</div>
<div class="section" id="id5">
<h2>加载和执行应用程序<a class="headerlink" href="#id5" title="永久链接至标题"></a></h2>
<div class="section" id="id6">
<h3>扩展任务控制块<a class="headerlink" href="#id6" title="永久链接至标题"></a></h3>
<p>为了让应用在运行时有一个安全隔离且符合编译器给应用设定的地址空间布局的虚拟地址空间,操作系统需要对任务进行更多的管理,所以任务控制块相比第三章也包含了更多内容:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos">1</span><span class="c1">// os/src/task/task.rs</span>
<span class="linenos">2</span>
<span class="linenos">3</span><span class="k">pub</span><span class="w"> </span><span class="k">struct</span> <span class="nc">TaskControlBlock</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="n">task_status</span>: <span class="nc">TaskStatus</span><span class="p">,</span><span class="w"></span>
<span class="linenos">5</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">task_cx</span>: <span class="nc">TaskContext</span><span class="p">,</span><span class="w"></span>
<span class="hll"><span class="linenos">6</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">memory_set</span>: <span class="nc">MemorySet</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">7</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">trap_cx_ppn</span>: <span class="nc">PhysPageNum</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">8</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="n">base_size</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="linenos">9</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>除了应用的地址空间 <code class="docutils literal notranslate"><span class="pre">memory_set</span></code> 之外,还有位于应用地址空间次高页的 Trap 上下文被实际存放在物理页帧的物理页号
<code class="docutils literal notranslate"><span class="pre">trap_cx_ppn</span></code> ,它能够方便我们对于 Trap 上下文进行访问。此外, <code class="docutils literal notranslate"><span class="pre">base_size</span></code> 统计了应用数据的大小,也就是
在应用地址空间中从 <span class="math notranslate nohighlight">\(\text{0x0}\)</span> 开始到用户栈结束一共包含多少字节。它后续还应该包含用于应用动态内存分配的
堆空间的大小,但我们暂不支持。</p>
</div>
<div class="section" id="id7">
<h3>更新对任务控制块的管理<a class="headerlink" href="#id7" title="永久链接至标题"></a></h3>
<p>下面是任务控制块的创建:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/config.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="sd">/// Return (bottom, top) of a kernel stack in kernel space.</span>
<span class="linenos"> 4</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">kernel_stack_position</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="p">(</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"> 5</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">top</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TRAMPOLINE</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">app_id</span><span class="w"> </span><span class="o">*</span><span class="w"> </span><span class="p">(</span><span class="n">KERNEL_STACK_SIZE</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"> 6</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">bottom</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">top</span><span class="w"> </span><span class="o">-</span><span class="w"> </span><span class="n">KERNEL_STACK_SIZE</span><span class="p">;</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="p">(</span><span class="n">bottom</span><span class="p">,</span><span class="w"> </span><span class="n">top</span><span class="p">)</span><span class="w"></span>
<span class="linenos"> 8</span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 9</span>
<span class="linenos">10</span><span class="c1">// os/src/task/task.rs</span>
<span class="linenos">11</span>
<span class="linenos">12</span><span class="k">impl</span><span class="w"> </span><span class="n">TaskControlBlock</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">13</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="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><span class="n">app_id</span>: <span class="kt">usize</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">14</span><span class="w"> </span><span class="c1">// memory_set with elf program headers/trampoline/trap context/user stack</span>
<span class="linenos">15</span><span class="w"> </span><span class="kd">let</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_sp</span><span class="p">,</span><span class="w"> </span><span class="n">entry_point</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">MemorySet</span>::<span class="n">from_elf</span><span class="p">(</span><span class="n">elf_data</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">trap_cx_ppn</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">memory_set</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="p">.</span><span class="n">translate</span><span class="p">(</span><span class="n">VirtAddr</span>::<span class="n">from</span><span class="p">(</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">18</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">19</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">20</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">task_status</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TaskStatus</span>::<span class="n">Ready</span><span class="p">;</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="c1">// map a kernel-stack in kernel space</span>
<span class="linenos">22</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="p">(</span><span class="n">kernel_stack_bottom</span><span class="p">,</span><span class="w"> </span><span class="n">kernel_stack_top</span><span class="p">)</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">kernel_stack_position</span><span class="p">(</span><span class="n">app_id</span><span class="p">);</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="n">KERNEL_SPACE</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="p">.</span><span class="n">exclusive_access</span><span class="p">()</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="p">.</span><span class="n">insert_framed_area</span><span class="p">(</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="n">kernel_stack_bottom</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">27</span><span class="w"> </span><span class="n">kernel_stack_top</span><span class="p">.</span><span class="n">into</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">28</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">29</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="linenos">30</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">task_control_block</span><span class="w"> </span><span class="o">=</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">31</span><span class="w"> </span><span class="n">task_status</span><span class="p">,</span><span class="w"></span>
<span class="linenos">32</span><span class="w"> </span><span class="n">task_cx</span>: <span class="nc">TaskContext</span>::<span class="n">goto_trap_return</span><span class="p">(</span><span class="n">kernel_stack_top</span><span class="p">),</span><span class="w"></span>
<span class="linenos">33</span><span class="w"> </span><span class="n">memory_set</span><span class="p">,</span><span class="w"></span>
<span class="linenos">34</span><span class="w"> </span><span class="n">trap_cx_ppn</span><span class="p">,</span><span class="w"></span>
<span class="linenos">35</span><span class="w"> </span><span class="n">base_size</span>: <span class="nc">user_sp</span><span class="p">,</span><span class="w"></span>
<span class="linenos">36</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="linenos">37</span><span class="w"> </span><span class="c1">// prepare TrapContext in user space</span>
<span class="linenos">38</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">trap_cx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">task_control_block</span><span class="p">.</span><span class="n">get_trap_cx</span><span class="p">();</span><span class="w"></span>
<span class="linenos">39</span><span class="w"> </span><span class="o">*</span><span class="n">trap_cx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TrapContext</span>::<span class="n">app_init_context</span><span class="p">(</span><span class="w"></span>
<span class="linenos">40</span><span class="w"> </span><span class="n">entry_point</span><span class="p">,</span><span class="w"></span>
<span class="linenos">41</span><span class="w"> </span><span class="n">user_sp</span><span class="p">,</span><span class="w"></span>
<span class="linenos">42</span><span class="w"> </span><span class="n">KERNEL_SPACE</span><span class="p">.</span><span class="n">exclusive_access</span><span class="p">().</span><span class="n">token</span><span class="p">(),</span><span class="w"></span>
<span class="linenos">43</span><span class="w"> </span><span class="n">kernel_stack_top</span><span class="p">,</span><span class="w"></span>
<span class="linenos">44</span><span class="w"> </span><span class="n">trap_handler</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">45</span><span class="w"> </span><span class="p">);</span><span class="w"></span>
<span class="linenos">46</span><span class="w"> </span><span class="n">task_control_block</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="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul class="simple">
<li><p>第 15 行,我们解析传入的 ELF 格式数据构造应用的地址空间 <code class="docutils literal notranslate"><span class="pre">memory_set</span></code> 并获得其他信息;</p></li>
<li><p>第 16 行,我们从地址空间 <code class="docutils literal notranslate"><span class="pre">memory_set</span></code> 中查多级页表找到应用地址空间中的 Trap 上下文实际被放在哪个物理页帧;</p></li>
<li><p>第 22 行,我们根据传入的应用 ID <code class="docutils literal notranslate"><span class="pre">app_id</span></code> 调用在 <code class="docutils literal notranslate"><span class="pre">config</span></code> 子模块中定义的 <code class="docutils literal notranslate"><span class="pre">kernel_stack_position</span></code> 找到
应用的内核栈预计放在内核地址空间 <code class="docutils literal notranslate"><span class="pre">KERNEL_SPACE</span></code> 中的哪个位置,并通过 <code class="docutils literal notranslate"><span class="pre">insert_framed_area</span></code> 实际将这个逻辑段
加入到内核地址空间中;</p></li>
</ul>
<ul id="trap-return-intro">
<li><p>我们在应用的内核栈顶压入一个跳转到 <code class="docutils literal notranslate"><span class="pre">trap_return</span></code> 而不是 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 的任务上下文,
这主要是为了能够支持对该应用的启动并顺利切换到用户地址空间执行。在构造方式上,只是将 ra 寄存器的值设置为
<code class="docutils literal notranslate"><span class="pre">trap_return</span></code> 的地址。 <code class="docutils literal notranslate"><span class="pre">trap_return</span></code> 是我们后面要介绍的新版的 Trap 处理的一部分。</p></li>
<li><p>初始化该应用的 Trap 上下文,由于它是在应用地址空间而不是在内核地址空间中,我们只能手动查页表找到
Trap 上下文实际被放在的物理页帧,再获得在用户空间的 Trap 上下文的可变引用用于初始化:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/task/task.rs</span>
<span class="k">impl</span><span class="w"> </span><span class="n">TaskControlBlock</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">fn</span> <span class="nf">get_trap_cx</span><span class="p">(</span><span class="o">&amp;</span><span class="bp">self</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="nc">mut</span><span class="w"> </span><span class="n">TrapContext</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">trap_cx_ppn</span><span class="p">.</span><span class="n">get_mut</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">'static</span></code> 的可变引用和之前一样可以看成一个绕过 unsafe 的裸指针;而 <code class="docutils literal notranslate"><span class="pre">PhysPageNum::get_mut</span></code>
是一个泛型函数,由于我们已经声明了总体返回 <code class="docutils literal notranslate"><span class="pre">TrapContext</span></code> 的可变引用则Rust编译器会给 <code class="docutils literal notranslate"><span class="pre">get_mut</span></code> 泛型函数针对具体类型 <code class="docutils literal notranslate"><span class="pre">TrapContext</span></code>
的情况生成一个特定版本的 <code class="docutils literal notranslate"><span class="pre">get_mut</span></code> 函数实现。在 <code class="docutils literal notranslate"><span class="pre">get_trap_cx</span></code> 函数中则会静态调用``get_mut`` 泛型函数的特定版本实现。</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/trap/context.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">impl</span><span class="w"> </span><span class="n">TrapContext</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">set_sp</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">sp</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="bp">self</span><span class="p">.</span><span class="n">x</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sp</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="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">app_init_context</span><span class="p">(</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="n">entry</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 7</span><span class="w"> </span><span class="n">sp</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
<span class="hll"><span class="linenos"> 8</span><span class="w"> </span><span class="n">kernel_satp</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos"> 9</span><span class="w"> </span><span class="n">kernel_sp</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">10</span><span class="w"> </span><span class="n">trap_handler</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
</span><span class="linenos">11</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">12</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">sstatus</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sstatus</span>::<span class="n">read</span><span class="p">();</span><span class="w"></span>
<span class="linenos">13</span><span class="w"> </span><span class="n">sstatus</span><span class="p">.</span><span class="n">set_spp</span><span class="p">(</span><span class="n">SPP</span>::<span class="n">User</span><span class="p">);</span><span class="w"></span>
<span class="linenos">14</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">cx</span><span class="w"> </span><span class="o">=</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">15</span><span class="w"> </span><span class="n">x</span>: <span class="p">[</span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="mi">32</span><span class="p">],</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="n">sstatus</span><span class="p">,</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="n">sepc</span>: <span class="nc">entry</span><span class="p">,</span><span class="w"></span>
<span class="hll"><span class="linenos">18</span><span class="w"> </span><span class="n">kernel_satp</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">19</span><span class="w"> </span><span class="n">kernel_sp</span><span class="p">,</span><span class="w"></span>
</span><span class="hll"><span class="linenos">20</span><span class="w"> </span><span class="n">trap_handler</span><span class="p">,</span><span class="w"></span>
</span><span class="linenos">21</span><span class="w"> </span><span class="p">};</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="n">cx</span><span class="p">.</span><span class="n">set_sp</span><span class="p">(</span><span class="n">sp</span><span class="p">);</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="n">cx</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">25</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>和之前相比 <code class="docutils literal notranslate"><span class="pre">TrapContext::app_init_context</span></code> 需要补充上让应用在 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 能够顺利进入到内核地址空间
并跳转到 trap handler 入口点的相关信息。</p>
</li>
</ul>
<p>在内核初始化的时候,需要将所有的应用加载到全局应用管理器中:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/task/mod.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">struct</span> <span class="nc">TaskManagerInner</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="n">tasks</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">TaskControlBlock</span><span class="o">&gt;</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="n">current_task</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 6</span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 7</span>
<span class="linenos"> 8</span><span class="n">lazy_static</span><span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 9</span><span class="w"> </span><span class="k">pub</span><span class="w"> </span><span class="k">static</span><span class="w"> </span><span class="k">ref</span><span class="w"> </span><span class="n">TASK_MANAGER</span>: <span class="nc">TaskManager</span><span class="w"> </span><span class="o">=</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">info</span><span class="o">!</span><span class="p">(</span><span class="s">"init TASK_MANAGER"</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">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="linenos">12</span><span class="w"> </span><span class="n">info</span><span class="o">!</span><span class="p">(</span><span class="s">"num_app = {}"</span><span class="p">,</span><span class="w"> </span><span class="n">num_app</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="k">mut</span><span class="w"> </span><span class="n">tasks</span>: <span class="nb">Vec</span><span class="o">&lt;</span><span class="n">TaskControlBlock</span><span class="o">&gt;</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span>
<span class="linenos">14</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">num_app</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">15</span><span class="w"> </span><span class="n">tasks</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="n">TaskControlBlock</span>::<span class="n">new</span><span class="p">(</span><span class="n">get_app_data</span><span class="p">(</span><span class="n">i</span><span class="p">),</span><span class="w"> </span><span class="n">i</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="n">TaskManager</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">18</span><span class="w"> </span><span class="n">num_app</span><span class="p">,</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="n">inner</span>: <span class="nc">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="n">UPSafeCell</span>::<span class="n">new</span><span class="p">(</span><span class="n">TaskManagerInner</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">tasks</span><span class="p">,</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="n">current_task</span>: <span class="mi">0</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="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="p">};</span><span class="w"></span>
<span class="linenos">27</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>可以看到,在 <code class="docutils literal notranslate"><span class="pre">TaskManagerInner</span></code> 中我们使用向量 <code class="docutils literal notranslate"><span class="pre">Vec</span></code> 来保存任务控制块。在全局任务管理器 <code class="docutils literal notranslate"><span class="pre">TASK_MANAGER</span></code>
初始化的时候,只需使用 <code class="docutils literal notranslate"><span class="pre">loader</span></code> 子模块提供的 <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">current_task</span></code> 设置
为 0 ,于是将从第 0 个应用开始执行。</p>
<p>回过头来介绍一下应用构建器 <code class="docutils literal notranslate"><span class="pre">os/build.rs</span></code> 的改动:</p>
<ul class="simple">
<li><p>首先,我们在 <code class="docutils literal notranslate"><span class="pre">.incbin</span></code> 中不再插入清除全部符号的应用二进制镜像 <code class="docutils literal notranslate"><span class="pre">*.bin</span></code> ,而是将构建得到的 ELF 格式文件直接链接进来;</p></li>
<li><p>其次,在链接每个 ELF 格式文件之前我们都加入一行 <code class="docutils literal notranslate"><span class="pre">.align</span> <span class="pre">3</span></code> 来确保它们对齐到 8 字节,这是由于如果不这样做,
<code class="docutils literal notranslate"><span class="pre">xmas-elf</span></code> crate 可能会在解析 ELF 的时候进行不对齐的内存读写,例如使用 <code class="docutils literal notranslate"><span class="pre">ld</span></code> 指令从内存的一个没有对齐到 8 字节的地址加载一个 64 位的值到一个通用寄存器。</p></li>
</ul>
<p>为了方便后续的实现,全局任务管理器还需要提供关于当前应用与地址空间有关的一些信息。通过 <code class="docutils literal notranslate"><span class="pre">current_user_token</span></code>
<code class="docutils literal notranslate"><span class="pre">current_trap_cx</span></code> 分别可以获得当前正在执行的应用的地址空间的 token 和可以在
内核地址空间中修改位于该应用地址空间中的 Trap 上下文的可变引用。</p>
</div>
</div>
<div class="section" id="trap">
<h2>改进 Trap 处理的实现<a class="headerlink" href="#trap" title="永久链接至标题"></a></h2>
<p>为了能够支持地址空间,让我们来看现在 <code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 的改进实现:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/trap/mod.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">fn</span> <span class="nf">set_kernel_trap_entry</span><span class="p">()</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">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="n">stvec</span>::<span class="n">write</span><span class="p">(</span><span class="n">trap_from_kernel</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">TrapMode</span>::<span class="n">Direct</span><span class="p">);</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 7</span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 8</span>
<span class="linenos"> 9</span><span class="cp">#[no_mangle]</span><span class="w"></span>
<span class="linenos">10</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">trap_from_kernel</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">11</span><span class="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">"a trap from kernel!"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">12</span><span class="p">}</span><span class="w"></span>
<span class="linenos">13</span>
<span class="linenos">14</span><span class="cp">#[no_mangle]</span><span class="w"></span>
<span class="linenos">15</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">trap_handler</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="n">set_kernel_trap_entry</span><span class="p">();</span><span class="w"></span>
<span class="linenos">17</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">cx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">current_trap_cx</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">scause</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">scause</span>::<span class="n">read</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="n">stval</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">stval</span>::<span class="n">read</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="n">scause</span><span class="p">.</span><span class="n">cause</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="o">..</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="n">trap_return</span><span class="p">();</span><span class="w"></span>
<span class="linenos">24</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>由于应用的 Trap 上下文不在内核地址空间,因此我们调用 <code class="docutils literal notranslate"><span class="pre">current_trap_cx</span></code> 来获取当前应用的 Trap 上下文的可变引用
而不是像之前那样作为参数传入 <code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 。至于 Trap 处理的过程则没有发生什么变化。</p>
<p>注意到,在 <code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 的开头还调用 <code class="docutils literal notranslate"><span class="pre">set_kernel_trap_entry</span></code><code class="docutils literal notranslate"><span class="pre">stvec</span></code> 修改为同模块下另一个函数
<code class="docutils literal notranslate"><span class="pre">trap_from_kernel</span></code> 的地址。这就是说,一旦进入内核后再次触发到 S 的 Trap则会在硬件设置一些 CSR 之后跳过寄存器
的保存过程直接跳转到 <code class="docutils literal notranslate"><span class="pre">trap_from_kernel</span></code> 函数,在这里我们直接 <code class="docutils literal notranslate"><span class="pre">panic</span></code> 退出。这是因为内核和应用的地址空间分离
之后,从 U 还是从 S Trap 到 S 的 Trap 上下文保存与恢复实现方式和 Trap 处理逻辑有很大差别,我们不得不实现两遍而
不太可能将二者整合起来。这里简单起见我们弱化了从 S 到 S 的 Trap ,省略了 Trap 上下文保存过程而直接 <code class="docutils literal notranslate"><span class="pre">panic</span></code></p>
<p><code class="docutils literal notranslate"><span class="pre">trap_handler</span></code> 完成 Trap 处理之后,我们需要调用 <code class="docutils literal notranslate"><span class="pre">trap_return</span></code> 返回用户态:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="linenos"> 1</span><span class="c1">// os/src/trap/mod.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">fn</span> <span class="nf">set_user_trap_entry</span><span class="p">()</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">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="n">stvec</span>::<span class="n">write</span><span class="p">(</span><span class="n">TRAMPOLINE</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">TrapMode</span>::<span class="n">Direct</span><span class="p">);</span><span class="w"></span>
<span class="linenos"> 6</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 7</span><span class="p">}</span><span class="w"></span>
<span class="linenos"> 8</span>
<span class="linenos"> 9</span><span class="cp">#[no_mangle]</span><span class="w"></span>
<span class="linenos">10</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">trap_return</span><span class="p">()</span><span class="w"> </span>-&gt; <span class="o">!</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">set_user_trap_entry</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">trap_cx_ptr</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">TRAP_CONTEXT</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">user_satp</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">current_user_token</span><span class="p">();</span><span class="w"></span>
<span class="linenos">14</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="linenos">15</span><span class="w"> </span><span class="k">fn</span> <span class="nf">__alltraps</span><span class="p">();</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="k">fn</span> <span class="nf">__restore</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="kd">let</span><span class="w"> </span><span class="n">restore_va</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">__restore</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="o">-</span><span class="w"> </span><span class="n">__alltraps</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="o">+</span><span class="w"> </span><span class="n">TRAMPOLINE</span><span class="p">;</span><span class="w"></span>
<span class="linenos">19</span><span class="w"> </span><span class="k">unsafe</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="linenos">20</span><span class="w"> </span><span class="n">core</span>::<span class="n">arch</span>::<span class="fm">asm!</span><span class="p">(</span><span class="w"></span>
<span class="linenos">21</span><span class="w"> </span><span class="s">"fence.i"</span><span class="p">,</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="s">"jr {restore_va}"</span><span class="p">,</span><span class="w"></span>
<span class="linenos">23</span><span class="w"> </span><span class="n">restore_va</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">in</span><span class="p">(</span><span class="n">reg</span><span class="p">)</span><span class="w"> </span><span class="n">restore_va</span><span class="p">,</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="k">in</span><span class="p">(</span><span class="s">"a0"</span><span class="p">)</span><span class="w"> </span><span class="n">trap_cx_ptr</span><span class="p">,</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="k">in</span><span class="p">(</span><span class="s">"a1"</span><span class="p">)</span><span class="w"> </span><span class="n">user_satp</span><span class="p">,</span><span class="w"></span>
<span class="linenos">26</span><span class="w"> </span><span class="n">options</span><span class="p">(</span><span class="n">noreturn</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="fm">panic!</span><span class="p">(</span><span class="s">"Unreachable in back_to_user!"</span><span class="p">);</span><span class="w"></span>
<span class="linenos">30</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<ul class="simple">
<li><p>第 11 行,在 <code class="docutils literal notranslate"><span class="pre">trap_return</span></code> 的开头我们调用 <code class="docutils literal notranslate"><span class="pre">set_user_trap_entry</span></code> 来让应用 Trap 到 S 的时候可以跳转到
<code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 。注意我们把 <code class="docutils literal notranslate"><span class="pre">stvec</span></code> 设置为内核和应用地址空间共享的跳板页面的起始地址 <code class="docutils literal notranslate"><span class="pre">TRAMPOLINE</span></code> 而不是
编译器在链接时看到的 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 的地址,因为启用分页模式之后我们只能通过跳板页面上的虚拟地址来实际取得
<code class="docutils literal notranslate"><span class="pre">__alltraps</span></code><code class="docutils literal notranslate"><span class="pre">__restore</span></code> 的汇编代码。</p></li>
<li><p>之前介绍的时候提到过 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 需要两个参数:分别是 Trap 上下文在应用地址空间中的虚拟地址和要继续执行的应用
地址空间的 token 。第 12 和第 13 行则分别准备好这两个参数。</p></li>
<li><p>最后我们需要跳转到 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 切换到应用地址空间从 Trap 上下文中恢复通用寄存器并 <code class="docutils literal notranslate"><span class="pre">sret</span></code> 继续执行应用。它的
关键在于如何找到 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 在内核/应用地址空间中共同的虚拟地址。第 18 行我们展示了计算它的过程:由于
<code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 是对齐到地址空间跳板页面的起始地址 <code class="docutils literal notranslate"><span class="pre">TRAMPOLINE</span></code> 上的, 则 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 的虚拟地址只需在
<code class="docutils literal notranslate"><span class="pre">TRAMPOLINE</span></code> 基础上加上 <code class="docutils literal notranslate"><span class="pre">__restore</span></code> 相对于 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code> 的偏移量即可。这里 <code class="docutils literal notranslate"><span class="pre">__alltraps</span></code>
<code class="docutils literal notranslate"><span class="pre">__restore</span></code> 都是指编译器在链接时看到的内核内存布局中的地址。我们使用 <code class="docutils literal notranslate"><span class="pre">jr</span></code> 指令完成了跳转的任务。</p></li>
<li><p>在开始执行应用之前,我们需要使用 <code class="docutils literal notranslate"><span class="pre">fence.i</span></code> 指令清空指令缓存 i-cache 。这是因为,在内核中进行的一些操作
可能导致一些原先存放某个应用代码的物理页帧如今用来存放数据或者是其他应用的代码i-cache 中可能还保存着该物理页帧的
错误快照。因此我们直接将整个 i-cache 清空避免错误。</p></li>
</ul>
</div>
<div class="section" id="sys-write">
<h2>改进 sys_write 的实现<a class="headerlink" href="#sys-write" title="永久链接至标题"></a></h2>
<p>同样由于内核和应用地址空间的隔离, <code class="docutils literal notranslate"><span class="pre">sys_write</span></code> 不再能够直接访问位于应用空间中的数据,而需要手动查页表才能知道那些
数据被放置在哪些物理页帧上并进行访问。</p>
<p>为此,页表模块 <code class="docutils literal notranslate"><span class="pre">page_table</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/page_table.rs</span>
<span class="linenos"> 2</span>
<span class="linenos"> 3</span><span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">translated_byte_buffer</span><span class="p">(</span><span class="w"></span>
<span class="linenos"> 4</span><span class="w"> </span><span class="n">token</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"></span>
<span class="linenos"> 5</span><span class="w"> </span><span class="n">ptr</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="linenos"> 6</span><span class="w"> </span><span class="n">len</span>: <span class="kt">usize</span>
<span class="linenos"> 7</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="nb">Vec</span><span class="o">&lt;&amp;'</span><span class="nb">static</span><span class="w"> </span><span class="p">[</span><span class="kt">u8</span><span class="p">]</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="kd">let</span><span class="w"> </span><span class="n">page_table</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">PageTable</span>::<span class="n">from_token</span><span class="p">(</span><span class="n">token</span><span class="p">);</span><span class="w"></span>
<span class="linenos"> 9</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="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ptr</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">10</span><span class="w"> </span><span class="kd">let</span><span class="w"> </span><span class="n">end</span><span class="w"> </span><span class="o">=</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">len</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="k">mut</span><span class="w"> </span><span class="n">v</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nb">Vec</span>::<span class="n">new</span><span class="p">();</span><span class="w"></span>
<span class="linenos">12</span><span class="w"> </span><span class="k">while</span><span class="w"> </span><span class="n">start</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">end</span><span class="w"> </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">start_va</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">VirtAddr</span>::<span class="n">from</span><span class="p">(</span><span class="n">start</span><span class="p">);</span><span class="w"></span>
<span class="linenos">14</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">vpn</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">15</span><span class="w"> </span><span class="kd">let</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">page_table</span><span class="w"></span>
<span class="linenos">16</span><span class="w"> </span><span class="p">.</span><span class="n">translate</span><span class="p">(</span><span class="n">vpn</span><span class="p">)</span><span class="w"></span>
<span class="linenos">17</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">18</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">19</span><span class="w"> </span><span class="n">vpn</span><span class="p">.</span><span class="n">step</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="k">mut</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="n">vpn</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="n">end_va</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">min</span><span class="p">(</span><span class="n">VirtAddr</span>::<span class="n">from</span><span class="p">(</span><span class="n">end</span><span class="p">));</span><span class="w"></span>
<span class="linenos">22</span><span class="w"> </span><span class="n">v</span><span class="p">.</span><span class="n">push</span><span class="p">(</span><span class="o">&amp;</span><span class="n">ppn</span><span class="p">.</span><span class="n">get_bytes_array</span><span class="p">()[</span><span class="n">start_va</span><span class="p">.</span><span class="n">page_offset</span><span class="p">()</span><span class="o">..</span><span class="n">end_va</span><span class="p">.</span><span class="n">page_offset</span><span class="p">()]);</span><span class="w"></span>
<span class="linenos">23</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">end_va</span><span class="p">.</span><span class="n">into</span><span class="p">();</span><span class="w"></span>
<span class="linenos">24</span><span class="w"> </span><span class="p">}</span><span class="w"></span>
<span class="linenos">25</span><span class="w"> </span><span class="n">v</span><span class="w"></span>
<span class="linenos">26</span><span class="p">}</span><span class="w"></span>
</pre></div>
</div>
<p>参数中的 <code class="docutils literal notranslate"><span class="pre">token</span></code> 是某个应用地址空间的 token <code class="docutils literal notranslate"><span class="pre">ptr</span></code><code class="docutils literal notranslate"><span class="pre">len</span></code> 则分别表示该地址空间中的一段缓冲区的起始地址
和长度。 <code class="docutils literal notranslate"><span class="pre">translated_byte_buffer</span></code> 会以向量的形式返回一组可以在内核空间中直接访问的字节数组切片,具体实现在这里
不再赘述。</p>
<p>进而,我们完成对 <code class="docutils literal notranslate"><span class="pre">sys_write</span></code> 系统调用的改造:</p>
<div class="highlight-rust notranslate"><div class="highlight"><pre><span></span><span class="c1">// os/src/syscall/fs.rs</span>
<span class="k">pub</span><span class="w"> </span><span class="k">fn</span> <span class="nf">sys_write</span><span class="p">(</span><span class="n">fd</span>: <span class="kt">usize</span><span class="p">,</span><span class="w"> </span><span class="n">buf</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="n">len</span>: <span class="kt">usize</span><span class="p">)</span><span class="w"> </span>-&gt; <span class="kt">isize</span> <span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="k">match</span><span class="w"> </span><span class="n">fd</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="n">FD_STDOUT</span><span class="w"> </span><span class="o">=&gt;</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">buffers</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">translated_byte_buffer</span><span class="p">(</span><span class="n">current_user_token</span><span class="p">(),</span><span class="w"> </span><span class="n">buf</span><span class="p">,</span><span class="w"> </span><span class="n">len</span><span class="p">);</span><span class="w"></span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">buffer</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="n">buffers</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
<span class="w"> </span><span class="fm">print!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span><span class="w"> </span><span class="n">core</span>::<span class="kt">str</span>::<span class="n">from_utf8</span><span class="p">(</span><span class="n">buffer</span><span class="p">).</span><span class="n">unwrap</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="n">len</span><span class="w"> </span><span class="k">as</span><span class="w"> </span><span class="kt">isize</span><span class="w"></span>
<span class="w"> </span><span class="p">},</span><span class="w"></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="w"> </span><span class="fm">panic!</span><span class="p">(</span><span class="s">"Unsupported fd in sys_write!"</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">&amp;str</span></code> 然后输出即可。</p>
</div>
</div>
</article>
<footer>
<div class="related-pages">
<a class="next-page" href="7exercise.html">
<div class="page-info">
<div class="context">
<span>Next</span>
</div>
<div class="title">chapter4练习</div>
</div>
<svg><use href="#svg-arrow-right"></use></svg>
</a>
<a class="prev-page" href="5kernel-app-spaces.html">
<svg><use href="#svg-arrow-right"></use></svg>
<div class="page-info">
<div class="context">
<span>Previous</span>
</div>
<div class="title">内核与应用的地址空间</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/6multitasking-based-on-as.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>
</ul>
</li>
<li><a class="reference internal" href="#term-trampoline">跳板的实现</a></li>
<li><a class="reference internal" href="#id5">加载和执行应用程序</a><ul>
<li><a class="reference internal" href="#id6">扩展任务控制块</a></li>
<li><a class="reference internal" href="#id7">更新对任务控制块的管理</a></li>
</ul>
</li>
<li><a class="reference internal" href="#trap">改进 Trap 处理的实现</a></li>
<li><a class="reference internal" href="#sys-write">改进 sys_write 的实现</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>