0intro.rst 12.3 KB
Newer Older
Y
Yifan Wu 已提交
1 2 3
引言
==============================

Y
Yifan Wu 已提交
4
本章导读
chyyuu1972's avatar
chyyuu1972 已提交
5
-------------------------------
Y
Yifan Wu 已提交
6

chyyuu1972's avatar
chyyuu1972 已提交
7 8 9
..
  chyyuu:有一个ascii图,画出我们做的OS。

chyyuu1972's avatar
chyyuu1972 已提交
10 11 12 13 14 15 16 17

本章展现了操作系统一系列功能:

- 通过动态内存分配,提高了应用程序对内存的动态使用效率
- 通过页表的虚实内存映射机制,简化了编译器对应用的地址空间设置
- 通过页表的虚实内存映射机制,加强了应用之间,应用与内核之间的内存隔离,增强了系统安全
- 通过页表的虚实内存映射机制,可以实现空分复用(提出,但没有实现)

Y
Yifan Wu 已提交
18 19 20
.. _term-illusion:
.. _term-time-division-multiplexing:
.. _term-transparent:
chyyuu1972's avatar
chyyuu1972 已提交
21

chyyuu1972's avatar
chyyuu1972 已提交
22
上一章,我们分别实现了多道程序和分时多任务系统,它们的核心机制都是任务切换。由于多道程序和分时多任务系统的设计初衷不同,它们在任务切换的时机和策略也不同。有趣的一点是,任务切换机制对于应用是完全 **透明** (Transparent) 的,应用可以不对内核实现该机制的策略做任何假定(除非要进行某些针对性优化),甚至可以完全不知道这机制的存在。
chyyuu1972's avatar
chyyuu1972 已提交
23

chyyuu1972's avatar
chyyuu1972 已提交
24 25 26
在大多数应用(也就是应用开发者)的视角中,它们会独占一整个 CPU 和特定(连续或不连续)的内存空间。当然,通过上一章的学习,我们知道在现代操作系统中,出于公平性的考虑,我们极少会让独占CPU这种情况发生。所以应用自认为的独占CPU只是内核想让应用看到的一种 **幻象** (Illusion) ,而 CPU 计算资源被 **时分复用** (TDM, Time-Division Multiplexing) 的实质被内核通过恰当的抽象隐藏了起来,对应用不可见。

与之相对,我们目前还没有对内存管理功能进行有效的管理,仅仅是把程序放到某处的物理内存中。在内存访问方面,所有的应用都直接通过物理地址访问物理内存,这使得应用开发者需要了解繁琐的物理地址空间布局,访问内存也很不方便。在上一章中,出于任务切换的需要,所有的应用都在初始化阶段被加载到内存中并同时驻留下去直到它们全部运行结束。而且,所有的应用都直接通过物理地址访问物理内存。这会带来以下问题:
Y
Yifan Wu 已提交
27

28 29 30
- 首先,内核提供给应用的内存访问接口不够透明,也不好用。由于应用直接访问物理内存,这需要它在构建的时候就需要规划自己需要被加载到哪个地址运行。为了避免冲突可能还需要应用的开发者们对此进行协商,这显然是一件在今天看来不可理喻且极端麻烦的事情。
- 其次,内核并没有对应用的访存行为进行任何保护措施,每个应用都有整块物理内存的读写权力。即使应用被限制在 U 特权级下运行,它还是能够造成很多麻烦:比如它可以读写其他应用的数据来窃取信息或者破坏它的正常运行;甚至它还可以修改内核的代码段来替换掉原本的 ``trap_handler`` 来挟持内核执行恶意代码。总之,这造成系统既不安全、也不稳定。
- 再次,目前应用的内存使用空间在其运行前已经限定死了,内核不能灵活地给应用程序提供的运行时动态可用内存空间。比如一个应用结束后,这个应用所占的空间就被释放了,但这块空间无法动态地给其它还在运行的应用使用。
chyyuu1972's avatar
chyyuu1972 已提交
31

chyyuu1972's avatar
chyyuu1972 已提交
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
因此,为了防止应用胡作非为,本章将更好的管理物理内存,并提供给应用一个抽象出来的更加透明易用、也更加安全的访存接口,这就是基于分页机制的虚拟内存。站在应用程序运行的角度看,就是存在一个从“0”地址开始的非常大的可读/可写/可执行的地址空间(Address Space)。

实现地址空间的第一步就是实现分页机制,建立好虚拟内存和物理内存的页映射关系。此过程涉及硬件细节,不同的地址映射关系组合,相对比较复杂。总体而言,我们需要思考如下问题:

- 硬件中物理内存的范围是什么?
- 哪些物理内存空间需要建立页映射关系?
- 如何建立页表使能分页机制?
- 如何确保OS能够在分页机制使能前后的不同时间段中都能正常寻址和执行代码?
- 页目录表(一级)的起始地址设置在哪里?
- 二级/三级等页表的起始地址设置在哪里,需要多大空间?
- 如何设置页目录表项的内容?
- 如何设置其它页表项的内容?
- 如果要让每个任务有自己的地址空间,那每个任务是否要有自己的页表?
- 代表应用程序的任务和操作系统需要有各自的页表吗?
- 在有了页表之后,任务和操作系统之间应该如何传递数据?
chyyuu1972's avatar
chyyuu1972 已提交
47

chyyuu1972's avatar
chyyuu1972 已提交
48
如果能解决上述问题,我们就能设计实现具有超强防护能力的侏罗纪“头甲龙”操作系统。并可更好地理解地址空间,虚拟地址等操作系统的抽象概念与操作系统的虚存具体实现之间的联系。
chyyuu1972's avatar
chyyuu1972 已提交
49 50 51

..
  chyyuu:在哪里讲解虚存的设计与实现???
Y
Yifan Wu 已提交
52 53


Y
Yifan Wu 已提交
54
实践体验
chyyuu1972's avatar
chyyuu1972 已提交
55
-----------------------
Y
Yifan Wu 已提交
56 57

本章的应用和上一章相同,只不过由于内核提供给应用的访存接口被替换,应用的构建方式发生了变化,这方面在下面会深入介绍。
chyyuu1972's avatar
chyyuu1972 已提交
58
因此应用运行起来的效果与上一章是一致的。
Y
Yifan Wu 已提交
59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74

获取本章代码:

.. code-block:: console

   $ git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
   $ cd rCore-Tutorial-v3
   $ git checkout ch4

在 qemu 模拟器上运行本章代码:

.. code-block:: console

   $ cd os
   $ make run

Y
Yifan Wu 已提交
75
将 Maix 系列开发板连接到 PC,并在上面运行本章代码:
Y
Yifan Wu 已提交
76 77 78 79 80 81 82 83 84 85

.. code-block:: console

   $ cd os
   $ make run BOARD=k210

如果顺利的话,我们将看到和上一章相同的运行结果(以 K210 平台为例):

.. code-block::

Y
Yifan Wu 已提交
86
   [rustsbi] RustSBI version 0.1.1
Y
Yifan Wu 已提交
87 88 89 90 91 92 93
   .______       __    __      _______.___________.  _______..______   __
   |   _  \     |  |  |  |    /       |           | /       ||   _  \ |  |
   |  |_)  |    |  |  |  |   |   (----`---|  |----`|   (----`|  |_)  ||  |
   |      /     |  |  |  |    \   \       |  |      \   \    |   _  < |  |
   |  |\  \----.|  `--'  |.----)   |      |  |  .----)   |   |  |_)  ||  |
   | _| `._____| \______/ |_______/       |__|  |_______/    |______/ |__|

Y
Yifan Wu 已提交
94
   [rustsbi] Platform: K210 (Version 0.1.0)
Y
Yifan Wu 已提交
95
   [rustsbi] misa: RV64ACDFIMSU
Y
Yifan Wu 已提交
96
   [rustsbi] mideleg: 0x22
Y
Yifan Wu 已提交
97 98 99 100 101
   [rustsbi] medeleg: 0x1ab
   [rustsbi] Kernel entry: 0x80020000
   [kernel] Hello, world!
   .text [0x80020000, 0x8002b000)
   .rodata [0x8002b000, 0x8002e000)
Y
Yifan Wu 已提交
102 103
   .data [0x8002e000, 0x8004c000)
   .bss [0x8004c000, 0x8035d000)
Y
Yifan Wu 已提交
104 105 106 107 108 109 110 111 112
   mapping .text section
   mapping .rodata section
   mapping .data section
   mapping .bss section
   mapping physical memory
   [kernel] back to world!
   remap_test passed!
   init TASK_MANAGER
   num_app = 4
Y
Yifan Wu 已提交
113
   power_3 [10000/300000power_5 [10000/210000]
Y
Yifan Wu 已提交
114 115
   power_5 [20000/210000]
   power_5 [30000/210000]
Y
Yifan Wu 已提交
116
   power_7 [10000/240000]
Y
Yifan Wu 已提交
117 118
   power_7 [20000/240000]
   power_7 [30000/240000]
Y
Yifan Wu 已提交
119 120 121
   ]
   power_3 [20000/300000]
   power_3 [30000/300000]
Y
Yifan Wu 已提交
122
   power_3 [40000/300000]
Y
Yifan Wu 已提交
123 124 125 126 127 128
   power_5 [40000/210000]
   power_5 [50000/210000]
   power_5 [60000/210000]
   power_7 [40000/240000]
   power_7 [50000/240000]
   power_7 [60000/240000]
Y
Yifan Wu 已提交
129 130 131 132 133 134 135 136 137 138 139 140
   power_3 [50000/300000]
   power_3 [60000/300000]
   power_3 [70000/300000]
   power_5 [70000/210000]
   power_5 [80000/210000]
   power_5 [90000/210000]
   power_7 [70000/240000]
   power_7 [80000/240000]
   power_7 [90000/240000]
   power_3 [80000/300000]
   power_3 [90000/300000]
   power_3 [100000/300000]
Y
Yifan Wu 已提交
141
   power_5 [100000/210000]
Y
Yifan Wu 已提交
142 143 144 145 146
   power_5 [110000/210000]
   power_5 [120000/210000]
   power_7 [100000/240000]
   power_7 [110000/240000]
   power_7 [120000/240000]
Y
Yifan Wu 已提交
147 148 149 150 151 152
   power_3 [110000/300000]
   power_3 [120000/300000]
   power_3 [130000/300000]
   power_5 [130000/210000]
   power_5 [140000/210000]
   power_5 [150000/210000]
Y
Yifan Wu 已提交
153
   power_7 [130000/240000]
Y
Yifan Wu 已提交
154 155
   power_7 [140000/240000]
   power_7 [150000/240000]
Y
Yifan Wu 已提交
156 157 158 159 160 161 162 163 164
   power_3 [140000/300000]
   power_3 [150000/300000]
   power_3 [160000/300000]
   power_5 [160000/210000]
   power_5 [170000/210000]
   power_5 [180000/210000]
   power_7 [160000/240000]
   power_7 [170000/240000]
   power_7 [180000/240000]
Y
Yifan Wu 已提交
165
   power_3 [170000/300000]
Y
Yifan Wu 已提交
166 167
   power_3 [180000/300000]
   power_3 [190000/300000]
Y
Yifan Wu 已提交
168
   power_5 [190000/210000]
Y
Yifan Wu 已提交
169 170
   power_5 [200000/210000]
   power_5 [210000/210000]
Y
Yifan Wu 已提交
171 172 173 174 175 176 177
   5^210000power_7 [190000/240000]
   power_7 [200000/240000]
   power_7 [210000/240000]
   power_3 [200000/300000]
   power_3 [210000/300000]
   power_3 [220000/300000]
   = 527227302(mod 998244353)
Y
Yifan Wu 已提交
178 179 180 181 182 183 184
   Test power_5 OK!
   [kernel] Application exited with code 0
   power_3 [230000/300000]
   power_3 [240000/300000]
   power_3 [250000/300000]
   power_7 [220000/240000]
   power_7 [230000/240000]
Y
Yifan Wu 已提交
185 186
   power_7 [240000/240000]
   7^240000 = 304164893power_3 [260000/300000]
Y
Yifan Wu 已提交
187 188
   power_3 [270000/300000]
   power_3 [280000/300000]
Y
Yifan Wu 已提交
189
   (mod 998244353)
Y
Yifan Wu 已提交
190 191
   Test power_7 OK!
   [kernel] Application exited with code 0
Y
Yifan Wu 已提交
192 193 194
   power_3 [290000/300000]
   power_3 [300000/300000]
   3^300000 = 612461288(mod 998244353)
Y
Yifan Wu 已提交
195 196 197 198 199
   Test power_3 OK!
   [kernel] Application exited with code 0
   Test sleep OK!
   [kernel] Application exited with code 0
   [kernel] Panicked at src/task/mod.rs:112 All applications completed!
Y
Yifan Wu 已提交
200
   [rustsbi] reset triggered! todo: shutdown all harts on k210; program halt. Type: 0, reason: 0
201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218

本章代码树
-----------------------------------------------------

.. code-block::
  :linenos:
  :emphasize-lines: 52

  ├── bootloader
  │   ├── rustsbi-k210.bin
  │   └── rustsbi-qemu.bin
  ├── LICENSE
  ├── os
  │   ├── build.rs
  │   ├── Cargo.lock
  │   ├── Cargo.toml
  │   ├── Makefile
  │   └── src
219
  │       ├── config.rs(修改:新增一些内存管理的相关配置)
220 221 222 223
  │       ├── console.rs
  │       ├── entry.asm
  │       ├── lang_items.rs
  │       ├── link_app.S
224 225 226
  │       ├── linker-k210.ld(修改:将跳板页引入内存布局)
  │       ├── linker-qemu.ld(修改:将跳板页引入内存布局)
  │       ├── loader.rs(修改:仅保留获取应用数量和数据的功能)
227
  │       ├── main.rs(修改)
228 229 230 231 232 233 234
  │       ├── mm(新增:内存管理的 mm 子模块)
  │       │   ├── address.rs(物理/虚拟 地址/页号的 Rust 抽象)
  │       │   ├── frame_allocator.rs(物理页帧分配器)
  │       │   ├── heap_allocator.rs(内核动态内存分配器)
  │       │   ├── memory_set.rs(引入地址空间 MemorySet 及逻辑段 MemoryArea 等)
  │       │   ├── mod.rs(定义了 mm 模块初始化方法 init)
  │       │   └── page_table.rs(多级页表抽象 PageTable 以及其他内容)
235 236
  │       ├── sbi.rs
  │       ├── syscall
237
  │       │   ├── fs.rs(修改:基于地址空间的 sys_write 实现)
238 239 240
  │       │   ├── mod.rs
  │       │   └── process.rs
  │       ├── task
241
  │       │   ├── context.rs(修改:构造一个跳转到不同位置的初始任务上下文)
242 243 244 245 246 247
  │       │   ├── mod.rs(修改,详见文档)
  │       │   ├── switch.rs
  │       │   ├── switch.S
  │       │   └── task.rs(修改,详见文档)
  │       ├── timer.rs
  │       └── trap
248 249 250
  │           ├── context.rs(修改:在 Trap 上下文中加入了更多内容)
  │           ├── mod.rs(修改:基于地址空间修改了 Trap 机制,详见文档)
  │           └── trap.S(修改:基于地址空间修改了 Trap 上下文保存与恢复汇编代码)
251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271
  ├── README.md
  ├── rust-toolchain
  ├── tools
  │   ├── kflash.py
  │   ├── LICENSE
  │   ├── package.json
  │   ├── README.rst
  │   └── setup.py
  └── user
      ├── build.py(移除)
      ├── Cargo.toml
      ├── Makefile
      └── src
          ├── bin
          │   ├── 00power_3.rs
          │   ├── 01power_5.rs
          │   ├── 02power_7.rs
          │   └── 03sleep.rs
          ├── console.rs
          ├── lang_items.rs
          ├── lib.rs
272
          ├── linker.ld(修改:将所有应用放在各自地址空间中固定的位置)
273
          └── syscall.rs