mirror of
https://github.com/phil-opp/blog_os.git
synced 2025-12-17 06:47:49 +00:00
Convert all external links to https (if supported)
This commit is contained in:
@@ -67,7 +67,7 @@ blog_os
|
||||
|
||||
The `Cargo.toml` contains the crate configuration, for example the crate name, the author, the [semantic version] number, and dependencies. The `src/main.rs` file contains the root module of our crate and our `main` function. You can compile your crate through `cargo build` and then run the compiled `blog_os` binary in the `target/debug` subfolder.
|
||||
|
||||
[semantic version]: http://semver.org/
|
||||
[semantic version]: https://semver.org/
|
||||
|
||||
### The `no_std` Attribute
|
||||
|
||||
@@ -152,8 +152,8 @@ While providing custom implementations of language items is possible, it should
|
||||
The [`eh_personality` language item] marks a function that is used for implementing [stack unwinding]. By default, Rust uses unwinding to run the destructors of all live stack variables in case of a [panic]. This ensures that all used memory is freed and allows the parent thread to catch the panic and continue execution. Unwinding, however, is a complicated process and requires some OS specific libraries (e.g. [libunwind] on Linux or [structured exception handling] on Windows), so we don't want to use it for our operating system.
|
||||
|
||||
[`eh_personality` language item]: https://github.com/rust-lang/rust/blob/edb368491551a77d77a48446d4ee88b35490c565/src/libpanic_unwind/gcc.rs#L11-L45
|
||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
[libunwind]: http://www.nongnu.org/libunwind/
|
||||
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
[libunwind]: https://www.nongnu.org/libunwind/
|
||||
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
||||
|
||||
### Disabling Unwinding
|
||||
|
||||
@@ -56,7 +56,7 @@ blog_os
|
||||
└── main.rs
|
||||
```
|
||||
|
||||
在这里,`Cargo.toml` 文件包含了包的**配置**(configuration),比如包的名称、作者、[semver版本](http://semver.org/) 和项目依赖项;`src/main.rs` 文件包含包的**根模块**(root module)和 main 函数。我们可以使用 `cargo build` 来编译这个包,然后在 `target/debug` 文件夹内找到编译好的 `blog_os` 二进制文件。
|
||||
在这里,`Cargo.toml` 文件包含了包的**配置**(configuration),比如包的名称、作者、[semver版本](https://semver.org/) 和项目依赖项;`src/main.rs` 文件包含包的**根模块**(root module)和 main 函数。我们可以使用 `cargo build` 来编译这个包,然后在 `target/debug` 文件夹内找到编译好的 `blog_os` 二进制文件。
|
||||
|
||||
### no_std 属性
|
||||
|
||||
@@ -128,7 +128,7 @@ fn panic(_info: &PanicInfo) -> ! {
|
||||
|
||||
我们可以自己实现语言项,但这是下下策:目前来看,语言项是高度不稳定的语言细节实现,它们不会经过编译期类型检查(所以编译器甚至不确保它们的参数类型是否正确)。幸运的是,我们有更稳定的方式,来修复上面的语言项错误。
|
||||
|
||||
`eh_personality` 语言项标记的函数,将被用于实现**栈展开**([stack unwinding](http://www.bogotobogo.com/cplusplus/stackunwinding.php))。在使用标准库的情况下,当 panic 发生时,Rust 将使用栈展开,来运行在栈上所有活跃的变量的**析构函数**(destructor)——这确保了所有使用的内存都被释放,允许调用程序的**父进程**(parent thread)捕获 panic,处理并继续运行。但是,栈展开是一个复杂的过程,如 Linux 的 [libunwind](http://www.nongnu.org/libunwind/) 或 Windows 的**结构化异常处理**([structured exception handling, SEH](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx)),通常需要依赖于操作系统的库;所以我们不在自己编写的操作系统中使用它。
|
||||
`eh_personality` 语言项标记的函数,将被用于实现**栈展开**([stack unwinding](https://www.bogotobogo.com/cplusplus/stackunwinding.php))。在使用标准库的情况下,当 panic 发生时,Rust 将使用栈展开,来运行在栈上所有活跃的变量的**析构函数**(destructor)——这确保了所有使用的内存都被释放,允许调用程序的**父进程**(parent thread)捕获 panic,处理并继续运行。但是,栈展开是一个复杂的过程,如 Linux 的 [libunwind](https://www.nongnu.org/libunwind/) 或 Windows 的**结构化异常处理**([structured exception handling, SEH](https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx)),通常需要依赖于操作系统的库;所以我们不在自己编写的操作系统中使用它。
|
||||
|
||||
### 禁用栈展开
|
||||
|
||||
|
||||
@@ -152,8 +152,8 @@ Language item 是一些編譯器需求的特殊函式或類型。舉例來說,
|
||||
|
||||
`eh_personality` language item 標記的函式將被用於實作[堆疊回溯][stack unwinding]。在預設情況下當 panic 發生時,Rust 會使用堆疊回溯來執行所有存在堆疊上變數的解構子(destructor)。這確保所有使用的記憶體都被釋放,並讓 parent thread 獲取 panic 資訊並繼續運行。但是堆疊回溯是一個複雜的過程,通常會需要一些 OS 的函式庫如 Linux 的 [libunwind] 或 Windows 的 [structured exception handling]。所以我們並不希望在我們的作業系統中使用它。
|
||||
|
||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
[libunwind]: http://www.nongnu.org/libunwind/
|
||||
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
[libunwind]: https://www.nongnu.org/libunwind/
|
||||
[structured exception handling]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms680657(v=vs.85).aspx
|
||||
|
||||
### 停用回溯
|
||||
|
||||
@@ -7,8 +7,8 @@ template = "second-edition/extra.html"
|
||||
|
||||
The [red zone] is an optimization of the [System V ABI] that allows functions to temporarily use the 128 bytes below its stack frame without adjusting the stack pointer:
|
||||
|
||||
[red zone]: http://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||
[System V ABI]: http://wiki.osdev.org/System_V_ABI
|
||||
[red zone]: https://eli.thegreenplace.net/2011/09/06/stack-frame-layout-on-x86-64#the-red-zone
|
||||
[System V ABI]: https://wiki.osdev.org/System_V_ABI
|
||||
|
||||
<!-- more -->
|
||||
|
||||
@@ -24,6 +24,6 @@ However, this optimization leads to huge problems with exceptions or hardware in
|
||||
|
||||
The CPU and the exception handler overwrite the data in red zone. But this data is still needed by the interrupted function. So the function won't work correctly anymore when we return from the exception handler. This might lead to strange bugs that [take weeks to debug].
|
||||
|
||||
[take weeks to debug]: http://forum.osdev.org/viewtopic.php?t=21720
|
||||
[take weeks to debug]: https://forum.osdev.org/viewtopic.php?t=21720
|
||||
|
||||
To avoid such bugs when we implement exception handling in the future, we disable the red zone right from the beginning. This is achieved by adding the `"disable-redzone": true` line to our target configuration file.
|
||||
|
||||
@@ -163,7 +163,7 @@ Instead of using the platform's default linker (which might not support Linux ta
|
||||
|
||||
This setting specifies that the target doesn't support [stack unwinding] on panic, so instead the program should abort directly. This has the same effect as the `panic = "abort"` option in our Cargo.toml, so we can remove it from there. (Note that in contrast to the Cargo.toml option, this target option also applies when we recompile the `core` library later in this post. So be sure to add this option, even if you prefer to keep the Cargo.toml option.)
|
||||
|
||||
[stack unwinding]: http://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
[stack unwinding]: https://www.bogotobogo.com/cplusplus/stackunwinding.php
|
||||
|
||||
```json
|
||||
"disable-redzone": true,
|
||||
|
||||
@@ -128,7 +128,7 @@ Nightly 版本的编译器允许我们在源码的开头插入**特性标签**
|
||||
"panic-strategy": "abort",
|
||||
```
|
||||
|
||||
这个配置项的意思是,我们的编译目标不支持 panic 时的**栈展开**([stack unwinding](http://www.bogotobogo.com/cplusplus/stackunwinding.php)),所以我们选择直接**在 panic 时中止**(abort on panic)。这和在 `Cargo.toml` 文件中添加 `panic = "abort"` 选项的作用是相同的,所以我们可以不在这里的配置清单中填写这一项。
|
||||
这个配置项的意思是,我们的编译目标不支持 panic 时的**栈展开**([stack unwinding](https://www.bogotobogo.com/cplusplus/stackunwinding.php)),所以我们选择直接**在 panic 时中止**(abort on panic)。这和在 `Cargo.toml` 文件中添加 `panic = "abort"` 选项的作用是相同的,所以我们可以不在这里的配置清单中填写这一项。
|
||||
|
||||
```json
|
||||
"disable-redzone": true,
|
||||
|
||||
@@ -237,7 +237,7 @@ impl Writer {
|
||||
The VGA text buffer only supports ASCII and the additional bytes of [code page 437]. Rust strings are [UTF-8] by default, so they might contain bytes that are not supported by the VGA text buffer. We use a match to differentiate printable ASCII bytes (a newline or anything in between a space character and a `~` character) and unprintable bytes. For unprintable bytes, we print a `■` character, which has the hex code `0xfe` on the VGA hardware.
|
||||
|
||||
[code page 437]: https://en.wikipedia.org/wiki/Code_page_437
|
||||
[UTF-8]: http://www.fileformat.info/info/unicode/utf8.htm
|
||||
[UTF-8]: https://www.fileformat.info/info/unicode/utf8.htm
|
||||
|
||||
#### Try it out!
|
||||
To write some characters to the screen, you can create a temporary function:
|
||||
@@ -307,8 +307,8 @@ volatile = "0.2.6"
|
||||
|
||||
The `0.2.6` is the [semantic] version number. For more information, see the [Specifying Dependencies] guide of the cargo documentation.
|
||||
|
||||
[semantic]: http://semver.org/
|
||||
[Specifying Dependencies]: http://doc.crates.io/specifying-dependencies.html
|
||||
[semantic]: https://semver.org/
|
||||
[Specifying Dependencies]: https://doc.crates.io/specifying-dependencies.html
|
||||
|
||||
Let's use it to make writes to the VGA buffer volatile. We update our `Buffer` type as follows:
|
||||
|
||||
|
||||
@@ -222,7 +222,7 @@ impl Writer {
|
||||
}
|
||||
```
|
||||
|
||||
VGA 字符缓冲区只支持 ASCII 码字节和**代码页 437**([Code page 437](https://en.wikipedia.org/wiki/Code_page_437))定义的字节。Rust 语言的字符串默认编码为 [UTF-8](http://www.fileformat.info/info/unicode/utf8.htm),也因此可能包含一些 VGA 字符缓冲区不支持的字节:我们使用 `match` 语句,来区别可打印的 ASCII 码或换行字节,和其它不可打印的字节。对每个不可打印的字节,我们打印一个 `■` 符号;这个符号在 VGA 硬件中被编码为十六进制的 `0xfe`。
|
||||
VGA 字符缓冲区只支持 ASCII 码字节和**代码页 437**([Code page 437](https://en.wikipedia.org/wiki/Code_page_437))定义的字节。Rust 语言的字符串默认编码为 [UTF-8](https://www.fileformat.info/info/unicode/utf8.htm),也因此可能包含一些 VGA 字符缓冲区不支持的字节:我们使用 `match` 语句,来区别可打印的 ASCII 码或换行字节,和其它不可打印的字节。对每个不可打印的字节,我们打印一个 `■` 符号;这个符号在 VGA 硬件中被编码为十六进制的 `0xfe`。
|
||||
|
||||
我们可以亲自试一试已经编写的代码。为了这样做,我们可以临时编写一个函数:
|
||||
|
||||
@@ -259,7 +259,7 @@ pub extern "C" fn _start() -> ! {
|
||||
|
||||

|
||||
|
||||
需要注意的是,`ö` 字符被打印为两个 `■` 字符。这是因为在 [UTF-8](http://www.fileformat.info/info/unicode/utf8.htm) 编码下,字符 `ö` 是由两个字节表述的——而这两个字节并不处在可打印的 ASCII 码字节范围之内。事实上,这是 UTF-8 编码的基本特点之一:**如果一个字符占用多个字节,那么每个组成它的独立字节都不是有效的 ASCII 码字节**(the individual bytes of multi-byte values are never valid ASCII)。
|
||||
需要注意的是,`ö` 字符被打印为两个 `■` 字符。这是因为在 [UTF-8](https://www.fileformat.info/info/unicode/utf8.htm) 编码下,字符 `ö` 是由两个字节表述的——而这两个字节并不处在可打印的 ASCII 码字节范围之内。事实上,这是 UTF-8 编码的基本特点之一:**如果一个字符占用多个字节,那么每个组成它的独立字节都不是有效的 ASCII 码字节**(the individual bytes of multi-byte values are never valid ASCII)。
|
||||
|
||||
### 易失操作
|
||||
|
||||
@@ -278,7 +278,7 @@ pub extern "C" fn _start() -> ! {
|
||||
volatile = "0.2.3"
|
||||
```
|
||||
|
||||
`0.2.3` 表示一个**语义版本号**([semantic version number](http://semver.org/)),在 cargo 文档的[《指定依赖项》章节](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)可以找到与它相关的使用指南。
|
||||
`0.2.3` 表示一个**语义版本号**([semantic version number](https://semver.org/)),在 cargo 文档的[《指定依赖项》章节](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html)可以找到与它相关的使用指南。
|
||||
|
||||
现在,我们使用它来完成 VGA 缓冲区的 volatile 写入操作。我们将 `Buffer` 类型的定义修改为下列代码:
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ chapter = "Interrupts"
|
||||
|
||||
CPU exceptions occur in various erroneous situations, for example when accessing an invalid memory address or when dividing by zero. To react to them we have to set up an _interrupt descriptor table_ that provides handler functions. At the end of this post, our kernel will be able to catch [breakpoint exceptions] and to resume normal execution afterwards.
|
||||
|
||||
[breakpoint exceptions]: http://wiki.osdev.org/Exceptions#Breakpoint
|
||||
[breakpoint exceptions]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||
|
||||
<!-- more -->
|
||||
|
||||
@@ -37,7 +37,7 @@ On x86 there are about 20 different CPU exception types. The most important are:
|
||||
|
||||
For the full list of exceptions check out the [OSDev wiki][exceptions].
|
||||
|
||||
[exceptions]: http://wiki.osdev.org/Exceptions
|
||||
[exceptions]: https://wiki.osdev.org/Exceptions
|
||||
|
||||
### The Interrupt Descriptor Table
|
||||
In order to catch and handle exceptions, we have to set up a so-called _Interrupt Descriptor Table_ (IDT). In this table we can specify a handler function for each CPU exception. The hardware uses this table directly, so we need to follow a predefined format. Each entry must have the following 16-byte structure:
|
||||
@@ -139,7 +139,7 @@ However, there is a major difference between exceptions and function calls: A fu
|
||||
[Calling conventions] specify the details of a function call. For example, they specify where function parameters are placed (e.g. in registers or on the stack) and how results are returned. On x86_64 Linux, the following rules apply for C functions (specified in the [System V ABI]):
|
||||
|
||||
[Calling conventions]: https://en.wikipedia.org/wiki/Calling_convention
|
||||
[System V ABI]: http://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
|
||||
[System V ABI]: https://refspecs.linuxbase.org/elf/x86_64-abi-0.99.pdf
|
||||
|
||||
- the first six integer arguments are passed in registers `rdi`, `rsi`, `rdx`, `rcx`, `r8`, `r9`
|
||||
- additional arguments are passed on the stack
|
||||
@@ -229,11 +229,11 @@ pub fn init_idt() {
|
||||
|
||||
Now we can add handler functions. We start by adding a handler for the [breakpoint exception]. The breakpoint exception is the perfect exception to test exception handling. Its only purpose is to temporarily pause a program when the breakpoint instruction `int3` is executed.
|
||||
|
||||
[breakpoint exception]: http://wiki.osdev.org/Exceptions#Breakpoint
|
||||
[breakpoint exception]: https://wiki.osdev.org/Exceptions#Breakpoint
|
||||
|
||||
The breakpoint exception is commonly used in debuggers: When the user sets a breakpoint, the debugger overwrites the corresponding instruction with the `int3` instruction so that the CPU throws the breakpoint exception when it reaches that line. When the user wants to continue the program, the debugger replaces the `int3` instruction with the original instruction again and continues the program. For more details, see the ["_How debuggers work_"] series.
|
||||
|
||||
["_How debuggers work_"]: http://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
|
||||
["_How debuggers work_"]: https://eli.thegreenplace.net/2011/01/27/how-debuggers-work-part-2-breakpoints
|
||||
|
||||
For our use case, we don't need to overwrite any instructions. Instead, we just want to print a message when the breakpoint instruction is executed and then continue the program. So let's create a simple `breakpoint_handler` function and add it to our IDT:
|
||||
|
||||
@@ -466,5 +466,5 @@ The `x86-interrupt` calling convention and the [`InterruptDescriptorTable`] type
|
||||
## What's next?
|
||||
We've successfully caught our first exception and returned from it! The next step is to ensure that we catch all exceptions, because an uncaught exception causes a fatal [triple fault], which leads to a system reset. The next post explains how we can avoid this by correctly catching [double faults].
|
||||
|
||||
[triple fault]: http://wiki.osdev.org/Triple_Fault
|
||||
[double faults]: http://wiki.osdev.org/Double_Fault#Double_Fault
|
||||
[triple fault]: https://wiki.osdev.org/Triple_Fault
|
||||
[double faults]: https://wiki.osdev.org/Double_Fault#Double_Fault
|
||||
|
||||
@@ -128,12 +128,12 @@ First Exception | Second Exception
|
||||
[Divide-by-zero],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault] | [Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||
[Page Fault] | [Page Fault],<br>[Invalid TSS],<br>[Segment Not Present],<br>[Stack-Segment Fault],<br>[General Protection Fault]
|
||||
|
||||
[Divide-by-zero]: http://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
||||
[Invalid TSS]: http://wiki.osdev.org/Exceptions#Invalid_TSS
|
||||
[Segment Not Present]: http://wiki.osdev.org/Exceptions#Segment_Not_Present
|
||||
[Stack-Segment Fault]: http://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
||||
[General Protection Fault]: http://wiki.osdev.org/Exceptions#General_Protection_Fault
|
||||
[Page Fault]: http://wiki.osdev.org/Exceptions#Page_Fault
|
||||
[Divide-by-zero]: https://wiki.osdev.org/Exceptions#Divide-by-zero_Error
|
||||
[Invalid TSS]: https://wiki.osdev.org/Exceptions#Invalid_TSS
|
||||
[Segment Not Present]: https://wiki.osdev.org/Exceptions#Segment_Not_Present
|
||||
[Stack-Segment Fault]: https://wiki.osdev.org/Exceptions#Stack-Segment_Fault
|
||||
[General Protection Fault]: https://wiki.osdev.org/Exceptions#General_Protection_Fault
|
||||
[Page Fault]: https://wiki.osdev.org/Exceptions#Page_Fault
|
||||
|
||||
|
||||
[AMD64 manual]: https://www.amd.com/system/files/TechDocs/24593.pdf
|
||||
@@ -206,7 +206,7 @@ For each exception handler, we can choose a stack from the IST through the `stac
|
||||
The Interrupt Stack Table (IST) is part of an old legacy structure called _[Task State Segment]_ \(TSS). The TSS used to hold various information (e.g. processor register state) about a task in 32-bit mode and was for example used for [hardware context switching]. However, hardware context switching is no longer supported in 64-bit mode and the format of the TSS changed completely.
|
||||
|
||||
[Task State Segment]: https://en.wikipedia.org/wiki/Task_state_segment
|
||||
[hardware context switching]: http://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
||||
[hardware context switching]: https://wiki.osdev.org/Context_Switching#Hardware_Context_Switching
|
||||
|
||||
On x86_64, the TSS no longer holds any task specific information at all. Instead, it holds two stack tables (the IST is one of them). The only common field between the 32-bit and 64-bit TSS is the pointer to the [I/O port permissions bitmap].
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ Apart from correctness, there are many secondary design goals. For example, the
|
||||
|
||||
[cache locality]: http://docs.cray.com/books/S-2315-50/html-S-2315-50/qmeblljm.html
|
||||
[_fragmentation_]: https://en.wikipedia.org/wiki/Fragmentation_(computing)
|
||||
[false sharing]: http://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||
[false sharing]: https://mechanical-sympathy.blogspot.de/2011/07/false-sharing.html
|
||||
|
||||
These requirements can make good allocators very complex. For example, [jemalloc] has over 30.000 lines of code. This complexity is often undesired in kernel code where a single bug can lead to severe security vulnerabilities. Fortunately, the allocation patterns of kernel code are often much simpler compared to userspace code, so that relatively simple allocator designs often suffice.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user