地址空间由段和页组成. 上一篇实现了页的分配,这篇看看段是怎么实现的.

实现地址空间抽象

直接引用原文^[https://rcore-os.cn/rCore-Tutorial-Book-v3/chapter4/5kernel-app-spaces.html]的概念:

逻辑段: 一段连续地址的虚拟内存

实现:

1
2
3
4
5
6
7
8
9
10
pub struct MapArea {
vpn_range: VPNRange, //iter
data_frames: BTreeMap<VirtPageNum, FrameTracker>, // b树实现的map, k为vpn, v为物理页帧
map_type: MapType, // 虚拟页面映射到物理页帧的方式
map_perm: MapPermission,
}
pub enum MapType {
Identical,
Framed,
}

MapType中,
Framed表示对于每个虚拟页面都有一个新分配的物理页帧与其对应.
Identical为恒等映射,主要用在启用多级页表之后,内核仍能够在虚存地址空间中访问一个特定的物理地址指向的物理内存.
当逻辑段采用 MapType::Framed 方式映射到物理内存的时候,
data_frames 是一个保存了该逻辑段内的每个虚拟页面和它被映射到的物理页帧 FrameTracker 的一个键值对容器 BTreeMap 中,
这些物理页帧被用来存放实际内存数据而不是作为多级页表中的中间节点.

相应的,要实现”为逻辑段绑定和解绑某个物理页帧”的功能.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
impl MapArea {
pub fn map_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
let ppn: PhysPageNum;
match self.map_type {
MapType::Identical => {
ppn = PhysPageNum(vpn.0);
}
// 这里只是将ppn注册到MapArea里,将vpn和ppn建立对应关系在page_table.map()那里
MapType::Framed => {
let frame = frame_alloc().unwrap();
ppn = frame.ppn;
self.data_frames.insert(vpn, frame);
}
}
let pte_flags = PTEFlags::from_bits(self.map_perm.bits).unwrap();
page_table.map(vpn, ppn, pte_flags);
}

pub fn unmap_one(&mut self, page_table: &mut PageTable, vpn: VirtPageNum) {
if self.map_type == MapType::Framed {
self.data_frames.remove(&vpn);
}
page_table.unmap(vpn);
}
}

地址空间:一系列有关联的逻辑段

一个任务(或者在实现了进程之后,可以称为进程)的地址空间就是这些逻辑段的集合.
前面提到了一个地址空间对应一个page_table,也就是这样实现的:

1
2
3
4
5
pub struct MemorySet {
page_table: PageTable,
areas: Vec<MapArea>,
}

内核和应用的地址空间

内核的

用户使用的内核栈的分布图(内核空间上半部分)
最顶部是trampoline(跳板),TODO.
后面存放的是每个应用对应的内核栈,中间留有几个page的保护页面(这里设为一个),防止内核栈溢出导致访问到下一个栈的东西.(留有空隙,则溢出时会返回空,也就说明异常了)
底部地址是2^64B-256GiB,因为sv39模式下实际可用的物理地址为2^39B, 即256GiB.

内核自己使用的内存分布图(内核空间下半部分)
这些段都是恒等映射到物理内存.

应用的

应用地址空间布局
看起来和内核态的差不多,只不过最上方是跳板+中断上下文.

并且现在可以解析elf头了.
也就是解析各个段的位置和权限什么的
解析elf的代码部分省略如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
pub fn from_elf(elf_data: &[u8]) -> (Self, usize, usize) {
let mut memory_set = Self::new_bare();
// map trampoline
memory_set.map_trampoline();
// map program headers of elf, with U flag
let elf = xmas_elf::ElfFile::new(elf_data).unwrap();
let elf_header = elf.header;
let magic = elf_header.pt1.magic;
assert_eq!(magic, [0x7f, 0x45, 0x4c, 0x46], "invalid elf!");
let ph_count = elf_header.pt2.ph_count();
let mut max_end_vpn = VirtPageNum(0);
for i in 0..ph_count {
let ph = elf.program_header(i).unwrap();
if ph.get_type().unwrap() == xmas_elf::program::Type::Load {
let start_va: VirtAddr = (ph.virtual_addr() as usize).into();
let end_va: VirtAddr = ((ph.virtual_addr() + ph.mem_size()) as usize).into();
let mut map_perm = MapPermission::U;
let ph_flags = ph.flags();
if ph_flags.is_read() {
map_perm |= MapPermission::R;
}
if ph_flags.is_write() {
map_perm |= MapPermission::W;
}
if ph_flags.is_execute() {
map_perm |= MapPermission::X;
}
let map_area = MapArea::new(start_va, end_va, MapType::Framed, map_perm);
max_end_vpn = map_area.vpn_range.get_end();
memory_set.push(
map_area,
Some(&elf.input[ph.offset() as usize..(ph.offset() + ph.file_size()) as usize]),
);
}
}
// map user stack with U flags
let max_end_va: VirtAddr = max_end_vpn.into();
let mut user_stack_bottom: usize = max_end_va.into();
// guard page
user_stack_bottom += PAGE_SIZE;
let user_stack_top = user_stack_bottom + USER_STACK_SIZE;
memory_set.push(
MapArea::new(
user_stack_bottom.into(),
user_stack_top.into(),
MapType::Framed,
MapPermission::R | MapPermission::W | MapPermission::U,
),
None,
);
// used in sbrk
memory_set.push(
MapArea::new(
user_stack_top.into(),
user_stack_top.into(),
MapType::Framed,
MapPermission::R | MapPermission::W | MapPermission::U,
),
None,
);
// map TrapContext
memory_set.push(
MapArea::new(
TRAP_CONTEXT.into(),
TRAMPOLINE.into(),
MapType::Framed,
MapPermission::R | MapPermission::W,
),
None,
);
(
memory_set,
user_stack_top,
elf.header.pt2.entry_point() as usize,
)
}

这一部分我还没有细读代码,因为感觉不是很重要.等有时间(又在等有时间了)再看看吧