Skip to content

Commit efd93b6

Browse files
committed
u2020-03-03 update
1 parent c909204 commit efd93b6

Some content is hidden

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

85 files changed

+274
-179
lines changed

Diff for: chapter1/introduction.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
## 第一章:独立化可执行程序
1+
# 第一章:独立化可执行程序
22

3-
### 本章概要
3+
## 本章概要
44

55
这一章你将会学到:
66

Diff for: chapter2/part3.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ SECTIONS
9696

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

99-
到这里我们大概看懂了这个链接脚本在做些什么事情。首先是从 _BASE_ADDRESS_`0x80200000` 开始向下放置各个段,依次是 _.text, .rodata, .data, .stack, .bss_ 。同时我们还记录下了每个段的开头和结尾地址,如 $\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

Diff for: chapter4/introduction.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
## 第四章:内存管理
1+
# 第四章:内存管理
22

3-
### 本章概要
3+
## 本章概要
44

55
本章你将会学到:
66

Diff for: chapter4/part1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ pub extern "C" fn rust_main() -> ! {
9797

9898
通常,我们在分配物理内存时并不是以字节为单位,而是以一**物理页帧(Frame)**,即连续的 $$2^{12}=4096$$ 字节为单位分配。我们希望用**物理页号(Physical Page Number, PPN)** 来代表一物理页,实际上代表物理地址范围在 $$[\text{PPN}\times 2^{12},(\text{PPN}+1)\times 2^{12})$$ 的一物理页。
9999

100-
不难看出,物理页号与物理页形成一一映射。为了能够使用物理页号这种表达方式,每个物理页的开头地址必须是 $$2^{12}=4096$$ 的倍数。但这也给了我们一个方便:对于一个物理地址,其除以 $$4096$$ (或者说右移 $12$ 位) 的商即为这个物理地址所在的物理页号。
100+
不难看出,物理页号与物理页形成一一映射。为了能够使用物理页号这种表达方式,每个物理页的开头地址必须是 $$2^{12}=4096$$ 的倍数。但这也给了我们一个方便:对于一个物理地址,其除以 $$4096$$ (或者说右移 $$12$$ 位) 的商即为这个物理地址所在的物理页号。
101101

102102
以这种方式,我们看一下可用物理内存的物理页号表达。将 `init.rs` 中的输出语句略做改动:
103103

Diff for: chapter5/introduction.md

-1
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,3 @@
66
- 如何使用页表完成虚拟地址到物理地址的映射
77
- 解释内核初始映射,进行内核重映射
88

9-
代码可以在[这里]()找到。

Diff for: chapter5/part1.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,4 @@ Sv39 同样是基于页的,在物理内存那一节曾经提到**物理页帧(
117117

118118
### 小结
119119

120-
这一节我们终于大概讲清楚了页表的前因后果,现在是时候应用这一套理论说明之前的所谓“魔法”到底是怎么一回事了!
120+
这一节我们终于大概讲清楚了页表的前因后果。接下来,我们将利用页表知识,来重新实现内核已有的功能,只不过从物理地址空间抽象为虚拟地址空间,这让内核更加符合它自身“程序”的属性。

Diff for: chapter5/part2.md

+35-11
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,54 @@
22

33
- [代码][code]
44

5-
让我们回顾一下在相当于 bootloader 的 OpenSBI 结束后,我们要面对的是怎样一种局面:
5+
之前的内核实现并未使能页表机制,实际上内核是直接在物理地址空间上运行的。这样虽然比较简单,但是为了后续能够支持多个用户进程能够在内核中并发运行,满足隔离等性质,我们要先运用学过的页表知识,把内核的运行环境从物理地址空间转移到虚拟地址空间,为之后的功能打好铺垫。
6+
7+
更具体的,我们现在想将内核代码放在虚拟地址空间中以 ``0xffffffffc0200000`` 开头的一段高地址空间中。因此,我们将下面的参数修改一下:
8+
9+
```diff
10+
# src/boot/linker64.ld
11+
-BASE_ADDRESS = 0x80200000;
12+
+BASE_ADDRESS = 0xffffffffc0200000;
13+
14+
# src/consts.rs
15+
-pub const KERNEL_BEGIN_VADDR: usize = 0x80200000;
16+
+pub const KERNEL_BEGIN_VADDR: usize = 0xffffffffc0200000;
17+
```
18+
19+
我们修改了链接脚本中的链接开头地址。但是这样做的话,就能从物理地址空间转移到虚拟地址空间了吗?让我们回顾一下在相当于 bootloader 的 OpenSBI 结束后,我们要面对的是怎样一种局面:
620

721
- 物理内存状态:OpenSBI 代码放在 `[0x80000000,0x80200000)` 中,内核代码放在以 `0x80200000` 开头的一块连续物理内存中。
822
- CPU 状态:处于 S Mode ,寄存器 `satp`$$\text{MODE}$$ 被设置为 `Bare` ,即无论取指还是访存我们通过物理地址直接访问物理内存。 $$\text{PC}=0\text{x}80200000$$ 指向内核的第一条指令。栈顶地址 $$\text{SP}$$ 处在 OpenSBI 代码内。
9-
- 内核代码:使用虚拟地址,代码和数据段均放在以[虚拟地址 `0xffffffffc0200000`](https://github.com/rcore-os/rCore_tutorial/blob/ch5-pa2/os/src/boot/linker64.ld#L9)开头的一段连续虚拟内存中
10-
- 我们所要做的事情:将 $$\text{SP}$$ 寄存器指向的栈空间从 OpenSBI 某处移到我们的内核定义的某块内存区域中,使得我们可以完全支配启动栈;同时需要跳转到函数 `rust_main` 中。
23+
- 内核代码:由于改动了链接脚本的起始地址,认为自己处在以虚拟地址 ``0xffffffffc0200000`` 开头的一段连续虚拟地址空间中
24+
1125

12-
我们已经在 [`src/boot/entry64.asm`](https://github.com/rcore-os/rCore_tutorial/blob/ch5-pa2/os/src/boot/entry64.asm#L19) 中自己分配了一块 $$16\text{KiB}$$ 的内存用来做启动栈:
26+
接下来,我们在入口点 ``entry64.asm`` 中所要做的事情是:将 $$\text{SP}$$ 寄存器指向的栈空间从 OpenSBI 某处移到我们的内核定义的某块内存区域中,使得我们可以完全支配启动栈;同时需要跳转到函数 `rust_main` 中。
27+
28+
在之前的实现中,我们已经在 [`src/boot/entry64.asm`](https://github.com/rcore-os/rCore_tutorial/blob/ch5-pa2/os/src/boot/entry64.asm#L19) 中自己分配了一块 $$16\text{KiB}$$ 的内存用来做启动栈:
1329

1430
```riscv
1531
# src/boot/entry64.asm
1632
17-
.section .bss.stack
18-
.align 12
19-
.global bootstack
33+
.section .text.entry
34+
.globl _start
35+
_start:
36+
la sp, bootstacktop
37+
call rust_main
38+
39+
.section .bss.stack
40+
.align 12
41+
.global bootstack
2042
bootstack:
21-
.space 4096 * 4
22-
.global bootstacktop
43+
.space 4096 * 4
44+
.global bootstacktop
2345
bootstacktop:
2446
```
2547

26-
符号 `bootstacktop` 就是我们需要的栈顶地址!同样符号 `rust_main` 也代表了我们要跳转到的地址。直接将 `bootstacktop` 的值给到 $$\text{SP}$$, 再跳转到 `rust_main` 就行了吗?
48+
符号 `bootstacktop` 就是我们需要的栈顶地址!同样符号 `rust_main` 也代表了我们要跳转到的地址。直接将 `bootstacktop` 的值给到 $$\text{SP}$$, 再跳转到 `rust_main` 就行了。看起来原来的代码仍然能用啊?
49+
50+
问题在于,由于我们修改了链接脚本的起始地址,编译器和链接器认为内核开头地址为 ``0xffffffffc0200000``,因此这两个符号会被翻译成比这个开头地址还要高的绝对虚拟地址。而我们的 CPU 目前处于 `Bare` 模式,会将地址都当成物理地址处理。这样,我们跳转到 `rust_main` 就会跳转到 `0xffffffffc0200000+` 的一个物理地址,物理地址都没有这么多位!这显然是会出问题的。
2751

28-
问题在于,编译器和链接器认为程序在虚拟内存空间中运行,因此这两个符号都会被翻译成虚拟地址。而我们的 CPU 目前处于 `Bare` 模式,会将地址都当成物理地址处理。这样,我们跳转到 `rust_main` 就会跳转到 `0xffffffffc0200000+` 的一个物理地址,物理地址都没有这么多位!这显然是会出问题的
52+
于是,我们只能想办法利用刚学的页表知识,帮内核将需要的虚拟地址空间构造出来
2953

3054
观察可以发现,同样的一条指令,其在虚拟内存空间中的虚拟地址与其在物理内存中的物理地址有着一个固定的**偏移量**。比如内核的第一条指令,虚拟地址为 `0xffffffffc0200000` ,物理地址为 `0x80200000` ,因此,我们只要将虚拟地址减去 `0xffffffff40000000` ,就得到了物理地址。
3155

Diff for: chapter8/introduction.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
## 用户进程
1+
# 第八章:用户进程
22

33
这一章我们终于要在自己的内核上跑用户程序啦!
44

Diff for: chapter9/introduction.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
## 第九章:文件系统
1+
# 第九章:文件系统
22

3-
### 本章概要
3+
## 本章概要
44

55
之前我们只能在内核代码中硬编码跑什么用户程序,现在我们实现一个简单的终端,可以由我们自己输入跑什么程序!这说明我们要同时将多个程序组成的镜像链接进内核,于是我们使用文件系统来打包镜像,在内核中解析镜像取出单个用户程序。
66

Diff for: chapter9/part2.md

+19-4
Original file line numberDiff line numberDiff line change
@@ -51,29 +51,44 @@ pub fn getc() -> u8 {
5151

5252
```rust
5353
// usr/rust/src/bin/notebook.rs
54-
5554
#![no_std]
5655
#![no_main]
5756

5857
#[macro_use]
5958
extern crate user;
6059

61-
use rust::io::getc;
60+
use user::io::getc;
61+
use user::io::putchar;
6262

6363
const LF: u8 = 0x0au8;
6464
const CR: u8 = 0x0du8;
65+
const BS: u8 = 0x08u8;
66+
const DL: u8 = 0x7fu8;
6567

6668
#[no_mangle]
6769
pub fn main() {
6870
println!("Welcome to notebook!");
71+
let mut line_count = 0;
6972
loop {
7073
let c = getc();
7174
match c {
7275
LF | CR => {
76+
line_count = 0;
7377
print!("{}", LF as char);
74-
print!("{}", CR as char)
78+
print!("{}", CR as char);
79+
}
80+
DL => if line_count > 0 {
81+
// 支持退格键
82+
// 写法来源:https://github.com/mit-pdos/xv6-riscv-fall19/blob/xv6-riscv-fall19/kernel/console.c#L41
83+
putchar(BS as char);
84+
putchar(' ');
85+
putchar(BS as char);
86+
line_count -= 1;
87+
}
88+
_ => {
89+
line_count += 1;
90+
print!("{}", c as char);
7591
}
76-
_ => print!("{}", c as char)
7792
}
7893
}
7994
}

Diff for: chapter9/part3.md

+42-29
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@
1515

1616
#![no_std]
1717
#![no_main]
18-
#![feature(alloc)]
1918

2019
extern crate alloc;
2120

@@ -24,39 +23,53 @@ extern crate user;
2423

2524
const LF: u8 = 0x0au8;
2625
const CR: u8 = 0x0du8;
26+
const DL: u8 = 0x7fu8;
27+
const BS: u8 = 0x08u8;
2728

28-
use rust::io::getc;
29-
use rust::syscall::sys_exec;
3029
use alloc::string::String;
30+
use user::io::getc;
31+
use user::io::putchar;
32+
use user::syscall::sys_exec;
3133

3234
#[no_mangle]
3335
pub fn main() {
34-
println!("Rust user shell");
35-
// 保存本行已经输入的内容
36-
let mut line: String = String::new();
37-
print!(">> ");
38-
loop {
39-
let c = getc();
40-
match c {
41-
LF | CR => {
42-
// 如果遇到回车或换行
43-
println!("");
44-
if !line.is_empty() {
45-
println!("searching for program {}", line);
46-
// 使用系统调用执行程序
47-
sys_exec(line.as_ptr());
48-
// 清空本行内容
49-
line.clear();
50-
}
51-
print!(">> ");
52-
},
53-
_ => {
54-
// 否则正常输入
55-
print!("{}", c as char);
56-
line.push(c as char);
57-
}
58-
}
59-
}
36+
println!("Rust user shell");
37+
// 保存本行已经输入的内容
38+
let mut line: String = String::new();
39+
print!(">> ");
40+
loop {
41+
let c = getc();
42+
match c {
43+
LF | CR => {
44+
// 如果遇到回车或换行
45+
println!("");
46+
if !line.is_empty() {
47+
println!("searching for program {}", line);
48+
// 这里在程序名结尾需要手动添加 '\0',因为 Rust 编译器不会帮我们在字符串结尾附上 '\0'
49+
line.push('\0');
50+
// 使用系统调用执行程序
51+
sys_exec(line.as_ptr());
52+
// 清空本行内容
53+
line.clear();
54+
}
55+
print!(">> ");
56+
}
57+
DL => {
58+
// 如果是退格键
59+
if !line.is_empty() {
60+
putchar(BS as char);
61+
putchar(' ');
62+
putchar(BS as char);
63+
line.pop();
64+
}
65+
}
66+
_ => {
67+
// 否则正常输入
68+
print!("{}", c as char);
69+
line.push(c as char);
70+
}
71+
}
72+
}
6073
}
6174
```
6275

Diff for: docs/appendix/inline_asm.html

+1-1
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ <h1 class="search-results-title">No results matching "<span class='search-query'
12971297
<script>
12981298
var gitbook = gitbook || [];
12991299
gitbook.push(function() {
1300-
gitbook.page.hasChanged({"page":{"title":"内联汇编","level":"1.14.1","depth":2,"next":{"title":"安装 rust","level":"1.14.2","depth":2,"path":"appendix/install_rust.md","ref":"appendix/install_rust.md","articles":[]},"previous":{"title":"附录","level":"1.14","depth":1,"path":"appendix/introduction.md","ref":"./appendix/introduction.md","articles":[{"title":"内联汇编","level":"1.14.1","depth":2,"path":"appendix/inline_asm.md","ref":"appendix/inline_asm.md","articles":[]},{"title":"安装 rust","level":"1.14.2","depth":2,"path":"appendix/install_rust.md","ref":"appendix/install_rust.md","articles":[]}]},"dir":"ltr"},"config":{"gitbook":"*","theme":"default","variables":{},"plugins":["hide-element","chapter-fold","katex","alerts","emphasize","-highlight","prism","localized-footer","mermaid-gb3"],"pluginsConfig":{"chapter-fold":{},"prism":{"css":["prismjs/themes/prism-tomorrow.css"]},"emphasize":{},"search":{},"localized-footer":{"filename":"extensions/comment/gitalk.html","hline":"true"},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"hide-element":{"elements":[".gitbook-link"]},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"mermaid-gb3":{},"alerts":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"}},"file":{"path":"appendix/inline_asm.md","mtime":"2020-02-13T08:47:45.383Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2020-03-03T01:56:30.720Z"},"basePath":"..","book":{"language":""}});
1300+
gitbook.page.hasChanged({"page":{"title":"内联汇编","level":"1.14.1","depth":2,"next":{"title":"安装 rust","level":"1.14.2","depth":2,"path":"appendix/install_rust.md","ref":"appendix/install_rust.md","articles":[]},"previous":{"title":"附录","level":"1.14","depth":1,"path":"appendix/introduction.md","ref":"./appendix/introduction.md","articles":[{"title":"内联汇编","level":"1.14.1","depth":2,"path":"appendix/inline_asm.md","ref":"appendix/inline_asm.md","articles":[]},{"title":"安装 rust","level":"1.14.2","depth":2,"path":"appendix/install_rust.md","ref":"appendix/install_rust.md","articles":[]}]},"dir":"ltr"},"config":{"gitbook":"*","theme":"default","variables":{},"plugins":["hide-element","chapter-fold","katex","alerts","emphasize","-highlight","prism","localized-footer","mermaid-gb3"],"pluginsConfig":{"chapter-fold":{},"prism":{"css":["prismjs/themes/prism-tomorrow.css"]},"emphasize":{},"search":{},"localized-footer":{"filename":"extensions/comment/gitalk.html","hline":"true"},"lunr":{"maxIndexSize":1000000,"ignoreSpecialCharacters":false},"hide-element":{"elements":[".gitbook-link"]},"katex":{},"fontsettings":{"theme":"white","family":"sans","size":2},"mermaid-gb3":{},"alerts":{},"sharing":{"facebook":true,"twitter":true,"google":false,"weibo":false,"instapaper":false,"vk":false,"all":["facebook","google","twitter","weibo","instapaper"]},"theme-default":{"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"},"showLevel":false}},"structure":{"langs":"LANGS.md","readme":"README.md","glossary":"GLOSSARY.md","summary":"SUMMARY.md"},"pdf":{"pageNumbers":true,"fontSize":12,"fontFamily":"Arial","paperSize":"a4","chapterMark":"pagebreak","pageBreaksBefore":"/","margin":{"right":62,"left":62,"top":56,"bottom":56}},"styles":{"website":"styles/website.css","pdf":"styles/pdf.css","epub":"styles/epub.css","mobi":"styles/mobi.css","ebook":"styles/ebook.css","print":"styles/print.css"}},"file":{"path":"appendix/inline_asm.md","mtime":"2020-02-19T11:36:27.646Z","type":"markdown"},"gitbook":{"version":"3.2.3","time":"2020-03-03T15:44:52.618Z"},"basePath":"..","book":{"language":""}});
13011301
});
13021302
</script>
13031303
</div>

0 commit comments

Comments
 (0)