post-3 translation refactor (#725)

This commit is contained in:
Rustin
2020-01-30 17:37:40 +08:00
committed by GitHub
parent 55f19fdcdc
commit 6d4f1d6c43

View File

@@ -6,7 +6,7 @@ date = 2018-02-26
+++
**VGA字符模式**[VGA text mode]是打印字符到屏幕的一种简单方式。在这篇文章中为了包装这个模式为一个安全而简单的接口我们包装unsafe代码到独立的模块。我们还将实现对Rust语言**格式化宏**[formatting macros])的支持。
**VGA 字符模式**[VGA text mode])是打印字符到屏幕的一种简单方式。在这篇文章中,为了包装这个模式为一个安全而简单的接口,我们包装 unsafe 代码到独立的模块。我们还将实现对 Rust 语言**格式化宏**[formatting macros])的支持。
[VGA text mode]: https://en.wikipedia.org/wiki/VGA-compatible_text_mode
[formatting macros]: https://doc.rust-lang.org/std/fmt/#related-macros
@@ -23,7 +23,7 @@ This blog is openly developed on [GitHub]. If you have any problems or questions
## VGA 字符缓冲区
为了在VGA字符模式向屏幕打印字符我们必须将它写入硬件提供的**VGA字符缓冲区**VGA text buffer。通常状况下VGA字符缓冲区是一个25行、80列的二维数组它的内容将被实时渲染到屏幕。这个数组的元素被称作**字符单元**character cell它使用下面的格式描述一个屏幕上的字符
为了在 VGA 字符模式向屏幕打印字符,我们必须将它写入硬件提供的 **VGA 字符缓冲区**VGA text buffer。通常状况下VGA 字符缓冲区是一个 25 行、80 列的二维数组,它的内容将被实时渲染到屏幕。这个数组的元素被称作**字符单元**character cell它使用下面的格式描述一个屏幕上的字符
| Bit(s) | Value |
|-----|----------------|
@@ -49,7 +49,7 @@ This blog is openly developed on [GitHub]. If you have any problems or questions
要修改 VGA 字符缓冲区,我们可以通过**存储器映射输入输出**[memory-mapped I/O](https://en.wikipedia.org/wiki/Memory-mapped_I/O))的方式,读取或写入地址 `0xb8000`;这意味着,我们可以像操作普通的内存区域一样操作这个地址。
需要主页的是,一些硬件虽然映射到存储器,可能不会完全支持所有的内存操作:可能会有一些设备支持按`u8`字节读取,在读取`u64`时返回无效的数据。幸运的是,字符缓冲区都[支持标准的读写操作](https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vgamem.htm#manip),所以我们不需要用特殊的标准对待它。
需要注意的是,一些硬件虽然映射到存储器,可能不会完全支持所有的内存操作:可能会有一些设备支持按 `u8` 字节读取,在读取 `u64` 时返回无效的数据。幸运的是,字符缓冲区都[支持标准的读写操作](https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vgamem.htm#manip),所以我们不需要用特殊的标准对待它。
## 包装到 Rust 模块
@@ -98,7 +98,7 @@ 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](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)),也让它可以被比较、被调试打印。
为了描述包含前景色和背景色的、完整的**颜色代码**color code我们基于 `u8` 创建一个新类型:
@@ -155,7 +155,7 @@ pub struct Writer {
}
```
我们将让这个`Writer`类型将字符写入屏幕的最后一行,并在一行写满或收到换行符`\n`的时候,将所有的字符向上位移一行。`column_position`变量将跟踪光标在最后一行的位置。当前字符的前景和背景色将由`color_code`变量指定另外我们存入一个VGA字符缓冲区的可变借用到`buffer`变量中。需要注意的是,这里我们对借用使用**显式生命周期**[explicit lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax)),告诉编译器这个借用在何时有效:我们使用**`'static`生命周期**['static lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime)意味着这个借用应该在整个程序的运行期间有效这对一个全局有效的VGA字符缓冲区来说是非常合理的。
我们将让这个 `Writer` 类型将字符写入屏幕的最后一行,并在一行写满或收到换行符 `\n` 的时候,将所有的字符向上位移一行。`column_position` 变量将跟踪光标在最后一行的位置。当前字符的前景和背景色将由 `color_code` 变量指定;另外,我们存入一个 VGA 字符缓冲区的可变借用到`buffer`变量中。需要注意的是,这里我们对借用使用**显式生命周期**[explicit lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#lifetime-annotation-syntax)),告诉编译器这个借用在何时有效:我们使用** `'static` 生命周期 **['static lifetime](https://doc.rust-lang.org/book/ch10-03-lifetime-syntax.html#the-static-lifetime)),意味着这个借用应该在整个程序的运行期间有效;这对一个全局有效的 VGA 字符缓冲区来说,是非常合理的。
### 打印字符
@@ -314,7 +314,7 @@ impl Writer {
### 格式化宏
支持Rust提供的**格式化宏**formatting macros也是一个相当棒的主意。通过这种途径,我们可以轻松地打印不同类型的变量,如整数或浮点数。为了支持它们,我们需要实现[`core::fmt::Write`](https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html) trait要实现它唯一需要提供的方法是`write_str`,它和我们先前编写的`write_string`方法差别不大,只是返回值类型变成了`fmt::Result`
支持 Rust 提供的**格式化宏**formatting macros也是一个很好的思路。通过这种途径,我们可以轻松地打印不同类型的变量,如整数或浮点数。为了支持它们,我们需要实现 [`core::fmt::Write`](https://doc.rust-lang.org/nightly/core/fmt/trait.Write.html) trait要实现它唯一需要提供的方法是 `write_str`,它和我们先前编写的 `write_string` 方法差别不大,只是返回值类型变成了 `fmt::Result`
```rust
// in src/vga_buffer.rs
@@ -399,7 +399,7 @@ impl Writer {
## 全局接口
编写其它模块时,我们希望无需随身携带`Writer`实例,便能使用它的方法。我们尝试创建一个静态的`WRITER`变量:
编写其它模块时,我们希望无需随时拥有 `Writer` 实例,便能使用它的方法。我们尝试创建一个静态的 `WRITER` 变量:
```rust
// in src/vga_buffer.rs
@@ -523,7 +523,7 @@ pub extern "C" fn _start() -> ! {
### 安全性
经过上的努力后我们现在的代码只剩一个unsafe语句块它用于创建一个指向`0xb8000`地址的`Buffer`类型引用在这步之后所有的操作都是安全的。Rust将为每个数组访问检查边界所以我们不会在不经意间越界到缓冲区之外。因此我们把需要的条件编码到Rust的类型系统这之后我们为外界提供的接口就符合内存安全原则了。
经过上的努力后,我们现在的代码只剩一个 unsafe 语句块,它用于创建一个指向 `0xb8000` 地址的 `Buffer` 类型引用在这步之后所有的操作都是安全的。Rust 将为每个数组访问检查边界,所以我们不会在不经意间越界到缓冲区之外。因此,我们把需要的条件编码到 Rust 的类型系统,这之后,我们为外界提供的接口就符合内存安全原则了。
### `println!` 宏