提交 ebd7309b 编写于 作者: chyyuu1972's avatar chyyuu1972

Merge branch 'main' of github.com:rcore-os/rCore-Tutorial-Book-v3 into main

......@@ -3,4 +3,16 @@
.. toctree::
:hidden:
:maxdepth: 4
\ No newline at end of file
:maxdepth: 4
RISC-V指令集的SBI标准规定了类Unix操作系统之下的运行环境规范。这个规范拥有多种实现,RustSBI是它的一种实现。
RISC-V架构中,存在着定义于操作系统之下的运行环境。这个运行环境不仅将引导启动RISC-V下的操作系统, 还将常驻后台,为操作系统提供一系列二进制接口,以便其获取和操作硬件信息。 RISC-V给出了此类环境和二进制接口的规范,称为“操作系统二进制接口”,即“SBI”。
SBI的实现是在M模式下运行的特定于平台的固件,它将管理S、U等特权上的程序或通用的操作系统。
RustSBI项目发起于鹏城实验室的“rCore代码之夏-2020”活动,它是完全由Rust语言开发的SBI实现。 现在它能够在支持的RISC-V设备上运行rCore教程和其它操作系统内核。
RustSBI项目的目标是,制作一个从固件启动的最小Rust语言SBI实现,为可能的复杂实现提供参考和支持。 RustSBI也可以作为一个库使用,帮助更多的SBI开发者适配自己的平台,以支持更多处理器核和片上系统。
当前项目实现源码:https://github.com/luojia65/rustsbi
\ No newline at end of file
扩展阅读
================================================
.. toctree::
:hidden:
:maxdepth: 5
扩展阅读部分
\ No newline at end of file
环境配置
实验环境配置
============
.. toctree::
......@@ -16,10 +16,10 @@
系统环境配置
-------------------------------
目前实验仅支持 Ubuntu18.04 操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
目前实验仅支持 Ubuntu18.04 + 操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。
特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 。
特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 / 20.04
步骤如下:
- 升级 Windows 10 到最新版(Windows 10 版本 18917 或以后的内部版本)。注意,如果
......@@ -44,7 +44,7 @@ VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。
>> wsl --set-default-version 2
- `下载 Linux 内核安装包 <https://docs.microsoft.com/zh-cn/windows/wsl/install-win10#step-4---download-the-linux-kernel-update-package>`_
- 在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 。
- 在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 / 20.04
如果你打算使用 VMware 安装虚拟机的话,我们已经配置好了一个能直接运行 rCore-Tutorial-v3 的
Ubuntu18.04 镜像,它是一个 ``vmdk`` 格式的虚拟磁盘文件,只需要在 VMware 中新建一台虚拟机,
......@@ -76,6 +76,14 @@ Rust 开发环境配置
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
curl https://sh.rustup.rs -sSf | sh
或者使用tuna源来加速 `参见 rustup 帮助 <https://mirrors.tuna.tsinghua.edu.cn/help/rustup/>`_:
.. code-block:: bash
export RUSTUP_DIST_SERVER=https://mirrors.tuna.edu.cn/rustup
export RUSTUP_UPDATE_ROOT=https://mirrors.tuna.edu.cn/rustup/rustup
curl https://sh.rustup.rs -sSf | sh
或者也可以通过在运行前设置命令行中的科学上网代理来实现:
.. code-block:: bash
......@@ -115,7 +123,36 @@ Rust 开发环境配置
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
至于 Rust 开发环境,推荐 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。
同样,也可以使用tuna源 `参见 crates.io 帮助 <https://mirrors.tuna.tsinghua.edu.cn/help/crates.io-index.git/>`_:
.. code-block:: toml
[source.crates-io]
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
接下来安装一些Rust相关的软件包
.. code-block:: bash
rustup target add riscv64gc-unknown-none-elf
cargo install cargo-binutils
rustup component add llvm-tools-preview
rustup component add rust-src
.. note::
rCore-Tutorial 仓库中的 ``Makefile`` 包含了这些工具的安装,如果你使用 ``make run`` 也可以不手动安装。
至于 Rust 开发环境,推荐 JetBrains Clion + Rust插件 或者 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。
.. note::
* JetBrains Clion是付费商业软件,但对于学生和教师,只要在 JetBrains 网站注册账号,可以享受一定期限(半年左右)的免费使用的福利。
* Visual Studio Code 是开源软件,不用付费就可使用。
* 当然,采用 VIM,Emacs 等传统的编辑器也是没有问题的。
Qemu 模拟器安装
----------------------------------------
......@@ -130,7 +167,7 @@ Qemu 模拟器安装
# 安装编译所需的依赖包
sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev \
gawk build-essential bison flex texinfo gperf libtool patchutils bc \
zlib1g-dev libexpat-dev git
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3
# 下载源码包
# 如果下载速度过慢可以使用我们提供的百度网盘链接:https://pan.baidu.com/s/1z-iWIPjxjxbdFS2Qf-NKxQ
# 提取码 8woe
......@@ -192,7 +229,7 @@ Qemu 模拟器安装
如果是在 Qemu 平台上运行,只需在 ``os`` 目录下 ``make run`` 即可。在内核加载完毕之后,可以看到目前可以用的
应用程序。 ``usertests`` 打包了其中的很大一部分,所以我们可以运行它,只需输入在终端中输入它的名字即可。
.. image:: resources/qemu-final.gif
.. image:: qemu-final.gif
之后,可以先按下 ``Ctrl+A`` ,再按下 ``X`` 来退出 Qemu。
......@@ -200,12 +237,12 @@ Qemu 模拟器安装
首先,我们需要将 MicroSD 插入 PC 来将文件系统镜像拷贝上去。
.. image:: resources/prepare-sd.gif
.. image:: prepare-sd.gif
随后,我们将 MicroSD 插入 K210 开发板,将 K210 开发板连接到 PC ,然后进入 ``os`` 目录 ``make run BOARD=k210``
在 K210 开发板上跑 Tutorial 。
.. image:: resources/k210-final.gif
.. image:: k210-final.gif
之后,可以按下 ``Ctrl+]`` 来退出串口终端。
......
......@@ -11,11 +11,10 @@
2os-interface
3os-hw-abstract
4os-features
5extend-reading
5setup-devel-env
为何要写这本操作系统书
-----------------------
**为何要写这本操作系统书**
现在国内外已有一系列优秀的操作系统教材,例如William Stallings的《Operating Systems Internals and Design Principles》,Avi Silberschatz、Peter Baer Galvin 和 Greg Gagne 的《Operating System Concepts》,Remzi H. Arpaci-Dusseau 和 Andrea C. Arpaci-Dusseau 的《Operating Systems: Three Easy Pieces》等。然而,从我们从2000年以来的教学实践来看,某些经典教材对操作系统的概念和原理很重视,但缺乏对操作系统的概念/原理与操作系统的实现之间建立一个联系的桥梁,导致学生发现操作系统实现相关的实验与操作系统的概念相比,有较大的鸿沟。此外,部分教材把 x86 作为的操作系统实验的硬件参考平台,缺乏对当前快速发展的RISC-V等体系结构的实验支持,使得学生在操作系统实验中可能需要花较大代价了解相对繁杂的x86硬件细节,影响操作系统实验的效果。还有部分教材也基本以 Linux/Unix 等实际操作系统为主,难以让学生在一个学期内掌握其中的核心设计。
......@@ -28,4 +27,16 @@
在具体撰写过程中,第零章是对操作系统的一个概述,让读者对操作系统的历史、定义、特征等概念上有一个大致的了解。后面的每个章节体现了操作系统的一个微缩的历史发展过程,即从对应用由简到繁的支持的角度出发,每章会讲解如何设计一个可运行应用的操作系统,满足应用的阶段性需求。从而读者可以通过对应配套的操作系统设计实验,了解如何从一个微不足道的小操作系统,根据应用需求,添加或增强操作系统功能,逐步形成一个类似UNIX的相对完善的操作系统。每一步都小到足以让人感觉到易于掌控,而在每一步结束时,你都有一个可以工作的操作系统。另外,通过足够详尽的测试程序 ,可以随时验证读者实现的操作系统在每次更新后是否正常工作。由于实验的代码规模和实现复杂度在一个逐步递增的可控范围内,读者可以结合对应于操作系统设计实验的进一步的原理讲解,来建立操作系统概念原理和实际实现的对应关系,从而能够通过操作系统实验的实践过程来加强对理论概念的理解,通过理论概念来进一步指导操作系统实验的实现与改进。
在你开始阅读与实践本书讲解的内容之前,你需要决定用什么编程语言来完成操作系统实验。你可以用任何你喜欢的编程语言来实现操作系统。我们推荐的编程语言是Rust语言或者C语言。
在你开始阅读与实践本书讲解的内容之前,你需要决定用什么编程语言来完成操作系统实验。你可以用任何你喜欢的编程语言来实现操作系统。我们推荐的编程语言是Rust。
.. note::
**目前常见的操作系统内核都是基于C语言的,为何要推荐Rust语言?**
- 没错,C语言就是为写UNIX而诞生的。Dennis Ritchie和KenThompson没有期望设计一种新语言能帮助高效简洁地开发复杂的应用业务逻辑,只是希望用一种简洁的方式抽象出计算机的行为,便于编写控制计算机硬件的操作系统,最终的结果就是C语言。
- C语言的指针的天使与魔鬼,且C语言缺少有效的并发支持,导致内存和并发漏洞成为当前操作系统的噩梦。
- 从某种角度上看,Rust的目标是解决C的短板,取代C。所以用Rust写OS具有很好的开发和运行的体验。
- 用Rust写OS的代价仅仅是学会用Rust编程。
应用程序行环境与平台支持
应用程序行环境与平台支持
================================================
.. toctree::
:hidden:
:maxdepth: 5
作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
执行应用程序
-------------------------------
我们先开发并运行一个简单的“Hello, world”应用程序,看看一个简单应用程序从开发到执行的全过程。作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
.. code-block:: console
......@@ -44,34 +47,34 @@
Running `target/debug/os`
Hello, world!
如我们预想的一样,我们在屏幕上看到了一行 ``Hello, world!`` 。但是,需要注意到我们所享受到的编程的方便并不是理所当然的,背后有着从硬件
到软件的多种机制的支持。
如我们预想的一样,我们在屏幕上看到了一行 ``Hello, world!`` 。但是,需要注意到我们所享受到的编程和执行程序的方便性并不是理所当然的,背后有着从硬件
到软件的多种机制的支持。特别是对于应用程序的运行,是需要有一个强大的执行环境来帮助。接下来,我们就要看看有操作系统加持的强大的执行环境。
应用程序行环境
应用程序行环境
-------------------------------
如下图所示,应用程序的运行需要下面一套行环境栈的支持:
如下图所示,应用程序的运行需要下面一套行环境栈的支持:
.. _app-software-stack:
.. figure:: app-software-stack.png
:align: center
应用程序运行环境栈:图中的白色块自上而下(越往下则越靠近底层,下层作为上层的执行环境支持上层代码的运行)表示各级运行环境,
黑色块则表示相邻两层行环境之间的接口。
应用程序执行环境栈:图中的白色块自上而下(越往下则越靠近底层,下层作为上层的执行环境支持上层代码的运行)表示各级执行环境,
黑色块则表示相邻两层行环境之间的接口。
.. _term-execution-environment:
我们的应用位于最上层,它可以通过调用编程语言提供的标准库或者其他三方库对外提供的功能强大的函数接口,使得仅需少量的源代码就能完成复杂的
功能。但是这些库的功能不仅限于此,事实上它们属于应用程序的 **执行环境** (Execution Environment),在我们通常不会注意到的地方,它
们还会在执行应用之前完成一些初始化工作,并在应用程序执行的时候对它进行监控。我们在打印 ``Hello, world!`` 时使用的 ``println!``
宏正是由 Rust 标准库 std 提供的。
宏正是由 Rust 标准库 std 和GNU Libc库等提供的。
.. _term-system-call:
从内核/操作系统的角度看来,它上面的一切都属于用户态,而它自身属于内核态。无论用户态应用如何编写,是手写汇编代码,还是基于某种编程语言利用
其标准库或三方库,某些功能总要直接或间接的通过内核/操作系统提供的 **系统调用** (System Call) 来实现。因此系统调用充当了用户和内核之间
的边界。内核作为用户态的行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。
的边界。内核作为用户态的行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。
.. note::
......@@ -107,7 +110,7 @@
**多层执行环境都是必需的吗?**
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 **抽象** (Abstraction),
并为上层提供了一套行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
并为上层提供了一套行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
应有的灵活性。比如,当你在考虑在项目中应该使用哪个函数库的时候,就常常需要这方面的权衡:过多的抽象和过少的抽象自然都是不合适的。
实际上,我们通过应用程序的特征来判断它需要什么程度的抽象。
......@@ -124,8 +127,8 @@
.. _term-platform:
对于一份用某种编程语言实现的源代码而言,编译器在将其通过编译、链接得到目标文件的时候需要知道程序要在哪个 **平台** (Platform) 上运行。
从上面给出的 :ref:`应用程序行环境栈 <app-software-stack>` 可以看出:
对于一份用某种编程语言实现的源代码而言,编译器在将其通过编译、链接得到目标文件的时候需要知道程序要在哪个 **平台** (Platform) 上运行。这里 **平台** 主要是指CPU类型、操作系统类型和标准运行时库的组合。
从上面给出的 :ref:`应用程序行环境栈 <app-software-stack>` 可以看出:
- 如果用户态基于的内核不同,会导致系统调用接口不同或者语义不一致;
- 如果底层硬件不同,对于硬件资源的访问方式会有差异。特别是 ISA 不同的话,对上提供的指令集和寄存器都不同。
......@@ -135,7 +138,7 @@
.. _term-target-triplet:
我们可以通过 **目标三元组** (Target Triplet) 来描述一个目标平台。它一般包括 CPU 架构、CPU 厂商和操作系统,它们确实都会控制目标文件的生成。
我们可以通过 **目标三元组** (Target Triplet) 来描述一个目标平台。它一般包括 CPU 架构、CPU 厂商、操作系统和运行时库,它们确实都会控制目标文件的生成。
比如,我们可以尝试看一下之前的 ``Hello, world!`` 的目标平台是什么。这可以通过打印编译器 rustc 的默认配置信息:
.. code-block:: console
......@@ -149,7 +152,7 @@
release: 1.48.0-nightly
LLVM version: 11.0
从其中的 host 一项可以看出默认的目标平台是 ``x86_64-unknown-linux-gnu``,其中 CPU 架构是 x86_64,CPU 厂商是 unknown,操作系统是 linux-gnu
从其中的 host 一项可以看出默认的目标平台是 ``x86_64-unknown-linux-gnu``,其中 CPU 架构是 x86_64,CPU 厂商是 unknown,操作系统是 linux,运行时库是gnu libc(封装了Linux系统调用,并提供POSIX接口为主的函数库)
这种无论编译器还是其目标文件都在我们当前所处的平台运行是一种最简单也最普遍的情况。但是很快我们就将遇到另外一种情况。
讲了这么多,终于该介绍我们的主线任务了。我们希望能够在另一个平台上运行 ``Hello, world!``,而与之前的默认平台不同的地方在于,我们将 CPU 架构从
......@@ -168,6 +171,7 @@ x86_64 换成 RISC-V。
.. code-block:: console
$ rustc --print target-list | grep riscv
riscv32gc-unknown-linux-gnu
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
......@@ -175,10 +179,9 @@ x86_64 换成 RISC-V。
riscv64gc-unknown-none-elf
riscv64imac-unknown-none-elf
这里我们选择的是 ``riscv64gc-unknown-none-elf``,目标三元组中的操作系统是 none-elf,表明没有任何系统调用支持。这里我们之所以不选择有
这里我们选择的是 ``riscv64gc-unknown-none-elf``,目标三元组中的CPU 架构是 riscv64gc,厂商是 unknown,操作系统是 none,elf表示没有标准的运行时库(表明没有任何系统调用的封装支持),但可以生成ELF格式的执行程序。这里我们之所以不选择有
linux-gnu 系统调用支持的版本 ``riscv64gc-unknown-linux-gnu``,是因为我们只是想跑一个 ``Hello, world!``,没有必要使用操作系统所提供的
那么高级的抽象。而且我们很清楚后续我们要开发的是一个内核,如果仅仅基于已有操作系统提供的系统调用的话,它自身的抽象能力会受到很大限制。所以它必须
直面底层硬件来解锁更大的抽象能力上限。
那么高级的抽象。而且我们很清楚后续我们要开发的是一个操作系统内核,它必须直面底层物理硬件(bare-metal)来提供更大的操作系统服务功能,已有操作系统(如Linux)提供的系统调用服务对这个内核而言是多余的。
.. note::
......@@ -212,12 +215,12 @@ Rust 标准库与核心库
.. _term-bare-metal:
在之前的环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。因此只是单纯的在这个目标平台上找不到
在之前的开发环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。这个问题只是单纯的表示在这个目标平台上找不到
Rust 标准库 std。我们之前曾经提到过,编程语言的标准库或三方库的某些功能会直接或间接的用到操作系统提供的系统调用。但目前我们所选的目标平台不存在
任何操作系统支持,于是 Rust 并没有为这个目标平台支持完整的标准库 std。类似这样的平台通常被我们称为 **裸机平台** (bare-metal)。
幸运的是,Rust 有一个对 std 裁剪过后的核心库 core,这个库是不需要任何操作系统支持的,相对的它的功能也比较受限,但是也包含了 Rust 语言
相当一部分的核心机制,可以满足我们的大部分需求。在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
相当一部分的核心机制,可以满足我们的大部分需求。Rust 语言是一种面向系统(包括操作系统)开发的语言,所以在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
程度上减轻我们的编程负担。它们是我们能够在裸机平台挣扎求生的最主要倚仗。
于是,我们知道在裸机平台上我们要将对于标准库 std 的引用换成核心库 core。但是做起来其实并没有那么容易。
\ No newline at end of file
......@@ -9,9 +9,9 @@
我们首先在 ``os`` 目录下新建 ``.cargo`` 目录,并在这个目录下创建 ``config`` 文件,并在里面输入如下内容:
.. code-block::
.. code-block:: toml
// os/.cargo/config
# os/.cargo/config
[build]
target = "riscv64gc-unknown-none-elf"
......
......@@ -14,12 +14,12 @@
5sbi-print
6practice
大多数程序员的第一行代码都从 ``Hello, world!`` 开始,当我们满怀着好奇心在编辑器内键入仅仅数个字节,再经过几行命令编译、运行,终于
在黑洞洞的终端窗口中看到期望中的结果的时候,一扇通往编程世界的大门已经打开。时至今日,我们能够隐约意识到编程工作能够如此方便简洁并不是
理所当然的,实际上有着多层硬件、软件隐藏在它背后,才让我们不必付出那么多努力就能够创造出功能强大的应用程序。
大多数程序员的第一行代码都从 ``Hello, world!`` 开始,当我们满怀着好奇心在编辑器内键入仅仅数个字节,再经过几行命令编译(靠的是编译器)、运行(靠的是操作系统),终于在黑洞洞的终端窗口中看到期望中的结果的时候,一扇通往编程世界的大门已经打开。在本章第一节 :doc:`1app-ee-platform` 中,可以看到用Rust语言编写的非常简单的“Hello, world”应用程序。
不过我们能够隐约意识到编程工作能够如此方便简洁并不是理所当然的,实际上有着多层硬件和软件工具和支撑环境隐藏在它背后,才让我们不必付出那么多努力就能够创造出功能强大的应用程序。
本章我们的目标仍然只是输出 ``Hello, world!`` ,但这一次,我们将离开舒适区,基于一个几乎空无一物的平台从零开始搭建我们自己的高楼大厦,
而不是仅仅通过一行语句就完成任务。
而不是仅仅通过一行语句就完成任务。所以,在接下来的内容中,我们将描述如何让 ``Hello, world!`` 应用程序逐步脱离对编译器、运行时和操作系统的依赖,最终在裸机上运行。这时,我们也可把这个能在裸机上运行的``Hello, world!`` 应用程序称为一种支持输出字符串的非常初级的“三叶虫”操作系统,它其实就是一个给应用提供各种服务(比如输出字符串)的库,方便了单一应用程序在裸机上的开发与运行。
获取本章代码:
......@@ -72,4 +72,11 @@
.bss [0x80033000, 0x80033000)
Panicked at src/main.rs:46 Shutdown machine!
除了 ``Hello, world!`` 之外还有一些额外的信息,最后关机。
\ No newline at end of file
除了 ``Hello, world!`` 之外还有一些额外的信息,最后关机。
.. note::
RustSBI是啥?
戳 :doc:`../appendix-c/index` 可以进一步了解RustSBI。
\ No newline at end of file
......@@ -4,7 +4,7 @@
在本章第一小节中我们简单介绍了分页的内存管理策略,本节我们在 RV64 架构提供的 SV39 分页机制的基础上完成内核中的
软件对应实现。
SV39 的虚拟地址和物理地址
虚拟地址和物理地址
------------------------------------------------------
默认情况下 MMU 未被使能,此时无论 CPU 位于哪个特权级,访存的地址都会作为一个物理地址交给对应的内存控制单元来直接
......@@ -18,7 +18,261 @@ SV39 的虚拟地址和物理地址
的时候,SV39 分页机制被启用,所有 S/U 特权级的访存被视为一个 39 位的虚拟地址,它们需要先经过 MMU 的地址转换流程,
如果顺利的话,则会变成一个 56 位的物理地址来访问物理内存;否则则会触发异常,这体现了该机制的内存保护能力。
虚拟地址和物理地址都是字节地址,39 位的虚拟地址可以用来访问理论上最大 :math:`512\text{GiB}` 的地址空间,
而 56 位的物理地址在理论上甚至可以访问一块大小比这个地址空间的还高出几个数量级的物理内存。但是实际上无论是
虚拟地址还是物理地址,真正有意义、能够通过 MMU 的地址转换或是 CPU 内存控制单元的检查的地址仅占其中的很小
一部分,因此它们的理论容量上限在目前都没有实际意义。
.. image:: sv39-va-pa.png
虚拟地址和物理地址都是字节地址, 39 位的虚拟地址可以用来访问大小为 :math:`512\text{GiB}` 的地址空间,
而 56 位的物理地址
\ No newline at end of file
.. _term-page-offset:
我们采用分页管理,单个页面的大小设置为 :math:`4\text{KiB}` ,每个虚拟页面和物理页帧都对齐到这个页面大小,也就是说
虚拟/物理地址区间 :math:`[0,4\text{KiB})` 为第 :math:`0` 个虚拟页面/物理页帧,而
:math:`[4\text{KiB},8\text{KiB})` 为第 :math:`1` 个,以此类推。 :math:`4\text{KiB}` 需要用 12 位字节地址
来表示,因此虚拟地址和物理地址都被分成两部分:它们的低 12 位,即 :math:`[11:0]` 被称为 **页内偏移**
(Page Offset) ,它描述一个地址指向的字节在它所在页面中的相对位置。而虚拟地址的高 27 位,即 :math:`[38:12]` 为
它的虚拟页号 VPN,同理物理地址的高 44 位,即 :math:`[55:12]` 为它的物理页号 VPN,页号可以用来定位一个虚拟/物理地址
属于哪一个虚拟页面/物理页帧。
地址转换是以页为单位进行的,在地址转换的前后地址的页内偏移部分不变。可以认为 MMU 只是从虚拟地址中取出 27 位虚拟页号,
在页表中查到其对应的物理页号(如果存在的话),最后将得到的物理页号与虚拟地址的页内偏移依序拼接到一起就变成了物理地址。
.. note::
**RV64 架构中虚拟地址为何只有 39 位?**
在 64 位架构上虚拟地址长度确实应该和位宽一致为 64 位,但是在启用 SV39 分页模式下,只有后 39 位是真正有意义的。
SV39 分页模式规定 64 位虚拟地址的 :math:`[63:39]` 这 25 位必须和第 38 位相同,否则 MMU 会直接认定它是一个
不合法的虚拟地址。通过这个检查之后 MMU 再取出后 39 位尝试将其转化为一个 56 位的物理地址。
也就是说,所有 :math:`2^{64}` 个虚拟地址中,只有最低的 :math:`256\text{GiB}` (当第 38 位为 0 时)
以及最高的 :math:`256\text{GiB}` (当第 38 位为 1 时)是可能通过 MMU 检查的。当我们写软件代码的时候,一个
地址的位宽毋庸置疑就是 64 位,我们要清楚可用的只有最高和最低这两部分,尽管它们已经巨大的超乎想象了;而本节中
我们专注于介绍 MMU 的机制,强调 MMU 看到的真正用来地址转换的虚拟地址,这只有 39 位。
正如本章第一小节所说,在分页内存管理中,地址转换的核心任务在于如何维护虚拟页号到物理页号的映射——也就是页表。不过在具体
实现它之前,我们先将地址和页号的概念抽象为 Rust 中的类型,借助 Rust 的类型安全特性来确保它们被正确实现。
首先是这些类型的定义:
.. code-block:: rust
// os/src/mm/address.rs
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysAddr(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtAddr(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysPageNum(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtPageNum(pub usize);
.. _term-type-convertion:
上面分别给出了物理地址、虚拟地址、物理页号、虚拟页号的 Rust 类型声明,它们都是 Rust 的元组式结构体,可以看成
usize 的一种简单包装。我们刻意将它们各自抽象出来而不是都使用 usize 保存,就是为了在 Rust 编译器的帮助下进行
多种方便且安全的 **类型转换** (Type Convertion) 。
首先,这些类型本身可以和 usize 之间互相转换,以物理地址 ``PhysAddr`` 为例,我们需要:
.. code-block:: rust
// os/src/mm/address.rs
impl From<usize> for PhysAddr {
fn from(v: usize) -> Self { Self(v) }
}
impl From<PhysAddr> for usize {
fn from(v: PhysAddr) -> Self { v.0 }
}
前者允许我们从一个 ``usize`` 来生成 ``PhysAddr`` ,即 ``PhysAddr::from(_: usize)`` 将得到一个 ``PhysAddr``
;反之亦然。其实由于我们在声明结构体的时候将字段公开了出来,从物理地址变量 ``pa`` 得到它的 usize 表示的更简便方法
是直接 ``pa.0`` 。
.. note::
**Rust 语法卡片:类型转换之 From 和 Into**
一般而言,当我们为类型 ``U`` 实现了 ``From<T>`` Trait 之后,可以使用 ``U::from(_: T)`` 来从一个 ``T``
类型的实例来构造一个 ``U`` 类型的实例;而当我们为类型 ``U`` 实现了 ``Into<T>`` Trait 之后,对于一个 ``U``
类型的实例 ``u`` ,可以使用 ``u.into()`` 来将其转化为一个类型为 ``T`` 的实例。
当我们为 ``U`` 实现了 ``From<T>`` 之后,Rust 会自动为 ``T`` 实现 ``Into<U>`` Trait,
因为它们两个本来就是在做相同的事情。因此我们只需相互实现 ``From`` 就可以相互 ``From/Into`` 了。
需要注意的是,当我们使用 ``From`` Trait 的 ``from`` 方法来构造一个转换后类型的实例的时候,``from`` 的参数
已经指明了转换前的类型,因而 Rust 编译器知道该使用哪个实现;而使用 ``Into`` Trait 的 ``into`` 方法来将当前
类型转化为另一种类型的时候,它并没有参数,因而函数签名中并没有指出要转化为哪一个类型,则我们必须在其他地方 *显式*
指出目标类型。比如,当我们要将 ``u.into()`` 绑定到一个新变量 ``t`` 的时候,必须通过 ``let t: T`` 显式声明
``t`` 的类型;又或是将 ``u.into()`` 的结果作为参数传给某一个函数,那么这个函数的函数签名中一定指出了传入位置
的参数的类型,Rust 编译器也就明确知道转换的类型。
请注意,解引用 ``Deref`` Trait 是 Rust 编译器唯一允许的一种隐式类型转换,而对于其他的类型转换,我们必须手动
调用类型转化方法或者是显式给出转换前后的类型。这体现了 Rust 的类型安全特性,在 C/C++ 中并不是如此,比如两个
不同的整数/浮点数类型进行二元运算的时候,编译器经常要先进行隐式类型转换使两个操作数类型相同,而后再进行运算,导致
了很多数值溢出或精度损失问题。Rust 不会进行这种隐式类型转换,它会在编译期直接报错,提示两个操作数类型不匹配。
其次,地址和页号之间可以相互转换。我们这里仍以物理地址和物理页号之间的转换为例:
.. code-block:: rust
:linenos:
// os/src/mm/address.rs
impl PhysAddr {
pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
}
impl From<PhysAddr> for PhysPageNum {
fn from(v: PhysAddr) -> Self {
assert_eq!(v.page_offset(), 0);
v.floor()
}
}
impl From<PhysPageNum> for PhysAddr {
fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
}
其中 ``PAGE_SIZE`` 为 :math:`4096` , ``PAGE_SIZE_BITS`` 为 :math:`12` ,它们均定义在 ``config`` 子模块
中,分别表示每个页面的大小和页内偏移的位宽。从物理页号到物理地址的转换只需左移 :math:`12` 位即可,但是物理地址需要
保证它与页面大小对齐才能通过右移转换为物理页号。
对于不对齐的情况,物理地址不能通过 ``From/Into`` 转换为物理页号,而是需要通过它自己的 ``floor`` 或 ``ceil`` 方法来
进行下取整或上取整的转换。
.. code-block:: rust
// os/src/mm/address.rs
impl PhysAddr {
pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) }
pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) }
}
我们暂时先介绍这两种最简单的类型转换。
页表项
-----------------------------------------
第一小节中我们提到,在页表中以虚拟页号作为索引不仅能够查到物理页号,还能查到一组保护位,它控制了应用对地址空间每个
虚拟页面的访问权限。但实际上还有更多的标志位,物理页号和全部的标志位以某种固定的格式保存在一个结构体中,它被称为
**页表项** (PTE, Page Table Entry) ,是利用虚拟页号在页表中查到的结果。
.. image:: sv39-pte.png
上图为 SV39 分页模式下的页表项,其中 :math:`[53:10]` 这 :math:`44` 位是物理页号,最低的 :math:`8` 位
:math:`[7:0]` 则是标志位,它们的含义如下(请注意,为方便说明,下文我们用 *页表项的对应虚拟页面* 来表示索引到
一个页表项的虚拟页号对应的虚拟页面):
- 仅当 V(Valid) 位为 1 时,页表项才是合法的;
- R/W/X 分别控制索引到这个页表项的对应虚拟页面是否允许读/写/取指;
- U 控制索引到这个页表项的对应虚拟页面是否在 CPU 处于 U 特权级的情况下是否被允许访问;
- G 我们暂且不理会;
- A(Accessed) 记录自从页表项上的这一位被清零之后,页表项的对应虚拟页面是否被访问过;
- D(Dirty) 则记录自从页表项上的这一位被清零之后,页表项的对应虚拟页表是否被修改过。
让我们先来实现页表项中的标志位 ``PTEFlags`` :
.. code-block:: rust
// os/src/main.rs
#[macro_use]
extern crate bitflags;
// os/src/mm/page_table.rs
use bitflags::*;
bitflags! {
pub struct PTEFlags: u8 {
const V = 1 << 0;
const R = 1 << 1;
const W = 1 << 2;
const X = 1 << 3;
const U = 1 << 4;
const G = 1 << 5;
const A = 1 << 6;
const D = 1 << 7;
}
}
`bitflags <https://docs.rs/bitflags/1.2.1/bitflags/>`_ 是一个 Rust 中常用来比特标志位的 crate 。它提供了
一个 ``bitflags!`` 宏,如上面的代码段所展示的那样,可以将一个 ``u8`` 封装成一个标志位的集合类型,支持一些常见的集合
运算。它的一些使用细节这里不展开,请读者自行参考它的官方文档。注意,在使用之前我们需要引入该 crate 的依赖:
.. code-block:: toml
# os/Cargo.toml
[dependencies]
bitflags = "1.2.1"
接下来我们实现页表项 ``PageTableEntry`` :
.. code-block:: rust
:linenos:
// os/src/mm/page_table.rs
#[derive(Copy, Clone)]
#[repr(C)]
pub struct PageTableEntry {
pub bits: usize,
}
impl PageTableEntry {
pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self {
PageTableEntry {
bits: ppn.0 << 10 | flags.bits as usize,
}
}
pub fn empty() -> Self {
PageTableEntry {
bits: 0,
}
}
pub fn ppn(&self) -> PhysPageNum {
(self.bits >> 10 & ((1usize << 44) - 1)).into()
}
pub fn flags(&self) -> PTEFlags {
PTEFlags::from_bits(self.bits as u8).unwrap()
}
}
- 第 3 行我们让编译器自动为 ``PageTableEntry`` 实现 ``Copy/Clone`` Trait,来让这个类型以值语义赋值/传参的时候
不会发生所有权转移,而是拷贝一份新的副本。从这一点来说 ``PageTableEntry`` 就和 usize 一样,因为它也只是后者的
一层简单包装,解释了 usize 各个比特段的含义。
- 第 10 行使得我们可以从一个物理页号 ``PhysPageNum`` 和一个页表项标志位 ``PTEFlags`` 生成一个页表项
``PageTableEntry`` 实例;而第 20 行和第 23 行则分别可以从一个页表项将它们两个取出。
- 第 15 行中,我们也可以通过 ``empty`` 方法生成一个全零的页表项,注意这隐含着该页表项的 V 标志位为 0 ,
因此它是不合法的。
后面我们还为 ``PageTableEntry`` 实现了一些工具函数,可以快速判断一个页表项的 V/R/W/X 标志位是否为 1,以 V
标志位的判断为例:
.. code-block:: rust
// os/src/mm/page_table.rs
impl PageTableEntry {
pub fn is_valid(&self) -> bool {
(self.flags() & PTEFlags::V) != PTEFlags::empty()
}
}
这里相当于判断两个集合的交集是否为空集,部分说明了 ``bitflags`` crate 的使用方法。
多级页表
-------------------------------
页表的一种最简单的实现是线性表,也就是按照地址从低到高、输入的虚拟页号从 :math:`0` 开始递增的顺序依次在内存中
(我们之前提到过页表的容量过大无法保存在 CPU 中)放置每个虚拟页号对应的页表项。
\ No newline at end of file
......@@ -12,7 +12,6 @@ rCore-Tutorial-Book 第三版
:hidden:
chapter0/index
quickstart
chapter1/index
chapter2/index
chapter3/index
......@@ -54,7 +53,8 @@ rCore-Tutorial-Book 第三版
请大家先阅读 :ref:`第零章 <link-chapter0>` ,对于项目的开发背景和操作系统的概念有一个整体把控。
在正式进行实验之前,请先按照 :doc:`/quickstart` 中的说明完成环境配置,再从第一章开始阅读正文。
在正式进行实验之前,请先按照第零章章末的 :doc:`/chapter0/5setup-devel-env` 中的说明完成环境配置,再从第一章开始
阅读正文。
项目协作
----------------------
......@@ -63,7 +63,7 @@ rCore-Tutorial-Book 第三版
- :doc:`/rest-example` 给出了目前编写文档才用的 ReStructuredText 标记语言的一些基础语法及用例;
- 项目的源代码仓库在 `这里 <https://github.com/rcore-os/rCore-Tutorial-v3>`_ ,
该文档自身仓库在 `这儿 <https://github.com/rcore-os/rCore-Tutorial-Book-v3>`_ ;
- 时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issue 或 PR,让我们
- 时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issues 或 Pull Requests,让我们
一起努力让这本书变得更好!
项目进度
......
......@@ -12,7 +12,7 @@ reStructuredText 基本语法
外部链接的引入方法。注意,链接的名字和用一对尖括号包裹起来的链接地址之间必须有一个空格。链接最后的下划线和片段的后续内容之间也需要
有一个空格。
接下来是一个文档内部引用的例子。比如,戳 :doc:`/quickstart` 可以进入快速上手环节。
接下来是一个文档内部引用的例子。比如,戳 :doc:`chapter0/5setup-devel-env` 可以进入快速上手环节。
.. warning::
......
......@@ -352,6 +352,12 @@
* - 资源获取即初始化
- RAII, Resource Acquisition Is Initialization
- :ref:`Rust 中的动态内存分配 <term-raii>`
* - 页内偏移
- Page Offset
- :ref:`实现 SV39 多级页表机制 <term-page-offset>`
* - 类型转换
- Type Convertion
- :ref:`实现 SV39 多级页表机制 <term-type-convertion>`
\ No newline at end of file
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -216,6 +215,12 @@ commentsRunWhenDOMLoaded(addUtterances);
<h1>附录 C:深入机器模式:RustSBI<a class="headerlink" href="#c-rustsbi" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<p>RISC-V指令集的SBI标准规定了类Unix操作系统之下的运行环境规范。这个规范拥有多种实现,RustSBI是它的一种实现。</p>
<p>RISC-V架构中,存在着定义于操作系统之下的运行环境。这个运行环境不仅将引导启动RISC-V下的操作系统, 还将常驻后台,为操作系统提供一系列二进制接口,以便其获取和操作硬件信息。 RISC-V给出了此类环境和二进制接口的规范,称为“操作系统二进制接口”,即“SBI”。</p>
<p>SBI的实现是在M模式下运行的特定于平台的固件,它将管理S、U等特权上的程序或通用的操作系统。</p>
<p>RustSBI项目发起于鹏城实验室的“rCore代码之夏-2020”活动,它是完全由Rust语言开发的SBI实现。 现在它能够在支持的RISC-V设备上运行rCore教程和其它操作系统内核。</p>
<p>RustSBI项目的目标是,制作一个从固件启动的最小Rust语言SBI实现,为可能的复杂实现提供参考和支持。 RustSBI也可以作为一个库使用,帮助更多的SBI开发者适配自己的平台,以支持更多处理器核和片上系统。</p>
<p>当前项目实现源码:<a class="reference external" href="https://github.com/luojia65/rustsbi">https://github.com/luojia65/rustsbi</a></p>
</div>
......
......@@ -137,11 +137,9 @@ commentsRunWhenDOMLoaded(addUtterances);
<li class="toctree-l2"><a class="reference internal" href="2os-interface.html">操作系统的接口</a></li>
<li class="toctree-l2"><a class="reference internal" href="3os-hw-abstract.html">操作系统抽象</a></li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2"><a class="reference internal" href="5extend-reading.html">扩展阅读</a></li>
<li class="toctree-l2"><a class="reference internal" href="index.html#id2">为何要写这本操作系统书</a></li>
<li class="toctree-l2"><a class="reference internal" href="5setup-devel-env.html">实验环境配置</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -128,11 +128,9 @@ commentsRunWhenDOMLoaded(addUtterances);
</li>
<li class="toctree-l2"><a class="reference internal" href="3os-hw-abstract.html">操作系统抽象</a></li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2"><a class="reference internal" href="5extend-reading.html">扩展阅读</a></li>
<li class="toctree-l2"><a class="reference internal" href="index.html#id2">为何要写这本操作系统书</a></li>
<li class="toctree-l2"><a class="reference internal" href="5setup-devel-env.html">实验环境配置</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -131,11 +131,9 @@ commentsRunWhenDOMLoaded(addUtterances);
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2"><a class="reference internal" href="5extend-reading.html">扩展阅读</a></li>
<li class="toctree-l2"><a class="reference internal" href="index.html#id2">为何要写这本操作系统书</a></li>
<li class="toctree-l2"><a class="reference internal" href="5setup-devel-env.html">实验环境配置</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -72,7 +72,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="next" title="扩展阅读" href="5extend-reading.html" />
<link rel="next" title="实验环境配置" href="5setup-devel-env.html" />
<link rel="prev" title="操作系统抽象" href="3os-hw-abstract.html" />
</head>
......@@ -136,11 +136,9 @@ commentsRunWhenDOMLoaded(addUtterances);
<li class="toctree-l3"><a class="reference internal" href="#id7">持久性</a></li>
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="5extend-reading.html">扩展阅读</a></li>
<li class="toctree-l2"><a class="reference internal" href="index.html#id2">为何要写这本操作系统书</a></li>
<li class="toctree-l2"><a class="reference internal" href="5setup-devel-env.html">实验环境配置</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -303,7 +301,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="5extend-reading.html" class="btn btn-neutral float-right" title="扩展阅读" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="5setup-devel-env.html" class="btn btn-neutral float-right" title="实验环境配置" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="3os-hw-abstract.html" class="btn btn-neutral float-left" title="操作系统抽象" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
......
<!DOCTYPE html>
<html class="writer-html5" lang="zh-CN" >
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>扩展阅读 &mdash; rCore-Tutorial-Book-v3 0.1 文档</title>
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
<!--[if lt IE 9]>
<script src="../_static/js/html5shiv.min.js"></script>
<![endif]-->
<script type="text/javascript" id="documentation_options" data-url_root="../" 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/language_data.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", "rcore-os/rCore-Tutorial-Book-v3");
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://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="next" title="环境配置" href="../quickstart.html" />
<link rel="prev" title="操作系统的特征" href="4os-features.html" />
</head>
<body class="wy-body-for-nav">
<div class="wy-grid-for-nav">
<nav data-toggle="wy-nav-shift" class="wy-nav-side">
<div class="wy-side-scroll">
<div class="wy-side-nav-search" >
<a href="../index.html" class="icon icon-home" alt="Documentation Home"> rCore-Tutorial-Book-v3
</a>
<div role="search">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
</form>
</div>
</div>
<div class="wy-menu wy-menu-vertical" data-spy="affix" role="navigation" aria-label="main navigation">
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第零章:操作系统概述</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1what-is-os.html">什么是操作系统</a></li>
<li class="toctree-l2"><a class="reference internal" href="2os-interface.html">操作系统的接口</a></li>
<li class="toctree-l2"><a class="reference internal" href="3os-hw-abstract.html">操作系统抽象</a></li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">扩展阅读</a><ul class="simple">
</ul>
</li>
<li class="toctree-l2"><a class="reference internal" href="index.html#id2">为何要写这本操作系统书</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter4/index.html">第四章:地址空间(施工中)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter5/index.html">第五章:进程及重要系统调用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter6/index.html">第六章:文件描述符与进程间通信</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter7/index.html">第七章:数据持久化存储</a></li>
</ul>
<p class="caption"><span class="caption-text">Part2 - Do it better!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../chapter8/index.html">第八章:阻塞(暂定)</a></li>
</ul>
<p class="caption"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../appendix-a/index.html">附录 A:Rust 快速入门</a></li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-b/index.html">附录 B:常见工具的使用方法</a></li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-c/index.html">附录 C:深入机器模式:RustSBI</a></li>
<li class="toctree-l1"><a class="reference internal" href="../terminology.html">术语中英文对照表</a></li>
</ul>
<p class="caption"><span class="caption-text">开发注记</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../setup-sphinx.html">修改和构建本项目</a></li>
<li class="toctree-l1"><a class="reference internal" href="../rest-example.html">reStructuredText 基本语法</a></li>
</ul>
</div>
</div>
</nav>
<section data-toggle="wy-nav-shift" class="wy-nav-content-wrap">
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="../index.html">rCore-Tutorial-Book-v3</a>
</nav>
<div class="wy-nav-content">
<div class="rst-content">
<div role="navigation" aria-label="breadcrumbs navigation">
<ul class="wy-breadcrumbs">
<li><a href="../index.html" class="icon icon-home"></a> &raquo;</li>
<li><a href="index.html">第零章:操作系统概述</a> &raquo;</li>
<li>扩展阅读</li>
<li class="wy-breadcrumbs-aside">
<a href="../_sources/chapter0/5extend-reading.rst.txt" rel="nofollow"> View page source</a>
</li>
</ul>
<hr/>
</div>
<div role="main" class="document" itemscope="itemscope" itemtype="http://schema.org/Article">
<div itemprop="articleBody">
<div class="section" id="id1">
<h1>扩展阅读<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<p>扩展阅读部分</p>
</div>
</div>
</div>
<footer>
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="../quickstart.html" class="btn btn-neutral float-right" title="环境配置" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="4os-features.html" class="btn btn-neutral float-left" title="操作系统的特征" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
<hr/>
<div role="contentinfo">
<p>
&copy; 版权所有 2020, Yifan Wu
</p>
</div>
Built with <a href="http://sphinx-doc.org/">Sphinx</a> using a
<a href="https://github.com/rtfd/sphinx_rtd_theme">theme</a>
provided by <a href="https://readthedocs.org">Read the Docs</a>.
</footer>
</div>
</div>
</section>
</div>
<script type="text/javascript">
jQuery(function () {
SphinxRtdTheme.Navigation.enable(true);
});
</script>
</body>
</html>
\ No newline at end of file
......@@ -7,12 +7,12 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>环境配置 &mdash; rCore-Tutorial-Book-v3 0.1 文档</title>
<title>实验环境配置 &mdash; rCore-Tutorial-Book-v3 0.1 文档</title>
<link rel="stylesheet" href="_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="_static/pygments.css" type="text/css" />
<link rel="stylesheet" href="../_static/css/theme.css" type="text/css" />
<link rel="stylesheet" href="../_static/pygments.css" type="text/css" />
......@@ -21,15 +21,15 @@
<!--[if lt IE 9]>
<script src="_static/js/html5shiv.min.js"></script>
<script src="../_static/js/html5shiv.min.js"></script>
<![endif]-->
<script type="text/javascript" id="documentation_options" data-url_root="./" 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/language_data.js"></script>
<script type="text/javascript" id="documentation_options" data-url_root="../" 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/language_data.js"></script>
<script kind="utterances">
var commentsRunWhenDOMLoaded = cb => {
......@@ -64,16 +64,16 @@ var addUtterances = () => {
}
commentsRunWhenDOMLoaded(addUtterances);
</script>
<script src="_static/translations.js"></script>
<script src="../_static/translations.js"></script>
<script async="async" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.7/latest.js?config=TeX-AMS-MML_HTMLorMML"></script>
<script type="text/javascript" src="_static/js/theme.js"></script>
<script type="text/javascript" src="../_static/js/theme.js"></script>
<link rel="index" title="索引" href="genindex.html" />
<link rel="search" title="搜索" href="search.html" />
<link rel="next" title="第一章:RV64 裸机应用" href="chapter1/index.html" />
<link rel="prev" title="扩展阅读" href="chapter0/5extend-reading.html" />
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="next" title="第一章:RV64 裸机应用" href="../chapter1/index.html" />
<link rel="prev" title="操作系统的特征" href="4os-features.html" />
</head>
<body class="wy-body-for-nav">
......@@ -87,7 +87,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<a href="index.html" class="icon icon-home" alt="Documentation Home"> rCore-Tutorial-Book-v3
<a href="../index.html" class="icon icon-home" alt="Documentation Home"> rCore-Tutorial-Book-v3
......@@ -100,7 +100,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<div role="search">
<form id="rtd-search-form" class="wy-form" action="search.html" method="get">
<form id="rtd-search-form" class="wy-form" action="../search.html" method="get">
<input type="text" name="q" placeholder="Search docs" />
<input type="hidden" name="check_keywords" value="yes" />
<input type="hidden" name="area" value="default" />
......@@ -120,38 +120,44 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">环境配置</a><ul>
<li class="toctree-l2"><a class="reference internal" href="#id2">系统环境配置</a></li>
<li class="toctree-l2"><a class="reference internal" href="#rust">Rust 开发环境配置</a></li>
<li class="toctree-l2"><a class="reference internal" href="#qemu">Qemu 模拟器安装</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id5">其他工具安装</a></li>
<li class="toctree-l2"><a class="reference internal" href="#rcore-tutorial-v3">运行 rCore-Tutorial-v3</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第零章:操作系统概述</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1what-is-os.html">什么是操作系统</a></li>
<li class="toctree-l2"><a class="reference internal" href="2os-interface.html">操作系统的接口</a></li>
<li class="toctree-l2"><a class="reference internal" href="3os-hw-abstract.html">操作系统抽象</a></li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">实验环境配置</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#id2">系统环境配置</a></li>
<li class="toctree-l3"><a class="reference internal" href="#rust">Rust 开发环境配置</a></li>
<li class="toctree-l3"><a class="reference internal" href="#qemu">Qemu 模拟器安装</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id5">其他工具安装</a></li>
<li class="toctree-l3"><a class="reference internal" href="#rcore-tutorial-v3">运行 rCore-Tutorial-v3</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter4/index.html">第四章:地址空间(施工中)</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter5/index.html">第五章:进程及重要系统调用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter6/index.html">第六章:文件描述符与进程间通信</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter7/index.html">第七章:数据持久化存储</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter4/index.html">第四章:地址空间(施工中)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter5/index.html">第五章:进程及重要系统调用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter6/index.html">第六章:文件描述符与进程间通信</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter7/index.html">第七章:数据持久化存储</a></li>
</ul>
<p class="caption"><span class="caption-text">Part2 - Do it better!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter8/index.html">第八章:阻塞(暂定)</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter8/index.html">第八章:阻塞(暂定)</a></li>
</ul>
<p class="caption"><span class="caption-text">附录</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="appendix-a/index.html">附录 A:Rust 快速入门</a></li>
<li class="toctree-l1"><a class="reference internal" href="appendix-b/index.html">附录 B:常见工具的使用方法</a></li>
<li class="toctree-l1"><a class="reference internal" href="appendix-c/index.html">附录 C:深入机器模式:RustSBI</a></li>
<li class="toctree-l1"><a class="reference internal" href="terminology.html">术语中英文对照表</a></li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-a/index.html">附录 A:Rust 快速入门</a></li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-b/index.html">附录 B:常见工具的使用方法</a></li>
<li class="toctree-l1"><a class="reference internal" href="../appendix-c/index.html">附录 C:深入机器模式:RustSBI</a></li>
<li class="toctree-l1"><a class="reference internal" href="../terminology.html">术语中英文对照表</a></li>
</ul>
<p class="caption"><span class="caption-text">开发注记</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="setup-sphinx.html">修改和构建本项目</a></li>
<li class="toctree-l1"><a class="reference internal" href="rest-example.html">reStructuredText 基本语法</a></li>
<li class="toctree-l1"><a class="reference internal" href="../setup-sphinx.html">修改和构建本项目</a></li>
<li class="toctree-l1"><a class="reference internal" href="../rest-example.html">reStructuredText 基本语法</a></li>
</ul>
......@@ -167,7 +173,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<nav class="wy-nav-top" aria-label="top navigation">
<i data-toggle="wy-nav-top" class="fa fa-bars"></i>
<a href="index.html">rCore-Tutorial-Book-v3</a>
<a href="../index.html">rCore-Tutorial-Book-v3</a>
</nav>
......@@ -196,15 +202,17 @@ commentsRunWhenDOMLoaded(addUtterances);
<ul class="wy-breadcrumbs">
<li><a href="index.html" class="icon icon-home"></a> &raquo;</li>
<li><a href="../index.html" class="icon icon-home"></a> &raquo;</li>
<li><a href="index.html">第零章:操作系统概述</a> &raquo;</li>
<li>环境配置</li>
<li>实验环境配置</li>
<li class="wy-breadcrumbs-aside">
<a href="_sources/quickstart.rst.txt" rel="nofollow"> View page source</a>
<a href="../_sources/chapter0/5setup-devel-env.rst.txt" rel="nofollow"> View page source</a>
</li>
......@@ -218,7 +226,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<div itemprop="articleBody">
<div class="section" id="id1">
<h1>环境配置<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<h1>实验环境配置<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<p>本节我们将完成环境配置并成功运行 rCore-Tutorial-v3 。整个流程分为下面几个部分:</p>
......@@ -231,9 +239,9 @@ commentsRunWhenDOMLoaded(addUtterances);
</ul>
<div class="section" id="id2">
<h2>系统环境配置<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
<p>目前实验仅支持 Ubuntu18.04 操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
<p>目前实验仅支持 Ubuntu18.04 + 操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。</p>
<p>特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 。
<p>特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 / 20.04
步骤如下:</p>
<ul>
<li><p>升级 Windows 10 到最新版(Windows 10 版本 18917 或以后的内部版本)。注意,如果
......@@ -257,7 +265,7 @@ VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。</p>
</div>
</li>
<li><p><a class="reference external" href="https://docs.microsoft.com/zh-cn/windows/wsl/install-win10#step-4---download-the-linux-kernel-update-package">下载 Linux 内核安装包</a></p></li>
<li><p>在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 。</p></li>
<li><p>在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 / 20.04</p></li>
</ul>
<p>如果你打算使用 VMware 安装虚拟机的话,我们已经配置好了一个能直接运行 rCore-Tutorial-v3 的
Ubuntu18.04 镜像,它是一个 <code class="docutils literal notranslate"><span class="pre">vmdk</span></code> 格式的虚拟磁盘文件,只需要在 VMware 中新建一台虚拟机,
......@@ -282,6 +290,12 @@ Visual Studio Code,能够更容易完成实验并撰写实验报告。</p>
curl https://sh.rustup.rs -sSf <span class="p">|</span> sh
</pre></div>
</div>
<p>或者使用tuna源来加速 <a class="reference external" href="https://mirrors.tuna.tsinghua.edu.cn/help/rustup/">参见 rustup 帮助</a></p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="nb">export</span> <span class="nv">RUSTUP_DIST_SERVER</span><span class="o">=</span>https://mirrors.tuna.edu.cn/rustup
<span class="nb">export</span> <span class="nv">RUSTUP_UPDATE_ROOT</span><span class="o">=</span>https://mirrors.tuna.edu.cn/rustup/rustup
curl https://sh.rustup.rs -sSf <span class="p">|</span> sh
</pre></div>
</div>
<p>或者也可以通过在运行前设置命令行中的科学上网代理来实现:</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># e.g. Shadowsocks 代理,请根据自身配置灵活调整下面的链接</span>
<span class="nb">export</span> <span class="nv">https_proxy</span><span class="o">=</span>http://127.0.0.1:1080
......@@ -311,7 +325,34 @@ curl https://sh.rustup.rs -sSf <span class="p">|</span> sh
<span class="n">registry</span> <span class="o">=</span> <span class="s">&quot;git://mirrors.ustc.edu.cn/crates.io-index&quot;</span>
</pre></div>
</div>
<p>至于 Rust 开发环境,推荐 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。</p>
<p>同样,也可以使用tuna源 <a class="reference external" href="https://mirrors.tuna.tsinghua.edu.cn/help/crates.io-index.git/">参见 crates.io 帮助</a></p>
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="k">[source.crates-io]</span>
<span class="n">replace-with</span> <span class="o">=</span> <span class="s">&#39;tuna&#39;</span>
<span class="k">[source.tuna]</span>
<span class="n">registry</span> <span class="o">=</span> <span class="s">&quot;https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git&quot;</span>
</pre></div>
</div>
<p>接下来安装一些Rust相关的软件包</p>
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span>rustup target add riscv64gc-unknown-none-elf
cargo install cargo-binutils
rustup component add llvm-tools-preview
rustup component add rust-src
</pre></div>
</div>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p>rCore-Tutorial 仓库中的 <code class="docutils literal notranslate"><span class="pre">Makefile</span></code> 包含了这些工具的安装,如果你使用 <code class="docutils literal notranslate"><span class="pre">make</span> <span class="pre">run</span></code> 也可以不手动安装。</p>
</div>
<p>至于 Rust 开发环境,推荐 JetBrains Clion + Rust插件 或者 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<ul class="simple">
<li><p>JetBrains Clion是付费商业软件,但对于学生和教师,只要在 JetBrains 网站注册账号,可以享受一定期限(半年左右)的免费使用的福利。</p></li>
<li><p>Visual Studio Code 是开源软件,不用付费就可使用。</p></li>
<li><p>当然,采用 VIM,Emacs 等传统的编辑器也是没有问题的。</p></li>
</ul>
</div>
</div>
<div class="section" id="qemu">
<h2>Qemu 模拟器安装<a class="headerlink" href="#qemu" title="永久链接至标题"></a></h2>
......@@ -321,7 +362,7 @@ curl https://sh.rustup.rs -sSf <span class="p">|</span> sh
<div class="highlight-bash notranslate"><div class="highlight"><pre><span></span><span class="c1"># 安装编译所需的依赖包</span>
sudo apt install autoconf automake autotools-dev curl libmpc-dev libmpfr-dev libgmp-dev <span class="se">\</span>
gawk build-essential bison flex texinfo gperf libtool patchutils bc <span class="se">\</span>
zlib1g-dev libexpat-dev git
zlib1g-dev libexpat-dev pkg-config libglib2.0-dev libpixman-1-dev git tmux python3
<span class="c1"># 下载源码包</span>
<span class="c1"># 如果下载速度过慢可以使用我们提供的百度网盘链接:https://pan.baidu.com/s/1z-iWIPjxjxbdFS2Qf-NKxQ</span>
<span class="c1"># 提取码 8woe</span>
......@@ -375,14 +416,14 @@ sudo apt install python-serial
<h2>运行 rCore-Tutorial-v3<a class="headerlink" href="#rcore-tutorial-v3" title="永久链接至标题"></a></h2>
<p>如果是在 Qemu 平台上运行,只需在 <code class="docutils literal notranslate"><span class="pre">os</span></code> 目录下 <code class="docutils literal notranslate"><span class="pre">make</span> <span class="pre">run</span></code> 即可。在内核加载完毕之后,可以看到目前可以用的
应用程序。 <code class="docutils literal notranslate"><span class="pre">usertests</span></code> 打包了其中的很大一部分,所以我们可以运行它,只需输入在终端中输入它的名字即可。</p>
<img alt="_images/qemu-final.gif" src="_images/qemu-final.gif" />
<img alt="../_images/qemu-final.gif" src="../_images/qemu-final.gif" />
<p>之后,可以先按下 <code class="docutils literal notranslate"><span class="pre">Ctrl+A</span></code> ,再按下 <code class="docutils literal notranslate"><span class="pre">X</span></code> 来退出 Qemu。</p>
<p>如果是在 K210 平台上运行则略显复杂。</p>
<p>首先,我们需要将 MicroSD 插入 PC 来将文件系统镜像拷贝上去。</p>
<img alt="_images/prepare-sd.gif" src="_images/prepare-sd.gif" />
<img alt="../_images/prepare-sd.gif" src="../_images/prepare-sd.gif" />
<p>随后,我们将 MicroSD 插入 K210 开发板,将 K210 开发板连接到 PC ,然后进入 <code class="docutils literal notranslate"><span class="pre">os</span></code> 目录 <code class="docutils literal notranslate"><span class="pre">make</span> <span class="pre">run</span> <span class="pre">BOARD=k210</span></code>
在 K210 开发板上跑 Tutorial 。</p>
<img alt="_images/k210-final.gif" src="_images/k210-final.gif" />
<img alt="../_images/k210-final.gif" src="../_images/k210-final.gif" />
<p>之后,可以按下 <code class="docutils literal notranslate"><span class="pre">Ctrl+]</span></code> 来退出串口终端。</p>
<p>到这里,恭喜你完成了实验环境的配置,可以开始阅读教程的正文部分了!</p>
</div>
......@@ -396,10 +437,10 @@ sudo apt install python-serial
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="chapter1/index.html" class="btn btn-neutral float-right" title="第一章:RV64 裸机应用" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="../chapter1/index.html" class="btn btn-neutral float-right" title="第一章:RV64 裸机应用" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="chapter0/5extend-reading.html" class="btn btn-neutral float-left" title="扩展阅读" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
<a href="4os-features.html" class="btn btn-neutral float-left" title="操作系统的特征" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
......
......@@ -125,11 +125,9 @@ commentsRunWhenDOMLoaded(addUtterances);
<li class="toctree-l2"><a class="reference internal" href="2os-interface.html">操作系统的接口</a></li>
<li class="toctree-l2"><a class="reference internal" href="3os-hw-abstract.html">操作系统抽象</a></li>
<li class="toctree-l2"><a class="reference internal" href="4os-features.html">操作系统的特征</a></li>
<li class="toctree-l2"><a class="reference internal" href="5extend-reading.html">扩展阅读</a></li>
<li class="toctree-l2"><a class="reference internal" href="#id2">为何要写这本操作系统书</a></li>
<li class="toctree-l2"><a class="reference internal" href="5setup-devel-env.html">实验环境配置</a></li>
</ul>
</li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -222,14 +220,22 @@ commentsRunWhenDOMLoaded(addUtterances);
<span id="id1"></span><h1>第零章:操作系统概述<a class="headerlink" href="#link-chapter0" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<div class="section" id="id2">
<h2>为何要写这本操作系统书<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
<p><strong>为何要写这本操作系统书</strong></p>
<p>现在国内外已有一系列优秀的操作系统教材,例如William Stallings的《Operating Systems Internals and Design Principles》,Avi Silberschatz、Peter Baer Galvin 和 Greg Gagne 的《Operating System Concepts》,Remzi H. Arpaci-Dusseau 和 Andrea C. Arpaci-Dusseau 的《Operating Systems: Three Easy Pieces》等。然而,从我们从2000年以来的教学实践来看,某些经典教材对操作系统的概念和原理很重视,但缺乏对操作系统的概念/原理与操作系统的实现之间建立一个联系的桥梁,导致学生发现操作系统实现相关的实验与操作系统的概念相比,有较大的鸿沟。此外,部分教材把 x86 作为的操作系统实验的硬件参考平台,缺乏对当前快速发展的RISC-V等体系结构的实验支持,使得学生在操作系统实验中可能需要花较大代价了解相对繁杂的x86硬件细节,影响操作系统实验的效果。还有部分教材也基本以 Linux/Unix 等实际操作系统为主,难以让学生在一个学期内掌握其中的核心设计。</p>
<p>对于在校的学生和已经参加工作的工程师而言,能否以较小的时间和精力比较全面地了解操作系统呢?陆游老夫子说过“纸上得来终觉浅,绝知此事要躬行”,也许在了解基本的操作系统概念和原理基础上,通过实际动手来一步一步分析、设计和实现一个操作系统,会发现操作系统原来如此,概念原理和实际实现之间有紧密的联系和巨大的差异。</p>
<p>也许学生有疑问,在本科期间自己能通过设计实现一个操作系统吗?这一点其实已经是一个实际存在的现实情况了。MIT教授 Frans Kaashoek等师生设计实现了基于UNIX v6的xv6教学操作系统用于每年的本科操作系统课的实验中,而且在2019年,他们进一步改进了xv6,让其运行在RISC-V CPU上。RISC-V CPU同样来源于高校,是Berkeley教授David Patterson等师生设计实现的第五代RISC CPU(现在简称RISC-V),用于计算机组成原理和计算机体系机构课程的教学和科研中。</p>
<p>这些都给了我们很大的启发:对一个计算机专业的本科生而言,设计实现一个操作系统(包括CPU)有挑战但可行! 所以本书的目标是以简洁的RISC-V CPU为底层硬件基础,根据上层应用从小到大的需求,逐步讲解如何设计并实现满足这些需求的“从小到大”的多个操作系统。并在实现操作系统的过程中,逐步解析操作系统各种概念与原理的知识点,对应的做到有“理”可循和有“码”可查,最终让读者了解和掌握操作系统的原理、设计与实现。</p>
<p>在具体撰写过程中,第零章是对操作系统的一个概述,让读者对操作系统的历史、定义、特征等概念上有一个大致的了解。后面的每个章节体现了操作系统的一个微缩的历史发展过程,即从对应用由简到繁的支持的角度出发,每章会讲解如何设计一个可运行应用的操作系统,满足应用的阶段性需求。从而读者可以通过对应配套的操作系统设计实验,了解如何从一个微不足道的小操作系统,根据应用需求,添加或增强操作系统功能,逐步形成一个类似UNIX的相对完善的操作系统。每一步都小到足以让人感觉到易于掌控,而在每一步结束时,你都有一个可以工作的操作系统。另外,通过足够详尽的测试程序 ,可以随时验证读者实现的操作系统在每次更新后是否正常工作。由于实验的代码规模和实现复杂度在一个逐步递增的可控范围内,读者可以结合对应于操作系统设计实验的进一步的原理讲解,来建立操作系统概念原理和实际实现的对应关系,从而能够通过操作系统实验的实践过程来加强对理论概念的理解,通过理论概念来进一步指导操作系统实验的实现与改进。</p>
<p>在你开始阅读与实践本书讲解的内容之前,你需要决定用什么编程语言来完成操作系统实验。你可以用任何你喜欢的编程语言来实现操作系统。我们推荐的编程语言是Rust语言或者C语言。</p>
<p>在你开始阅读与实践本书讲解的内容之前,你需要决定用什么编程语言来完成操作系统实验。你可以用任何你喜欢的编程语言来实现操作系统。我们推荐的编程语言是Rust。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p><strong>目前常见的操作系统内核都是基于C语言的,为何要推荐Rust语言?</strong></p>
<ul class="simple">
<li><p>没错,C语言就是为写UNIX而诞生的。Dennis Ritchie和KenThompson没有期望设计一种新语言能帮助高效简洁地开发复杂的应用业务逻辑,只是希望用一种简洁的方式抽象出计算机的行为,便于编写控制计算机硬件的操作系统,最终的结果就是C语言。</p></li>
<li><p>C语言的指针的天使与魔鬼,且C语言缺少有效的并发支持,导致内存和并发漏洞成为当前操作系统的噩梦。</p></li>
<li><p>从某种角度上看,Rust的目标是解决C的短板,取代C。所以用Rust写OS具有很好的开发和运行的体验。</p></li>
<li><p>用Rust写OS的代价仅仅是学会用Rust编程。</p></li>
</ul>
</div>
</div>
......
......@@ -7,7 +7,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>应用程序行环境与平台支持 &mdash; rCore-Tutorial-Book-v3 0.1 文档</title>
<title>应用程序行环境与平台支持 &mdash; rCore-Tutorial-Book-v3 0.1 文档</title>
......@@ -121,11 +121,11 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2 current"><a class="current reference internal" href="#">应用程序运行环境与平台支持</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#id2">应用程序运行环境</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id3">平台与目标三元组</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">应用程序执行环境与平台支持</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#id2">执行应用程序</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id3">应用程序执行环境</a></li>
<li class="toctree-l3"><a class="reference internal" href="#id4">平台与目标三元组</a></li>
<li class="toctree-l3"><a class="reference internal" href="#rust">Rust 标准库与核心库</a></li>
</ul>
</li>
......@@ -206,7 +206,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<li><a href="index.html">第一章:RV64 裸机应用</a> &raquo;</li>
<li>应用程序行环境与平台支持</li>
<li>应用程序行环境与平台支持</li>
<li class="wy-breadcrumbs-aside">
......@@ -226,10 +226,12 @@ commentsRunWhenDOMLoaded(addUtterances);
<div itemprop="articleBody">
<div class="section" id="id1">
<h1>应用程序行环境与平台支持<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<h1>应用程序行环境与平台支持<a class="headerlink" href="#id1" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<p>作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:</p>
<div class="section" id="id2">
<h2>执行应用程序<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
<p>我们先开发并运行一个简单的“Hello, world”应用程序,看看一个简单应用程序从开发到执行的全过程。作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$</span> cargo new os --bin
</pre></div>
</div>
......@@ -245,8 +247,8 @@ commentsRunWhenDOMLoaded(addUtterances);
</div>
<p>其中 <code class="docutils literal notranslate"><span class="pre">Cargo.toml</span></code> 中保存着项目的配置,包括作者的信息、联系方式以及库依赖等等。显而易见源代码保存在 <code class="docutils literal notranslate"><span class="pre">src</span></code> 目录下,目前为止只有 <code class="docutils literal notranslate"><span class="pre">main.rs</span></code>
一个文件,让我们看一下里面的内容:</p>
<div class="literal-block-wrapper docutils container" id="id4">
<div class="code-block-caption"><span class="caption-text">最简单的 Rust 应用</span><a class="headerlink" href="#id4" title="永久链接至代码"></a></div>
<div class="literal-block-wrapper docutils container" id="id5">
<div class="code-block-caption"><span class="caption-text">最简单的 Rust 应用</span><a class="headerlink" href="#id5" title="永久链接至代码"></a></div>
<div class="highlight-rust notranslate"><table class="highlighttable"><tr><td class="linenos"><div class="linenodiv"><pre>1
2
3</pre></div></td><td class="code"><div class="highlight"><pre><span></span><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"></span>
......@@ -263,23 +265,24 @@ commentsRunWhenDOMLoaded(addUtterances);
<span class="go">Hello, world!</span>
</pre></div>
</div>
<p>如我们预想的一样,我们在屏幕上看到了一行 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 。但是,需要注意到我们所享受到的编程的方便并不是理所当然的,背后有着从硬件
到软件的多种机制的支持。</p>
<div class="section" id="id2">
<h2>应用程序运行环境<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
<p>如下图所示,应用程序的运行需要下面一套运行环境栈的支持:</p>
<div class="figure align-center" id="id5">
<p>如我们预想的一样,我们在屏幕上看到了一行 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 。但是,需要注意到我们所享受到的编程和执行程序的方便性并不是理所当然的,背后有着从硬件
到软件的多种机制的支持。特别是对于应用程序的运行,是需要有一个强大的执行环境来帮助。接下来,我们就要看看有操作系统加持的强大的执行环境。</p>
</div>
<div class="section" id="id3">
<h2>应用程序执行环境<a class="headerlink" href="#id3" title="永久链接至标题"></a></h2>
<p>如下图所示,应用程序的运行需要下面一套执行环境栈的支持:</p>
<div class="figure align-center" id="id6">
<span id="app-software-stack"></span><img alt="../_images/app-software-stack.png" src="../_images/app-software-stack.png" />
<p class="caption"><span class="caption-text">应用程序运行环境栈:图中的白色块自上而下(越往下则越靠近底层,下层作为上层的执行环境支持上层代码的运行)表示各级运行环境,
黑色块则表示相邻两层运行环境之间的接口。</span><a class="headerlink" href="#id5" title="永久链接至图片"></a></p>
<p class="caption"><span class="caption-text">应用程序执行环境栈:图中的白色块自上而下(越往下则越靠近底层,下层作为上层的执行环境支持上层代码的运行)表示各级执行环境,
黑色块则表示相邻两层执行环境之间的接口。</span><a class="headerlink" href="#id6" title="永久链接至图片"></a></p>
</div>
<p id="term-execution-environment">我们的应用位于最上层,它可以通过调用编程语言提供的标准库或者其他三方库对外提供的功能强大的函数接口,使得仅需少量的源代码就能完成复杂的
功能。但是这些库的功能不仅限于此,事实上它们属于应用程序的 <strong>执行环境</strong> (Execution Environment),在我们通常不会注意到的地方,它
们还会在执行应用之前完成一些初始化工作,并在应用程序执行的时候对它进行监控。我们在打印 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 时使用的 <code class="docutils literal notranslate"><span class="pre">println!</span></code>
宏正是由 Rust 标准库 std 提供的。</p>
宏正是由 Rust 标准库 std 和GNU Libc库等提供的。</p>
<p id="term-system-call">从内核/操作系统的角度看来,它上面的一切都属于用户态,而它自身属于内核态。无论用户态应用如何编写,是手写汇编代码,还是基于某种编程语言利用
其标准库或三方库,某些功能总要直接或间接的通过内核/操作系统提供的 <strong>系统调用</strong> (System Call) 来实现。因此系统调用充当了用户和内核之间
的边界。内核作为用户态的行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。</p>
的边界。内核作为用户态的行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p><strong>Hello, world! 用到了哪些系统调用?</strong></p>
......@@ -303,7 +306,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="admonition-title">注解</p>
<p><strong>多层执行环境都是必需的吗?</strong></p>
<p>除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 <strong>抽象</strong> (Abstraction),
并为上层提供了一套行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
并为上层提供了一套行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
应有的灵活性。比如,当你在考虑在项目中应该使用哪个函数库的时候,就常常需要这方面的权衡:过多的抽象和过少的抽象自然都是不合适的。</p>
<p>实际上,我们通过应用程序的特征来判断它需要什么程度的抽象。</p>
<ul class="simple">
......@@ -316,17 +319,17 @@ commentsRunWhenDOMLoaded(addUtterances);
</ul>
</div>
</div>
<div class="section" id="id3">
<h2>平台与目标三元组<a class="headerlink" href="#id3" title="永久链接至标题"></a></h2>
<p id="term-platform">对于一份用某种编程语言实现的源代码而言,编译器在将其通过编译、链接得到目标文件的时候需要知道程序要在哪个 <strong>平台</strong> (Platform) 上运行。
从上面给出的 <a class="reference internal" href="#app-software-stack"><span class="std std-ref">应用程序行环境栈</span></a> 可以看出:</p>
<div class="section" id="id4">
<h2>平台与目标三元组<a class="headerlink" href="#id4" title="永久链接至标题"></a></h2>
<p id="term-platform">对于一份用某种编程语言实现的源代码而言,编译器在将其通过编译、链接得到目标文件的时候需要知道程序要在哪个 <strong>平台</strong> (Platform) 上运行。这里 <strong>平台</strong> 主要是指CPU类型、操作系统类型和标准运行时库的组合。
从上面给出的 <a class="reference internal" href="#app-software-stack"><span class="std std-ref">应用程序行环境栈</span></a> 可以看出:</p>
<ul class="simple">
<li><p>如果用户态基于的内核不同,会导致系统调用接口不同或者语义不一致;</p></li>
<li><p>如果底层硬件不同,对于硬件资源的访问方式会有差异。特别是 ISA 不同的话,对上提供的指令集和寄存器都不同。</p></li>
</ul>
<p>它们都会导致最终生成的目标文件有很大不同。需要指出的是,某些编译器支持同一份源代码无需修改就可编译到多个不同的目标平台并在上面运行。这种
情况下,源代码是 <strong>跨平台</strong> 的。而另一些编译器则已经预设好了一个固定的目标平台。</p>
<p id="term-target-triplet">我们可以通过 <strong>目标三元组</strong> (Target Triplet) 来描述一个目标平台。它一般包括 CPU 架构、CPU 厂商和操作系统,它们确实都会控制目标文件的生成。
<p id="term-target-triplet">我们可以通过 <strong>目标三元组</strong> (Target Triplet) 来描述一个目标平台。它一般包括 CPU 架构、CPU 厂商、操作系统和运行时库,它们确实都会控制目标文件的生成。
比如,我们可以尝试看一下之前的 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 的目标平台是什么。这可以通过打印编译器 rustc 的默认配置信息:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$</span> rustc --version --verbose
<span class="go">rustc 1.48.0-nightly (73dc675b9 2020-09-06)</span>
......@@ -338,7 +341,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<span class="go">LLVM version: 11.0</span>
</pre></div>
</div>
<p>从其中的 host 一项可以看出默认的目标平台是 <code class="docutils literal notranslate"><span class="pre">x86_64-unknown-linux-gnu</span></code>,其中 CPU 架构是 x86_64,CPU 厂商是 unknown,操作系统是 linux-gnu
<p>从其中的 host 一项可以看出默认的目标平台是 <code class="docutils literal notranslate"><span class="pre">x86_64-unknown-linux-gnu</span></code>,其中 CPU 架构是 x86_64,CPU 厂商是 unknown,操作系统是 linux,运行时库是gnu libc(封装了Linux系统调用,并提供POSIX接口为主的函数库)
这种无论编译器还是其目标文件都在我们当前所处的平台运行是一种最简单也最普遍的情况。但是很快我们就将遇到另外一种情况。</p>
<p>讲了这么多,终于该介绍我们的主线任务了。我们希望能够在另一个平台上运行 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code>,而与之前的默认平台不同的地方在于,我们将 CPU 架构从
x86_64 换成 RISC-V。</p>
......@@ -351,6 +354,7 @@ x86_64 换成 RISC-V。</p>
</div>
<p>可以看一下目前 Rust 编译器支持哪些基于 RISC-V 的平台:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$</span> rustc --print target-list <span class="p">|</span> grep riscv
<span class="go">riscv32gc-unknown-linux-gnu</span>
<span class="go">riscv32i-unknown-none-elf</span>
<span class="go">riscv32imac-unknown-none-elf</span>
<span class="go">riscv32imc-unknown-none-elf</span>
......@@ -359,10 +363,9 @@ x86_64 换成 RISC-V。</p>
<span class="go">riscv64imac-unknown-none-elf</span>
</pre></div>
</div>
<p>这里我们选择的是 <code class="docutils literal notranslate"><span class="pre">riscv64gc-unknown-none-elf</span></code>,目标三元组中的操作系统是 none-elf,表明没有任何系统调用支持。这里我们之所以不选择有
<p>这里我们选择的是 <code class="docutils literal notranslate"><span class="pre">riscv64gc-unknown-none-elf</span></code>,目标三元组中的CPU 架构是 riscv64gc,厂商是 unknown,操作系统是 none,elf表示没有标准的运行时库(表明没有任何系统调用的封装支持),但可以生成ELF格式的执行程序。这里我们之所以不选择有
linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"><span class="pre">riscv64gc-unknown-linux-gnu</span></code>,是因为我们只是想跑一个 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code>,没有必要使用操作系统所提供的
那么高级的抽象。而且我们很清楚后续我们要开发的是一个内核,如果仅仅基于已有操作系统提供的系统调用的话,它自身的抽象能力会受到很大限制。所以它必须
直面底层硬件来解锁更大的抽象能力上限。</p>
那么高级的抽象。而且我们很清楚后续我们要开发的是一个操作系统内核,它必须直面底层物理硬件(bare-metal)来提供更大的操作系统服务功能,已有操作系统(如Linux)提供的系统调用服务对这个内核而言是多余的。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p><strong>RISC-V 指令集拓展</strong></p>
......@@ -390,11 +393,11 @@ linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"
<span class="go"> = note: the `riscv64gc-unknown-none-elf` target may not be installed</span>
</pre></div>
</div>
<p id="term-bare-metal">在之前的环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。因此只是单纯的在这个目标平台上找不到
<p id="term-bare-metal">在之前的开发环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。这个问题只是单纯的表示在这个目标平台上找不到
Rust 标准库 std。我们之前曾经提到过,编程语言的标准库或三方库的某些功能会直接或间接的用到操作系统提供的系统调用。但目前我们所选的目标平台不存在
任何操作系统支持,于是 Rust 并没有为这个目标平台支持完整的标准库 std。类似这样的平台通常被我们称为 <strong>裸机平台</strong> (bare-metal)。</p>
<p>幸运的是,Rust 有一个对 std 裁剪过后的核心库 core,这个库是不需要任何操作系统支持的,相对的它的功能也比较受限,但是也包含了 Rust 语言
相当一部分的核心机制,可以满足我们的大部分需求。在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
相当一部分的核心机制,可以满足我们的大部分需求。Rust 语言是一种面向系统(包括操作系统)开发的语言,所以在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
程度上减轻我们的编程负担。它们是我们能够在裸机平台挣扎求生的最主要倚仗。</p>
<p>于是,我们知道在裸机平台上我们要将对于标准库 std 的引用换成核心库 core。但是做起来其实并没有那么容易。</p>
</div>
......
......@@ -73,7 +73,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="next" title="重建最小化运行时" href="3minimal-rt.html" />
<link rel="prev" title="应用程序行环境与平台支持" href="1app-ee-platform.html" />
<link rel="prev" title="应用程序行环境与平台支持" href="1app-ee-platform.html" />
</head>
<body class="wy-body-for-nav">
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">移除标准库依赖</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#println">移除 println! 宏</a></li>
<li class="toctree-l3"><a class="reference internal" href="#panic-handler">提供语义项 panic_handler</a></li>
......@@ -231,9 +230,9 @@ commentsRunWhenDOMLoaded(addUtterances);
</div>
<p>本节我们尝试移除之前的 <code class="docutils literal notranslate"><span class="pre">Hello</span> <span class="pre">world!</span></code> 程序对于标准库的依赖,使得它能够编译到裸机平台 RV64GC 上。</p>
<p>我们首先在 <code class="docutils literal notranslate"><span class="pre">os</span></code> 目录下新建 <code class="docutils literal notranslate"><span class="pre">.cargo</span></code> 目录,并在这个目录下创建 <code class="docutils literal notranslate"><span class="pre">config</span></code> 文件,并在里面输入如下内容:</p>
<div class="highlight-default notranslate"><div class="highlight"><pre><span></span><span class="o">//</span> <span class="n">os</span><span class="o">/.</span><span class="n">cargo</span><span class="o">/</span><span class="n">config</span>
<span class="p">[</span><span class="n">build</span><span class="p">]</span>
<span class="n">target</span> <span class="o">=</span> <span class="s2">&quot;riscv64gc-unknown-none-elf&quot;</span>
<div class="highlight-toml notranslate"><div class="highlight"><pre><span></span><span class="c1"># os/.cargo/config</span>
<span class="k">[build]</span>
<span class="n">target</span> <span class="o">=</span> <span class="s">&quot;riscv64gc-unknown-none-elf&quot;</span>
</pre></div>
</div>
<p id="term-cross-compile">这会对于 Cargo 工具在 os 目录下的行为进行调整:现在默认会使用 riscv64gc 作为目标平台而不是原先的默认 x86_64-unknown-linux-gnu。
......@@ -356,7 +355,7 @@ error: requires `start` lang_item
<a href="3minimal-rt.html" class="btn btn-neutral float-right" title="重建最小化运行时" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="1app-ee-platform.html" class="btn btn-neutral float-left" title="应用程序行环境与平台支持" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
<a href="1app-ee-platform.html" class="btn btn-neutral float-left" title="应用程序行环境与平台支持" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
......
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="2remove-std.html">移除标准库依赖</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">重建最小化运行时</a><ul>
<li class="toctree-l3"><a class="reference internal" href="#function-call-and-stack">函数调用与栈</a></li>
......
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="2remove-std.html">移除标准库依赖</a></li>
<li class="toctree-l2"><a class="reference internal" href="3minimal-rt.html">重建最小化运行时</a></li>
<li class="toctree-l2 current"><a class="current reference internal" href="#">手动加载、运行应用程序</a><ul>
......
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="2remove-std.html">移除标准库依赖</a></li>
<li class="toctree-l2"><a class="reference internal" href="3minimal-rt.html">重建最小化运行时</a></li>
<li class="toctree-l2"><a class="reference internal" href="4load-manually.html">手动加载、运行应用程序</a></li>
......
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第一章:RV64 裸机应用</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="2remove-std.html">移除标准库依赖</a></li>
<li class="toctree-l2"><a class="reference internal" href="3minimal-rt.html">重建最小化运行时</a></li>
<li class="toctree-l2"><a class="reference internal" href="4load-manually.html">手动加载、运行应用程序</a></li>
......
......@@ -72,8 +72,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<link rel="index" title="索引" href="../genindex.html" />
<link rel="search" title="搜索" href="../search.html" />
<link rel="next" title="应用程序行环境与平台支持" href="1app-ee-platform.html" />
<link rel="prev" title="环境配置" href="../quickstart.html" />
<link rel="next" title="应用程序行环境与平台支持" href="1app-ee-platform.html" />
<link rel="prev" title="实验环境配置" href="../chapter0/5setup-devel-env.html" />
</head>
<body class="wy-body-for-nav">
......@@ -121,9 +121,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">第一章:RV64 裸机应用</a><ul>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="1app-ee-platform.html">应用程序行环境与平台支持</a></li>
<li class="toctree-l2"><a class="reference internal" href="2remove-std.html">移除标准库依赖</a></li>
<li class="toctree-l2"><a class="reference internal" href="3minimal-rt.html">重建最小化运行时</a></li>
<li class="toctree-l2"><a class="reference internal" href="4load-manually.html">手动加载、运行应用程序</a></li>
......@@ -222,11 +221,10 @@ commentsRunWhenDOMLoaded(addUtterances);
<span id="link-chapter1"></span><h1>第一章:RV64 裸机应用<a class="headerlink" href="#rv64" title="永久链接至标题"></a></h1>
<div class="toctree-wrapper compound">
</div>
<p>大多数程序员的第一行代码都从 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 开始,当我们满怀着好奇心在编辑器内键入仅仅数个字节,再经过几行命令编译、运行,终于
在黑洞洞的终端窗口中看到期望中的结果的时候,一扇通往编程世界的大门已经打开。时至今日,我们能够隐约意识到编程工作能够如此方便简洁并不是
理所当然的,实际上有着多层硬件、软件隐藏在它背后,才让我们不必付出那么多努力就能够创造出功能强大的应用程序。</p>
<p>大多数程序员的第一行代码都从 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 开始,当我们满怀着好奇心在编辑器内键入仅仅数个字节,再经过几行命令编译(靠的是编译器)、运行(靠的是操作系统),终于在黑洞洞的终端窗口中看到期望中的结果的时候,一扇通往编程世界的大门已经打开。在本章第一节 <a class="reference internal" href="1app-ee-platform.html"><span class="doc">应用程序执行环境与平台支持</span></a> 中,可以看到用Rust语言编写的非常简单的“Hello, world”应用程序。</p>
<p>不过我们能够隐约意识到编程工作能够如此方便简洁并不是理所当然的,实际上有着多层硬件和软件工具和支撑环境隐藏在它背后,才让我们不必付出那么多努力就能够创造出功能强大的应用程序。</p>
<p>本章我们的目标仍然只是输出 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> ,但这一次,我们将离开舒适区,基于一个几乎空无一物的平台从零开始搭建我们自己的高楼大厦,
而不是仅仅通过一行语句就完成任务。</p>
而不是仅仅通过一行语句就完成任务。所以,在接下来的内容中,我们将描述如何让 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 应用程序逐步脱离对编译器、运行时和操作系统的依赖,最终在裸机上运行。这时,我们也可把这个能在裸机上运行的``Hello, world!`` 应用程序称为一种支持输出字符串的非常初级的“三叶虫”操作系统,它其实就是一个给应用提供各种服务(比如输出字符串)的库,方便了单一应用程序在裸机上的开发与运行。</p>
<p>获取本章代码:</p>
<div class="highlight-console notranslate"><div class="highlight"><pre><span></span><span class="gp">$</span> git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
<span class="gp">$</span> <span class="nb">cd</span> rCore-Tutorial-v3
......@@ -271,6 +269,11 @@ Panicked at src/main.rs:46 Shutdown machine!
</pre></div>
</div>
<p>除了 <code class="docutils literal notranslate"><span class="pre">Hello,</span> <span class="pre">world!</span></code> 之外还有一些额外的信息,最后关机。</p>
<div class="admonition note">
<p class="admonition-title">注解</p>
<p>RustSBI是啥?</p>
<p><a class="reference internal" href="../appendix-c/index.html"><span class="doc">附录 C:深入机器模式:RustSBI</span></a> 可以进一步了解RustSBI。</p>
</div>
</div>
......@@ -281,10 +284,10 @@ Panicked at src/main.rs:46 Shutdown machine!
<div class="rst-footer-buttons" role="navigation" aria-label="footer navigation">
<a href="1app-ee-platform.html" class="btn btn-neutral float-right" title="应用程序行环境与平台支持" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="1app-ee-platform.html" class="btn btn-neutral float-right" title="应用程序行环境与平台支持" accesskey="n" rel="next">Next <span class="fa fa-arrow-circle-right"></span></a>
<a href="../quickstart.html" class="btn btn-neutral float-left" title="环境配置" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
<a href="../chapter0/5setup-devel-env.html" class="btn btn-neutral float-left" title="实验环境配置" accesskey="p" rel="prev"><span class="fa fa-arrow-circle-left"></span> Previous</a>
</div>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第二章:批处理系统</a><ul class="current">
<li class="toctree-l2 current"><a class="current reference internal" href="#">RISC-V 特权级架构</a><ul class="simple">
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第二章:批处理系统</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1rv-privilege.html">RISC-V 特权级架构</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第二章:批处理系统</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1rv-privilege.html">RISC-V 特权级架构</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第二章:批处理系统</a><ul class="current">
<li class="toctree-l2"><a class="reference internal" href="1rv-privilege.html">RISC-V 特权级架构</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">第二章:批处理系统</a><ul>
<li class="toctree-l2"><a class="reference internal" href="1rv-privilege.html">RISC-V 特权级架构</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第三章:多道程序与分时多任务</a><ul class="current">
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第三章:多道程序与分时多任务</a><ul class="current">
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第三章:多道程序与分时多任务</a><ul class="current">
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1 current"><a class="reference internal" href="index.html">第三章:多道程序与分时多任务</a><ul class="current">
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1 current"><a class="current reference internal" href="#">第三章:多道程序与分时多任务</a><ul>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul class="current">
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="../chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="../quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="../chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -119,7 +119,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -120,7 +120,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -223,7 +222,8 @@ commentsRunWhenDOMLoaded(addUtterances);
<div class="section" id="id1">
<h2>导读<a class="headerlink" href="#id1" title="永久链接至标题"></a></h2>
<p>请大家先阅读 <a class="reference internal" href="chapter0/index.html#link-chapter0"><span class="std std-ref">第零章</span></a> ,对于项目的开发背景和操作系统的概念有一个整体把控。</p>
<p>在正式进行实验之前,请先按照 <a class="reference internal" href="quickstart.html"><span class="doc">环境配置</span></a> 中的说明完成环境配置,再从第一章开始阅读正文。</p>
<p>在正式进行实验之前,请先按照第零章章末的 <a class="reference internal" href="chapter0/5setup-devel-env.html"><span class="doc">实验环境配置</span></a> 中的说明完成环境配置,再从第一章开始
阅读正文。</p>
</div>
<div class="section" id="id2">
<h2>项目协作<a class="headerlink" href="#id2" title="永久链接至标题"></a></h2>
......@@ -232,7 +232,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<li><p><a class="reference internal" href="rest-example.html"><span class="doc">reStructuredText 基本语法</span></a> 给出了目前编写文档才用的 ReStructuredText 标记语言的一些基础语法及用例;</p></li>
<li><p>项目的源代码仓库在 <a class="reference external" href="https://github.com/rcore-os/rCore-Tutorial-v3">这里</a>
该文档自身仓库在 <a class="reference external" href="https://github.com/rcore-os/rCore-Tutorial-Book-v3">这儿</a></p></li>
<li><p>时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issue 或 PR,让我们
<li><p>时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issues 或 Pull Requests,让我们
一起努力让这本书变得更好!</p></li>
</ul>
</div>
......
无法预览此类型文件
......@@ -120,7 +120,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -221,7 +220,7 @@ commentsRunWhenDOMLoaded(addUtterances);
<p><a class="reference external" href="https://www.sphinx-doc.org/en/master/usage/restructuredtext/basics.html#hyperlinks">这里</a> 给出了在 Sphinx 中
外部链接的引入方法。注意,链接的名字和用一对尖括号包裹起来的链接地址之间必须有一个空格。链接最后的下划线和片段的后续内容之间也需要
有一个空格。</p>
<p>接下来是一个文档内部引用的例子。比如,戳 <a class="reference internal" href="quickstart.html"><span class="doc">环境配置</span></a> 可以进入快速上手环节。</p>
<p>接下来是一个文档内部引用的例子。比如,戳 <a class="reference internal" href="chapter0/5setup-devel-env.html"><span class="doc">实验环境配置</span></a> 可以进入快速上手环节。</p>
</div>
<div class="admonition warning">
<p class="admonition-title">警告</p>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
因为 它太大了无法显示 source diff 。你可以改为 查看blob
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......
......@@ -121,7 +121,6 @@ commentsRunWhenDOMLoaded(addUtterances);
<p class="caption"><span class="caption-text">Part1 - Just do it!</span></p>
<ul>
<li class="toctree-l1"><a class="reference internal" href="chapter0/index.html">第零章:操作系统概述</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">环境配置</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter1/index.html">第一章:RV64 裸机应用</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter2/index.html">第二章:批处理系统</a></li>
<li class="toctree-l1"><a class="reference internal" href="chapter3/index.html">第三章:多道程序与分时多任务</a></li>
......@@ -689,6 +688,14 @@ commentsRunWhenDOMLoaded(addUtterances);
<td><p>RAII, Resource Acquisition Is Initialization</p></td>
<td><p><a class="reference internal" href="chapter4/2rust-dynamic-allocation.html#term-raii"><span class="std std-ref">Rust 中的动态内存分配</span></a></p></td>
</tr>
<tr class="row-odd"><td><p>页内偏移</p></td>
<td><p>Page Offset</p></td>
<td><p><a class="reference internal" href="chapter4/3sv39-implementation.html#term-page-offset"><span class="std std-ref">实现 SV39 多级页表机制</span></a></p></td>
</tr>
<tr class="row-even"><td><p>类型转换</p></td>
<td><p>Type Convertion</p></td>
<td><p><a class="reference internal" href="chapter4/3sv39-implementation.html#term-type-convertion"><span class="std std-ref">实现 SV39 多级页表机制</span></a></p></td>
</tr>
</tbody>
</table>
</div>
......
......@@ -16,10 +16,10 @@
系统环境配置
-------------------------------
目前实验仅支持 Ubuntu18.04 +操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
目前实验仅支持 Ubuntu18.04 + 操作系统。对于 Windows10 和 macOS 上的用户,可以使用 VMware 或
VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。
特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 。
特别的,Windows10 的用户可以通过系统内置的 WSL2 虚拟机(请不要使用 WSL1)来安装 Ubuntu 18.04 / 20.04
步骤如下:
- 升级 Windows 10 到最新版(Windows 10 版本 18917 或以后的内部版本)。注意,如果
......@@ -44,7 +44,7 @@ VirtualBox 安装一台 Ubuntu18.04 虚拟机并在上面进行实验。
>> wsl --set-default-version 2
- `下载 Linux 内核安装包 <https://docs.microsoft.com/zh-cn/windows/wsl/install-win10#step-4---download-the-linux-kernel-update-package>`_
- 在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 。
- 在微软商店(Microsoft Store)中搜索并安装 Ubuntu18.04 / 20.04
如果你打算使用 VMware 安装虚拟机的话,我们已经配置好了一个能直接运行 rCore-Tutorial-v3 的
Ubuntu18.04 镜像,它是一个 ``vmdk`` 格式的虚拟磁盘文件,只需要在 VMware 中新建一台虚拟机,
......@@ -76,6 +76,14 @@ Rust 开发环境配置
export RUSTUP_UPDATE_ROOT=https://mirrors.ustc.edu.cn/rust-static/rustup
curl https://sh.rustup.rs -sSf | sh
或者使用tuna源来加速 `参见 rustup 帮助 <https://mirrors.tuna.tsinghua.edu.cn/help/rustup/>`_:
.. code-block:: bash
export RUSTUP_DIST_SERVER=https://mirrors.tuna.edu.cn/rustup
export RUSTUP_UPDATE_ROOT=https://mirrors.tuna.edu.cn/rustup/rustup
curl https://sh.rustup.rs -sSf | sh
或者也可以通过在运行前设置命令行中的科学上网代理来实现:
.. code-block:: bash
......@@ -115,6 +123,15 @@ Rust 开发环境配置
[source.ustc]
registry = "git://mirrors.ustc.edu.cn/crates.io-index"
同样,也可以使用tuna源 `参见 crates.io 帮助 <https://mirrors.tuna.tsinghua.edu.cn/help/crates.io-index.git/>`_:
.. code-block:: toml
[source.crates-io]
replace-with = 'tuna'
[source.tuna]
registry = "https://mirrors.tuna.tsinghua.edu.cn/git/crates.io-index.git"
接下来安装一些Rust相关的软件包
......@@ -124,15 +141,18 @@ Rust 开发环境配置
cargo install cargo-binutils
rustup component add llvm-tools-preview
rustup component add rust-src
至于 Rust 开发环境,推荐JetBrains Clion + Rust插件 或者 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。
.. note::
rCore-Tutorial 仓库中的 ``Makefile`` 包含了这些工具的安装,如果你使用 ``make run`` 也可以不手动安装。
至于 Rust 开发环境,推荐 JetBrains Clion + Rust插件 或者 Visual Studio Code 搭配 rust-analyzer 和 RISC-V Support 插件。
.. note::
* JetBrains Clion是付费商业软件,但对于学生和教师,只要在JetBrains网网站注册账号,可以享受一定期限(半年左右)的免费使用的福利。
* Visual Studio Code是开源软件,不用付费就可使用。
* 当然,采用VIM,Emacs等传统的编辑器也是没有问题的。
* JetBrains Clion是付费商业软件,但对于学生和教师,只要在 JetBrains 网站注册账号,可以享受一定期限(半年左右)的免费使用的福利。
* Visual Studio Code 是开源软件,不用付费就可使用。
* 当然,采用 VIM,Emacs 等传统的编辑器也是没有问题的。
Qemu 模拟器安装
----------------------------------------
......
......@@ -16,7 +16,6 @@
**为何要写这本操作系统书**
现在国内外已有一系列优秀的操作系统教材,例如William Stallings的《Operating Systems Internals and Design Principles》,Avi Silberschatz、Peter Baer Galvin 和 Greg Gagne 的《Operating System Concepts》,Remzi H. Arpaci-Dusseau 和 Andrea C. Arpaci-Dusseau 的《Operating Systems: Three Easy Pieces》等。然而,从我们从2000年以来的教学实践来看,某些经典教材对操作系统的概念和原理很重视,但缺乏对操作系统的概念/原理与操作系统的实现之间建立一个联系的桥梁,导致学生发现操作系统实现相关的实验与操作系统的概念相比,有较大的鸿沟。此外,部分教材把 x86 作为的操作系统实验的硬件参考平台,缺乏对当前快速发展的RISC-V等体系结构的实验支持,使得学生在操作系统实验中可能需要花较大代价了解相对繁杂的x86硬件细节,影响操作系统实验的效果。还有部分教材也基本以 Linux/Unix 等实际操作系统为主,难以让学生在一个学期内掌握其中的核心设计。
对于在校的学生和已经参加工作的工程师而言,能否以较小的时间和精力比较全面地了解操作系统呢?陆游老夫子说过“纸上得来终觉浅,绝知此事要躬行”,也许在了解基本的操作系统概念和原理基础上,通过实际动手来一步一步分析、设计和实现一个操作系统,会发现操作系统原来如此,概念原理和实际实现之间有紧密的联系和巨大的差异。
......@@ -32,7 +31,7 @@
.. note::
目前常见的操作系统内核都是基于C语言的,为何要推荐Rust语言?
**目前常见的操作系统内核都是基于C语言的,为何要推荐Rust语言?**
- 没错,C语言就是为写UNIX而诞生的。Dennis Ritchie和KenThompson没有期望设计一种新语言能帮助高效简洁地开发复杂的应用业务逻辑,只是希望用一种简洁的方式抽象出计算机的行为,便于编写控制计算机硬件的操作系统,最终的结果就是C语言。
- C语言的指针的天使与魔鬼,且C语言缺少有效的并发支持,导致内存和并发漏洞成为当前操作系统的噩梦。
......
......@@ -171,6 +171,7 @@ x86_64 换成 RISC-V。
.. code-block:: console
$ rustc --print target-list | grep riscv
riscv32gc-unknown-linux-gnu
riscv32i-unknown-none-elf
riscv32imac-unknown-none-elf
riscv32imc-unknown-none-elf
......
......@@ -9,9 +9,9 @@
我们首先在 ``os`` 目录下新建 ``.cargo`` 目录,并在这个目录下创建 ``config`` 文件,并在里面输入如下内容:
.. code-block::
.. code-block:: toml
// os/.cargo/config
# os/.cargo/config
[build]
target = "riscv64gc-unknown-none-elf"
......
......@@ -4,7 +4,7 @@
在本章第一小节中我们简单介绍了分页的内存管理策略,本节我们在 RV64 架构提供的 SV39 分页机制的基础上完成内核中的
软件对应实现。
SV39 的虚拟地址和物理地址
虚拟地址和物理地址
------------------------------------------------------
默认情况下 MMU 未被使能,此时无论 CPU 位于哪个特权级,访存的地址都会作为一个物理地址交给对应的内存控制单元来直接
......@@ -18,7 +18,261 @@ SV39 的虚拟地址和物理地址
的时候,SV39 分页机制被启用,所有 S/U 特权级的访存被视为一个 39 位的虚拟地址,它们需要先经过 MMU 的地址转换流程,
如果顺利的话,则会变成一个 56 位的物理地址来访问物理内存;否则则会触发异常,这体现了该机制的内存保护能力。
虚拟地址和物理地址都是字节地址,39 位的虚拟地址可以用来访问理论上最大 :math:`512\text{GiB}` 的地址空间,
而 56 位的物理地址在理论上甚至可以访问一块大小比这个地址空间的还高出几个数量级的物理内存。但是实际上无论是
虚拟地址还是物理地址,真正有意义、能够通过 MMU 的地址转换或是 CPU 内存控制单元的检查的地址仅占其中的很小
一部分,因此它们的理论容量上限在目前都没有实际意义。
.. image:: sv39-va-pa.png
虚拟地址和物理地址都是字节地址, 39 位的虚拟地址可以用来访问大小为 :math:`512\text{GiB}` 的地址空间,
而 56 位的物理地址
\ No newline at end of file
.. _term-page-offset:
我们采用分页管理,单个页面的大小设置为 :math:`4\text{KiB}` ,每个虚拟页面和物理页帧都对齐到这个页面大小,也就是说
虚拟/物理地址区间 :math:`[0,4\text{KiB})` 为第 :math:`0` 个虚拟页面/物理页帧,而
:math:`[4\text{KiB},8\text{KiB})` 为第 :math:`1` 个,以此类推。 :math:`4\text{KiB}` 需要用 12 位字节地址
来表示,因此虚拟地址和物理地址都被分成两部分:它们的低 12 位,即 :math:`[11:0]` 被称为 **页内偏移**
(Page Offset) ,它描述一个地址指向的字节在它所在页面中的相对位置。而虚拟地址的高 27 位,即 :math:`[38:12]` 为
它的虚拟页号 VPN,同理物理地址的高 44 位,即 :math:`[55:12]` 为它的物理页号 VPN,页号可以用来定位一个虚拟/物理地址
属于哪一个虚拟页面/物理页帧。
地址转换是以页为单位进行的,在地址转换的前后地址的页内偏移部分不变。可以认为 MMU 只是从虚拟地址中取出 27 位虚拟页号,
在页表中查到其对应的物理页号(如果存在的话),最后将得到的物理页号与虚拟地址的页内偏移依序拼接到一起就变成了物理地址。
.. note::
**RV64 架构中虚拟地址为何只有 39 位?**
在 64 位架构上虚拟地址长度确实应该和位宽一致为 64 位,但是在启用 SV39 分页模式下,只有后 39 位是真正有意义的。
SV39 分页模式规定 64 位虚拟地址的 :math:`[63:39]` 这 25 位必须和第 38 位相同,否则 MMU 会直接认定它是一个
不合法的虚拟地址。通过这个检查之后 MMU 再取出后 39 位尝试将其转化为一个 56 位的物理地址。
也就是说,所有 :math:`2^{64}` 个虚拟地址中,只有最低的 :math:`256\text{GiB}` (当第 38 位为 0 时)
以及最高的 :math:`256\text{GiB}` (当第 38 位为 1 时)是可能通过 MMU 检查的。当我们写软件代码的时候,一个
地址的位宽毋庸置疑就是 64 位,我们要清楚可用的只有最高和最低这两部分,尽管它们已经巨大的超乎想象了;而本节中
我们专注于介绍 MMU 的机制,强调 MMU 看到的真正用来地址转换的虚拟地址,这只有 39 位。
正如本章第一小节所说,在分页内存管理中,地址转换的核心任务在于如何维护虚拟页号到物理页号的映射——也就是页表。不过在具体
实现它之前,我们先将地址和页号的概念抽象为 Rust 中的类型,借助 Rust 的类型安全特性来确保它们被正确实现。
首先是这些类型的定义:
.. code-block:: rust
// os/src/mm/address.rs
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysAddr(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtAddr(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct PhysPageNum(pub usize);
#[derive(Copy, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct VirtPageNum(pub usize);
.. _term-type-convertion:
上面分别给出了物理地址、虚拟地址、物理页号、虚拟页号的 Rust 类型声明,它们都是 Rust 的元组式结构体,可以看成
usize 的一种简单包装。我们刻意将它们各自抽象出来而不是都使用 usize 保存,就是为了在 Rust 编译器的帮助下进行
多种方便且安全的 **类型转换** (Type Convertion) 。
首先,这些类型本身可以和 usize 之间互相转换,以物理地址 ``PhysAddr`` 为例,我们需要:
.. code-block:: rust
// os/src/mm/address.rs
impl From<usize> for PhysAddr {
fn from(v: usize) -> Self { Self(v) }
}
impl From<PhysAddr> for usize {
fn from(v: PhysAddr) -> Self { v.0 }
}
前者允许我们从一个 ``usize`` 来生成 ``PhysAddr`` ,即 ``PhysAddr::from(_: usize)`` 将得到一个 ``PhysAddr``
;反之亦然。其实由于我们在声明结构体的时候将字段公开了出来,从物理地址变量 ``pa`` 得到它的 usize 表示的更简便方法
是直接 ``pa.0`` 。
.. note::
**Rust 语法卡片:类型转换之 From 和 Into**
一般而言,当我们为类型 ``U`` 实现了 ``From<T>`` Trait 之后,可以使用 ``U::from(_: T)`` 来从一个 ``T``
类型的实例来构造一个 ``U`` 类型的实例;而当我们为类型 ``U`` 实现了 ``Into<T>`` Trait 之后,对于一个 ``U``
类型的实例 ``u`` ,可以使用 ``u.into()`` 来将其转化为一个类型为 ``T`` 的实例。
当我们为 ``U`` 实现了 ``From<T>`` 之后,Rust 会自动为 ``T`` 实现 ``Into<U>`` Trait,
因为它们两个本来就是在做相同的事情。因此我们只需相互实现 ``From`` 就可以相互 ``From/Into`` 了。
需要注意的是,当我们使用 ``From`` Trait 的 ``from`` 方法来构造一个转换后类型的实例的时候,``from`` 的参数
已经指明了转换前的类型,因而 Rust 编译器知道该使用哪个实现;而使用 ``Into`` Trait 的 ``into`` 方法来将当前
类型转化为另一种类型的时候,它并没有参数,因而函数签名中并没有指出要转化为哪一个类型,则我们必须在其他地方 *显式*
指出目标类型。比如,当我们要将 ``u.into()`` 绑定到一个新变量 ``t`` 的时候,必须通过 ``let t: T`` 显式声明
``t`` 的类型;又或是将 ``u.into()`` 的结果作为参数传给某一个函数,那么这个函数的函数签名中一定指出了传入位置
的参数的类型,Rust 编译器也就明确知道转换的类型。
请注意,解引用 ``Deref`` Trait 是 Rust 编译器唯一允许的一种隐式类型转换,而对于其他的类型转换,我们必须手动
调用类型转化方法或者是显式给出转换前后的类型。这体现了 Rust 的类型安全特性,在 C/C++ 中并不是如此,比如两个
不同的整数/浮点数类型进行二元运算的时候,编译器经常要先进行隐式类型转换使两个操作数类型相同,而后再进行运算,导致
了很多数值溢出或精度损失问题。Rust 不会进行这种隐式类型转换,它会在编译期直接报错,提示两个操作数类型不匹配。
其次,地址和页号之间可以相互转换。我们这里仍以物理地址和物理页号之间的转换为例:
.. code-block:: rust
:linenos:
// os/src/mm/address.rs
impl PhysAddr {
pub fn page_offset(&self) -> usize { self.0 & (PAGE_SIZE - 1) }
}
impl From<PhysAddr> for PhysPageNum {
fn from(v: PhysAddr) -> Self {
assert_eq!(v.page_offset(), 0);
v.floor()
}
}
impl From<PhysPageNum> for PhysAddr {
fn from(v: PhysPageNum) -> Self { Self(v.0 << PAGE_SIZE_BITS) }
}
其中 ``PAGE_SIZE`` 为 :math:`4096` , ``PAGE_SIZE_BITS`` 为 :math:`12` ,它们均定义在 ``config`` 子模块
中,分别表示每个页面的大小和页内偏移的位宽。从物理页号到物理地址的转换只需左移 :math:`12` 位即可,但是物理地址需要
保证它与页面大小对齐才能通过右移转换为物理页号。
对于不对齐的情况,物理地址不能通过 ``From/Into`` 转换为物理页号,而是需要通过它自己的 ``floor`` 或 ``ceil`` 方法来
进行下取整或上取整的转换。
.. code-block:: rust
// os/src/mm/address.rs
impl PhysAddr {
pub fn floor(&self) -> PhysPageNum { PhysPageNum(self.0 / PAGE_SIZE) }
pub fn ceil(&self) -> PhysPageNum { PhysPageNum((self.0 + PAGE_SIZE - 1) / PAGE_SIZE) }
}
我们暂时先介绍这两种最简单的类型转换。
页表项
-----------------------------------------
第一小节中我们提到,在页表中以虚拟页号作为索引不仅能够查到物理页号,还能查到一组保护位,它控制了应用对地址空间每个
虚拟页面的访问权限。但实际上还有更多的标志位,物理页号和全部的标志位以某种固定的格式保存在一个结构体中,它被称为
**页表项** (PTE, Page Table Entry) ,是利用虚拟页号在页表中查到的结果。
.. image:: sv39-pte.png
上图为 SV39 分页模式下的页表项,其中 :math:`[53:10]` 这 :math:`44` 位是物理页号,最低的 :math:`8` 位
:math:`[7:0]` 则是标志位,它们的含义如下(请注意,为方便说明,下文我们用 *页表项的对应虚拟页面* 来表示索引到
一个页表项的虚拟页号对应的虚拟页面):
- 仅当 V(Valid) 位为 1 时,页表项才是合法的;
- R/W/X 分别控制索引到这个页表项的对应虚拟页面是否允许读/写/取指;
- U 控制索引到这个页表项的对应虚拟页面是否在 CPU 处于 U 特权级的情况下是否被允许访问;
- G 我们暂且不理会;
- A(Accessed) 记录自从页表项上的这一位被清零之后,页表项的对应虚拟页面是否被访问过;
- D(Dirty) 则记录自从页表项上的这一位被清零之后,页表项的对应虚拟页表是否被修改过。
让我们先来实现页表项中的标志位 ``PTEFlags`` :
.. code-block:: rust
// os/src/main.rs
#[macro_use]
extern crate bitflags;
// os/src/mm/page_table.rs
use bitflags::*;
bitflags! {
pub struct PTEFlags: u8 {
const V = 1 << 0;
const R = 1 << 1;
const W = 1 << 2;
const X = 1 << 3;
const U = 1 << 4;
const G = 1 << 5;
const A = 1 << 6;
const D = 1 << 7;
}
}
`bitflags <https://docs.rs/bitflags/1.2.1/bitflags/>`_ 是一个 Rust 中常用来比特标志位的 crate 。它提供了
一个 ``bitflags!`` 宏,如上面的代码段所展示的那样,可以将一个 ``u8`` 封装成一个标志位的集合类型,支持一些常见的集合
运算。它的一些使用细节这里不展开,请读者自行参考它的官方文档。注意,在使用之前我们需要引入该 crate 的依赖:
.. code-block:: toml
# os/Cargo.toml
[dependencies]
bitflags = "1.2.1"
接下来我们实现页表项 ``PageTableEntry`` :
.. code-block:: rust
:linenos:
// os/src/mm/page_table.rs
#[derive(Copy, Clone)]
#[repr(C)]
pub struct PageTableEntry {
pub bits: usize,
}
impl PageTableEntry {
pub fn new(ppn: PhysPageNum, flags: PTEFlags) -> Self {
PageTableEntry {
bits: ppn.0 << 10 | flags.bits as usize,
}
}
pub fn empty() -> Self {
PageTableEntry {
bits: 0,
}
}
pub fn ppn(&self) -> PhysPageNum {
(self.bits >> 10 & ((1usize << 44) - 1)).into()
}
pub fn flags(&self) -> PTEFlags {
PTEFlags::from_bits(self.bits as u8).unwrap()
}
}
- 第 3 行我们让编译器自动为 ``PageTableEntry`` 实现 ``Copy/Clone`` Trait,来让这个类型以值语义赋值/传参的时候
不会发生所有权转移,而是拷贝一份新的副本。从这一点来说 ``PageTableEntry`` 就和 usize 一样,因为它也只是后者的
一层简单包装,解释了 usize 各个比特段的含义。
- 第 10 行使得我们可以从一个物理页号 ``PhysPageNum`` 和一个页表项标志位 ``PTEFlags`` 生成一个页表项
``PageTableEntry`` 实例;而第 20 行和第 23 行则分别可以从一个页表项将它们两个取出。
- 第 15 行中,我们也可以通过 ``empty`` 方法生成一个全零的页表项,注意这隐含着该页表项的 V 标志位为 0 ,
因此它是不合法的。
后面我们还为 ``PageTableEntry`` 实现了一些工具函数,可以快速判断一个页表项的 V/R/W/X 标志位是否为 1,以 V
标志位的判断为例:
.. code-block:: rust
// os/src/mm/page_table.rs
impl PageTableEntry {
pub fn is_valid(&self) -> bool {
(self.flags() & PTEFlags::V) != PTEFlags::empty()
}
}
这里相当于判断两个集合的交集是否为空集,部分说明了 ``bitflags`` crate 的使用方法。
多级页表
-------------------------------
页表的一种最简单的实现是线性表,也就是按照地址从低到高、输入的虚拟页号从 :math:`0` 开始递增的顺序依次在内存中
(我们之前提到过页表的容量过大无法保存在 CPU 中)放置每个虚拟页号对应的页表项。
\ No newline at end of file
......@@ -53,7 +53,8 @@ rCore-Tutorial-Book 第三版
请大家先阅读 :ref:`第零章 <link-chapter0>` ,对于项目的开发背景和操作系统的概念有一个整体把控。
在正式进行实验之前,请先按照 :doc:`chapter0/5setup-devel-env` 中的说明完成环境配置,再从第一章开始阅读正文。
在正式进行实验之前,请先按照第零章章末的 :doc:`/chapter0/5setup-devel-env` 中的说明完成环境配置,再从第一章开始
阅读正文。
项目协作
----------------------
......@@ -62,7 +63,7 @@ rCore-Tutorial-Book 第三版
- :doc:`/rest-example` 给出了目前编写文档才用的 ReStructuredText 标记语言的一些基础语法及用例;
- 项目的源代码仓库在 `这里 <https://github.com/rcore-os/rCore-Tutorial-v3>`_ ,
该文档自身仓库在 `这儿 <https://github.com/rcore-os/rCore-Tutorial-Book-v3>`_ ;
- 时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issue 或 PR,让我们
- 时间仓促,本项目还有很多不完善之处,欢迎大家积极在每一个章节的评论区留言,或者提交 Issues 或 Pull Requests,让我们
一起努力让这本书变得更好!
项目进度
......
......@@ -352,6 +352,12 @@
* - 资源获取即初始化
- RAII, Resource Acquisition Is Initialization
- :ref:`Rust 中的动态内存分配 <term-raii>`
* - 页内偏移
- Page Offset
- :ref:`实现 SV39 多级页表机制 <term-page-offset>`
* - 类型转换
- Type Convertion
- :ref:`实现 SV39 多级页表机制 <term-type-convertion>`
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册