Skip to content

Commit f3d3a0c

Browse files
committed
docs: lab0
1 parent 669a6ca commit f3d3a0c

File tree

3 files changed

+103
-85
lines changed

3 files changed

+103
-85
lines changed

docs/lab/lab0-1.png

49.8 KB
Loading

docs/lab/lab0-2.png

112 KB
Loading

docs/lab/lab0.md

Lines changed: 103 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Lab 0: Booting
22

3-
负责助教:唐傑伟
3+
负责助教:唐傑伟 徐厚泽
44

55
本学期,我们将实现一个简单的操作系统内核。在 Lab 0 中,我们将配置好实验环境并完成 3 个实验任务。
66

@@ -42,43 +42,48 @@ cat ~/.ssh/id_rsa.pub
4242
2. 打开 Github 并登录自己的账号。
4343
3. 点击右上角头像,进入 Settings :
4444

45+
![1](lab0-1.png)
46+
4547
4. 进入页面后,在左侧选择 `SSH and GPG keys`, 在右侧点击 `New SSH Key`
4648

49+
![2](lab0-2.png)
50+
4751
5. 在框中粘贴入自己复制的公钥,点击 Add SSH key 即可。
4852

4953
### 使用 SSH 克隆仓库
5054

5155
```shell
5256
cd ~/data
53-
54-
# SSH克隆代码仓库(HTTPS克隆不稳定)
55-
git clone [email protected]:FDUCSLG/OS-24Fall-FDU.git
56-
```
5757

58-
如果出现报错:(成功克隆则进入下一步)
59-
60-
```shell
61-
Cloning into 'OS-24Fall-FDU'...
62-
[email protected]: Permission denied (publickey).
63-
fatal: Could not read from remote repository.
64-
65-
Please make sure you have the correct access rights
66-
and the repository exists.
58+
# SSH克隆代码仓库(HTTPS克隆不稳定)
59+
git clone [email protected]:FDUCSLG/OS-25Fall-FDU.git
6760
```
6861

69-
请检查前两步:在服务器上生成 SSH 和在 Github 上配置 SSH 是否正确完成。
62+
> [!info]
63+
>
64+
> 如果出现报错:(成功克隆则进入下一步)
65+
>
66+
> ```shell
67+
> Cloning into 'OS-25Fall-FDU'...
68+
> [email protected]: Permission denied (publickey).
69+
> fatal: Could not read from remote repository.
70+
> Please make sure you have the correct access rights
71+
> and the repository exists.
72+
> ```
73+
>
74+
> 请检查前两步:在服务器上生成 SSH 和在 Github 上配置 SSH 是否正确完成。
7075
7176
### 进入仓库,开始实验
7277
7378
```sh
74-
cd OS-24Fall-FDU
75-
79+
cd OS-25Fall-FDU
80+
7681
# 切换到本实验分支
7782
git checkout lab0
78-
83+
7984
# 新建一个dev分支
8085
git checkout -b lab0-dev
81-
86+
8287
# 创建build目录用来构建运行内核
8388
mkdir -p build
8489
cd build
@@ -87,7 +92,7 @@ cmake ..
8792
8893
之后每次构建运行内核只要在 `build` 目录下 `cmake .. && make qemu`
8994

90-
**更新代码仓库**
95+
### 更新代码仓库
9196

9297
本次实验中无需执行此部分,后续新实验发布后可参考此处更新代码仓库。
9398

@@ -115,30 +120,34 @@ git merge lab1-dev
115120

116121
本学期的实验将在 QEMU 模拟的 [virt 通用虚拟平台](https://www.qemu.org/docs/master/system/arm/virt.html) 上运行我们编写的内核。我们已经为大家配置好了 QEMU,运行内核只需在 `build` 目录下输入 `make qemu`
117122

118-
**QEMU 命令解析(不要求掌握)**
123+
> [!info]
124+
>
125+
> **QEMU 命令解析(不要求掌握)**
126+
>
127+
> 本学期实验所用的 QEMU 命令如下:
128+
>
129+
> ```shell
130+
> qemu-system-aarch64 -machine virt,gic-version=3 \
131+
> -cpu cortex-a72 \
132+
> -smp 4 \
133+
> -m 4096 \
134+
> -nographic \
135+
> -monitor none \
136+
> -serial mon:stdio \
137+
> -global virtio-mmio.force-legacy=false \
138+
> -kernel kernel8.elf\
139+
> ```
140+
>
141+
> * `-machine virt,gic-version=3` 指定模拟的机器类型为 `virt`(QEMU 中用于模拟虚拟的 ARM 系统的标准平台)。 `gic-version=3` 指定要使用的全局中断控制器 (GIC) 的版本为 3,用于支持更现代的中断处理功能。
142+
> * `-cpu cortex-a72` 指定模拟的 CPU 类型为 Cortex-A72(一款高性能的 ARMv8 处理器)。
143+
> * `-smp 4` 表示要模拟 4 个 CPU 核心(即 4 个对称多处理单元),后续我们的实验将涉及 SMP 的内容。
144+
> * `-nographic` 配置 QEMU 运行在无图形输出模式下,即所有的输入输出都通过命令行界面进行,而不是通过 GUI 窗口。
145+
> * `-monitor none` 禁用 QEMU 的监控控制台(QEMU monitor),避免了干扰正常的输出流。
146+
> * `-serial mon:stdio` 将虚拟机的串行端口绑定到标准输入输出(stdio),这意味着虚拟机的输出会显示在当前的终端窗口中,输入也来自终端。
147+
> * `-global virtio-mmio.force-legacy=false` 配置 QEMU 中的 VirtIO 设备(VirtIO 是一种用于加速虚拟化设备的标准)。`virtio-mmio.force-legacy=false` 禁用 VirtIO MMIO 设备的传统模式,确保这些设备使用现代化的接口。
148+
> * `-kernel kernel8.elf` 指定要加载的内核镜像文件 `kernel8.elf`。QEMU 会在虚拟机中启动这个内核,模拟其运行环境。
119149
120-
本学期实验所用的 QEMU 命令如下:
121150
122-
```shell
123-
qemu-system-aarch64 -machine virt,gic-version=3 \
124-
-cpu cortex-a72 \
125-
-smp 4 \
126-
-m 4096 \
127-
-nographic \
128-
-monitor none \
129-
-serial mon:stdio \
130-
-global virtio-mmio.force-legacy=false \
131-
-kernel kernel8.elf\
132-
```
133-
134-
* `-machine virt,gic-version=3` 指定模拟的机器类型为 `virt`(QEMU 中用于模拟虚拟的 ARM 系统的标准平台)。 `gic-version=3` 指定要使用的全局中断控制器 (GIC) 的版本为 3,用于支持更现代的中断处理功能。
135-
* `-cpu cortex-a72` 指定模拟的 CPU 类型为 Cortex-A72(一款高性能的 ARMv8 处理器)。
136-
* `-smp 4` 表示要模拟 4 个 CPU 核心(即 4 个对称多处理单元),后续我们的实验将涉及 SMP 的内容。
137-
* `-nographic` 配置 QEMU 运行在无图形输出模式下,即所有的输入输出都通过命令行界面进行,而不是通过 GUI 窗口。
138-
* `-monitor none` 禁用 QEMU 的监控控制台(QEMU monitor),避免了干扰正常的输出流。
139-
* `-serial mon:stdio` 将虚拟机的串行端口绑定到标准输入输出(stdio),这意味着虚拟机的输出会显示在当前的终端窗口中,输入也来自终端。
140-
* `-global virtio-mmio.force-legacy=false` 配置 QEMU 中的 VirtIO 设备(VirtIO 是一种用于加速虚拟化设备的标准)。`virtio-mmio.force-legacy=false` 禁用 VirtIO MMIO 设备的传统模式,确保这些设备使用现代化的接口。
141-
* `-kernel kernel8.elf` 指定要加载的内核镜像文件 `kernel8.elf`。QEMU 会在虚拟机中启动这个内核,模拟其运行环境。
142151
143152
**`qemu` 的退出方法为: `Ctrl+A`,松开后按 `x`**
144153
@@ -156,10 +165,10 @@ qemu-system-aarch64 -machine virt,gic-version=3 \
156165
157166
## 5. 操作系统
158167
159-
本课程的先修课程包括但不限于 COMP120006 程序设计,COMP130201计算机系统基础,COMP130191 计算机组成与体系结构。
168+
本课程的先修课程包括但不限于**程序设计****计算机系统基础****计算机组成与体系结构**
160169
161-
* **COMP120006 程序设计**中,我们学习了用 C 语言编写**用户态**程序。**用户态**这一概念是相较于操作系统的**内核态**而言的,本课程实验的重心即为内核态编程。在后续的实验中,我们将逐步理解(1)用户态/内核态的定义;(2)划分用户态/内核态的意义;(3)用户态与内核态的交互等问题。
162-
* **COMP130201 计算机系统基础****COMP130191 计算机组成与体系结构**中,我们初步认识了计算机系统中的各类硬件组成,如 CPU,内存,缓存,外设等等。操作系统内核的作用在于**管理并虚拟化(或者说抽象)这些资源** \[1]。在后续的实验中,我们将逐步理解虚拟化这一概念。
170+
* **程序设计**中,我们学习了用 C 语言编写**用户态**程序。**用户态**这一概念是相较于操作系统的**内核态**而言的,本课程实验的重心即为内核态编程。在后续的实验中,我们将逐步理解(1)用户态/内核态的定义;(2)划分用户态/内核态的意义;(3)用户态与内核态的交互等问题。
171+
* **计算机系统基础****计算机组成与体系结构**中,我们初步认识了计算机系统中的各类硬件组成,如 CPU,内存,缓存,外设等等。操作系统内核的作用在于**管理并虚拟化(或者说抽象)这些资源** \[1]。在后续的实验中,我们将逐步理解虚拟化这一概念。
163172
* 除此以外,一系列软件开发实践中的重要问题,如并发和持久化,也将由操作系统中的特定的机制或策略解决。
164173
165174
换而言之,操作系统是「硬件资源」与「用户态程序」的中间层,负责管理用户态程序对于硬件资源的访问(随着学习的深入,我们将对这一命题做出补充)。
@@ -168,27 +177,31 @@ qemu-system-aarch64 -machine virt,gic-version=3 \
168177
169178
`build` 目录中执行 `make qemu` 后,构建系统将自动编译操作系统内核并启动 QEMU 运行内核。下面我们将介绍本实验操作系统内核的启动流程。
170179
171-
**入口:梦开始的地方**
180+
### 入口:梦开始的地方
172181
173182
我们通过链接器脚本(请见第7节)指定内核的入口为 `_start (src/start.S:28)` 函数。
174183
175-
**为什么入口函数不是 `main` 函数?**
176-
177-
在学习用户态 C 语言编程时,我们认为 `main` 函数是程序的入口。然而,这一说法省略了编译层面的一些重要细节,编译器会在 `main` 函数插入一系列初始化代码,因而 `main` 函数不是函数严格意义上的入口(相关背景知识请自行回顾 COMP130201 计算机系统基础)。同样,尽管我们的内核定义了 `main` 函数,我们仍需要将入口设为 `_start` 函数并完成一些必要的准备动作。
184+
> [!note]
185+
>
186+
> **为什么入口函数不是 `main` 函数?**
187+
>
188+
> 在学习用户态 C 语言编程时,我们认为 `main` 函数是程序的入口。然而,这一说法省略了编译层面的一些重要细节,编译器会在 `main` 函数插入一系列初始化代码,因而 `main` 函数不是函数严格意义上的入口(相关背景知识请自行回顾**计算机系统基础**)。同样,尽管我们的内核定义了 `main` 函数,我们仍需要将入口设为 `_start` 函数并完成一些必要的准备动作。
189+
>
190+
> 目前,我们无需深究 `_start` 函数中的细节,具体内容我们会在合适的时机提供解读文档。
178191
179-
目前,我们无需深究 `_start` 函数中的细节,具体内容我们会在合适的时机提供解读文档。
180-
181-
**`main`:内核,启动!**
192+
### `main`:内核,启动!
182193
183194
`_start` 函数最终会跳转到 `main` 函数。在 `main` 函数中,我们将完成内核的初始化工作。至此,我们已经成功启动了我们的内核。
184195
185196
目前,`main` 函数中的主要内容是一个分支判断,其作用是区分 CPU 0 和其余核心的初始化逻辑。下面我们具体介绍之。
186197
187-
**对称多处理器(SMP)**
198+
### 对称多处理器(SMP)
188199
189200
现代计算机广泛采用多处理器架构,感兴趣的同学可以通过 `lscpu` 命令查看我们服务器的处理器信息。同样地,我们的内核运行在 QEMU 虚拟出来的 4 核机器上。这 4 个核心可以并发(或者说并行,在这里我们暂不严格区分这两个概念)地执行不同的指令流。
190201
191-
**注意**:以下内容可能理解难度较高,因此为了完成实验任务,请着重注意加粗字体。此外,欢迎在 Github Issues 区提出问题。随着本课程实验的进行,同学们将逐步加深对这些问题的理解。
202+
> [!warning]
203+
>
204+
> **注意**:以下内容可能理解难度较高,因此为了完成实验任务,请着重注意加粗字体。此外,欢迎在 Github Issues 区提出问题。随着本课程实验的进行,同学们将逐步加深对这些问题的理解。
192205
193206
在我们的实验平台上,机器启动时仅有一个核(CPU 0)处于唤醒状态。作为唯一一个唤醒的核心,其主要负责:
194207
@@ -200,40 +213,45 @@ qemu-system-aarch64 -machine virt,gic-version=3 \
200213
* CPU 0 启动核心,并完成一系列初始化动作,最后放行其他核心。
201214
* 其余核心被 CPU 0 唤醒后,等待 CPU 0 完成初始化动作并放行。
202215
203-
**任务 1**
204-
205-
CPU 0 完成内核服务初始化后,打印 "Hello, world! (Core 0)" (提示:上面介绍了如何打印字符串)。
206-
207-
**任务 2**
208-
209-
其余 CPU 被放行后,各自打印 "Hello, world! (Core \<cpuid>)" (提示:上面介绍了如何打印字符串)。
210-
211-
**预期输出**(后面三行的顺序可变)
212-
213-
```shell
214-
Hello, world! (Core 0)
215-
Hello, world! (Core 2)
216-
Hello, world! (Core 3)
217-
Hello, world! (Core 1)
218-
```
216+
> [!important]
217+
> **任务 1**
218+
>
219+
> CPU 0 完成内核服务初始化后,打印 "Hello, world! (Core 0)" (提示:上面介绍了如何打印字符串)。
220+
>
221+
> **任务 2**
222+
>
223+
> 其余 CPU 被放行后,各自打印 "Hello, world! (Core \<cpuid>)" (提示:上面介绍了如何打印字符串)。
224+
225+
> [!info]
226+
>
227+
> **预期输出**(后面三行的顺序可变)
228+
>
229+
> ```shell
230+
> Hello, world! (Core 0)
231+
> Hello, world! (Core 2)
232+
> Hello, world! (Core 3)
233+
> Hello, world! (Core 1)
234+
> ```
219235
220236
## 7. 链接器脚本
221237
222238
链接器脚本位于 `src/linker.ld`
223239
224240
我们执行 `make qemu` 编译内核并启动QEMU,编译内核得到的产物是一个 ELF 文件。此 ELF 文件同我们之前用户态程序的 ELF 文件一样,具有 `text``rodata``bss` 等段,而链接器脚本规定了这些段在内存中的位置。
225241
226-
**复习**
227-
228-
如果对 ELF 文件的结构及上述 `text``rodata``bss` 等段的性质不熟悉,请复习 COMP130201 计算机系统基础所学相关内容。
242+
> [!caution]
243+
>
244+
> **复习**
245+
>
246+
> 如果对 ELF 文件的结构及上述 `text``rodata``bss` 等段的性质不熟悉,请复习 **计算机系统基础** 所学相关内容。
229247
230248
此外,链接器脚本还暴露了一系列符号,用于描述内存地址。举例如下:
231249
232250
```linker-script
233251
PROVIDE(data = .);
234252
.data : AT(ADDR(.data) - 0xFFFF000000000000) {
235-
*(.data)
236-
*(.data.*)
253+
*(.data)
254+
*(.data.*)
237255
}
238256
PROVIDE(edata = .);
239257
```
@@ -251,21 +269,21 @@ extern char data[], edata[];
251269
printf("data is %p; edata is %p", (void*)data, (void*)edata);
252270
```
253271
254-
255-
**任务 3**
256-
257-
按照惯例 BSS 段应当置零(为什么?请复习 COMP130201 计算机系统基础或咨询助教)。然而,当操作系统内核被装载到内存中时,BSS 段对应的内存无法保证是置零状态(因为很多时候没有比操作系统更高一级的有关方面来负责“打扫”内存,此时操作系统内核本身就是内存的管理者)。因此,请在CPU 0 进行内核初始化前增加**清零 BSS 段**的逻辑。
258-
259-
**提示 1**: 清零 BSS 段首先需要获取 BSS 段起始和终止的地址,上面已经演示了如何获取某段的起始和终止地址。
260-
261-
**提示 2**: 查找 `memset` 函数,使用此函数清零一段连续的内存空间。
262-
272+
> [!important]
273+
>
274+
> **任务 3**
275+
>
276+
> 按照惯例 BSS 段应当置零(为什么?请复习计算机系统基础或咨询助教)。然而,当操作系统内核被装载到内存中时,BSS 段对应的内存无法保证是置零状态(因为很多时候没有比操作系统更高一级的有关方面来负责“打扫”内存,此时操作系统内核本身就是内存的管理者)。因此,请在CPU 0 进行内核初始化前增加**清零 BSS 段**的逻辑。
277+
>
278+
> **提示 1**: 清零 BSS 段首先需要获取 BSS 段起始和终止的地址,上面已经演示了如何获取某段的起始和终止地址。
279+
>
280+
> **提示 2**: 查找 `memset` 函数,使用此函数清零一段连续的内存空间。
263281
264282
## 8. 提交
265283
266284
**提交方式**:将实验报告提交到 elearning 上,格式为`学号-lab0.pdf`。本次实验中,**报告不计分**。
267285
268-
**截止时间**: **9 月 13 日 23:59**。逾期提交将扣除部分分数。
286+
**截止时间**: **9 月 19 日 23:59**。逾期提交将扣除部分分数。
269287
270288
报告中可以包括下面内容
271289

0 commit comments

Comments
 (0)