Skip to content

Commit 7a69a07

Browse files
committed
ch4:注释代码2
1 parent 0655f41 commit 7a69a07

File tree

9 files changed

+137
-46
lines changed

9 files changed

+137
-46
lines changed

os/src/linker.ld

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,10 @@ SECTIONS
1111
.text : {
1212
*(.text.entry)
1313
. = ALIGN(4K);
14-
strampoline = .;
15-
*(.text.trampoline);
14+
strampoline = .; /* 定义符号 strampoline 指向 trampoline 段起始 */
15+
*(.text.trampoline); /* 放置 trampoline 代码 (__alltraps, __restore) */
1616
. = ALIGN(4K);
17-
*(.text .text.*)
17+
*(.text .text.*) /* 放置其余内核代码 */
1818
}
1919

2020
. = ALIGN(4K);

os/src/mm/frame_allocator.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@ trait FrameAllocator {
4949
}
5050
/// an implementation for frame allocator
5151
pub struct StackFrameAllocator {
52-
current: usize,
53-
end: usize,
52+
current: usize, // 起始页号
53+
end: usize, // 结束页号
5454
recycled: Vec<usize>,
5555
}
5656

os/src/mm/memory_set.rs

Lines changed: 88 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ lazy_static! {
3333
pub static ref KERNEL_SPACE: Arc<UPSafeCell<MemorySet>> =
3434
Arc::new(unsafe { UPSafeCell::new(MemorySet::new_kernel()) });
3535
}
36-
/// address space
36+
/// 地址空间
3737
pub struct MemorySet {
3838
page_table: PageTable,
3939
areas: Vec<MapArea>,
@@ -63,22 +63,31 @@ impl MemorySet {
6363
None,
6464
);
6565
}
66+
///@ push 将一个MapArea push到自己的areas,如果有数据就写入数据到内存。
67+
/// 把MapArea内的每个都页都放入页表(map)
68+
/// 对于每个应用程序,其trampoline虚拟地址对应的物理页号也都放入了自己的地址空间,这样它才能跳转到trap处理程序
6669
fn push(&mut self, mut map_area: MapArea, data: Option<&[u8]>) {
6770
map_area.map(&mut self.page_table);
6871
if let Some(data) = data {
6972
map_area.copy_data(&mut self.page_table, data);
7073
}
7174
self.areas.push(map_area);
7275
}
73-
/// Mention that trampoline is not collected by areas.
76+
/// 将跳板页面映射到地址空间.
77+
/// 注意: 跳板页面不由 MemoryArea 管理.
7478
fn map_trampoline(&mut self) {
79+
80+
//@ 将TRAMPOLINE->strampoline放入页表
7581
self.page_table.map(
82+
// 虚拟地址: TRAMPOLINE (最高虚拟页,说最高是因为 TRAMPOLINE 定义为 usizeMAX - 1Page)
7683
VirtAddr::from(TRAMPOLINE).into(),
84+
// 物理地址: strampoline 符号的地址 (代码所在物理页帧)
7785
PhysAddr::from(strampoline as usize).into(),
86+
// 权限: 可读 (R) | 可执行 (X)
7887
PTEFlags::R | PTEFlags::X,
7988
);
8089
}
81-
/// Without kernel stacks.
90+
///@ 建立页表恒等映射 (Identity Mapping)
8291
pub fn new_kernel() -> Self {
8392
let mut memory_set = Self::new_bare();
8493
// map trampoline
@@ -94,8 +103,10 @@ impl MemorySet {
94103
info!("mapping .text section");
95104
memory_set.push(
96105
MapArea::new(
106+
// 指定虚拟地址范围
97107
(stext as usize).into(),
98108
(etext as usize).into(),
109+
// 指定恒等映射
99110
MapType::Identical,
100111
MapPermission::R | MapPermission::X,
101112
),
@@ -132,6 +143,7 @@ impl MemorySet {
132143
None,
133144
);
134145
info!("mapping physical memory");
146+
// ekernel 到 MEMORY_END 的映射,使内核可以用简单的、与物理地址相同的虚拟地址来访问任意物理内存(例如,用于物理页帧分配、访问设备寄存器等)。
135147
memory_set.push(
136148
MapArea::new(
137149
(ekernel as usize).into(),
@@ -141,26 +153,49 @@ impl MemorySet {
141153
),
142154
None,
143155
);
156+
// 上述一通push完成,当系统启用分页后:
157+
// CPU 产生的任何虚拟地址,如果落在了 .text, .rodata, .data, .bss 或 ekernel 到 MEMORY_END 的范围内,
158+
// MMU 通过查询内核页表进行地址转换时,找到的物理地址与该虚拟地址完全相同。
159+
// 恒等映射也保证了我们启动分页的平滑过渡:启动分页前最后一条指令和启动分页后第一条指令连续。
160+
161+
//? 问题:恒等映射的这些页表占多少空间?
162+
// 含这个问题的上下文的ai: https://gemini.google.com/app/51ce53f706953582
144163
memory_set
145164
}
146165
/// Include sections in elf and trampoline and TrapContext and user stack,
147166
/// also returns user_sp_base and entry point.
167+
/// 解析 ELF 文件,根据其中的信息创建用户程序的代码、数据、栈、Trap 上下文等内存区域,
168+
/// 并建立这些区域对应的页表映射。
169+
/// 返回一个元组,包含:
170+
/// - 构建好的 MemorySet 实例,代表用户程序的地址空间。
171+
/// - 用户栈的栈顶虚拟地址,作为用户程序启动时的栈指针。
172+
/// - 用户程序的入口点虚拟地址,即程序开始执行的第一条指令的地址。
148173
pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
149174
let mut memory_set = Self::new_bare();
150175
// map trampoline
176+
// 跳板页用于用户态和内核态之间的切换。它在所有用户地址空间中映射到TRAMPOLINE位置
151177
memory_set.map_trampoline();
178+
152179
// map program headers of elf, with U flag
180+
// 使用 xmas_elf 库解析输入的 ELF 文件数据。
153181
let elf = xmas_elf::ElfFile::new(elf_data).unwrap();
154182
let elf_header = elf.header;
155183
let magic = elf_header.pt1.magic;
184+
// 检查 ELF 文件头的 magic number ([0x7f, 'E', 'L', 'F']),确保是有效的 ELF 格式。
156185
assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
157186
let ph_count = elf_header.pt2.ph_count();
158187
let mut max_end_vpn = VirtPageNum(0);
188+
189+
// 遍历 ELF 文件中的程序头 (Program Headers),程序头描述了 ELF 文件中各个段(如代码段、数据段)应该如何被加载到内存中。
159190
for i in 0..ph_count {
160191
let ph = elf.program_header(i).unwrap();
192+
// 只处理类型为 'Load' 的程序头,其要被加载到虚拟地址空间并建立映射的。
161193
if ph.get_type().unwrap() == xmas_elf::program::Type::Load {
194+
// 获取该加载段在虚拟地址空间中的起始地址和结束地址 (基于其内存大小)。
162195
let start_va: VirtAddr = (ph.virtual_addr() as usize).into();
163196
let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into();
197+
198+
// 根据 ELF 段的标志 (flags) 确定该内存区域的访问权限。
164199
let mut map_perm = MapPermission::U;
165200
let ph_flags = ph.flags();
166201
if ph_flags.is_read() {
@@ -172,20 +207,38 @@ impl MemorySet {
172207
if ph_flags.is_execute() {
173208
map_perm |= MapPermission::X;
174209
}
210+
// 创建一个 MapArea 实例,添加到 MemorySet 中,并提供段的初始数据。
175211
let map_area = MapArea::new(start_va, end_va, MapType::Framed, map_perm);
176-
max_end_vpn = map_area.vpn_range.get_end();
212+
max_end_vpn = map_area.vpn_range.get_end(); // 最大虚拟页号
213+
// push 方法会执行以下操作:
214+
// - 调用 map_area.map():遍历 MapArea 范围内的虚拟页,
215+
// 为每个页分配物理页帧 (因为 MapType::Framed),并在页表中建立 vpn -> ppn 的映射。
216+
// - 调用 map_area.copy_data():将 ELF 文件中该段的内容复制到刚刚分配的物理页帧中。
217+
// - 将 map_area 添加到 memory_set.areas 列表中。
218+
177219
memory_set.push(
178-
map_area,
179-
Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize]),
220+
map_area, // data: Some(&[u8])
221+
Some(&elf.input[ph.offset() as usize .. (ph.offset() + ph.file_size()) as usize]),
180222
);
181223
}
182224
}
183225
// map user stack with U flags
226+
// 映射用户栈区域。
184227
let max_end_va: VirtAddr = max_end_vpn.into();
185228
let mut user_stack_bottom: usize = max_end_va.into();
186229
// guard page
230+
// 在用户栈和 ELF 段之间留出一个页大小的未映射或特殊映射区域。
231+
// 这样,如果用户栈溢出,触碰到这个 Guard Page 会立即触发页错误,而不是覆盖到前面的代码/数据段。
232+
// 这里通过简单地将栈底地址跳过一个页大小PAGE_SIZE来实现,Guard Page 本身没有被 push 到 areas 中,因此是未映射的。
187233
user_stack_bottom += PAGE_SIZE;
234+
235+
// 计算用户栈的栈顶地址 (栈从高地址向低地址增长,所以栈顶地址 > 栈底地址)
188236
let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
237+
238+
// 创建并映射用户栈的 MapArea。
239+
// 映射类型为 Framed (需要分配新的物理页帧作为栈空间)。
240+
// 权限为 用户可读写 (R | W | U),用户栈不需要可执行。
241+
// 初始数据为 None,因为栈内容由程序运行时动态生成。
189242
memory_set.push(
190243
MapArea::new(
191244
user_stack_bottom.into(),
@@ -195,17 +248,22 @@ impl MemorySet {
195248
),
196249
None,
197250
);
198-
// used in sbrk
251+
// 映射 sbrk 区域 (用于支持动态堆扩展)。
252+
// 通常在用户栈之后放置一个零大小的 MapArea,标记动态内存分配(如堆)的起始位置。
253+
// 用户程序通过 sbrk 等系统调用请求更多内存时,OS 会扩展这个 MapArea
199254
memory_set.push(
200255
MapArea::new(
201-
user_stack_top.into(),
202-
user_stack_top.into(),
256+
user_stack_top.into(), // sbrk 区域起始地址,通常紧随用户栈顶
257+
user_stack_top.into(), // sbrk 区域结束地址,初始大小为 0
203258
MapType::Framed,
204-
MapPermission::R | MapPermission::W | MapPermission::U,
259+
MapPermission::R | MapPermission::W | MapPermission::U, // 用户可读写权限
205260
),
206261
None,
207262
);
208-
// map TrapContext
263+
// 映射 Trap 上下文 (TrapContext) 区域。
264+
// TrapContext 用于在用户/内核模式切换时保存用户态的 CPU 寄存器状态。
265+
// 它被放置在用户地址空间中一个固定的高地址 (TRAP_CONTEXT_BASE),紧邻跳板下方。
266+
// 这样在内核处理 Trap 时,可以通过一个固定的虚拟地址访问到保存的用户上下文。
209267
memory_set.push(
210268
MapArea::new(
211269
TRAP_CONTEXT_BASE.into(),
@@ -215,16 +273,22 @@ impl MemorySet {
215273
),
216274
None,
217275
);
276+
// 返回构建好的 MemorySet、初始用户栈顶地址和程序入口点。
277+
// 操作系统调度器在第一次运行这个用户进程时会使用这些信息:
278+
// - 将 memory_set 的根页表地址加载到 satp 寄存器。
279+
// - 将 user_stack_top 加载到用户栈指针寄存器 (如 sp)。
280+
// - 将 entry_point 加载到程序计数器 (PC),开始执行用户程序。
218281
(
219282
memory_set,
220283
user_stack_top,
221-
elf.header.pt2.entry_point() as usize,
284+
elf.header.pt2.entry_point() as usize, // 用户进程执行的第一个指令地址
222285
)
223286
}
224287
/// Change page table by writing satp CSR Register.
225288
pub fn activate(&self) {
226289
let satp = self.page_table.token();
227290
unsafe {
291+
// 从执行 satp::write 指令的时刻起,SV39 分页模式就被启用了,MMU 开始使用内核地址空间的多级页表进行后续的地址转换。
228292
satp::write(satp);
229293
asm!("sfence.vma");
230294
}
@@ -287,23 +351,23 @@ impl MapArea {
287351
map_perm,
288352
}
289353
}
290-
/// 建立单页VPN的映射
354+
/// 建立单页VPN的映射:向自身添加VPN->PPN(直接映射)、向页表里添加一个VPN->PPN(三级映射)。
291355
pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
292356
let ppn: PhysPageNum;
293357
//1. 更新data_frames
294358
match self.map_type {
295359
MapType::Identical => {
296-
// 不向data_frames里推数据
360+
// 不向data_frames里推数据(恒等映射)
297361
ppn = PhysPageNum(vpn.0);
298362
}
299363
MapType::Framed => {
300-
let frame = frame_alloc().unwrap();
364+
let frame = frame_alloc().unwrap(); // 自己分配一个物理页
301365
ppn = frame.ppn;
302366
self.data_frames.insert(vpn, frame);
303367
}
304368
}
305369
let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
306-
//2. 更新页表
370+
// 页表内添加vpn->ppn的映射
307371
page_table.map(vpn, ppn, pte_flags);
308372
}
309373
#[allow(unused)]
@@ -340,7 +404,9 @@ impl MapArea {
340404
}
341405
/// data: start-aligned but maybe with shorter length
342406
/// assume that all frames were cleared before
343-
/// 将数据放入物理页,一页一页的放,顺序是虚拟页递增的顺序。
407+
/// 向已经分配并映射好的物理页帧中写入数据
408+
/// 通常是在MapArea 调用了 map 方法完成所有页的映射后(此时 map 内部多次调用了 map_one),data_frames 被填充了需要追踪的物理页帧信息,
409+
/// 然后才会调用 copy_data 来向这些已分配并映射的物理页写入数据。
344410
pub fn copy_data(&mut self, page_table: &mut PageTable, data: &[u8]) {
345411
assert_eq!(self.map_type, MapType::Framed);
346412
let mut start: usize = 0;
@@ -384,8 +450,9 @@ bitflags! {
384450
}
385451
}
386452

387-
/// Return (bottom, top) of a kernel stack in kernel space.
453+
/// 根据 app_id 计算内核栈在内核地址空间中的位置 (bottom, top)
388454
pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
455+
// 内核栈位于跳板页下方,每个栈之间有一个 PADDING 页
389456
let top = TRAMPOLINE - app_id * (KERNEL_STACK_SIZE + PAGE_SIZE);
390457
let bottom = top - KERNEL_STACK_SIZE;
391458
(bottom, top)
@@ -394,10 +461,12 @@ pub fn kernel_stack_position(app_id: usize) -> (usize, usize) {
394461
/// remap test in kernel space
395462
#[allow(unused)]
396463
pub fn remap_test() {
464+
// 获取代码段、只读数据段、数据段中间位置的虚拟地址
397465
let mut kernel_space = KERNEL_SPACE.exclusive_access();
398466
let mid_text: VirtAddr = ((stext as usize + etext as usize) / 2).into();
399467
let mid_rodata: VirtAddr = ((srodata as usize + erodata as usize) / 2).into();
400468
let mid_data: VirtAddr = ((sdata as usize + edata as usize) / 2).into();
469+
// 检查页面是否不可写(代码段和只读数据段的页面不可写)
401470
assert!(!kernel_space
402471
.page_table
403472
.translate(mid_text.floor())
@@ -408,6 +477,7 @@ pub fn remap_test() {
408477
.translate(mid_rodata.floor())
409478
.unwrap()
410479
.writable(),);
480+
// 检查数据段页面不可执行
411481
assert!(!kernel_space
412482
.page_table
413483
.translate(mid_data.floor())

os/src/mm/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,5 @@ pub use page_table::{PTEFlags, PageTable};
2424
pub fn init() {
2525
heap_allocator::init_heap();
2626
frame_allocator::init_frame_allocator();
27-
KERNEL_SPACE.exclusive_access().activate();
27+
KERNEL_SPACE.exclusive_access().activate(); // 开启分页
2828
}

os/src/mm/page_table.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ impl PageTableEntry {
5656
PTEFlags::from_bits(self.bits as u8).unwrap()
5757
}
5858
/// The page pointered by page table entry is valid?
59-
pub fn is_valid(&self) -> bool {
59+
pub fn is_valid(&self) -> bool { //? 什么时候变为无效?
6060
(self.flags() & PTEFlags::V) != PTEFlags::empty()
6161
}
6262
/// The page pointered by page table entry is readable?
@@ -74,7 +74,11 @@ impl PageTableEntry {
7474
}
7575

7676
/// page table structure
77-
/// 页表结构:根页号,页帧
77+
/// 页表结构:根页号,FrameTracker向量
78+
/// FrameTracker向量:这些FrameTracker里的物理页号都是页表页号,包含所有的一级、二级、三级页表页。**页号**实际上存在三级页表页内。
79+
/// 当页表被释放,这个向量也被释放;向量里对应的FrameTracker会释放;FrameTracker的Drop实现了对FrameTraker
80+
/// 自身包含的物理页号指向的物理页的释放。所以这会释放PTE指向的页.
81+
///
7882
pub struct PageTable {
7983
root_ppn: PhysPageNum,
8084
frames: Vec<FrameTracker>,
@@ -87,7 +91,7 @@ impl PageTable {
8791
let frame = frame_alloc().unwrap();
8892
PageTable {
8993
root_ppn: frame.ppn,
90-
frames: vec![frame],
94+
frames: vec![frame], // root_ppn指向的一级页表放在里面
9195
}
9296
}
9397
/// Temporarily used to get arguments from user space.
@@ -98,10 +102,10 @@ impl PageTable {
98102
}
99103
}
100104
/// Find PageTableEntry by VirtPageNum, create a frame for a 4KB page table if not exist
101-
/// 根据给定的虚拟页号 vpn,在多级页表树中查找对应的三级(叶子)页表项 (PTE)
105+
/// 根据给定的虚拟页号 vpn,在多级页表树中查找对应的PTE(不是物理页号)
102106
/// 如果在查找过程中发现任何中间级别的页表节点不存在(即父级 PTE 无效),它会自动分配
103-
/// 一个新的物理页帧来创建该节点,并更新父级 PTE 使其指向新节点。
104-
/// 这个方法常用于建立新的虚拟地址映射时,确保页表路径完整。
107+
/// 一个新的物理页来创建该节点,并更新父级 PTE 使其指向新节点。
108+
/// !返回的页可能是无效的
105109
// 1. 首先获取 `vpn` 的三级索引。
106110
// 2. 从根页表(一级页表,其位置由 `self.root_ppn` 给出)开始。
107111
// 3. 循环遍历页表层级(一级 -> 二级 -> 三级)。在每一级:
@@ -162,6 +166,7 @@ impl PageTable {
162166
}
163167
result
164168
}
169+
/// 要求传来一个物理页号,调用者要分配一个物理页。
165170
/// 在当前的页表 (self) 中,为虚拟页号 VPN 建立一个到物理页号 PPN 的映射,
166171
/// 并设置该映射的权限和状态标志 flags。
167172
#[allow(unused)]
@@ -197,11 +202,14 @@ impl PageTable {
197202
}
198203
/// get the page table entry from the virtual page number
199204
pub fn translate(&self, vpn: VirtPageNum) -> Option<PageTableEntry> {
200-
self.find_pte(vpn).map(|pte| *pte)
205+
self.find_pte(vpn)
206+
.map(|pte| *pte)
201207
}
202-
/// get the token from the page table
208+
/// 获取本页表的satp
203209
pub fn token(&self) -> usize {
210+
// |Mode| ASID | PPN |, Mode=8表示开启SV39,Umode和Smode的访存都视为39位虚拟地址。
204211
8usize << 60 | self.root_ppn.0
212+
// https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter4/3sv39-implementation-1.html#satp-layout
205213
}
206214
}
207215

0 commit comments

Comments
 (0)