MIT_xv6_Lab3

JOS 的一个进程可以看作是一个线程与地址空间的结合, 因为运行一个进程需要将 CPU 建立在保存的寄存器与用户空间之上.

Lab3 以后内存出现的问题

对于GCC 7.0 以上的版本在Lab3以后, 会出现 kernel panic at kern/pmap.c:147: PADDR called with invalid kva 00000000 这个错误, 我本身也很疑惑, 明明在 Lab2 代码是正确的, 这里为什么是错的呢?

从错误的结果来看是

boot_alloc memory at f018e000, next memory allocate at f018f000
kernel panic at kern/pmap.c:154: PADDR called with invalid kva 00000000

错误的部分是在 boot_alloc 函数中.

static void *
boot_alloc(uint32_t n)
{
	static char *nextfree;	// virtual address of next byte of free memory
	char *result;
	if (!nextfree) {
		extern char end[];
		// 将地址 end 向上以页面大小对齐
		nextfree = ROUNDUP((char *) end, PGSIZE);
	}
	result = nextfree;
	nextfree = ROUNDUP((char *) result+n, PGSIZE);
	cprintf("boot_alloc memory at %x, next memory allocate at %x\n", result, nextfree);
	return result;
}

然后我发现,在

// 分配一个物理页大小的虚拟空间, 对于页目录来说, 这里使用 boot_alloc
	kern_pgdir = (pde_t *) boot_alloc(PGSIZE);
	// 初始化页目录
	memset(kern_pgdir, 0, PGSIZE);

删除 memset 之后, 可以得到部分正确, 而且在 memset 之前, kern_pgdir 都是 f018e000, 那么这一步到底发生了什么呢? 其实错误的真正原因不在这里, 而是在 end 这里, 我们知道, end是内核段的末尾, 也就是 bss 段的末尾, 根据 objdump -h obj/kern/kernel 可知 bss 段在内存的位置:

Idx Name          Size      VMA       LMA       File off  Algn
  9 .bss        00000f14  f018d100  0018d100   0008e100   2**5
                CONTENTS,   ALLOC,    LOAD,       DATA

bss 段的起始位置是 f018d100 , 大小为 00000f14, 所以虚拟内存范围是 0xf018d1000xf018e014 , 从result 返回的数据可以知道,end 的地址为 f018e000, 在bss 的范围内, 然后我们使用 objdump -t obj/kern/kernel | grep kern_pgdir 查看一下 kern_pgdir 的情况可以得到:

f018e00c g O .bss 00000004 kern_pgdir

kern_pgdir 存储的地址 f018e00c 恰好就在 end 后面, 这会导致什么呢? 我们要知道 0xf018e000 恰好是 PAGESIZE 对齐的.所以会发生下面的问题:

// kern_pgdir == NULL, &kern_pgdir == 0xf018f00c
// in mem_init()
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);  // call boot_alloc(4096)
// in boot_alloc()
if (!nextfree) {
    extern char end[];  // end == 0xf018e000
    nextfree = ROUNDUP((char *) end, PGSIZE);  // nextfree = 0xf018e000
}
// ...
// 0xf018f000 is returned to to mem_init()
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);  // kern_pgdir = 0xf018e000, 这里是返回的 result 的数据
// 注意这一步, 恰好把 kern_pgdir清空了
memset(kern_pgdir, 0, PGSIZE);  // Damn! 0xf018e000 to 0xf0180000 are set to 0, including 0xf018e00c
                                // Now kern_pgdir == 0
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P; // call PADDR(0), panic!

为什么 end 会设置错误呢? 我们可以看下 kern/kernel.ld , 这个文件是内核的各个段的描述, 解决方法就是添加了 *(COMMON) 段, 至于为什么了, 个人还是不太清楚,

.bss : {
    PROVIDE(edata = .);
    *(.bss)
    *(COMMON)
    PROVIDE(end = .);
    BYTE(0)
}

猜你喜欢

转载自www.cnblogs.com/wevolf/p/12740793.html