diff --git a/10-heap-allocation.md b/10-heap-allocation.md index 73053dd..74e4422 100644 --- a/10-heap-allocation.md +++ b/10-heap-allocation.md @@ -130,7 +130,7 @@ Rust的所有权系统更进了一步,它不仅可以防止使用后使用的 ### 用例 -现在我们知道Rust中动态内存分配的基础知识,但是什么时候应该使用它呢? 没有动态内存分配的内核已经走得很远了,那么为什么现在需要它呢? +现在我们知道Rust中动态内存分配的基础知识,但是应该什么时候使用它呢? 没有动态内存分配的内核已经走得很远了,那么为什么现在需要它呢? 首先,动态内存分配总是会带来一些性能开销,因为我们需要为每个分配在堆上找到一个空闲空间。 因此,通常最好使用局部变量,尤其是在性能敏感的内核代码中。 但是,在某些情况下,动态内存分配是最佳选择。 @@ -150,6 +150,17 @@ extern crate alloc; 与其他依赖不同,我们不需要修改`Cargo.toml` 。 原因是`alloc`crate与Rust编译器一起作为标准库的一部分提供,因此我们只需要启用它即可。 这就是这个`extern crate`语句的作用。(历史上,所有依赖项都需要一个`extern crate`语句,该语句现在是可选的)。 +由于我们是在编译一个特定的目标,因此我们不能使用Rust安装时预编译版本的`alloc`。取而代之的是,我们可以把`alloc`加入到位于`.cargo/config.toml`文件的`unstable.build-std`数组中,实现使cargo通过源代码来重新编译crate。 + +```rust +# in .cargo/config.toml + +[unstable] +build-std = ["core", "compiler_builtins", "alloc"] +``` + +现在,编译器将重新编译并在我们的内核中包含alloc crate。 + `#[no_std]`默认禁用了`alloc`crate,其原因是它还有其他要求。 现在尝试编译项目时,我们可以从错误中提示看到这些要求: ```shell @@ -358,9 +369,9 @@ pub fn init_heap( 实现可以分为两部分: -- **创建页面范围::**要创建我们要映射的页面范围,我们将`HEAP_START`指针转换为[`VirtAddr`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/struct.VirtAddr.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhNOHf3zU7AgNVZhjYJkAVQ39Xufw)类型。 然后,我们通过添加`HEAP_SIZE`从中计算出堆结束地址。 我们需要一个包含性的边界(堆最后一个字节的地址),因此我们减去1。接下来,我们使用[`containing_address`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw#method.containing_address)函数将地址转换为[`Page`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw)类型。 最后,我们使用[`Page::range_inclusive`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw#method.range_inclusive)函数从起始页面和结束页面创建页面范围。 +- **创建页面范围:**要创建我们要映射的页面范围,我们将`HEAP_START`指针转换为[`VirtAddr`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/struct.VirtAddr.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhNOHf3zU7AgNVZhjYJkAVQ39Xufw)类型。 然后,我们通过添加`HEAP_SIZE`从中计算出堆结束地址。 我们需要一个包含性的边界(堆最后一个字节的地址),因此我们减去1。接下来,我们使用[`containing_address`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw#method.containing_address)函数将地址转换为[`Page`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw)类型。 最后,我们使用[`Page::range_inclusive`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/page/struct.Page.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjSe7J0VwXNgWPo3i7TiwHf8vSwFw#method.range_inclusive)函数从起始页面和结束页面创建页面范围。 - **映射页面:**第二步是映射我们刚刚创建的页面范围的所有页面。 为此,我们使用`for`循环遍历该范围内的页面。 对于每个页面,我们执行以下操作: - - 我们使用[`FrameAllocator::allocate_frame`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/trait.FrameAllocator.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjggS52v2i2ScjsxhDb74n9oOyHIA#tymethod.allocate_frame)方法[`FrameAllocator::allocate_frame`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/trait.FrameAllocator.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjggS52v2i2ScjsxhDb74n9oOyHIA#tymethod.allocate_frame)页面应映射到的物理帧。 当没有剩余的帧时,此方法将返回[`None`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/core/option/enum.Option.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhaJ_Il3_-qAEhTV0CU_eroK_zIqA#variant.None) 。 我们通过使用[`Option::ok_or`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/core/option/enum.Option.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhaJ_Il3_-qAEhTV0CU_eroK_zIqA#method.ok_or)方法将其映射到[`MapToError::FrameAllocationFailed`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/mapper/enum.MapToError.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhicxFwlIHtXfyNzcvL79bpGKF_HjA#variant.FrameAllocationFailed)错误来处理这种情况,然后应用[问号运算符](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhg1cg548fqAFT-Y_9h9-bsEfN4Qpw)以在出现错误的情况下尽早返回。 + - 该页应该被映射到我们使用[`FrameAllocator::allocate_frame`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/trait.FrameAllocator.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhjggS52v2i2ScjsxhDb74n9oOyHIA#tymethod.allocate_frame)方法分配的物理帧上。 当没有剩余的帧时,此方法将返回[`None`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/core/option/enum.Option.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhaJ_Il3_-qAEhTV0CU_eroK_zIqA#variant.None) 。 我们通过使用[`Option::ok_or`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/core/option/enum.Option.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhaJ_Il3_-qAEhTV0CU_eroK_zIqA#method.ok_or)方法将其映射到[`MapToError::FrameAllocationFailed`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/mapper/enum.MapToError.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhicxFwlIHtXfyNzcvL79bpGKF_HjA#variant.FrameAllocationFailed)错误来处理没有剩余帧的情况,并应用[问号运算符](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhg1cg548fqAFT-Y_9h9-bsEfN4Qpw)以在出现错误的情况下尽早返回。 - 我们为页面设置了必需的`PRESENT`标志和`WRITABLE`标志。 使用这些标志,允许读取和写入访问,这对于堆内存是有意义的。 - 我们使用不安全的[`Mapper::map_to`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/mapper/trait.Mapper.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhgVFv5BTMRwDVcKqwVIbnPTvsmTPg#tymethod.map_to)方法在活动页面表中创建映射。 该方法可能会失败,因此我们再次使用[问号运算符](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://doc.rust-lang.org/edition-guide/rust-2018/error-handling-and-panics/the-question-mark-operator-for-easier-error-handling.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhg1cg548fqAFT-Y_9h9-bsEfN4Qpw)将错误转发给调用方。 成功后,该方法将返回一个[`MapperFlush`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/mapper/struct.MapperFlush.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhu0tpL-XJy7I4bFlQNnOuMtOYO7g)实例,我们可以使用该实例使用[`flush`](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://docs.rs/x86_64/0.7.5/x86_64/structures/paging/mapper/struct.MapperFlush.html&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhu0tpL-XJy7I4bFlQNnOuMtOYO7g#method.flush)方法来更新[*转换后备缓冲区*](https://translate.googleusercontent.com/translate_c?depth=1&rurl=translate.google.com.hk&sl=en&sp=nmt4&tl=zh-CN&u=https://os.phil-opp.com/paging-introduction/&xid=17259,15700022,15700186,15700191,15700256,15700259,15700262,15700265&usg=ALkJrhhkanSS8TkxwpQrLCkMYDffTLWobg#the-translation-lookaside-buffer)。 @@ -434,7 +445,7 @@ static ALLOCATOR: LockedHeap = LockedHeap::empty(); pub fn init_heap( mapper: &mut impl Mapper, frame_allocator: &mut impl FrameAllocator, -) -> Result<(), MapToError> { +) -> Result<(), MapToError> { // […] map all heap pages to physical frames // new