Skip to content

Commit 1f82c64

Browse files
committed
Merge branch 'main' of github.com:rcore-os/rCore-Tutorial-Book-v3 into main
2 parents 0fd483f + e61a364 commit 1f82c64

15 files changed

+60
-26
lines changed

source/appendix-b/index.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -118,8 +118,8 @@ rust-readobj
118118
119119
每个 section header 则描述一个段的元数据。
120120

121-
其中,我们看到了代码段 ``.text`` 需要被加载到地址 ``0x5070`` ,大小 208067 字节
122-
它们分别由元数据的字段 Offset、 Size 和 Address 给出。
121+
其中,我们看到了代码段 ``.text`` 需要被加载到地址 ``0x5070`` 大小 208067 字节。
122+
它们分别由元数据的字段 Offset、 Size 和 Address 给出。
123123

124124
我们还能够看到程序中的符号表:
125125

source/chapter0/1what-is-os.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,19 @@
102102
侏罗纪 [#侏罗纪]_ 与白垩纪 [#白垩纪]_ 的恐龙时代
103103
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
104104

105-
摩尔定律(“当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。”)的累积效应意味着计算成本下降到可以为用户优化系统而不是为有效使用处理器。例如,UNIX 是在 70 年代初在当时没有人使用的备用计算机上开发的。20 世纪 50 年代末,提高人机交互方式的分时操作系统越来越崭露头角。分时是指多个用户和多个程序以很小的时间间隔来共享使用同一台计算机上的 CPU 和其他硬件资源。1961年,麻省理工学院的Fernando Corbató带领团队成功研发了在IBM 709计算机上的CTSS(Compatible Time-Sharing System, 兼容时间共享系统)操作系统 [#UNIX25Y]_ ,它拥有分时系统必须有的特征:支持多个用户分享使用同一台计算机,即宏观上的同一时间段内能完成多个人机交互工作。在CTSS的鼓舞下,1964 年,麻省理工学院、贝尔实验室及美国通用电气公司共同参与研发一个目标远大的操作系统:MULTICS (MULTiplexed Information and Computing System) ,它是一套安装在大型主机上、支持多人多任务的操作系统。 MULTICS 以兼容分时系统 (CTSS) 做基础,建置在美国通用电力公司的大型机 GE-645 ,目标是连接 1000 部终端机,支持 300 位用户同时上线。因 MULTICS 的目标太宏大,而研发工作进度过于缓慢,1969 年 AT&T 的 Bell 实验室从 MULTICS 研发中撤出。CTSS和MULTICS这就像侏罗纪时期体型庞大的食肉恐龙--霸王龙,称霸一时,但进化缓慢,最终灭绝。
105+
摩尔定律(“当价格不变时,集成电路上可容纳的晶体管数目,约每隔18个月便会增加一倍,性能也将提升一倍。”)的累积效应使得计算成本逐渐下降,这意味着软件开发者不必将全部精力用于提高处理器利用率,而是可以开始努力提升用户的使用体验。例如,UNIX 是在 70 年代初在当时没有人使用的备用计算机上开发的。20 世纪 50 年代末,提高人机交互方式的分时操作系统越来越崭露头角。分时是指多个用户和多个程序以很小的时间间隔来共享使用同一台计算机上的 CPU 和其他硬件资源。1961 年,麻省理工学院的Fernando Corbató带领团队成功研发了在 IBM 709 计算机上的 CTSS(Compatible Time-Sharing System, 兼容时间共享系统)操作系统 [#UNIX25Y]_ ,它拥有分时系统必须有的特征:支持多个用户分享使用同一台计算机,即宏观上的同一时间段内能完成多个人机交互工作。在 CTSS 的鼓舞下,1964 年,麻省理工学院、贝尔实验室及美国通用电气公司共同参与研发一个目标远大的操作系统:MULTICS (MULTiplexed Information and Computing System) ,它是一套安装在大型主机上、支持多人多任务的操作系统。 MULTICS 以兼容分时系统 (CTSS) 做基础,建置在美国通用电力公司的大型机 GE-645 ,目标是连接 1000 部终端机,支持 300 位用户同时上线。因 MULTICS 的目标太宏大,而研发工作进度过于缓慢,1969 年 AT&T 的 Bell 实验室从 MULTICS 研发中撤出。CTSS 和 MULTICS 这就像侏罗纪时期体型庞大的食肉恐龙--霸王龙,称霸一时,但进化缓慢,最终灭绝。
106106

107107
但贝尔实验室的两位软件工程师 Ken Thompson 与 Dennis Ritchie借鉴了一些重要的 MULTICS 设计思想和理念,以 C 语言为基础发展出小巧灵活的 UNIX 操作系统 [#UNIX]_ 。UNIX 操作系统的早期版本是完全免费的,可以轻易获得并随意修改,所以它得到了广泛的接受。后来,它成为开发小型机操作系统的起点。由于早期的广泛应用,它已经成为分时操作系统的典范。这好像一种生活在侏罗纪晚期的小型恐龙--始祖鸟,它可能是鸟类的祖先,最终进化为可以展翅高飞的飞鸟。
108108

109109

110110
.. note::
111111

112-
Ken Thompson 与 Dennis Ritchie于1974年7月在 the Communications of the ACM期刊上发表 “The UNIX Time Sharing System”,引起了学术界的广泛兴趣并向他们要源码进行分析和学习,所以Unix v5 以“仅用于教育目的”的开放协议,提供给各个大学作为操作系统教学之用,成为当时操作系统课程中的重要学习资料,而且各大学和公司开始进一步研究Unix,并对Unix进行改进和扩展,从而使得Unix在世界上广泛流行
112+
Ken Thompson 与 Dennis Ritchie 于 1974 年 7 月在 the Communications of the ACM 期刊上发表 “The UNIX Time Sharing System”,引起了学术界的广泛兴趣并向他们要源码进行分析和学习,所以 Unix v5 以“仅用于教育目的”的开放协议,提供给各个大学作为操作系统教学之用,成为当时操作系统课程中的重要学习资料,而且各大学和公司开始进一步研究 Unix,并对 Unix 进行改进和扩展,从而使得 Unix 在世界上广泛流行
113113

114114

115115
.. chyyuu https://new.qq.com/omn/20210305/20210305A0G8VW00.html
116116
117-
1973年,南京大学徐家福、中科院软件所仲萃豪、北京大学杨芙清合作,研制了系统程序设计语言XCY。XCY由徐(Xu)家福、仲萃(Cui)豪、杨(Yang)芙清的姓名汉语拼音各取一个字母组成。南京大学的开发小组还用XCY语言编写开发了240机的DJS200/XT I、DJS200/XT II、XW等具有分时和进程管理能力的通用操作系统
117+
1973年,南京大学徐家福、中科院软件所仲萃豪、北京大学杨芙清合作,研制了系统程序设计语言 XCY。XCY 由徐(Xu)家福、仲萃(Cui)豪、杨(Yang)芙清的姓名汉语拼音各取一个字母组成。南京大学的开发小组还用 XCY 语言编写开发了 240 机的 DJS200/XT I、DJS200/XT II、XW 等具有分时和进程管理能力的通用操作系统
118118

119119
.. note::
120120

source/chapter0/5setup-devel-env.rst

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,3 +315,36 @@ GDB 调试支持
315315
由于教程的 ch1~ch5 分支还没有文件系统,在 K210 上运行这些分支无需 MicroSD 卡也不需要进行文件系统镜像烧写工作,直接切换到 `os` 目录下 `make run BOARD=k210` 即可。
316316

317317
到这里,恭喜你完成了实验环境的配置,可以开始阅读教程的正文部分了!
318+
319+
Q & A
320+
----------------------------------------------------------------
321+
322+
当代码跑不起来的时候,可以尝试:
323+
324+
- 分支是否与 rCore-Tutorial-v3 原版仓库(而非 fork 出来的仓库)的对应分支同步。如不同步的话考虑通过 ``git pull`` 进行更新。注:这是因为 Rust 的版本更迭较快,如不及时更新的话曾经能正常运行的代码也会无法运行。
325+
- 项目根目录下的 ``rust-toolchain`` 非常重要,它代表整个项目采用的 Rust 工具链版本。请务必保持其与原版仓库对应分支一致。
326+
- 项目根目录下是否存在放置 RustSBI 的 ``bootloader/`` 目录。如不存在的话可从原版仓库的各分支上获取。
327+
- 通过 ``make clean`` 或者 ``cargo clean`` 删除 ``os`` 或 ``user`` 目录下的构建产物,并重新 ``make run`` 。注:出现这样的问题通常说明框架的构建脚本存在 bug,可以提 issue。
328+
329+
如果怀疑遇到了网络问题,可以检查:
330+
331+
- 请按照本节说明进行 Rust 安装和 crates.io 镜像配置。通常情况下能够解决 Rust 工具链更新和下载已发布到 crates.io 上库的问题。
332+
- 如果发现在试图从 github 上下载下述几个库的时候卡死,可以修改 ``os`` 和 ``user`` 目录下的 ``Cargo.toml`` 替换为 gitee 上的镜像。例如,将:
333+
334+
.. code-block:: toml
335+
336+
riscv = { git = "https://github.com/rcore-os/riscv", features = ["inline-asm"] }
337+
virtio-drivers = { git = "https://github.com/rcore-os/virtio-drivers" }
338+
k210-pac = { git = "https://github.com/wyfcyx/k210-pac" }
339+
k210-hal = { git = "https://github.com/wyfcyx/k210-hal" }
340+
k210-soc = { git = "https://github.com/wyfcyx/k210-soc" }
341+
342+
替换为:
343+
344+
.. code-block:: toml
345+
346+
riscv = { git = "https://gitee.com/rcore-os/riscv", features = ["inline-asm"] }
347+
virtio-drivers = { git = "https://gitee.com/rcore-os/virtio-drivers" }
348+
k210-pac = { git = "https://gitee.com/wyfcyx/k210-pac" }
349+
k210-hal = { git = "https://gitee.com/wyfcyx/k210-hal" }
350+
k210-soc = { git = "https://gitee.com/wyfcyx/k210-soc" }

source/chapter1/2remove-std.rst

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@
109109
110110
.. 为了满足编译器和运行时库的灵活性,Rust 编译器内部的某些功能并不仅仅硬编码在语言内部来实现,而是以一种可插入的形式在库中提供,而且可以定制。标准库或第三方库只需要通过某种方式(在方法前面加上一个标记,称为`语义项`标记),如 ``#[panic_handler]`` 、 ``#[]`` 、 ``#[]`` 、 ``#[]`` 等,即可告诉编译器它实现了编译器内部的哪些功能,编译器就会采用库提供的方法来替换它内部对应的功能。
111111
112-
我们创建一个新的子模块 ``lang_items.rs`` 实现panic函数,并通过 ``#[panic_handler]`` 属性通知编译器用panic函数来对接 ``panic!`` 宏:
112+
我们创建一个新的子模块 ``lang_items.rs`` 实现panic函数,并通过 ``#[panic_handler]`` 属性通知编译器用panic函数来对接 ``panic!`` 宏。为了将该子模块添加到项目中,我们还需要在 ``main.rs`` 的 ``#![no_std]`` 的下方加上 ``mod lang_items;`` ,相关知识可参考 :ref:`Rust 模块编程 <rust-modular-programming>`
113113

114114
.. code-block:: rust
115115
@@ -166,6 +166,8 @@
166166
167167
本小节我们固然脱离了标准库,通过了编译器的检验,但也是伤筋动骨,将原有的很多功能弱化甚至直接删除,看起来距离在 RV64GC 平台上打印 ``Hello world!`` 相去甚远了(我们甚至连 ``println!`` 和 ``main`` 函数都删除了)。不要着急,接下来我们会以自己的方式来重塑这些基本功能,并最终完成我们的目标。
168168

169+
.. _rust-modular-programming:
170+
169171
.. note::
170172

171173
**Rust Tips:Rust 模块化编程**

source/chapter1/4first-instruction-in-kernel2.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@
215215
-device loader,file=target/riscv64gc-unknown-none-elf/release/os.bin,addr=0x80200000 \
216216
-s -S
217217
218-
``-s`` 可以使 Qemu 监听本地 TCP 端口 1234 等待 GDB 客户端连接,而 ``-S`` 可以使 Qemu 在收到 GDB 的请求后再开始运行。因此,Qemu 暂时没有任何输出。
218+
``-s`` 可以使 Qemu 监听本地 TCP 端口 1234 等待 GDB 客户端连接,而 ``-S`` 可以使 Qemu 在收到 GDB 的请求后再开始运行。因此,Qemu 暂时没有任何输出。注意,如果不想通过 GDB 对于 Qemu 进行调试而是直接运行 Qemu 的话,则要删掉最后一行的 ``-s -S`` 。
219219

220220
打开另一个终端,启动一个 GDB 客户端连接到 Qemu :
221221

source/chapter1/6print-and-shutdown-based-on-sbi.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@
4747
.. code-block:: rust
4848
4949
// os/src/sbi.rs
50-
#![allow(unused)]
50+
#![allow(unused)] // 此行请放在该文件最开头
5151
const SBI_SET_TIMER: usize = 0;
5252
const SBI_CONSOLE_PUTCHAR: usize = 1;
5353
const SBI_CONSOLE_GETCHAR: usize = 2;

source/chapter2/2application.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@
138138
- ``x10~x17`` : 对应 ``a0~a7``
139139
- ``x1`` :对应 ``ra``
140140

141-
在 RISC-V 调用规范中,和函数调用的 ABI 情形类似,约定寄存器 ``a0~a6`` 保存系统调用的参数, ``a0~a1`` 保存系统调用的返回值。有些许不同的是寄存器 ``a7`` 用来传递 syscall ID,这是因为所有的 syscall 都是通过 ``ecall`` 指令触发的,除了各输入参数之外我们还额外需要一个寄存器来保存要请求哪个系统调用。由于这超出了 Rust 语言的表达能力,我们需要在代码中使用内嵌汇编来完成参数/返回值绑定和 ``ecall`` 指令的插入:
141+
在 RISC-V 调用规范中,和函数调用的 ABI 情形类似,约定寄存器 ``a0~a6`` 保存系统调用的参数, ``a0`` 保存系统调用的返回值。有些许不同的是寄存器 ``a7`` 用来传递 syscall ID,这是因为所有的 syscall 都是通过 ``ecall`` 指令触发的,除了各输入参数之外我们还额外需要一个寄存器来保存要请求哪个系统调用。由于这超出了 Rust 语言的表达能力,我们需要在代码中使用内嵌汇编来完成参数/返回值绑定和 ``ecall`` 指令的插入:
142142

143143
.. code-block:: rust
144144
:linenos:

source/chapter2/4trap-handling.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -292,7 +292,7 @@ Trap 处理的总体流程如下:首先通过 ``__alltraps`` 将 Trap 上下
292292
call trap_handler
293293
294294
- 第 7 行我们使用 ``.align`` 将 ``__alltraps`` 的地址 4 字节对齐,这是 RISC-V 特权级规范的要求;
295-
- 第 8 行的 ``csrrw`` 原型是 :math:`\text{csrrw rd, csr, rs}` 可以将 CSR 当前的值读到通用寄存器 :math:`\text{rd}` 中,然后将通用寄存器 :math:`\text{rs}` 的值写入该 CSR 。因此这里起到的是交换 sscratch 和 sp 的效果。在这一行之前 sp 指向用户栈, sscratch 指向内核栈(原因稍后说明),现在 sp 指向内核栈, sscratch 指向用户栈。
295+
- 第 9 行的 ``csrrw`` 原型是 :math:`\text{csrrw rd, csr, rs}` 可以将 CSR 当前的值读到通用寄存器 :math:`\text{rd}` 中,然后将通用寄存器 :math:`\text{rs}` 的值写入该 CSR 。因此这里起到的是交换 sscratch 和 sp 的效果。在这一行之前 sp 指向用户栈, sscratch 指向内核栈(原因稍后说明),现在 sp 指向内核栈, sscratch 指向用户栈。
296296
- 第 12 行,我们准备在内核栈上保存 Trap 上下文,于是预先分配 :math:`34\times 8` 字节的栈帧,这里改动的是 sp ,说明确实是在内核栈上。
297297
- 第 13~24 行,保存 Trap 上下文的通用寄存器 x0~x31,跳过 x0 和 tp(x4),原因之前已经说明。我们在这里也不保存 sp(x2),因为我们要基于它来找到每个寄存器应该被保存到的正确的位置。实际上,在栈帧分配之后,我们可用于保存 Trap 上下文的地址区间为 :math:`[\text{sp},\text{sp}+8\times34)` ,按照 ``TrapContext`` 结构体的内存布局,基于内核栈的位置(sp所指地址)来从低地址到高地址分别按顺序放置 x0~x31这些通用寄存器,最后是 sstatus 和 sepc 。因此通用寄存器 xn 应该被保存在地址区间 :math:`[\text{sp}+8n,\text{sp}+8(n+1))` 。
298298

source/chapter3/2task-switching.rst

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
.. _term-task:
2222
.. _term-task-switch:
2323

24-
到这里,我们就把应用程序的一次执行过程(也是一段控制流)称为一个 **任务** ,把应用执行过程中的一个时间片段上的执行片段或空闲片段称为 “ **计算任务片** ” 或“ **空闲任务片** ” 。但应用程序的所有任务片都完成后,应用程序的一次任务也就完成了。从一个程序的任务切换到另外一个程序的任务称为 **任务切换** 。为了确保切换后的任务能够正确继续执行,操作系统需要支持让任务的执行“暂停”和“继续”。
24+
到这里,我们就把应用程序的一次执行过程(也是一段控制流)称为一个 **任务** ,把应用执行过程中的一个时间片段上的执行片段或空闲片段称为 “ **计算任务片** ” 或“ **空闲任务片** ” 。当应用程序的所有任务片都完成后,应用程序的一次任务也就完成了。从一个程序的任务切换到另外一个程序的任务称为 **任务切换** 。为了确保切换后的任务能够正确继续执行,操作系统需要支持让任务的执行“暂停”和“继续”。
2525

2626
.. _term-task-context:
2727

@@ -137,7 +137,7 @@ Trap 控制流在调用 ``__switch`` 之前就需要明确知道即将切换到
137137
# 阶段 [4]
138138
ret
139139
140-
我们手写汇编代码来实现 ``__switch`` 。在阶段 [1] 可以看到它的函数原型中的两个参数分别是当前 A 任务上下文指针 ``next_task_cx_ptr`` 和即将被切换到的 B 任务上下文指针 ``next_task_cx_ptr`` ,从 :ref:`RISC-V 调用规范 <term-calling-convention>` 可以知道它们分别通过寄存器 ``a0/a1`` 传入。阶段 [2] 体现在第 19~27 行,即根据 B 任务上下文保存的内容来恢复 ``ra`` 寄存器、``s0~s11`` 寄存器以及 ``sp`` 寄存器。从中我们也能够看出 ``TaskContext`` 里面究竟包含哪些寄存器:
140+
我们手写汇编代码来实现 ``__switch`` 。在阶段 [1] 可以看到它的函数原型中的两个参数分别是当前 A 任务上下文指针 ``current_task_cx_ptr`` 和即将被切换到的 B 任务上下文指针 ``next_task_cx_ptr`` ,从 :ref:`RISC-V 调用规范 <term-calling-convention>` 可以知道它们分别通过寄存器 ``a0/a1`` 传入。阶段 [2] 体现在第 19~27 行,即根据 B 任务上下文保存的内容来恢复 ``ra`` 寄存器、``s0~s11`` 寄存器以及 ``sp`` 寄存器。从中我们也能够看出 ``TaskContext`` 里面究竟包含哪些寄存器:
141141

142142
.. code-block:: rust
143143
:linenos:
@@ -174,4 +174,4 @@ Trap 控制流在调用 ``__switch`` 之前就需要明确知道即将切换到
174174

175175
仔细观察的话可以发现 ``TaskContext`` 很像一个普通函数栈帧中的内容。正如之前所说, ``__switch`` 的实现除了换栈之外几乎就是一个普通函数,也能在这里得到体现。尽管如此,二者的内涵却有着很大的不同。
176176

177-
同学可以自行对照注释看看图示中的后面几个阶段各是如何实现的。另外,当内核仅运行单个应用的时候,无论该任务主动/被动交出 CPU 资源最终都会交还给自己,这将导致传给 ``__switch`` 的两个参数相同,也就是某个 Trap 控制流自己切换到自己的情形,请同学对照图示思考目前的实现能否对它进行正确处理。
177+
同学可以自行对照注释看看图示中的后面几个阶段各是如何实现的。另外,当内核仅运行单个应用的时候,无论该任务主动/被动交出 CPU 资源最终都会交还给自己,这将导致传给 ``__switch`` 的两个参数相同,也就是某个 Trap 控制流自己切换到自己的情形,请同学对照图示思考目前的实现能否对它进行正确处理。

0 commit comments

Comments
 (0)