From 57ddecc28ab7a1632241cf619ebe91fd8ad4579c Mon Sep 17 00:00:00 2001 From: Zhang Yao Date: Fri, 23 Jun 2023 12:51:35 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=AC=AC10=E7=AB=A0?= =?UTF-8?q?=E2=80=9CThe=20Allocator=20Interface=E2=80=9D=E9=83=A8=E5=88=86?= =?UTF-8?q?=E7=9A=84=E9=83=A8=E5=88=86=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 翻译版教程已无法编译,查询原版教程发现有新增内容,故参考原版教程更新了翻译内容。原版见[https://os.phil-opp.com/heap-allocation/#the-allocator-interface](https://os.phil-opp.com/heap-allocation/#the-allocator-interface) --- 10-heap-allocation.md | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/10-heap-allocation.md b/10-heap-allocation.md index 73053dd..daa9609 100644 --- a/10-heap-allocation.md +++ b/10-heap-allocation.md @@ -150,6 +150,17 @@ extern crate alloc; 与其他依赖不同,我们不需要修改`Cargo.toml` 。 原因是`alloc`crate与Rust编译器一起作为标准库的一部分提供,因此我们只需要启用它即可。 这就是这个`extern crate`语句的作用。(历史上,所有依赖项都需要一个`extern crate`语句,该语句现在是可选的)。 +因为我们正在编译自定义的目标,所以我们不能直接使用Rust安装时所附带的预编译版`alloc`。相反地,我们必须告诉cargo从源代码把它重新编译一遍。我们可以通过在文件`.cargo/config.toml`中的`unstable.build-std`数组里添加`alloc`来实现这个操作: + +```toml +# in .cargo/config.toml + +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] +``` + +现在,编译器就会重新编译`alloc`,并把它包含在我们的内核里了。 + `#[no_std]`默认禁用了`alloc`crate,其原因是它还有其他要求。 现在尝试编译项目时,我们可以从错误中提示看到这些要求: ```shell @@ -641,4 +652,4 @@ many_boxes... [ok] ## 接下来? -尽管我们已经在本文中添加了堆分配支持,但我们将大部分工作交给了`linked_list_allocator` crate。下一篇文章将详细显示如何从头开始实现分配器。它将介绍多种可能的分配器设计,展示如何实现它们的简单版本,并说明其优缺点。 \ No newline at end of file +尽管我们已经在本文中添加了堆分配支持,但我们将大部分工作交给了`linked_list_allocator` crate。下一篇文章将详细显示如何从头开始实现分配器。它将介绍多种可能的分配器设计,展示如何实现它们的简单版本,并说明其优缺点。 From 432f1e4dfa1fe794b819b027d3ddc33f3fcf2c84 Mon Sep 17 00:00:00 2001 From: Cinea Zhan Date: Fri, 23 Jun 2023 20:57:13 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=AC=AC2=E7=AB=A0?= =?UTF-8?q?=E7=9A=84=E8=BF=87=E6=97=B6=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- 02-minimal-rust-kernel.md | 55 ++++++++++++++++++++++++++++----------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/02-minimal-rust-kernel.md b/02-minimal-rust-kernel.md index 67575e8..ef14548 100644 --- a/02-minimal-rust-kernel.md +++ b/02-minimal-rust-kernel.md @@ -191,32 +191,57 @@ error[E0463]: can't find crate for `compiler_builtins` 通常状况下,`core`库以**预编译库**(precompiled library)的形式与Rust编译器一同发布——这时,`core`库只对支持的宿主系统有效,而我们自定义的目标系统无效。如果我们想为其它系统编译代码,我们需要为这些系统重新编译整个`core`库。 -### Cargo xbuild +#### `build-std`选项 -这就是为什么我们需要[cargo xbuild工具](https://github.com/rust-osdev/cargo-xbuild)。这个工具封装了`cargo build`;但不同的是,它将自动交叉编译`core`库和一些**编译器内建库**(compiler built-in libraries)。我们可以用下面的命令安装它: +这就是cargo的`build-std`特性可以出场的地方了。它允许我们根据自己的需求重新编译`core`和其他标准库里的包,而不是使用安装Rust时所附带的预编译版本。这项特性目前还很新、没有彻底完成,所以它被标记为“不稳定”并且只在 Nightly Rust 中提供。 -```bash -cargo install cargo-xbuild +我们需要在`.cargo/config.toml`新建一个[cargo配置文件](https://doc.rust-lang.org/cargo/reference/config.html)来使用这项特性: + +```toml +# in .cargo/config.toml + +[unstable] +build-std = ["core", "compiler_builtins"] ``` -这个工具依赖于Rust的源代码;我们可以使用`rustup component add rust-src`来安装源代码。 +这会告诉cargo它应当重新编译`core`和`compiler_builtins`库。后者是`core`的依赖,所以也被包括进来。为了编译这些库,cargo需要使用rust的源代码,我们可以使用`rustup component add rust-src`来将其安装。 -现在我们可以使用`xbuild`代替`build`重新编译: +> **注意**:配置项`unstable.build-std`在2020-07-15之后的Rust nightly上可用。 -```bash -> cargo xbuild --target x86_64-blog_os.json +在设置`unstable.build-std`和安装了`rust-src`组件之后,我们可以重新运行我们的编译指令了: + +``` +> cargo build --target x86_64-blog_os.json Compiling core v0.0.0 (/…/rust/src/libcore) - Compiling compiler_builtins v0.1.5 - Compiling rustc-std-workspace-core v1.0.0 (/…/rust/src/tools/rustc-std-workspace-core) - Compiling alloc v0.0.0 (/tmp/xargo.PB7fj9KZJhAI) - Finished release [optimized + debuginfo] target(s) in 45.18s - Compiling blog_os v0.1.0 (file:///…/blog_os) + Compiling rustc-std-workspace-core v1.99.0 (/…/rust/src/tools/rustc-std-workspace-core) + Compiling compiler_builtins v0.1.32 + Compiling blog_os v0.1.0 (/…/blog_os) Finished dev [unoptimized + debuginfo] target(s) in 0.29 secs ``` -我们能看到,`cargo xbuild`为我们自定义的目标交叉编译了`core`、`compiler_builtin`和`alloc`三个部件。这些部件使用了大量的**不稳定特性**(unstable features),所以只能在[nightly版本的Rust编译器](https://os.phil-opp.com/freestanding-rust-binary/#installing-rust-nightly)中工作。这之后,`cargo xbuild`成功地编译了我们的`blog_os`包。 +可以看到,`cargo build`现在为我们的自定义目标重新编译了`core`、`rustc-std-workspace-core`(`compiler_builtins`的依赖)和`compiler_builtins`。 + +#### 和内存相关的内置函数 + +Rust编译器假定了在所有系统上都有特定的一组内置函数可用。这些函数中的绝大部分都是由我们刚刚重新编译的`compiler_builtins`包提供的。不过,`compiler_builtins`里也有一部分和内存相关的函数是默认不启用的,因为一般情况下系统的C library会提供它们。这些函数包括`memset`,一个能把内存块中全部填上某个值的函数、`memcpy`,一个能把内存块复制到另一个内存块的函数、`memcmp`,一个能比较两个内存块差异的函数。虽然目前我们内核的编译过程还不需要它们,但是只要我们继续向内核中添加更多的代码,它们就会马上被需要(比如把结构体进行复制的时候)。 + +考虑到我们不能把系统自带的C library链接进来,我们需要一种替代的方法来向编译器提供这些函数。一种可能的方案是我们自己实现一套`memset`等函数,然后加上`#[no_mangle]`属性(来防止这个函数的名字在编译期间被自动重命名)。但是,这种做法实际上是很危险的,因为在实现过程中但凡再小的错误也会引发一个未定义行为(undefined behavior)。例如,用`for`循环来写`memcpy`可能会导致其进入无限递归,因为`for`循环隐式地调用了`[IntoIterator::into_iter](https://doc.rust-lang.org/stable/core/iter/trait.IntoIterator.html#tymethod.into_iter)`特征方法,而它又会再次调用`memcpy`。所以,重复利用现有的、已经经过充分测试的实现是一个更好的主意。 + +幸运的是,`compiler_builtins`包已经包含了所需的所有函数的实现,它们只是默认没有启用,以免和C library的实现产生冲突。我们可以通过设置cargo的`[build-std-features](https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#build-std-features)`配置项为`["compiler-builtins-mem"]`来启用它们。就像`build-std`配置项一样,这个配置项既可以在命令行参数上作为一个`-Z`参数传递,也可以在`.cargo/config.toml`文件中的`unstable`表中设置。考虑到我们总是需要在编译时使用这个配置项,把它写到配置文件里面对我们来说更有帮助: + +```toml +# in .cargo/config.toml + +[unstable] +build-std-features = ["compiler-builtins-mem"] +build-std = ["core", "compiler_builtins"] +``` + +(对`compiler-builtins-mem`特性的支持[在最近才被加入](https://github.com/rust-lang/rust/pull/77284),所以你至少需要2020-09-30版本的Rust nightly。) + +这个配置项在幕后启用了`compiler_builtins`包的[`mem`特性](https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/Cargo.toml#L54-L55),这使得这个包里的[`memcpy`等函数的实现](https://github.com/rust-lang/compiler-builtins/blob/eff506cd49b637f1ab5931625a33cef7e91fbbf6/src/mem.rs#L12-L69)被加上了`#[no_mangle]`属性,让它们可以被链接器发现。 -现在我们可以为裸机编译内核了;但是,我们提供给引导程序的入口点`_start`函数还是空的。我们可以添加一些东西进去,不过我们可以先做一些优化工作。 +通过这个更改,我们的内核在编译时拥有了所有需要函数的实现,所以哪怕之后我们的代码变得更复杂了,编译器也会继续完成编译。 ### 设置默认目标