Fix dead links

This commit is contained in:
Philipp Oppermann
2020-02-17 12:55:40 +01:00
parent f21bef93b1
commit 65da18d143
24 changed files with 57 additions and 42 deletions

View File

@@ -245,7 +245,7 @@ It needs to be a function with a defined [calling convention], as it called dire
It is important that the function is [diverging], i.e. it must never return. The reason is that the hardware doesn't _call_ the handler functions, it just _jumps_ to them after pushing some values to the stack. So our stack might look different:
[diverging]: https://doc.rust-lang.org/book/functions.html#diverging-functions
[diverging]: https://doc.rust-lang.org/rust-by-example/fn/diverging.html
![normal function return vs interrupt function return](normal-vs-interrupt-function-return.svg)

View File

@@ -126,9 +126,9 @@ Instead of pushing a return address, the CPU pushes the stack and instruction po
So we can't use a normal `ret` instruction, since it expects a different stack frame layout. Instead, there is a special instruction for returning from exceptions: `iretq`.
### The `iretq` Instruction
The `iretq` instruction is the one and only way to return from exceptions and is specifically designed for this purpose. The AMD64 manual ([PDF][amd-manual]) even demands that `iretq` “_must_ be used to terminate the exception or interrupt handler associated with the exception”.
The `iretq` instruction is the one and only way to return from exceptions and is specifically designed for this purpose. The AMD64 instruction manual ([PDF][amd-manual]) even demands that `iretq` “_must_ be used to terminate the exception or interrupt handler associated with the exception”.
[amd-manual]: https://support.amd.com/TechDocs/24594.pdf
[amd-manual]: https://www.amd.com/system/files/TechDocs/24594.pdf
IRETQ restores `rip`, `cs`, `rflags`, `rsp`, and `ss` from the values saved on the stack and thus continues the interrupted program. The instruction does not handle the optional error code, so it must be popped from the stack before.

View File

@@ -32,7 +32,7 @@ Remote 'g' packet reply is too long: [a very long number]
```
This issue is known [since 2012][gdb issue patch] but it is still not fixed. Maybe we find the reason in the [issue thread][gdb issue thread]:
[gdb issue patch]: http://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
[gdb issue patch]: https://web.archive.org/web/20190114181420/http://www.cygwin.com/ml/gdb-patches/2012-03/msg00116.html
[gdb issue thread]: https://sourceware.org/bugzilla/show_bug.cgi?id=13984#c11
> from my (limited) experience, unless you ping the gdb-patches list weekly, this patch is more likely to remain forgotten :-)

View File

@@ -49,7 +49,7 @@ crate-type = ["staticlib"]
```
The `package` section contains required project metadata such as the [semantic crate version]. The `lib` section specifies that we want to build a static library, i.e. a library that contains all of its dependencies. This is required to link the Rust project with our kernel.
[semantic crate version]: http://doc.crates.io/manifest.html#the-package-section
[semantic crate version]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-package-section
Now we place our root source file in `src/lib.rs`:
@@ -408,7 +408,7 @@ So the linker can't find a function named `_Unwind_Resume` that is referenced e.
By default, the destructors of all stack variables are run when a `panic` occurs. This is called _unwinding_ and allows parent threads to [recover from panics]. However, it requires a platform specific gcc library, which isn't available in our kernel.
[recover from panics]: https://doc.rust-lang.org/book/concurrency.html#panics
[recover from panics]: https://www.howtobuildsoftware.com/index.php/how-do/fFH/rust-recovering-from-panic-in-another-thread
Fortunately, Rust allows us to disable unwinding for our target. For that we add the following line to our `x86_64-blog_os.json` file:
@@ -479,7 +479,7 @@ Some notes:
- `buffer_ptr` is a [raw pointer] that points to the center of the VGA text buffer
- Rust doesn't know the VGA buffer and thus can't guarantee that writing to the `buffer_ptr` is safe (it could point to important data). So we need to tell Rust that we know what we are doing by using an [unsafe block].
[byte string]: https://doc.rust-lang.org/reference.html#characters-and-strings
[byte string]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
[enumerate]: https://doc.rust-lang.org/nightly/core/iter/trait.Iterator.html#method.enumerate
[unsafe block]: https://doc.rust-lang.org/book/unsafe.html

View File

@@ -190,7 +190,7 @@ When printing a byte, the writer checks if the current line is full. In that cas
The `buffer()` auxiliary method converts the raw pointer in the `buffer` field into a safe mutable buffer reference. The unsafe block is needed because the [as_mut()] method of `Unique` is unsafe. But our `buffer()` method itself isn't marked as unsafe, so it must not introduce any unsafety (e.g. cause segfaults). To guarantee that, it's very important that the `buffer` field always points to a valid `Buffer`. It's like a contract that we must stand to every time we create a `Writer`. To ensure that it's not possible to create an invalid `Writer` from outside of the module, the struct must have at least one private field and public creation functions are not allowed either.
[as_mut()]: https://doc.rust-lang.org/1.10.0/core/ptr/struct.Unique.html#method.as_mut
[as_mut()]: https://doc.rust-lang.org/1.26.0/core/ptr/struct.Unique.html#method.as_mut
### Cannot Move out of Borrowed Content
When we try to compile it, we get the following error:
@@ -257,7 +257,7 @@ It just creates a new Writer that points to the VGA buffer at `0xb8000`. To use
Then it writes the byte `b'H'` to it. The `b` prefix creates a [byte character], which represents an ASCII code point. When we call `vga_buffer::print_something` in main, a `H` should be printed in the _lower_ left corner of the screen in light green:
[byte character]: https://doc.rust-lang.org/reference.html#characters-and-strings
[byte character]: https://doc.rust-lang.org/reference/tokens.html#characters-and-strings
![QEMU output with a green `H` in the lower left corner](vga-H-lower-left.png)
@@ -441,14 +441,14 @@ But we can't use it to print anything! You can try it yourself in the `print_som
To resolve it, we could use a [mutable static]. But then every read and write to it would be unsafe since it could easily introduce data races and other bad things. Using `static mut` is highly discouraged, there are even proposals to [remove it][remove static mut].
[mutable static]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
[mutable static]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
[remove static mut]: https://internals.rust-lang.org/t/pre-rfc-remove-static-mut/1437
But what are the alternatives? We could try to use a cell type like [RefCell] or even [UnsafeCell] to provide [interior mutability]. But these types aren't [Sync] \(with good reason), so we can't use them in statics.
[RefCell]: https://doc.rust-lang.org/nightly/core/cell/struct.RefCell.html
[UnsafeCell]: https://doc.rust-lang.org/nightly/core/cell/struct.UnsafeCell.html
[interior mutability]: https://doc.rust-lang.org/book/mutability.html#interior-vs.-exterior-mutability
[interior mutability]: https://doc.rust-lang.org/1.30.0/book/first-edition/mutability.html#interior-vs-exterior-mutability
[Sync]: https://doc.rust-lang.org/nightly/core/marker/trait.Sync.html
To get synchronized interior mutability, users of the standard library can use [Mutex]. It provides mutual exclusion by blocking threads when the resource is already locked. But our basic kernel does not have any blocking support or even a concept of threads, so we can't use it either. However there is a really basic kind of mutex in computer science that requires no operating system features: the [spinlock]. Instead of blocking, the threads simply try to lock it again and again in a tight loop and thus burn CPU time until the mutex is free again.
@@ -529,7 +529,7 @@ The macro expands to a call of the [`_print` function] in the `io` module. The [
The [`format_args` macro] builds a [fmt::Arguments] type from the passed arguments, which is passed to `_print`. The [`_print` function] of libstd is rather complicated, as it supports different `Stdout` devices. We don't need that complexity since we just want to print to the VGA buffer.
[`_print` function]: https://github.com/rust-lang/rust/blob/46d39f3329487115e7d7dcd37bc64eea6ef9ba4e/src/libstd/io/stdio.rs#L631
[`$crate` variable]: https://doc.rust-lang.org/book/macros.html#the-variable-crate
[`$crate` variable]: https://doc.rust-lang.org/1.30.0/book/first-edition/macros.html#the-variable-crate
[`format_args` macro]: https://doc.rust-lang.org/nightly/std/macro.format_args.html
[fmt::Arguments]: https://doc.rust-lang.org/nightly/core/fmt/struct.Arguments.html

View File

@@ -394,7 +394,7 @@ Note that we call `choose_next_area` manually here because `allocate_frame` retu
### Testing it
In order to test it in main, we need to [re-export] the `AreaFrameAllocator` in the `memory` module. Then we can create a new allocator:
[re-export]: https://doc.rust-lang.org/book/crates-and-modules.html#re-exporting-with-pub-use
[re-export]: https://doc.rust-lang.org/1.30.0/book/first-edition/crates-and-modules.html#re-exporting-with-pub-use
```rust
let mut frame_allocator = memory::AreaFrameAllocator::new(

View File

@@ -323,7 +323,7 @@ We convert the address into raw pointers through `as` casts and then convert the
Note that `self` stays borrowed as long as the returned reference is valid. This is because of Rust's [lifetime elision] rules. Basically, these rules say that the lifetime of an output reference is the same as the lifetime of the input reference by default. So the above function signatures are expanded to:
[lifetime elision]: https://doc.rust-lang.org/book/lifetimes.html#lifetime-elision
[lifetime elision]: https://doc.rust-lang.org/1.30.0/book/first-edition/lifetimes.html#lifetime-elision
```rust
pub fn next_table<'a>(&'a self, index: usize) -> Option<&'a Table> {...}

View File

@@ -641,7 +641,7 @@ SECTIONS {
```
The `.` is the “current location counter” and represents the current virtual address. At the beginning of the `SECTIONS` tag we set it to `1M`, so our kernel starts at 1MiB. We use the [ALIGN][linker align] function to align the current location counter to the next `4K` boundary (`4K` is the page size). Thus the end of the `.text` section and the beginning of the next section are page aligned.
[linker align]: http://www.math.utah.edu/docs/info/ld_3.html#SEC12
[linker align]: https://www.math.utah.edu/docs/info/ld_3.html#SEC12
To put all sections on their own page, we add the `ALIGN` statement to all of them:
@@ -882,7 +882,7 @@ Now we should see the `NEW TABLE!!!` message (and also the `It did not crash!` l
### Fixing the Frame Allocator
The same problem as above occurs when we try to use our [AreaFrameAllocator] again. Try to add the following to `rust_main` after switching to the new table:
[AreaFrameAllocator]: http://os.phil-opp.com/allocating-frames.html#the-allocator
[AreaFrameAllocator]: @/first-edition/posts/05-allocating-frames/index.md#the-allocator
```rust
// in src/lib.rs

View File

@@ -332,7 +332,7 @@ let heap_test = Box::new(42);
When we run it, a triple fault occurs and causes permanent rebooting. Let's try debug it using QEMU and objdump as described [in the previous post][qemu debugging]:
[qemu debugging]: http://os.phil-opp.com/remap-the-kernel.html#debugging
[qemu debugging]: @/first-edition/posts/07-remap-the-kernel/index.md#debugging
```
> qemu-system-x86_64 -d int -no-reboot -cdrom build/os-x86_64.iso

View File

@@ -118,7 +118,7 @@ type HandlerFunc = extern "x86-interrupt" fn(_: &mut ExceptionStackFrame);
It's a [type alias] for an `extern "x86-interrupt" fn` type. The `extern` keyword defines a function with a [foreign calling convention] and is often used to communicate with C code (`extern "C" fn`). But what is the `x86-interrupt` calling convention?
[type alias]: https://doc.rust-lang.org/book/type-aliases.html
[foreign calling convention]: https://doc.rust-lang.org/book/ffi.html#foreign-calling-conventions
[foreign calling convention]: https://doc.rust-lang.org/1.30.0/book/first-edition/ffi.html#foreign-calling-conventions
## The Interrupt Calling Convention
Exceptions are quite similar to function calls: The CPU jumps to the first instruction of the called function and executes it. Afterwards, if the function is not diverging, the CPU jumps to the return address and continues the execution of the parent function.
@@ -317,7 +317,7 @@ pub fn init() {
There are two problems with this. First, statics are immutable, so we can't modify the breakpoint entry from our `init` function. Second, the `Idt::new` function is not a [`const` function], so it can't be used to initialize a `static`. We could solve this problem by using a [`static mut`] of type `Option<Idt>`:
[`const` function]: https://github.com/rust-lang/rfcs/blob/master/text/0911-const-fn.md
[`static mut`]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
```rust
static mut IDT: Option<Idt> = None;
@@ -334,7 +334,7 @@ pub fn init() {
This variant compiles without errors but it's far from idiomatic. `static mut`s are very prone to data races, so we need an [`unsafe` block] on each access. Also, we need to explicitly `unwrap` the `IDT` on each use, since might be `None`.
[`unsafe` block]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
[`unsafe` block]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
#### Lazy Statics to the Rescue
The one-time initialization of statics with non-const functions is a common problem in Rust. Fortunately, there already exists a good solution in a crate named [lazy_static]. This crate provides a `lazy_static!` macro that defines a lazily initialized `static`. Instead of computing its value at compile time, the `static` laziliy initializes itself when it's accessed the first time. Thus, the initialization happens at runtime so that arbitrarily complex initialization code is possible.

View File

@@ -155,7 +155,7 @@ Let's look at the fourth question:
When our kernel overflows its stack and hits the guard page, a _page fault_ occurs. The CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, our current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
[exception stack frame]: http://os.phil-opp.com/better-exception-messages.html#exceptions-in-detail
[exception stack frame]: @/first-edition/posts/09-handling-exceptions/index.md#the-exception-stack-frame
So the CPU tries to call our _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. Our stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.

View File

@@ -38,7 +38,7 @@ There are lots of alternatives to `make`, including some Rust tools such as [jus
[just]: https://github.com/casey/just
[cargo-make]: https://sagiegurari.github.io/cargo-make/
[`global_asm`]: https://doc.rust-lang.org/unstable-book/language-features/global-asm.html
[`global_asm`]: https://doc.rust-lang.org/unstable-book/library-features/global-asm.html
## A custom Bootloader
To avoid the dependency on GRUB and to make things more ergonomic, we decided to write [our own bootloader] using Rust's [`global_asm`] feature. This way, the kernel can be significantly simplified, since the switch to long mode and the initial page table layout can already be done in the bootloader. Thus, we can avoid the initial assembly level blog posts in the second edition and directly start with high level Rust code.

View File

@@ -56,7 +56,7 @@ cargo new blog_os --bin --edition 2018
I named the project `blog_os`, but of course you can choose your own name. The `--bin` flag specifies that we want to create an executable binary (in contrast to a library) and the `--edition 2018` flag specifies that we want to use the [2018 edition] of Rust for our crate. When we run the command, cargo creates the following directory structure for us:
[2018 edition]: https://rust-lang-nursery.github.io/edition-guide/rust-2018/index.html
[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
```
blog_os
@@ -397,7 +397,7 @@ error: linking with `cc` failed: exit code: 1
macOS [does not officially support statically linked binaries] and requires programs to link the `libSystem` library by default. To override this and link a static binary, we pass the `-static` flag to the linker:
[does not officially support statically linked binaries]: https://developer.apple.com/library/content/qa/qa1118/_index.html
[does not officially support statically linked binaries]: https://developer.apple.com/library/archive/qa/qa1118/_index.html
```
cargo rustc -- -C link-args="-e __start -static"

View File

@@ -45,7 +45,9 @@ translators = ["luojia65", "Rustin-Liu", "TheBegining"]
> cargo new blog_os
```
在这里我把项目命名为 `blog_os`当然读者也可以选择自己的项目名称。这里cargo 默认为我们添加了`--bin` 选项说明我们将要创建一个可执行文件而不是一个库cargo还为我们添加了`--edition 2018` 标签,指明项目的包要使用 Rust 的 **2018 版次**[2018 edition](https://rust-lang-nursery.github.io/edition-guide/rust-2018/index.html)。当我们执行这行指令的时候cargo 为我们创建的目录结构如下:
在这里我把项目命名为 `blog_os`当然读者也可以选择自己的项目名称。这里cargo 默认为我们添加了`--bin` 选项说明我们将要创建一个可执行文件而不是一个库cargo还为我们添加了`--edition 2018` 标签,指明项目的包要使用 Rust 的 **2018 版次**[2018 edition]。当我们执行这行指令的时候cargo 为我们创建的目录结构如下:
[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
```
blog_os
@@ -116,7 +118,9 @@ fn panic(_info: &PanicInfo) -> ! {
}
```
类型为 [PanicInfo](https://doc.rust-lang.org/nightly/core/panic/struct.PanicInfo.html) 的参数包含了 panic 发生的文件名、代码行数和可选的错误信息。这个函数从不返回,所以他被标记为**发散函数**[diverging function](https://doc.rust-lang.org/book/first-edition/functions.html#diverging-functions))。发散函数的返回类型称作 **Never 类型**["never" type](https://doc.rust-lang.org/nightly/std/primitive.never.html)),记为`!`。对这个函数,我们目前能做的很少,所以我们只需编写一个无限循环 `loop {}`
类型为 [PanicInfo](https://doc.rust-lang.org/nightly/core/panic/struct.PanicInfo.html) 的参数包含了 panic 发生的文件名、代码行数和可选的错误信息。这个函数从不返回,所以他被标记为**发散函数**[diverging function])。发散函数的返回类型称作 **Never 类型**["never" type](https://doc.rust-lang.org/nightly/std/primitive.never.html)),记为`!`。对这个函数,我们目前能做的很少,所以我们只需编写一个无限循环 `loop {}`
[diverging function]: https://doc.rust-lang.org/1.30.0/book/first-edition/functions.html#diverging-functions
## eh_personality 语言项

View File

@@ -60,7 +60,7 @@ cargo new blog_os --bin --edition 2018
我將專案命名為 `blog_os`,當然讀者也可以自己的名稱。`--bin` 選項說明我們將要建立一個執行檔(而不是一個函式庫),`--edition 2018` 選項指明我們的 crate 想使用 Rust [2018 版本][2018 edition]。當我們執行這行指令的時候cargo 會為我們建立以下目錄結構:
[2018 edition]: https://rust-lang-nursery.github.io/edition-guide/rust-2018/index.html
[2018 edition]: https://doc.rust-lang.org/nightly/edition-guide/rust-2018/index.html
```
blog_os
@@ -397,7 +397,7 @@ error: linking with `cc` failed: exit code: 1
macOS [官方並不支援靜態連結執行檔][does not officially support statically linked binaries]且要求程式預設要連結到 `libSystem` 函式庫。要覆蓋這個設定並連結靜態執行檔,我們傳入 `-static` 給連結器:
[does not officially support statically linked binaries]: https://developer.apple.com/library/content/qa/qa1118/_index.html
[does not officially support statically linked binaries]: https://developer.apple.com/library/archive/qa/qa1118/_index.html
```
cargo rustc -- -C link-args="-e __start -static"

View File

@@ -96,7 +96,7 @@ To manage Rust installations I highly recommend [rustup]. It allows you to insta
The nightly compiler allows us to opt-in to various experimental features by using so-called _feature flags_ at the top of our file. For example, we could enable the experimental [`asm!` macro] for inline assembly by adding `#![feature(asm)]` to the top of our `main.rs`. Note that such experimental features are completely unstable, which means that future Rust versions might change or remove them without prior warning. For this reason we will only use them if absolutely necessary.
[`asm!` macro]: https://doc.rust-lang.org/nightly/unstable-book/language-features/asm.html
[`asm!` macro]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
### Target Specification
Cargo supports different target systems through the `--target` parameter. The target is described by a so-called _[target triple]_, which describes the CPU architecture, the vendor, the operating system, and the [ABI]. For example, the `x86_64-unknown-linux-gnu` target triple describes a system with a `x86_64` CPU, no clear vendor and a Linux operating system with the GNU ABI. Rust supports [many different target triples][platform-support], including `arm-linux-androideabi` for Android or [`wasm32-unknown-unknown` for WebAssembly](https://www.hellorust.com/setup/wasm-target/).

View File

@@ -70,7 +70,9 @@ Rust 语言有三个**发行频道**release channel分别是 stable、b
要管理安装好的 Rust我强烈建议使用 [rustup](https://www.rustup.rs/):它允许你同时安装 nightly、beta 和 stable 版本的编译器,而且让更新 Rust 变得容易。你可以输入 `rustup override add nightly` 来选择在当前目录使用 nightly 版本的 Rust。或者你也可以在项目根目录添加一个名称为 `rust-toolchain`、内容为 `nightly` 的文件。要检查你是否已经安装了一个 nightly你可以运行 `rustc --version`:返回的版本号末尾应该包含`-nightly`
Nightly 版本的编译器允许我们在源码的开头插入**特性标签**feature flag来自由选择并使用大量实验性的功能。举个例子要使用实验性的[内联汇编asm!宏)](https://doc.rust-lang.org/nightly/unstable-book/language-features/asm.html),我们可以在 `main.rs` 的顶部添加 `#![feature(asm)]`。要注意的是,这样的实验性功能**不稳定**unstable意味着未来的 Rust 版本可能会修改或移除这些功能,而不会有预先的警告过渡。因此我们只有在绝对必要的时候,才应该使用这些特性。
Nightly 版本的编译器允许我们在源码的开头插入**特性标签**feature flag来自由选择并使用大量实验性的功能。举个例子要使用实验性的[内联汇编asm!宏)][asm feature],我们可以在 `main.rs` 的顶部添加 `#![feature(asm)]`。要注意的是,这样的实验性功能**不稳定**unstable意味着未来的 Rust 版本可能会修改或移除这些功能,而不会有预先的警告过渡。因此我们只有在绝对必要的时候,才应该使用这些特性。
[asm feature]: https://doc.rust-lang.org/unstable-book/library-features/asm.html
### 目标配置清单
@@ -231,7 +233,9 @@ cargo install cargo-xbuild
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 xbuild` 为我们自定义的目标交叉编译了 `core``compiler_builtin``alloc` 三个部件。这些部件使用了大量的**不稳定特性**unstable features所以只能在[nightly 版本的 Rust 编译器][installing rust nightly]中工作。这之后,`cargo xbuild` 成功地编译了我们的 `blog_os` 包。
[installing rust nightly]: #an-zhuang-nightly-rust
现在我们可以为裸机编译内核了;但是,我们提供给引导程序的入口点 `_start` 函数还是空的。我们可以添加一些东西进去,不过我们可以先做一些优化工作。
@@ -282,9 +286,13 @@ pub extern "C" fn _start() -> ! {
}
```
在这段代码中,我们预先定义了一个**字节字符串**byte string类型的**静态变量**static variable名为 `HELLO`。我们首先将整数 `0xb8000` **转换**cast为一个**裸指针**[raw pointer](https://doc.rust-lang.org/stable/book/second-edition/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer))。这之后,我们迭代 `HELLO` 的每个字节,使用 [enumerate](https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate) 获得一个额外的序号变量 `i`。在 `for` 语句的循环体中,我们使用 [offset](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset) 偏移裸指针,解引用它,来将字符串的每个字节和对应的颜色字节——`0xb` 代表淡青色——写入内存位置。
在这段代码中,我们预先定义了一个**字节字符串**byte string类型的**静态变量**static variable名为 `HELLO`。我们首先将整数 `0xb8000` **转换**cast为一个**裸指针**[raw pointer])。这之后,我们迭代 `HELLO` 的每个字节,使用 [enumerate](https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.enumerate) 获得一个额外的序号变量 `i`。在 `for` 语句的循环体中,我们使用 [offset](https://doc.rust-lang.org/std/primitive.pointer.html#method.offset) 偏移裸指针,解引用它,来将字符串的每个字节和对应的颜色字节——`0xb` 代表淡青色——写入内存位置。
要注意的是,所有的裸指针内存操作都被一个 **unsafe 语句块**[unsafe block](https://doc.rust-lang.org/stable/book/second-edition/ch19-01-unsafe-rust.html))包围。这是因为,此时编译器不能确保我们创建的裸指针是有效的;一个裸指针可能指向任何一个你内存位置;直接解引用并写入它,也许会损坏正常的数据。使用 `unsafe` 语句块时,程序员其实在告诉编译器,自己保证语句块内的操作是有效的。事实上,`unsafe` 语句块并不会关闭 Rust 的安全检查机制;它允许你多做的事情[只有四件](https://doc.rust-lang.org/stable/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers)。
[raw pointer]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#dereferencing-a-raw-pointer
要注意的是,所有的裸指针内存操作都被一个 **unsafe 语句块**[unsafe block](https://doc.rust-lang.org/stable/book/second-edition/ch19-01-unsafe-rust.html))包围。这是因为,此时编译器不能确保我们创建的裸指针是有效的;一个裸指针可能指向任何一个你内存位置;直接解引用并写入它,也许会损坏正常的数据。使用 `unsafe` 语句块时,程序员其实在告诉编译器,自己保证语句块内的操作是有效的。事实上,`unsafe` 语句块并不会关闭 Rust 的安全检查机制;它允许你多做的事情[只有四件][unsafe superpowers]。
[unsafe superpowers]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
使用 `unsafe` 语句块要求程序员有足够的自信,所以必须强调的一点是,**肆意使用 unsafe 语句块并不是 Rust 编程的一贯方式**。在缺乏足够经验的前提下,直接在 `unsafe` 语句块内操作裸指针,非常容易把事情弄得很糟糕;比如,在不注意的情况下,我们很可能会意外地操作缓冲区以外的内存。

View File

@@ -113,7 +113,7 @@ By [deriving] the [`Copy`], [`Clone`], [`Debug`], [`PartialEq`], and [`Eq`] trai
[`Debug`]: https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html
[`PartialEq`]: https://doc.rust-lang.org/nightly/core/cmp/trait.PartialEq.html
[`Eq`]: https://doc.rust-lang.org/nightly/core/cmp/trait.Eq.html
[copy semantics]: https://doc.rust-lang.org/book/first-edition/ownership.html#copy-types
[copy semantics]: https://doc.rust-lang.org/1.30.0/book/first-edition/ownership.html#copy-types
To represent a full color code that specifies foreground and background color, we create a [newtype] on top of `u8`:

View File

@@ -103,7 +103,10 @@ pub enum Color {
通常来说,编译器会对每个未使用的变量发出**警告**warning使用 `#[allow(dead_code)]`,我们可以对 `Color` 枚举类型禁用这个警告。
我们还**生成**[derive](http://rustbyexample.com/trait/derive.html))了 [`Copy`](https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html)、[`Clone`](https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html)、[`Debug`](https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html)、[`PartialEq`](https://doc.rust-lang.org/nightly/core/cmp/trait.PartialEq.html) 和 [`Eq`](https://doc.rust-lang.org/nightly/core/cmp/trait.Eq.html) 这几个 trait这让我们的类型遵循**复制语义**[copy semantics](https://doc.rust-lang.org/book/first-edition/ownership.html#copy-types)),也让它可以被比较、被调试和打印。
我们还**生成**[derive])了 [`Copy`](https://doc.rust-lang.org/nightly/core/marker/trait.Copy.html)、[`Clone`](https://doc.rust-lang.org/nightly/core/clone/trait.Clone.html)、[`Debug`](https://doc.rust-lang.org/nightly/core/fmt/trait.Debug.html)、[`PartialEq`](https://doc.rust-lang.org/nightly/core/cmp/trait.PartialEq.html) 和 [`Eq`](https://doc.rust-lang.org/nightly/core/cmp/trait.Eq.html) 这几个 trait这让我们的类型遵循**复制语义**[copy semantics]),也让它可以被比较、被调试和打印。
[derive]: https://doc.rust-lang.org/rust-by-example/trait/derive.html
[copy semantics]: https://doc.rust-lang.org/1.30.0/book/first-edition/ownership.html#copy-types
为了描述包含前景色和背景色的、完整的**颜色代码**color code我们基于 `u8` 创建一个新类型:

View File

@@ -325,7 +325,7 @@ pub fn init_idt() {
However, there is a problem: Statics are immutable, so we can't modify the breakpoint entry from our `init` function. We could solve this problem by using a [`static mut`]:
[`static mut`]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
[`static mut`]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#accessing-or-modifying-a-mutable-static-variable
```rust
static mut IDT: InterruptDescriptorTable = InterruptDescriptorTable::new();
@@ -340,7 +340,7 @@ pub fn init_idt() {
This variant compiles without errors but it's far from idiomatic. `static mut`s are very prone to data races, so we need an [`unsafe` block] on each access.
[`unsafe` block]: https://doc.rust-lang.org/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
[`unsafe` block]: https://doc.rust-lang.org/1.30.0/book/second-edition/ch19-01-unsafe-rust.html#unsafe-superpowers
#### Lazy Statics to the Rescue
Fortunately the `lazy_static` macro exists. Instead of evaluating a `static` at compile time, the macro performs the initialization when the `static` is referenced the first time. Thus, we can do almost everything in the initialization block and are even able to read runtime values.

View File

@@ -155,9 +155,9 @@ Let's look at the fourth question:
A guard page is a special memory page at the bottom of a stack that makes it possible to detect stack overflows. The page is not mapped to any physical frame, so accessing it causes a page fault instead of silently corrupting other memory. The bootloader sets up a guard page for our kernel stack, so a stack overflow causes a _page fault_.
When a page fault occurs the CPU looks up the page fault handler in the IDT and tries to push the [exception stack frame] onto the stack. However, the current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
When a page fault occurs the CPU looks up the page fault handler in the IDT and tries to push the [interrupt stack frame] onto the stack. However, the current stack pointer still points to the non-present guard page. Thus, a second page fault occurs, which causes a double fault (according to the above table).
[exception stack frame]: http://os.phil-opp.com/better-exception-messages.html#exceptions-in-detail
[interrupt stack frame]: @/second-edition/posts/05-cpu-exceptions/index.md#the-interrupt-stack-frame
So the CPU tries to call the _double fault handler_ now. However, on a double fault the CPU tries to push the exception stack frame, too. The stack pointer still points to the guard page, so a _third_ page fault occurs, which causes a _triple fault_ and a system reboot. So our current double fault handler can't avoid a triple fault in this case.

View File

@@ -1026,7 +1026,7 @@ The block must be have at least the size and alignment required by the given `La
[maximum]: https://doc.rust-lang.org/core/cmp/trait.Ord.html#method.max
[`size()`]: https://doc.rust-lang.org/core/alloc/struct.Layout.html#method.size
[`align()`]: https://doc.rust-lang.org/core/alloc/struct.Layout.html#method.align
[`iter()`]: https://doc.rust-lang.org/core/primitive.slice.html#method.iter
[`iter()`]: https://doc.rust-lang.org/std/primitive.slice.html#method.iter
[`position()`]: https://doc.rust-lang.org/core/iter/trait.Iterator.html#method.position
Note that we don't return the block size itself, but the index into the `BLOCK_SIZES` slice. The reason is that we want to use the returned index as an index into the `list_heads` array.

View File

@@ -55,7 +55,7 @@ error[E0152]: duplicate lang item found: `panic_impl`.
The problem is that unit tests are built for the host machine, with the `std` library included. This makes sense because they should be able to run as a normal application on the host operating system. Since the standard library has it's own `panic_handler` function, we get the above error. To fix it, we use [conditional compilation] to include our implementation of the panic handler only in non-test environments:
[conditional compilation]: https://doc.rust-lang.org/reference/attributes.html#conditional-compilation
[conditional compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
```rust

View File

@@ -280,7 +280,7 @@ Now QEMU runs completely in the background and no window is opened anymore. This
Right now we're doing the serial output and the QEMU exit from the `_start` function in our `main.rs` and can no longer run our kernel in a normal way. We could try to fix this by adding an `integration-test` [cargo feature] and using [conditional compilation]:
[cargo feature]: https://doc.rust-lang.org/cargo/reference/manifest.html#the-features-section
[conditional compilation]: https://doc.rust-lang.org/reference/attributes.html#conditional-compilation
[conditional compilation]: https://doc.rust-lang.org/reference/conditional-compilation.html
```toml
# in Cargo.toml