- 学习xv6 lab2 内存管理
1.Setup of paging
当kernel开启分页机制之后,kernel code and data in RAM 前4M的物理地址就会映射到虚拟地址,即0x00000000 to 0x003fffff映射到0xf0000000 to 0xf03fffff 。那么如果访问0xf0400000会出现什么情况?
42 .globl entry
43 entry:
44 movw $0x1234,0x472 # warm boot
54
55 # Load the physical address of entry_pgdir into cr3. entry_pgdir
56 # is defined in entrypgdir.c.
57 movl $(RELOC(entry_pgdir)), %eax
58 movl %eax, %cr3
59 # Turn on paging.
60 movl %cr0, %eax
61 orl $(CR0_PE|CR0_PG|CR0_WP), %eax
62 movl %eax, %cr0
未开启分页机制前,查看地址0xf0400000:
(gdb)b *0x100015
(gdb)c
(gdb) x/1b 0xf0400000
0xf0400000: 0x00
执行si指令到0x100025 mov %eax,%cr0 ,即开启分页机制,再次查看该地址:
(gdb) x/1b 0xf0400000
0xf0400000: Cannot access memory at address 0xf0400000
2.Physical Page Management
首先区分物理内存和虚拟内存,分布区如下所示:
物理内存分布图:
+------------------+ <- 0xFFFFFFFF (4GB)
| 32-bit |
| memory mapped |
| devices |
| |
/\/\/\/\/\/\/\/\/\/\
/\/\/\/\/\/\/\/\/\/\
| |
| Unused |
| |
+------------------+ <- depends on amount of RAM
| |
| |
| Extended Memory |
|------------------|
| kernnel |
+------------------+ <- 0x00100000 (1MB)
| BIOS ROM |
+------------------+ <- 0x000F0000 (960KB)
| 16-bit devices, |
| expansion ROMs |
+------------------+ <- 0x000C0000 (768KB)
| VGA Display |
+------------------+ <- 0x000A0000 (640KB)
| |
| Low Memory |
|------------------| <-0x00010000 (elf herader here!)
|------------------| <-0x00007c00 (boot loader here!)
| bootmain stack |
+------------------+ <- 0x00000000
虚拟内存分布图:
2.1.Exercise 1
实验之前首先介绍gdb调试kernel如何设置断点:
进入kernel之前代码执行地址都是物理地址,所以设置断点也是使用物理地址,在执行到下面指令的时候开启虚拟地址寻址,此时设置断点必须使用虚拟地址。
obj/kern/kernel.asm:
42 mov $relocated, %eax
43 f0100028: b8 2f 00 10 f0 mov $0xf010002f,%eax
44 jmp *%eax
45 f010002d: ff e0 jmp *%eax
例如:
The target architecture is assumed to be i8086
[f000:fff0] 0xffff0: ljmp $0xf000,$0xe05b
0x0000fff0 in ?? ()
+ symbol-file obj/kern/kernel
(gdb) b *0x10002d
Breakpoint 1 at 0x10002d
(gdb) c
Continuing.
The target architecture is assumed to be i386
=> 0x10002d: jmp *%eax
Breakpoint 1, 0x0010002d in ?? ()
(gdb) b *0xf0100929 #page_init address
Breakpoint 2 at 0xf0100929: file kern/pmap.c, line 245.
(gdb) c
Continuing.
=> 0xf0100929 <page_init>: push %ebp
Breakpoint 2, page_init () at kern/pmap.c:245
245 {
介绍boot_alloc实现流程:
kern/init.c:
i386_init->mem_init->boot_alloc
首先分析变量extern char end[] in boot_alloc function
In the linker script we see: PROVIDE(end = .); right after the .bss section. So this is where the linker placed the symbol represented by end.
kern/kernel.ld:
52 .bss : {
53 *(.bss)
54 }
55
56 PROVIDE(end = .);
57
58 /DISCARD/ : {
59 *(.eh_frame .note.GNU-stack)
60 }
61 }
gdb 调试查看end:
(gdb) b *0x10002d
(gdb) si
(gdb) x/x &nextfree
0xf011b7d4 <nextfree>: 0x00000000
(gdb) x/x &end
0xf011bbf0: 0x00000000
如上所示:指针变量nextfree地址为0xf011b7d4, 变量end地址为0xf011bbf0,对应查看 obj/kern/kernel.asm第2553行,value of kernel.asm to confirm:
2550 extern char end[];
2551 nextfree = ROUNDUP((char *) end, PGSIZE);
2552 f01014e4: c7 45 f4 00 10 00 00 movl $0x1000,-0xc(%ebp)
2553 f01014eb: b8 f0 bb 11 f0 mov $0xf011bbf0,%eax
Let’s verify the value of 0xf011bbf0 from the executable itself:
jos/lab2$ objdump -h obj/kern/kernel | egrep 'Idx|\.data|\.bss'
Idx Name Size VMA LMA File off Algn
4 .data 0000a588 f0111000 00111000 00012000 2**12
5 .bss 00000650 f011b5a0 0011b5a0 0001c588 2**5
jos/lab2$ python -c 'print(hex(0xf011b5a0+0x00000650))'
0xf011bbf0
如上所示,end地址0xf011bbf0正好位于.bss段地址末尾。通过执行si命令到分配内存成功时,如下所示:
汇编指令执行到这时,查看nextfree:
nextfree = ROUNDUP((char *) end, PGSIZE);
f010150f: a3 d4 b7 11 f0 mov %eax,0xf011b7d4
(gdb) x/x &nextfree
0xf011b7d4 <nextfree>: 0xf011c000
Addresses that are lower than nextfree are considered allocated, and addresses equal or above nextfree (up to 0xf03fffff) are considered unallocated. As a consequence, nextfree points to the first free address.It is also worth mentioning that the addresses of nextfree and result must be divisible by 4096 (page size). Thus, the first address returned by this function is 0xf011c000).
An array of npages实现流程:
148 //
149 // Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
150 // The kernel uses this array to keep track of physical pages: for
151 // each physical page, there is a corresponding struct PageInfo in this
152 // array. 'npages' is the number of physical pages in memory. Use memset
153 // to initialize all fields of each struct PageInfo to 0.
154 // Your code goes here:
155 pages = (struct PageInfo *) boot_alloc(npages * sizeof(struct PageInfo));
156 uintptr_t pages_region_sz = (uintptr_t)boot_alloc(0) - (uintptr_t)pages;
157 memset(pages, 0, pages_region_sz);
关于pages_region_sz的理解:
通过gdb调试:pages_region_sz = 0xf0155000 - 0xf0115000 = 0x4000 = 4页。