Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
Miykael_xxm
RCore Tutorial Book V3
提交
c0d3b248
R
RCore Tutorial Book V3
项目概览
Miykael_xxm
/
RCore Tutorial Book V3
与 Fork 源项目一致
Fork自
rcore-os / RCore Tutorial Book V3
通知
1
Star
0
Fork
0
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
0
列表
看板
标记
里程碑
合并请求
0
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
R
RCore Tutorial Book V3
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
0
Issue
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
前往新版Gitcode,体验更适合开发者的 AI 搜索 >>
提交
c0d3b248
编写于
11月 03, 2020
作者:
Y
Yifan Wu
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
Update 1-1/2
上级
96a76265
变更
11
展开全部
隐藏空白更改
内联
并排
Showing
11 changed file
with
707 addition
and
19 deletion
+707
-19
docs/_sources/chapter1/1app-ee-platform.rst.txt
docs/_sources/chapter1/1app-ee-platform.rst.txt
+40
-5
docs/_sources/chapter1/2remove-std.rst.txt
docs/_sources/chapter1/2remove-std.rst.txt
+129
-0
docs/_sources/chapter1/index.rst.txt
docs/_sources/chapter1/index.rst.txt
+2
-1
docs/chapter1/1app-ee-platform.html
docs/chapter1/1app-ee-platform.html
+38
-6
docs/chapter1/2remove-std.html
docs/chapter1/2remove-std.html
+325
-0
docs/chapter1/index.html
docs/chapter1/index.html
+1
-0
docs/objects.inv
docs/objects.inv
+0
-0
docs/searchindex.js
docs/searchindex.js
+1
-1
source/chapter1/1app-ee-platform.rst
source/chapter1/1app-ee-platform.rst
+40
-5
source/chapter1/2remove-std.rst
source/chapter1/2remove-std.rst
+129
-0
source/chapter1/index.rst
source/chapter1/index.rst
+2
-1
未找到文件。
docs/_sources/chapter1/1app-ee-platform.rst.txt
浏览文件 @
c0d3b248
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
.. toctree::
.. toctree::
:hidden:
:hidden:
:maxdepth:
3
:maxdepth:
4
作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
...
@@ -34,7 +34,7 @@
...
@@ -34,7 +34,7 @@
println!("Hello, world!");
println!("Hello, world!");
}
}
利用 Cargo 工具即可一条命令实现构建并运行项目:
进入 os 项目根目录下,
利用 Cargo 工具即可一条命令实现构建并运行项目:
.. code-block:: console
.. code-block:: console
...
@@ -72,7 +72,7 @@
...
@@ -72,7 +72,7 @@
.. note::
.. note::
``Hello, world!`` 用到了哪些系统调用?
**Hello, world! 用到了哪些系统调用?**
从之前的 ``cargo run`` 的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
从之前的 ``cargo run`` 的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
在 Ubuntu 系统上,可以通过 ``strace`` 工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
在 Ubuntu 系统上,可以通过 ``strace`` 工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
...
@@ -97,7 +97,7 @@
...
@@ -97,7 +97,7 @@
.. note::
.. note::
多层执行环境都是必需的吗?
**多层执行环境都是必需的吗?**
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 **抽象** (Abstraction),
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 **抽象** (Abstraction),
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
...
@@ -171,5 +171,40 @@ linux-gnu 系统调用支持的版本 ``riscv64gc-unknown-linux-gnu``,是因
...
@@ -171,5 +171,40 @@ linux-gnu 系统调用支持的版本 ``riscv64gc-unknown-linux-gnu``,是因
.. note::
.. note::
RISC-V 指令集拓展
**RISC-V 指令集拓展**
由于基于 RISC-V 架构的处理器可能用于嵌入式场景或是通用计算场景,因此指令集规范将指令集划分为最基本的 RV32/64I 以及若干标准指令集拓展。
每款处理器只需按照其实际应用场景按需实现指令集拓展即可。
- RV32/64I:每款处理器都必须实现的基本整数指令集。在 RV32I 中,每个通用寄存器的位宽为 32 位;在 RV64I 中则为 64 位。它可以用来模拟
绝大多数标准指令集拓展中的指令,除了比较特殊的 A 拓展,因为它需要特别的硬件支持。
- M 拓展:提供整数乘除法相关指令。
- A 拓展:提供原子指令和一些相关的内存同步机制,这个后面会展开。
- F/D 拓展:提供单/双精度浮点数运算支持。
- C 拓展:提供压缩指令拓展。
G 拓展是基本整数指令集 I 再加上标准指令集拓展 MAFD 的总称,因此 riscv64gc 也就等同于 riscv64imafdc。我们剩下的内容都基于该处理器
架构完成。除此之外 RISC-V 架构还有很多标准指令集拓展,有一些还在持续更新中尚未稳定,有兴趣的读者可以浏览最新版的 RISC-V 指令集规范。
Rust 标准库与核心库
----------------------------------
我们尝试一下将当前的 ``Hello, world!`` 程序的目标平台换成 riscv64gc-unknown-none-elf 看看会发生什么事情:
.. code-block:: console
$ cargo run --target riscv64gc-unknown-none-elf
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error[E0463]: can't find crate for `std`
|
= note: the `riscv64gc-unknown-none-elf` target may not be installed
在之前的环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。因此只是单纯的在这个目标平台上找不到
Rust 标准库 std。我们之前曾经提到过,编程语言的标准库或三方库的某些功能会直接或间接的用到操作系统提供的系统调用。但目前我们所选的目标平台不存在
任何操作系统支持,于是 Rust 并没有为这个目标平台支持完整的标准库 std。类似这样的平台通常被我们称为 **裸机平台** (bare-metal)。
幸运的是,Rust 有一个对 std 裁剪过后的核心库 core,这个库是不需要任何操作系统支持的,相对的它的功能也比较受限,但是也包含了 Rust 语言
相当一部分的核心机制,可以满足我们的大部分需求。在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
程度上减轻我们的编程负担。它们是我们能够在裸机平台挣扎求生的最主要倚仗。
于是,我们知道在裸机平台上我们要将对于标准库 std 的引用换成核心库 core。但是做起来其实并没有那么容易。
\ No newline at end of file
docs/_sources/chapter1/2remove-std.rst.txt
0 → 100644
浏览文件 @
c0d3b248
移除标准库依赖
==========================
.. toctree::
:hidden:
:maxdepth: 4
本节我们尝试移除之前的 ``Hello world!`` 程序对于标准库的依赖,使得它能够编译到裸机平台 RV64GC 上。
我们首先在 ``os`` 目录下新建 ``.cargo`` 目录,并在这个目录下创建 ``config`` 文件,并在里面输入如下内容:
.. code-block::
[build]
target = "riscv64gc-unknown-none-elf"
这会对于 Cargo 工具在 os 目录下的行为进行调整:现在默认会使用 riscv64gc 作为目标平台而不是原先的默认 x86_64-unknown-linux-gnu。
事实上,这是一种编译器运行所在的平台与编译器生成可执行文件的目标平台不同(分别是后者和前者)的情况。这是一种 **交叉编译** (Cross Compile)。
当然,这只是使得我们之后在 ``cargo build`` 的时候不必再加上 ``--target`` 参数的一个小 trick。如果我们现在 ``cargo build`` ,还是会和
上一小节一样出现找不到标准库 std 的错误。于是我们开始着手移除标准库。当然,这会产生一些副作用。
移除 println! 宏
----------------------------------
我们在 ``main.rs`` 的开头加上一行 ``#![no_std]`` 来告诉 Rust 编译器不使用 Rust 标准库 std 转而使用核心库 core。编译器报出如下错误:
.. error::
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: cannot find macro `println` in this scope
--> src/main.rs:4:5
|
4 | println!("Hello, world!");
| ^^^^^^^
我们之前提到过, println! 宏是由标准库 std 提供的,且会使用到一个名为 write 的系统调用。现在我们的条件还不足以自己实现一个 println! 宏,由于
使用了系统调用也不能在核心库 core 中找到它。我们目前先通过将它注释掉来绕过它。
提供语义项 panic_handler
----------------------------------------------------
.. error::
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: `#[panic_handler]` function required, but not found
在使用 Rust 编写应用程序的时候,我们常常在遇到了一些无法恢复的致命错误导致程序无法继续向下运行的时候手动或自动调用 panic! 宏来并打印出错的
位置让我们能够意识到它的存在,并进行一些后续处理。panic! 宏最典型的应用场景包括断言宏 assert! 失败或者对 ``Option::None/Result::Err``
进行 ``unwrap`` 操作。
在标准库 std 中提供了 panic 的处理函数 ``#[panic_handler]``,其大致功能是打印出错位置和原因并杀死当前应用。可惜的是在核心库 core 中并没有提供,
因此我们需要自己实现 panic 处理函数。
.. note::
**Rust 语义项 lang_items**
Rust 编译器内部的某些功能的实现并不是硬编码在语言内部的,而是以一种可插入的形式在库中提供。库只需要通过某种方式告诉编译器它的某个方法实现了
编译器内部的哪些功能,编译器就会采用库提供的方法来实现它内部对应的功能。通常只需要在库的方法前面加上一个标记即可。
我们开一个新的子模块 ``lang_items.rs`` 保存这些语义项,在里面提供 panic 处理函数的实现并通过标记通知编译器采用我们的实现:
.. code-block:: rust
// os/src/lang_items.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
注意,panic 处理函数的函数签名需要一个 ``PanicInfo`` 的不可变借用作为输入参数,它在核心库中得以保留,这也是我们第一次与核心库打交道。之后我们
会从 ``PanicInfo`` 解析出错位置并打印出来,然后杀死应用程序。但目前我们什么都不做只是在原地 loop。
移除 main 函数
-----------------------------
.. error::
.. code-block::
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: requires `start` lang_item
编译器提醒我们缺少一个名为 ``start`` 的语义项。我们回忆一下,之前提到语言标准库和三方库作为应用程序的执行环境,需要负责在执行应用程序之前进行
一些初始化工作,然后才跳转到应用程序的入口点(也就是跳转到我们编写的 ``main`` 函数)开始执行。事实上 ``start`` 语义项正代表着标准库 std 在
执行应用程序之前需要进行的一些初始化工作。由于我们禁用了标准库,编译器也就找不到这项功能的实现了。
最简单的解决方案就是压根不让编译器使用这项功能。我们在 ``main.rs`` 的开头加入设置 ``#![no_main]`` 告诉编译器我们没有一般意义上的 ``main`` 函数,
并将原来的 ``main`` 函数删除。在失去了 ``main`` 函数的情况下,编译器也就不需要完成所谓的初始化工作了。
至此,我们成功移除了标准库的依赖并完成裸机平台上的构建。
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
目前的代码如下:
.. code-block:: rust
// os/src/main.rs
#![no_std]
#![no_main]
mod lang_items;
// os/src/lang_items.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
本小节我们固然脱离了标准库,通过了编译器的检验,但也是伤筋动骨,将原有的很多功能弱化甚至直接删除,看起来距离在 RV64GC 平台上打印
``Hello world!`` 相去甚远了(我们甚至连 println! 和 ``main`` 函数都删除了)。不要着急,接下来我们会以自己的方式来重塑这些
功能,并最终完成我们的目标。
\ No newline at end of file
docs/_sources/chapter1/index.rst.txt
浏览文件 @
c0d3b248
...
@@ -6,4 +6,5 @@
...
@@ -6,4 +6,5 @@
:hidden:
:hidden:
:maxdepth: 3
:maxdepth: 3
1app-ee-platform
1app-ee-platform
\ No newline at end of file
2remove-std
\ No newline at end of file
docs/chapter1/1app-ee-platform.html
浏览文件 @
c0d3b248
...
@@ -36,7 +36,7 @@
...
@@ -36,7 +36,7 @@
<link
rel=
"stylesheet"
href=
"../_static/pygments.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"../_static/pygments.css"
type=
"text/css"
/>
<link
rel=
"index"
title=
"Index"
href=
"../genindex.html"
/>
<link
rel=
"index"
title=
"Index"
href=
"../genindex.html"
/>
<link
rel=
"search"
title=
"Search"
href=
"../search.html"
/>
<link
rel=
"search"
title=
"Search"
href=
"../search.html"
/>
<link
rel=
"next"
title=
"
reStructuredText 基本语法"
href=
"../rest-example
.html"
/>
<link
rel=
"next"
title=
"
移除标准库依赖"
href=
"2remove-std
.html"
/>
<link
rel=
"prev"
title=
"第一章:编译运行 RV64 裸机程序"
href=
"index.html"
/>
<link
rel=
"prev"
title=
"第一章:编译运行 RV64 裸机程序"
href=
"index.html"
/>
</head>
</head>
...
@@ -88,8 +88,10 @@
...
@@ -88,8 +88,10 @@
<li
class=
"toctree-l2 current"
><a
class=
"current reference internal"
href=
"#"
>
应用程序运行环境与平台支持
</a><ul>
<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=
"#id2"
>
应用程序运行环境
</a></li>
<li
class=
"toctree-l3"
><a
class=
"reference internal"
href=
"#id3"
>
平台与目标三元组
</a></li>
<li
class=
"toctree-l3"
><a
class=
"reference internal"
href=
"#id3"
>
平台与目标三元组
</a></li>
<li
class=
"toctree-l3"
><a
class=
"reference internal"
href=
"#rust"
>
Rust 标准库与核心库
</a></li>
</ul>
</ul>
</li>
</li>
<li
class=
"toctree-l2"
><a
class=
"reference internal"
href=
"2remove-std.html"
>
移除标准库依赖
</a></li>
</ul>
</ul>
</li>
</li>
</ul>
</ul>
...
@@ -192,7 +194,7 @@
...
@@ -192,7 +194,7 @@
</pre></div>
</pre></div>
</td></tr></table></div>
</td></tr></table></div>
</div>
</div>
<p>
利用 Cargo 工具即可一条命令实现构建并运行项目:
</p>
<p>
进入 os 项目根目录下,
利用 Cargo 工具即可一条命令实现构建并运行项目:
</p>
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo run
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo run
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
Finished dev [unoptimized + debuginfo] target(s) in 1.15s
</span>
<span
class=
"go"
>
Finished dev [unoptimized + debuginfo] target(s) in 1.15s
</span>
...
@@ -220,7 +222,7 @@
...
@@ -220,7 +222,7 @@
的边界。内核作为用户态的运行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。
</p>
的边界。内核作为用户态的运行环境,它不仅要提供系统调用接口,还需要对用户态应用的执行进行监控和管理。
</p>
<div
class=
"admonition note"
>
<div
class=
"admonition note"
>
<p
class=
"admonition-title"
>
Note
</p>
<p
class=
"admonition-title"
>
Note
</p>
<p><
code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
Hello,
</span>
<span
class=
"pre"
>
world!
</span></code>
用到了哪些系统调用?
</p>
<p><
strong>
Hello, world! 用到了哪些系统调用?
</strong>
</p>
<p>
从之前的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
cargo
</span>
<span
class=
"pre"
>
run
</span></code>
的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
<p>
从之前的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
cargo
</span>
<span
class=
"pre"
>
run
</span></code>
的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
在 Ubuntu 系统上,可以通过
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
strace
</span></code>
工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
在 Ubuntu 系统上,可以通过
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
strace
</span></code>
工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
我们只需输入
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
strace
</span>
<span
class=
"pre"
>
target/debug/os
</span></code>
即可看到一长串的系统调用。
</p>
我们只需输入
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
strace
</span>
<span
class=
"pre"
>
target/debug/os
</span></code>
即可看到一长串的系统调用。
</p>
...
@@ -239,7 +241,7 @@
...
@@ -239,7 +241,7 @@
三者之间流动。
</p>
三者之间流动。
</p>
<div
class=
"admonition note"
>
<div
class=
"admonition note"
>
<p
class=
"admonition-title"
>
Note
</p>
<p
class=
"admonition-title"
>
Note
</p>
<p>
多层执行环境都是必需的吗?
</p>
<p>
<strong>
多层执行环境都是必需的吗?
</strong>
</p>
<p>
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了
<strong>
抽象
</strong>
(Abstraction),
<p>
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了
<strong>
抽象
</strong>
(Abstraction),
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
应有的灵活性。比如,当你在考虑在项目中应该使用哪个函数库的时候,就常常需要这方面的权衡:过多的抽象和过少的抽象自然都是不合适的。
</p>
应有的灵活性。比如,当你在考虑在项目中应该使用哪个函数库的时候,就常常需要这方面的权衡:过多的抽象和过少的抽象自然都是不合适的。
</p>
...
@@ -303,8 +305,38 @@ linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"
...
@@ -303,8 +305,38 @@ linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"
直面底层硬件来解锁更大的抽象能力上限。
</p>
直面底层硬件来解锁更大的抽象能力上限。
</p>
<div
class=
"admonition note"
>
<div
class=
"admonition note"
>
<p
class=
"admonition-title"
>
Note
</p>
<p
class=
"admonition-title"
>
Note
</p>
<p>
RISC-V 指令集拓展
</p>
<p><strong>
RISC-V 指令集拓展
</strong></p>
<p>
由于基于 RISC-V 架构的处理器可能用于嵌入式场景或是通用计算场景,因此指令集规范将指令集划分为最基本的 RV32/64I 以及若干标准指令集拓展。
每款处理器只需按照其实际应用场景按需实现指令集拓展即可。
</p>
<ul
class=
"simple"
>
<li><p>
RV32/64I:每款处理器都必须实现的基本整数指令集。在 RV32I 中,每个通用寄存器的位宽为 32 位;在 RV64I 中则为 64 位。它可以用来模拟
绝大多数标准指令集拓展中的指令,除了比较特殊的 A 拓展,因为它需要特别的硬件支持。
</p></li>
<li><p>
M 拓展:提供整数乘除法相关指令。
</p></li>
<li><p>
A 拓展:提供原子指令和一些相关的内存同步机制,这个后面会展开。
</p></li>
<li><p>
F/D 拓展:提供单/双精度浮点数运算支持。
</p></li>
<li><p>
C 拓展:提供压缩指令拓展。
</p></li>
</ul>
<p>
G 拓展是基本整数指令集 I 再加上标准指令集拓展 MAFD 的总称,因此 riscv64gc 也就等同于 riscv64imafdc。我们剩下的内容都基于该处理器
架构完成。除此之外 RISC-V 架构还有很多标准指令集拓展,有一些还在持续更新中尚未稳定,有兴趣的读者可以浏览最新版的 RISC-V 指令集规范。
</p>
</div>
</div>
<div
class=
"section"
id=
"rust"
>
<h2>
Rust 标准库与核心库
<a
class=
"headerlink"
href=
"#rust"
title=
"Permalink to this headline"
>
¶
</a></h2>
<p>
我们尝试一下将当前的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
Hello,
</span>
<span
class=
"pre"
>
world!
</span></code>
程序的目标平台换成 riscv64gc-unknown-none-elf 看看会发生什么事情:
</p>
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo run --target riscv64gc-unknown-none-elf
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
error[E0463]: can
'
t find crate for `std`
</span>
<span
class=
"go"
>
|
</span>
<span
class=
"go"
>
= note: the `riscv64gc-unknown-none-elf` target may not be installed
</span>
</pre></div>
</div>
</div>
<p>
在之前的环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。因此只是单纯的在这个目标平台上找不到
Rust 标准库 std。我们之前曾经提到过,编程语言的标准库或三方库的某些功能会直接或间接的用到操作系统提供的系统调用。但目前我们所选的目标平台不存在
任何操作系统支持,于是 Rust 并没有为这个目标平台支持完整的标准库 std。类似这样的平台通常被我们称为
<strong>
裸机平台
</strong>
(bare-metal)。
</p>
<p>
幸运的是,Rust 有一个对 std 裁剪过后的核心库 core,这个库是不需要任何操作系统支持的,相对的它的功能也比较受限,但是也包含了 Rust 语言
相当一部分的核心机制,可以满足我们的大部分需求。在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
程度上减轻我们的编程负担。它们是我们能够在裸机平台挣扎求生的最主要倚仗。
</p>
<p>
于是,我们知道在裸机平台上我们要将对于标准库 std 的引用换成核心库 core。但是做起来其实并没有那么容易。
</p>
</div>
</div>
</div>
</div>
...
@@ -316,7 +348,7 @@ linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"
...
@@ -316,7 +348,7 @@ linux-gnu 系统调用支持的版本 <code class="docutils literal notranslate"
<div
class=
"rst-footer-buttons"
role=
"navigation"
aria-label=
"footer navigation"
>
<div
class=
"rst-footer-buttons"
role=
"navigation"
aria-label=
"footer navigation"
>
<a
href=
"
../rest-example.html"
class=
"btn btn-neutral float-right"
title=
"reStructuredText 基本语法
"
accesskey=
"n"
rel=
"next"
>
Next
<span
class=
"fa fa-arrow-circle-right"
></span></a>
<a
href=
"
2remove-std.html"
class=
"btn btn-neutral float-right"
title=
"移除标准库依赖
"
accesskey=
"n"
rel=
"next"
>
Next
<span
class=
"fa fa-arrow-circle-right"
></span></a>
<a
href=
"index.html"
class=
"btn btn-neutral float-left"
title=
"第一章:编译运行 RV64 裸机程序"
accesskey=
"p"
rel=
"prev"
><span
class=
"fa fa-arrow-circle-left"
></span>
Previous
</a>
<a
href=
"index.html"
class=
"btn btn-neutral float-left"
title=
"第一章:编译运行 RV64 裸机程序"
accesskey=
"p"
rel=
"prev"
><span
class=
"fa fa-arrow-circle-left"
></span>
Previous
</a>
...
...
docs/chapter1/2remove-std.html
0 → 100644
浏览文件 @
c0d3b248
<!DOCTYPE html>
<!--[if IE 8]><html class="no-js lt-ie9" lang="en" > <![endif]-->
<!--[if gt IE 8]><!-->
<html
class=
"no-js"
lang=
"en"
>
<!--<![endif]-->
<head>
<meta
charset=
"utf-8"
>
<meta
name=
"viewport"
content=
"width=device-width, initial-scale=1.0"
>
<title>
移除标准库依赖
—
rCore-Tutorial-Book-v3 0.1 documentation
</title>
<script
type=
"text/javascript"
src=
"../_static/js/modernizr.min.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
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=
"stylesheet"
href=
"../_static/css/theme.css"
type=
"text/css"
/>
<link
rel=
"stylesheet"
href=
"../_static/pygments.css"
type=
"text/css"
/>
<link
rel=
"index"
title=
"Index"
href=
"../genindex.html"
/>
<link
rel=
"search"
title=
"Search"
href=
"../search.html"
/>
<link
rel=
"next"
title=
"reStructuredText 基本语法"
href=
"../rest-example.html"
/>
<link
rel=
"prev"
title=
"应用程序运行环境与平台支持"
href=
"1app-ee-platform.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"
>
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"
>
正文
</span></p>
<ul
class=
"current"
>
<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 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>
<li
class=
"toctree-l3"
><a
class=
"reference internal"
href=
"#main"
>
移除 main 函数
</a></li>
</ul>
</li>
</ul>
</li>
</ul>
<p
class=
"caption"
><span
class=
"caption-text"
>
开发注记
</span></p>
<ul>
<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"
>
Docs
</a>
»
</li>
<li><a
href=
"index.html"
>
第一章:编译运行 RV64 裸机程序
</a>
»
</li>
<li>
移除标准库依赖
</li>
<li
class=
"wy-breadcrumbs-aside"
>
<a
href=
"../_sources/chapter1/2remove-std.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=
"Permalink to this headline"
>
¶
</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>
程序对于标准库的依赖,使得它能够编译到裸机平台 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=
"p"
>
[
</span><span
class=
"n"
>
build
</span><span
class=
"p"
>
]
</span>
<span
class=
"n"
>
target
</span>
<span
class=
"o"
>
=
</span>
<span
class=
"s2"
>
"
riscv64gc-unknown-none-elf
"
</span>
</pre></div>
</div>
<p>
这会对于 Cargo 工具在 os 目录下的行为进行调整:现在默认会使用 riscv64gc 作为目标平台而不是原先的默认 x86_64-unknown-linux-gnu。
事实上,这是一种编译器运行所在的平台与编译器生成可执行文件的目标平台不同(分别是后者和前者)的情况。这是一种
<strong>
交叉编译
</strong>
(Cross Compile)。
</p>
<p>
当然,这只是使得我们之后在
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
cargo
</span>
<span
class=
"pre"
>
build
</span></code>
的时候不必再加上
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
--target
</span></code>
参数的一个小 trick。如果我们现在
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
cargo
</span>
<span
class=
"pre"
>
build
</span></code>
,还是会和
上一小节一样出现找不到标准库 std 的错误。于是我们开始着手移除标准库。当然,这会产生一些副作用。
</p>
<div
class=
"section"
id=
"println"
>
<h2>
移除 println! 宏
<a
class=
"headerlink"
href=
"#println"
title=
"Permalink to this headline"
>
¶
</a></h2>
<p>
我们在
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main.rs
</span></code>
的开头加上一行
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
#![no_std]
</span></code>
来告诉 Rust 编译器不使用 Rust 标准库 std 转而使用核心库 core。编译器报出如下错误:
</p>
<div
class=
"admonition error"
>
<p
class=
"admonition-title"
>
Error
</p>
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo build
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
error: cannot find macro `println` in this scope
</span>
<span
class=
"go"
>
--
>
src/main.rs:4:5
</span>
<span
class=
"go"
>
|
</span>
<span
class=
"go"
>
4 | println!(
"
Hello, world!
"
);
</span>
<span
class=
"go"
>
| ^^^^^^^
</span>
</pre></div>
</div>
</div>
<p>
我们之前提到过, println! 宏是由标准库 std 提供的,且会使用到一个名为 write 的系统调用。现在我们的条件还不足以自己实现一个 println! 宏,由于
使用了系统调用也不能在核心库 core 中找到它。我们目前先通过将它注释掉来绕过它。
</p>
</div>
<div
class=
"section"
id=
"panic-handler"
>
<h2>
提供语义项 panic_handler
<a
class=
"headerlink"
href=
"#panic-handler"
title=
"Permalink to this headline"
>
¶
</a></h2>
<div
class=
"admonition error"
>
<p
class=
"admonition-title"
>
Error
</p>
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo build
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
error: `#[panic_handler]` function required, but not found
</span>
</pre></div>
</div>
</div>
<p>
在使用 Rust 编写应用程序的时候,我们常常在遇到了一些无法恢复的致命错误导致程序无法继续向下运行的时候手动或自动调用 panic! 宏来并打印出错的
位置让我们能够意识到它的存在,并进行一些后续处理。panic! 宏最典型的应用场景包括断言宏 assert! 失败或者对
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
Option::None/Result::Err
</span></code>
进行
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
unwrap
</span></code>
操作。
</p>
<p>
在标准库 std 中提供了 panic 的处理函数
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
#[panic_handler]
</span></code>
,其大致功能是打印出错位置和原因并杀死当前应用。可惜的是在核心库 core 中并没有提供,
因此我们需要自己实现 panic 处理函数。
</p>
<div
class=
"admonition note"
>
<p
class=
"admonition-title"
>
Note
</p>
<p><strong>
Rust 语义项 lang_items
</strong></p>
<p>
Rust 编译器内部的某些功能的实现并不是硬编码在语言内部的,而是以一种可插入的形式在库中提供。库只需要通过某种方式告诉编译器它的某个方法实现了
编译器内部的哪些功能,编译器就会采用库提供的方法来实现它内部对应的功能。通常只需要在库的方法前面加上一个标记即可。
</p>
</div>
<p>
我们开一个新的子模块
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
lang_items.rs
</span></code>
保存这些语义项,在里面提供 panic 处理函数的实现并通过标记通知编译器采用我们的实现:
</p>
<div
class=
"highlight-rust notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"c1"
>
// os/src/lang_items.rs
</span>
<span
class=
"k"
>
use
</span><span
class=
"w"
>
</span><span
class=
"n"
>
core
</span>
::
<span
class=
"n"
>
panic
</span>
::
<span
class=
"n"
>
PanicInfo
</span><span
class=
"p"
>
;
</span><span
class=
"w"
></span>
<span
class=
"cp"
>
#[panic_handler]
</span><span
class=
"w"
></span>
<span
class=
"k"
>
fn
</span>
<span
class=
"nf"
>
panic
</span><span
class=
"p"
>
(
</span><span
class=
"n"
>
_info
</span>
:
<span
class=
"kp"
>
&
</span><span
class=
"nc"
>
PanicInfo
</span><span
class=
"p"
>
)
</span><span
class=
"w"
>
</span>
-
>
<span
class=
"o"
>
!
</span><span
class=
"w"
>
</span><span
class=
"p"
>
{
</span><span
class=
"w"
></span>
<span
class=
"w"
>
</span><span
class=
"k"
>
loop
</span><span
class=
"w"
>
</span><span
class=
"p"
>
{}
</span><span
class=
"w"
></span>
<span
class=
"p"
>
}
</span><span
class=
"w"
></span>
</pre></div>
</div>
<p>
注意,panic 处理函数的函数签名需要一个
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
PanicInfo
</span></code>
的不可变借用作为输入参数,它在核心库中得以保留,这也是我们第一次与核心库打交道。之后我们
会从
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
PanicInfo
</span></code>
解析出错位置并打印出来,然后杀死应用程序。但目前我们什么都不做只是在原地 loop。
</p>
</div>
<div
class=
"section"
id=
"main"
>
<h2>
移除 main 函数
<a
class=
"headerlink"
href=
"#main"
title=
"Permalink to this headline"
>
¶
</a></h2>
<div
class=
"admonition error"
>
<p
class=
"admonition-title"
>
Error
</p>
<div
class=
"highlight-default notranslate"
><div
class=
"highlight"
><pre><span></span>
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: requires `start` lang_item
</pre></div>
</div>
</div>
<p>
编译器提醒我们缺少一个名为
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
start
</span></code>
的语义项。我们回忆一下,之前提到语言标准库和三方库作为应用程序的执行环境,需要负责在执行应用程序之前进行
一些初始化工作,然后才跳转到应用程序的入口点(也就是跳转到我们编写的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main
</span></code>
函数)开始执行。事实上
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
start
</span></code>
语义项正代表着标准库 std 在
执行应用程序之前需要进行的一些初始化工作。由于我们禁用了标准库,编译器也就找不到这项功能的实现了。
</p>
<p>
最简单的解决方案就是压根不让编译器使用这项功能。我们在
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main.rs
</span></code>
的开头加入设置
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
#![no_main]
</span></code>
告诉编译器我们没有一般意义上的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main
</span></code>
函数,
并将原来的
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main
</span></code>
函数删除。在失去了
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main
</span></code>
函数的情况下,编译器也就不需要完成所谓的初始化工作了。
</p>
<p>
至此,我们成功移除了标准库的依赖并完成裸机平台上的构建。
</p>
<div
class=
"highlight-console notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"gp"
>
$
</span>
cargo build
<span
class=
"go"
>
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
</span>
<span
class=
"go"
>
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
</span>
</pre></div>
</div>
<p>
目前的代码如下:
</p>
<div
class=
"highlight-rust notranslate"
><div
class=
"highlight"
><pre><span></span><span
class=
"c1"
>
// os/src/main.rs
</span>
<span
class=
"cp"
>
#![no_std]
</span><span
class=
"w"
></span>
<span
class=
"cp"
>
#![no_main]
</span><span
class=
"w"
></span>
<span
class=
"k"
>
mod
</span>
<span
class=
"nn"
>
lang_items
</span><span
class=
"p"
>
;
</span><span
class=
"w"
></span>
<span
class=
"c1"
>
// os/src/lang_items.rs
</span>
<span
class=
"k"
>
use
</span><span
class=
"w"
>
</span><span
class=
"n"
>
core
</span>
::
<span
class=
"n"
>
panic
</span>
::
<span
class=
"n"
>
PanicInfo
</span><span
class=
"p"
>
;
</span><span
class=
"w"
></span>
<span
class=
"cp"
>
#[panic_handler]
</span><span
class=
"w"
></span>
<span
class=
"k"
>
fn
</span>
<span
class=
"nf"
>
panic
</span><span
class=
"p"
>
(
</span><span
class=
"n"
>
_info
</span>
:
<span
class=
"kp"
>
&
</span><span
class=
"nc"
>
PanicInfo
</span><span
class=
"p"
>
)
</span><span
class=
"w"
>
</span>
-
>
<span
class=
"o"
>
!
</span><span
class=
"w"
>
</span><span
class=
"p"
>
{
</span><span
class=
"w"
></span>
<span
class=
"w"
>
</span><span
class=
"k"
>
loop
</span><span
class=
"w"
>
</span><span
class=
"p"
>
{}
</span><span
class=
"w"
></span>
<span
class=
"p"
>
}
</span><span
class=
"w"
></span>
</pre></div>
</div>
<p>
本小节我们固然脱离了标准库,通过了编译器的检验,但也是伤筋动骨,将原有的很多功能弱化甚至直接删除,看起来距离在 RV64GC 平台上打印
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
Hello
</span>
<span
class=
"pre"
>
world!
</span></code>
相去甚远了(我们甚至连 println! 和
<code
class=
"docutils literal notranslate"
><span
class=
"pre"
>
main
</span></code>
函数都删除了)。不要着急,接下来我们会以自己的方式来重塑这些
功能,并最终完成我们的目标。
</p>
</div>
</div>
</div>
</div>
<footer>
<div
class=
"rst-footer-buttons"
role=
"navigation"
aria-label=
"footer navigation"
>
<a
href=
"../rest-example.html"
class=
"btn btn-neutral float-right"
title=
"reStructuredText 基本语法"
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>
</div>
<hr/>
<div
role=
"contentinfo"
>
<p>
©
Copyright 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
docs/chapter1/index.html
浏览文件 @
c0d3b248
...
@@ -86,6 +86,7 @@
...
@@ -86,6 +86,7 @@
<li
class=
"toctree-l1"
><a
class=
"reference internal"
href=
"../quickstart.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-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>
</ul>
</ul>
</li>
</li>
</ul>
</ul>
...
...
docs/objects.inv
浏览文件 @
c0d3b248
无法预览此类型文件
docs/searchindex.js
浏览文件 @
c0d3b248
此差异已折叠。
点击以展开。
source/chapter1/1app-ee-platform.rst
浏览文件 @
c0d3b248
...
@@ -3,7 +3,7 @@
...
@@ -3,7 +3,7 @@
.. toctree::
.. toctree::
:hidden:
:hidden:
:maxdepth:
3
:maxdepth:
4
作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
作为一切的开始,让我们使用 Cargo 工具来创建一个 Rust 项目。它看上去没有任何特别之处:
...
@@ -34,7 +34,7 @@
...
@@ -34,7 +34,7 @@
println!("Hello, world!");
println!("Hello, world!");
}
}
利用 Cargo 工具即可一条命令实现构建并运行项目:
进入 os 项目根目录下,
利用 Cargo 工具即可一条命令实现构建并运行项目:
.. code-block:: console
.. code-block:: console
...
@@ -72,7 +72,7 @@
...
@@ -72,7 +72,7 @@
.. note::
.. note::
``Hello, world!`` 用到了哪些系统调用?
**Hello, world! 用到了哪些系统调用?**
从之前的 ``cargo run`` 的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
从之前的 ``cargo run`` 的输出可以看出之前构建的可执行文件是在 target/debug 目录下的 os 。
在 Ubuntu 系统上,可以通过 ``strace`` 工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
在 Ubuntu 系统上,可以通过 ``strace`` 工具来运行一个程序并输出程序运行过程当中向内核请求的所有的系统调用及其返回值。
...
@@ -97,7 +97,7 @@
...
@@ -97,7 +97,7 @@
.. note::
.. note::
多层执行环境都是必需的吗?
**多层执行环境都是必需的吗?**
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 **抽象** (Abstraction),
除了最上层的应用程序和最下层的硬件平台必须存在之外,作为中间层的函数库和内核并不是必须存在的:它们都是对下层资源进行了 **抽象** (Abstraction),
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
并为上层提供了一套运行环境。抽象的优点在于它让上层以较小的代价获得所需的功能,并同时可以提供一些保护。但抽象同时也是一种限制,会丧失一些
...
@@ -171,5 +171,40 @@ linux-gnu 系统调用支持的版本 ``riscv64gc-unknown-linux-gnu``,是因
...
@@ -171,5 +171,40 @@ linux-gnu 系统调用支持的版本 ``riscv64gc-unknown-linux-gnu``,是因
.. note::
.. note::
RISC-V 指令集拓展
**RISC-V 指令集拓展**
由于基于 RISC-V 架构的处理器可能用于嵌入式场景或是通用计算场景,因此指令集规范将指令集划分为最基本的 RV32/64I 以及若干标准指令集拓展。
每款处理器只需按照其实际应用场景按需实现指令集拓展即可。
- RV32/64I:每款处理器都必须实现的基本整数指令集。在 RV32I 中,每个通用寄存器的位宽为 32 位;在 RV64I 中则为 64 位。它可以用来模拟
绝大多数标准指令集拓展中的指令,除了比较特殊的 A 拓展,因为它需要特别的硬件支持。
- M 拓展:提供整数乘除法相关指令。
- A 拓展:提供原子指令和一些相关的内存同步机制,这个后面会展开。
- F/D 拓展:提供单/双精度浮点数运算支持。
- C 拓展:提供压缩指令拓展。
G 拓展是基本整数指令集 I 再加上标准指令集拓展 MAFD 的总称,因此 riscv64gc 也就等同于 riscv64imafdc。我们剩下的内容都基于该处理器
架构完成。除此之外 RISC-V 架构还有很多标准指令集拓展,有一些还在持续更新中尚未稳定,有兴趣的读者可以浏览最新版的 RISC-V 指令集规范。
Rust 标准库与核心库
----------------------------------
我们尝试一下将当前的 ``Hello, world!`` 程序的目标平台换成 riscv64gc-unknown-none-elf 看看会发生什么事情:
.. code-block:: console
$ cargo run --target riscv64gc-unknown-none-elf
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error[E0463]: can't find crate for `std`
|
= note: the `riscv64gc-unknown-none-elf` target may not be installed
在之前的环境配置中,我们已经在 rustup 工具链中安装了这个目标平台支持,因此并不是该目标平台未安装的问题。因此只是单纯的在这个目标平台上找不到
Rust 标准库 std。我们之前曾经提到过,编程语言的标准库或三方库的某些功能会直接或间接的用到操作系统提供的系统调用。但目前我们所选的目标平台不存在
任何操作系统支持,于是 Rust 并没有为这个目标平台支持完整的标准库 std。类似这样的平台通常被我们称为 **裸机平台** (bare-metal)。
幸运的是,Rust 有一个对 std 裁剪过后的核心库 core,这个库是不需要任何操作系统支持的,相对的它的功能也比较受限,但是也包含了 Rust 语言
相当一部分的核心机制,可以满足我们的大部分需求。在 Rust 语言生态中,有很多三方库也不依赖标准库 std 而仅仅依赖核心库 core,它们也可以很大
程度上减轻我们的编程负担。它们是我们能够在裸机平台挣扎求生的最主要倚仗。
于是,我们知道在裸机平台上我们要将对于标准库 std 的引用换成核心库 core。但是做起来其实并没有那么容易。
\ No newline at end of file
source/chapter1/2remove-std.rst
0 → 100644
浏览文件 @
c0d3b248
移除标准库依赖
==========================
.. toctree::
:hidden:
:maxdepth: 4
本节我们尝试移除之前的 ``Hello world!`` 程序对于标准库的依赖,使得它能够编译到裸机平台 RV64GC 上。
我们首先在 ``os`` 目录下新建 ``.cargo`` 目录,并在这个目录下创建 ``config`` 文件,并在里面输入如下内容:
.. code-block::
[build]
target = "riscv64gc-unknown-none-elf"
这会对于 Cargo 工具在 os 目录下的行为进行调整:现在默认会使用 riscv64gc 作为目标平台而不是原先的默认 x86_64-unknown-linux-gnu。
事实上,这是一种编译器运行所在的平台与编译器生成可执行文件的目标平台不同(分别是后者和前者)的情况。这是一种 **交叉编译** (Cross Compile)。
当然,这只是使得我们之后在 ``cargo build`` 的时候不必再加上 ``--target`` 参数的一个小 trick。如果我们现在 ``cargo build`` ,还是会和
上一小节一样出现找不到标准库 std 的错误。于是我们开始着手移除标准库。当然,这会产生一些副作用。
移除 println! 宏
----------------------------------
我们在 ``main.rs`` 的开头加上一行 ``#![no_std]`` 来告诉 Rust 编译器不使用 Rust 标准库 std 转而使用核心库 core。编译器报出如下错误:
.. error::
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: cannot find macro `println` in this scope
--> src/main.rs:4:5
|
4 | println!("Hello, world!");
| ^^^^^^^
我们之前提到过, println! 宏是由标准库 std 提供的,且会使用到一个名为 write 的系统调用。现在我们的条件还不足以自己实现一个 println! 宏,由于
使用了系统调用也不能在核心库 core 中找到它。我们目前先通过将它注释掉来绕过它。
提供语义项 panic_handler
----------------------------------------------------
.. error::
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: `#[panic_handler]` function required, but not found
在使用 Rust 编写应用程序的时候,我们常常在遇到了一些无法恢复的致命错误导致程序无法继续向下运行的时候手动或自动调用 panic! 宏来并打印出错的
位置让我们能够意识到它的存在,并进行一些后续处理。panic! 宏最典型的应用场景包括断言宏 assert! 失败或者对 ``Option::None/Result::Err``
进行 ``unwrap`` 操作。
在标准库 std 中提供了 panic 的处理函数 ``#[panic_handler]``,其大致功能是打印出错位置和原因并杀死当前应用。可惜的是在核心库 core 中并没有提供,
因此我们需要自己实现 panic 处理函数。
.. note::
**Rust 语义项 lang_items**
Rust 编译器内部的某些功能的实现并不是硬编码在语言内部的,而是以一种可插入的形式在库中提供。库只需要通过某种方式告诉编译器它的某个方法实现了
编译器内部的哪些功能,编译器就会采用库提供的方法来实现它内部对应的功能。通常只需要在库的方法前面加上一个标记即可。
我们开一个新的子模块 ``lang_items.rs`` 保存这些语义项,在里面提供 panic 处理函数的实现并通过标记通知编译器采用我们的实现:
.. code-block:: rust
// os/src/lang_items.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
注意,panic 处理函数的函数签名需要一个 ``PanicInfo`` 的不可变借用作为输入参数,它在核心库中得以保留,这也是我们第一次与核心库打交道。之后我们
会从 ``PanicInfo`` 解析出错位置并打印出来,然后杀死应用程序。但目前我们什么都不做只是在原地 loop。
移除 main 函数
-----------------------------
.. error::
.. code-block::
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
error: requires `start` lang_item
编译器提醒我们缺少一个名为 ``start`` 的语义项。我们回忆一下,之前提到语言标准库和三方库作为应用程序的执行环境,需要负责在执行应用程序之前进行
一些初始化工作,然后才跳转到应用程序的入口点(也就是跳转到我们编写的 ``main`` 函数)开始执行。事实上 ``start`` 语义项正代表着标准库 std 在
执行应用程序之前需要进行的一些初始化工作。由于我们禁用了标准库,编译器也就找不到这项功能的实现了。
最简单的解决方案就是压根不让编译器使用这项功能。我们在 ``main.rs`` 的开头加入设置 ``#![no_main]`` 告诉编译器我们没有一般意义上的 ``main`` 函数,
并将原来的 ``main`` 函数删除。在失去了 ``main`` 函数的情况下,编译器也就不需要完成所谓的初始化工作了。
至此,我们成功移除了标准库的依赖并完成裸机平台上的构建。
.. code-block:: console
$ cargo build
Compiling os v0.1.0 (/home/shinbokuow/workspace/v3/rCore-Tutorial-v3/os)
Finished dev [unoptimized + debuginfo] target(s) in 0.06s
目前的代码如下:
.. code-block:: rust
// os/src/main.rs
#![no_std]
#![no_main]
mod lang_items;
// os/src/lang_items.rs
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {}
}
本小节我们固然脱离了标准库,通过了编译器的检验,但也是伤筋动骨,将原有的很多功能弱化甚至直接删除,看起来距离在 RV64GC 平台上打印
``Hello world!`` 相去甚远了(我们甚至连 println! 和 ``main`` 函数都删除了)。不要着急,接下来我们会以自己的方式来重塑这些
功能,并最终完成我们的目标。
\ No newline at end of file
source/chapter1/index.rst
浏览文件 @
c0d3b248
...
@@ -6,4 +6,5 @@
...
@@ -6,4 +6,5 @@
:hidden:
:hidden:
:maxdepth: 3
:maxdepth: 3
1app-ee-platform
1app-ee-platform
\ No newline at end of file
2remove-std
\ No newline at end of file
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录