Skip to content

Commit c29f9b2

Browse files
committed
fix Latex in chapter0~3
1 parent a32e983 commit c29f9b2

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

82 files changed

+247
-249
lines changed

SUMMARY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,4 @@
7676
- [7. 同步互斥(TODO)](./exercise/part7.md)
7777
- [8. 文件系统](./exercise/part8.md)
7878
- [附录]
79-
- [内链汇编](appendix/inline-asm.md)
79+
- [内联汇编](appendix/inline-asm.md)

build.sh

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,6 @@ gitbook install
22
cp extensions/highlight/prism-riscv.js node_modules/prismjs/components/
33
python3 extensions/highlight/add_riscv_component.py
44
rm -rf docs/
5-
# git filter-branch --force --index-filter 'git rm --cached -r --ignore-unmatch docs' --prune-empty --tag-name-filter cat -- --all
65
gitbook build
76
mv _book/ docs/
87
python3 extensions/highlight/add_code_style.py
9-
# git add docs
10-
# git commit -m "update html"
11-
# git push origin master:master --tags --force

chapter1/part2.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
使用 `cargo new` 创建一个新的 Rust binary 项目,命令如下:
66

7-
```bash
7+
```sh
88
$ cargo new os --bin
99
```
1010

@@ -24,7 +24,7 @@ os
2424

2525
接下来我们进入 `os` 项目文件夹,并尝试构建、运行项目:
2626

27-
```bash
27+
```sh
2828
$ cargo run
2929
...
3030
Hello, world!

chapter2/part1.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ cargo 在编译项目时,可以附加目标参数 `--target <target triple>`
88

99
`rustc --version --verbose` 来查看 Rust 的默认目标三元组:
1010

11-
```bash
11+
```sh
1212
$ rustc --version --verbose
1313
rustc 1.42.0-nightly (859764425 2020-01-07)
1414
binary: rustc
@@ -24,7 +24,7 @@ LLVM version: 9.0
2424
官方对一些平台提供了默认的目标三元组,我们可以通过以下命令来查看完整列表:
2525

2626
```sh
27-
rustc --print target-list
27+
$ rustc --print target-list
2828
```
2929

3030
### 目标三元组 JSON 描述文件
@@ -34,7 +34,7 @@ rustc --print target-list
3434
首先我们来看一下默认的目标三元组 **x86_64-unknown-linux-gnu****JSON** 文件描述,输入以下命令:
3535

3636
```sh
37-
rustc -Z unstable-options --print target-spec-json --target x86_64-unknown-linux-gnu
37+
$ rustc -Z unstable-options --print target-spec-json --target x86_64-unknown-linux-gnu
3838
```
3939

4040
可以得到如下输出:
@@ -77,7 +77,7 @@ rustc -Z unstable-options --print target-spec-json --target x86_64-unknown-linux
7777
我们查看一下它的 JSON 描述文件:
7878

7979
```sh
80-
rustc -Z unstable-options --print target-spec-json --target riscv64imac-unknown-none-elf
80+
$ rustc -Z unstable-options --print target-spec-json --target riscv64imac-unknown-none-elf
8181
```
8282

8383
```json

chapter2/part2.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ $ rustup target add riscv64imac-unknown-none-elf
2626
2727
很快下载安装好后,我们重试一下,发现就可以成功编译了。
2828
29-
编译出的结果被放在了 `target/riscv64imac-unknown-none-elf/debug` 文件夹中。可以看到其中有一个名为 `os` 的可执行文件。不过由于它的目标平台是 riscv64,我们暂时还不能执行它。
29+
编译出的结果被放在了 *target/riscv64imac-unknown-none-elf/debug* 文件夹中。可以看到其中有一个名为 *os* 的可执行文件。不过由于它的目标平台是 riscv64,我们暂时还不能执行它。
3030
3131
### 为项目设置默认目标三元组
3232
3333
由于我们之后都会使用 riscv64 作为编译目标,为了避免每次都要加 `--target` 参数,我们可以使用 [Cargo 配置文件](https://doc.rust-lang.org/cargo/reference/config.html) 为项目配置默认的编译选项。
3434
35-
`os` 文件夹中创建一个 `.cargo` 文件夹,并在其中创建一个名为 `config` 的文件,在其中填入以下内容:
35+
*os* 文件夹中创建一个 *.cargo* 文件夹,并在其中创建一个名为 *config* 的文件,在其中填入以下内容:
3636
3737
```toml
3838
# .cargo/config
@@ -64,7 +64,7 @@ $ rustup component add llvm-tools-preview
6464
6565
### 查看生成的可执行文件
6666
67-
我们编译之后的产物为 `target/riscv64imac-unknown-none-elf/debug/os` ,让我们先看看它的文件类型:
67+
我们编译之后的产物为 *target/riscv64imac-unknown-none-elf/debug/os* ,让我们先看看它的文件类型:
6868
6969
```bash
7070
$ file target/riscv64imac-unknown-none-elf/debug/os
@@ -129,10 +129,10 @@ Dynamic Section:
129129
130130
我们按顺序逐个查看:
131131
132-
- `start address` 是程序的入口地址。
133-
- `Sections`,从这里我们可以看到程序各段的各种信息。后面以 `debug` 开头的段是调试信息。
134-
- `SYMBOL TABLE` 即符号表,从中我们可以看到程序中所有符号的地址。例如 `_start` 就位于入口地址上。
135-
- `Program Header` 是程序加载时所需的段信息。
132+
- *start address* 是程序的入口地址。
133+
- *Sections*,从这里我们可以看到程序各段的各种信息。后面以 `debug` 开头的段是调试信息。
134+
- *SYMBOL TABLE* 即符号表,从中我们可以看到程序中所有符号的地址。例如 `_start` 就位于入口地址上。
135+
- *Program Header* 是程序加载时所需的段信息。
136136
137137
其中 `off` 是它在文件中的位置,`vaddr` 和 `paddr` 是要加载到的虚拟地址和物理地址,`align` 规定了地址的对齐,`filesz` 和 `memsz` 分别表示它在文件和内存中的大小,`flags` 描述了相关权限(r:可读,w:可写,x:可执行)
138138
@@ -159,19 +159,19 @@ Disassembly of section .text:
159159
160160
### 生成内核镜像
161161
162-
我们之前生成的 `elf` 格式可执行文件有以下特点:
162+
我们之前生成的 *elf* 格式可执行文件有以下特点:
163163
164164
- 含有冗余的调试信息,使得程序体积较大;
165-
- 需要对 `program header` 部分进行手动解析才能知道各段的信息,而这需要我们了解 `program header` 的二进制格式,并以字节为单位进行解析。
165+
- 需要对 *program header* 部分进行手动解析才能知道各段的信息,而这需要我们了解 *program header* 的二进制格式,并以字节为单位进行解析。
166166
167-
由于我们目前没有调试的手段,不需要调试信息;同时也不会解析 `elf` 格式文件,所以使用工具 `rust-objcopy` 从 `elf` 格式可执行文件生成内核镜像:
167+
由于我们目前没有调试的手段,不需要调试信息;同时也不会解析 *elf* 格式文件,所以使用工具 `rust-objcopy` 从 `elf` 格式可执行文件生成内核镜像:
168168
169169
```bash
170170
$ rust-objcopy target/riscv64imac-unknown-none-elf/debug/os --strip-all -O binary target/riscv64imac-unknown-none-elf/debug/kernel.bin
171171
```
172172
173173
这里 `--strip-all` 表明丢弃所有符号表及调试信息,`-O binary` 表示输出为二进制文件。
174174
175-
至此,我们编译并生成了内核镜像 `kernel.bin` 。接下来,我们将使用 Qemu 模拟器真正将我们的内核镜像跑起来。不过在此之前还需要完成两个工作:**调整内存布局** 和 **重写入口函数** 。
175+
至此,我们编译并生成了内核镜像 *kernel.bin* 。接下来,我们将使用 Qemu 模拟器真正将我们的内核镜像跑起来。不过在此之前还需要完成两个工作:**调整内存布局** 和 **重写入口函数** 。
176176
177177
[code]: https://github.com/rcore-os/rCore_tutorial/tree/ch2-pa4

chapter2/part3.md

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,12 @@ Program Header:
2222
>
2323
> 一般来说,一个程序按照功能不同会分为下面这些段:
2424
>
25-
> - $\text{.text}$ 段,即代码段,存放汇编代码;
26-
> - $\text{.rodata}$ 段,即只读数据段,顾名思义里面存放只读数据,通常是程序中的常量;
27-
> - $\text{.data}$ 段,存放被初始化的可读写数据,通常保存程序中的全局变量;
28-
> - $\text{.bss}$ 段,存放被初始化为 $0$ 的可读写数据,与 $\text{.data}$ 段的不同之处在于我们知道它要被初始化为 $0$ ,因此在可执行文件中只需记录这个段的大小以及所在位置即可,而不用记录里面的数据。
29-
> - $\text{stack}$ ,即栈,用来存储程序运行过程中的局部变量,以及负责函数调用时的各种机制。它从高地址向低地址增长;
30-
> - $\text{heap}$ ,即堆,用来支持程序**运行过程中**内存的**动态分配**,比如说你要读进来一个字符串,在你写程序的时候你也不知道它的长度究竟为多少,于是你只能在运行过程中,知道了字符串的长度之后,再在堆中给这个字符串分配内存。
25+
> - *.text* 段,即代码段,存放汇编代码;
26+
> - *.rodata* 段,即只读数据段,顾名思义里面存放只读数据,通常是程序中的常量;
27+
> - *.data* 段,存放被初始化的可读写数据,通常保存程序中的全局变量;
28+
> - *.bss* 段,存放被初始化为 $$0$$ 的可读写数据,与 *.data* 段的不同之处在于我们知道它要被初始化为 $$0$$ ,因此在可执行文件中只需记录这个段的大小以及所在位置即可,而不用记录里面的数据。
29+
> - *stack* ,即栈,用来存储程序运行过程中的局部变量,以及负责函数调用时的各种机制。它从高地址向低地址增长;
30+
> - *heap* ,即堆,用来支持程序**运行过程中**内存的**动态分配**,比如说你要读进来一个字符串,在你写程序的时候你也不知道它的长度究竟为多少,于是你只能在运行过程中,知道了字符串的长度之后,再在堆中给这个字符串分配内存。
3131
>
3232
> 内存布局,也就是指这些段各自所放的位置。一种典型的内存布局如下:
3333
>
@@ -88,19 +88,19 @@ SECTIONS
8888

8989
时至今日我们已经不太可能将所有代码都写在一个文件里面。在编译过程中,我们的编译器和链接器已经给每个文件都自动生成了一个内存布局。这里,我们的链接工具所要做的是最终将各个文件的内存布局装配起来生成整个程序的内存布局。
9090

91-
我们首先使用 `OUTPUT_ARCH` 指定了架构,随后使用 `ENTRY_POINT` 指定了 **入口点**`_start` ,即程序第一条被执行的指令所在之处。在这个链接脚本中我们并未看到 `_start` ,回忆上一章,我们为了移除运行时环境依赖,重写了 C runtime 的入口 `_start` 。所以,链接脚本宣布整个程序会从那里开始运行。
91+
我们首先使用 *OUTPUT_ARCH* 指定了架构,随后使用 *ENTRY_POINT* 指定了 **入口点**`_start` ,即程序第一条被执行的指令所在之处。在这个链接脚本中我们并未看到 `_start` ,回忆上一章,我们为了移除运行时环境依赖,重写了 C runtime 的入口 `_start` 。所以,链接脚本宣布整个程序会从那里开始运行。
9292

93-
链接脚本的整体写在 `SECTION{ }` 中,里面有多个形如 $\text{output section: \{ input section list \}}$ 的语句,每个都描述了一个整个程序内存布局中的一个输出段 $\text{output section}$ 是由各个文件中的哪些输入段 $\text{input section}$ 组成的。
93+
链接脚本的整体写在 *SECTION{ }* 中,里面有多个形如 *output section:{ input section list }* 的语句,每个都描述了一个整个程序内存布局中的一个输出段 *output section* 是由各个文件中的哪些输入段 *input section* 组成的。
9494

95-
我们可以用 $*()$ 来表示将各个文件中所有符合括号内要求的输入段放在当前的位置。而括号内,你可以直接使用段的名字,也可以包含通配符 $*$ 。
95+
我们可以用 $$*()$$ 来表示将各个文件中所有符合括号内要求的输入段放在当前的位置。而括号内,你可以直接使用段的名字,也可以包含通配符 $$*$$
9696

9797
单独的一个 `.`**当前地址 (Location Counter)** ,可以对其赋值来从设置的地址继续向高地址放置各个段。如果不进行赋值的话,则默认各个段会紧挨着向高地址放置。将一个 **符号** 赋值为 `.` 则会记录下这个符号的地址。
9898

99-
到这里我们大概看懂了这个链接脚本在做些什么事情。首先是从 `BASE_ADDRESS``0x80200000` 开始向下放置各个段,依次是 $\text{.text, .rodata, .data, .stack, .bss}$ 。同时我们还记录下了每个段的开头和结尾地址,如 $\text{.text}$ 段的开头、结尾地址分别就是符号 $\text{stext, etext}$ 的地址,我们接下来会用到。
99+
到这里我们大概看懂了这个链接脚本在做些什么事情。首先是从 *BASE_ADDRESS*`0x80200000` 开始向下放置各个段,依次是 *.text, .rodata, .data, .stack, .bss* 。同时我们还记录下了每个段的开头和结尾地址,如 $\text{.text}$ 段的开头、结尾地址分别就是符号 *stext, etext* 的地址,我们接下来会用到。
100100

101101
> OpenSBI 将自身放在 `0x80000000` ,完成初始化后会跳转到 `0x80200000` ,因此 `_start` 必须位于这个地址。`.text` 为代码段标识,其第一个函数就是 `_start`
102102
103-
这里面有两个输入段与其他长的不太一样,即 $$\text{.text.entry,.bss.stack}$$ ,似乎编译器不会自动生成这样名字的段。事实上,它们是我们在后面自己定义的。
103+
这里面有两个输入段与其他长的不太一样,即 *.text.entry,.bss.stack* ,似乎编译器不会自动生成这样名字的段。事实上,它们是我们在后面自己定义的。
104104

105105
### 使用链接脚本
106106

chapter2/part4.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,11 @@ OpenSBI 所做的一件事情就是把 CPU 从 M Mode 切换到 S Mode ,接着
3434
>
3535
> M-mode(机器模式,缩写为 M 模式)是 RISC-V 中 hart(hardware thread,硬件线程)可以执行的最高权限模式。在 M 模式下运行的 hart 对内存,I/O 和一些对于启动和配置系统来说必要的底层功能有着完全的使用权。
3636
>
37-
> **[info] riscv64 的 S Mode**
37+
> **riscv64 的 S Mode**
3838
>
3939
> S-mode(监管者模式,缩写为 S 模式)是支持现代类 Unix 操作系统的权限模式,支持现代类 Unix 操作系统所需要的基于页面的虚拟内存机制是其核心。
40+
>
41+
4042

4143
接着我们要在 `_start` 中设置内核的运行环境了,我们直接来看代码:
4244

@@ -58,11 +60,11 @@ bootstack:
5860
bootstacktop:
5961
```
6062

61-
可以看到之前未被定义的 $\text{.bss.stack}$ 段出现了,我们只是在这里分配了一块 $4096\times{4}\text{Bytes}=\text{16KiB}$ 的内存作为内核的栈。之前的 $\text{.text.entry}$ 也出现了:我们将 `_start` 函数放在了 $\text{.text}$ 段的开头。
63+
可以看到之前未被定义的 *.bss.stack* 段出现了,我们只是在这里分配了一块 $$4096\times{4}\text{Bytes}=\text{16KiB}$$ 的内存作为内核的栈。之前的 *.text.entry* 也出现了:我们将 `_start` 函数放在了 *.text* 段的开头。
6264

6365
我们看看 `_start` 里面做了什么:
6466

65-
1. 修改栈指针寄存器 $\text{sp}$$\text{.bss.stack}$ 段的结束地址,由于栈是从高地址往低地址增长,所以高地址是栈顶;
67+
1. 修改栈指针寄存器 `sp`*.bss.stack* 段的结束地址,由于栈是从高地址往低地址增长,所以高地址是栈顶;
6668
2. 使用 `call` 指令跳转到 `rust_main` 。这意味着我们的内核运行环境设置完成了,正式进入内核。
6769

6870
我们将 `src/main.rs` 里面的 `_start` 函数删除,并换成 `rust_main`

chapter2/part6.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ void sbi_console_putchar(int ch)
8080
8181
> 实现了编号在 `0-8` 之间的系统调用,具体请看 [OpenSBI 文档 # function list](https://github.com/riscv/riscv-sbi-doc/blob/master/riscv-sbi.adoc#function-listing-1)
8282
83-
执行 ecall 前需要指定系统调用的编号,传递参数。一般而言,$a_7$ 为系统调用编号,$a_0 , a_1 , a_2$ 为参数:
83+
执行 ecall 前需要指定系统调用的编号,传递参数。一般而言,$$a_7$$ 为系统调用编号,$$a_0 , a_1 , a_2$$ 为参数:
8484
8585
```rust
8686
// src/lib.rs
@@ -120,9 +120,9 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
120120
>
121121
> 等更多事项。通常编译器按照某种规范去翻译所有的函数调用,这种规范被称为 [calling convention](https://en.wikipedia.org/wiki/Calling_convention) 。值得一提的是,为了实现函数调用,我们需要预先分配一块内存作为 **调用栈** ,后面会看到调用栈在函数调用过程中极其重要。你也可以理解为什么第一章刚开始我们就要分配栈了。
122122
123-
对于参数比较少且是基本数据类型的时候,我们从左到右使用寄存器 $a_0 \sim a_7$ 就可以完成参数的传递。(可参考 [riscv calling convention](https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf)
123+
对于参数比较少且是基本数据类型的时候,我们从左到右使用寄存器 $$a_0 \sim a_7$$ 就可以完成参数的传递。(可参考 [riscv calling convention](https://riscv.org/wp-content/uploads/2015/01/riscv-calling.pdf)
124124

125-
然而,如这种情况一样,设置寄存器并执行汇编指令,这超出了 Rust 语言的描述能力。然而又与之前 `global_asm!` 大段插入汇编代码不同,我们要把 `u8` 类型的单个字符传给 $a_0$ 作为输入参数,这种情况较为强调 Rust 与汇编代码的交互。此时我们通常使用 **内联汇编(inline assembly)**
125+
然而,如这种情况一样,设置寄存器并执行汇编指令,这超出了 Rust 语言的描述能力。然而又与之前 `global_asm!` 大段插入汇编代码不同,我们要把 `u8` 类型的单个字符传给 $$a_0$$ 作为输入参数,这种情况较为强调 Rust 与汇编代码的交互。此时我们通常使用 **内联汇编(inline assembly)**
126126

127127
> **[info] 拓展内联汇编**
128128
>
@@ -145,9 +145,9 @@ fn sbi_call(which: usize, arg0: usize, arg1: usize, arg2: usize) -> usize {
145145
> - `option` 是 Rust 语言内联汇编 **特有** 的(相对于 C 语言),用来对内联汇编整体进行配置。
146146
> - 如果想进一步了解上面例子中的内联汇编(**"asm!"**),请参考[附录:内联汇编](../appendix/inline_asm.md)。
147147
148-
输出部分,我们将结果保存到变量 `ret` 中,限制条件 `{x10}` 告诉编译器使用寄存器 $x_{10}(a_0)$ ,前面的 `=` 表明汇编代码会修改该寄存器并作为最后的返回值。一般情况下 `output operands` 的 constraint 部分前面都要加上 `=` 。
148+
输出部分,我们将结果保存到变量 `ret` 中,限制条件 `{x10}` 告诉编译器使用寄存器 $$x_{10}(a_0)$$ ,前面的 `=` 表明汇编代码会修改该寄存器并作为最后的返回值。一般情况下 `output operands` 的 constraint 部分前面都要加上 `=` 。
149149
150-
输入部分,我们分别通过寄存器 $x_{10}(a_0),x_{11}(a_1),x_{12}(a_2),x_{17}(a_7)$ 传入参数 `arg0,arg1,arg2,which` ,它们分别代表接口可能所需的三个输入参数(`arg0,arg1,arg2`),以及用来区分我们调用的是哪个接口的 `SBI Extension ID(`which`) 。这里之所以提供三个输入参数是为了将所有接口囊括进去,对于某些接口有的输入参数是冗余的,比如`sbi_console_putchar`` 由于只需一个输入参数,它就只关心寄存器 $a_0$ 的值。
150+
输入部分,我们分别通过寄存器 $$x_{10}(a_0),x_{11}(a_1),x_{12}(a_2),x_{17}(a_7)$$ 传入参数 *arg0,arg1,arg2,which* ,它们分别代表接口可能所需的三个输入参数(*arg0,arg1,arg2*),以及用来区分我们调用的是哪个接口的 `SBI Extension ID(*which*)` 。这里之所以提供三个输入参数是为了将所有接口囊括进去,对于某些接口有的输入参数是冗余的,比如*sbi_console_putchar* 由于只需一个输入参数,它就只关心寄存器 $$a_0$$ 的值。
151151
152152
clobbered registers list 中,出现了一个 `"memory"` ,这用来告诉编译器汇编代码隐式的修改了在汇编代码中未曾出现的某些寄存器。所以,它也不能认为汇编代码中未出现的寄存器就会在内联汇编前后保持不变了。
153153

0 commit comments

Comments
 (0)