目录
JOS 将处理器的 32 位线性地址空间(4G)分为两部分。我们将在实验 3 中开始加载和运行的用户环境(进程)将控制更低地址的布局和内容,而内核始终保持对上部的完全控制。分隔线由 inc/memlayout.h 中的符号 ULIM 随意定义,为内核保留了大约 256MB 的虚拟地址空间。 这就解释了为什么我们需要在实验室 1 中给内核一个如此高的链接地址:否则内核的虚拟地址空间将没有足够的空间同时映射到它下面的用户环境。
权限和故障隔离
由于内核和用户内存都存在于每个进程的地址空间中,我们将不得不在 x86 页表中使用权限位来允许用户代码仅访问地址空间的用户部分。否则用户代码中的错误可能会覆盖内核数据,导致崩溃或更微妙的故障;用户代码也可能窃取其他环境的私人数据。请注意,可写权限位 ( PTE_W ) 会影响用户和内核代码。
用户环境对ULIM
上面的任何内存都没有权限,而内核将能够读写这块内存。对于地址范围 [UTOP,ULIM)
,内核和用户环境都拥有相同的权限:可以读但不能写这个地址范围。该地址范围用于向用户环境公开某些只读的内核数据结构。最后,UTOP
下面的地址空间 是供用户环境使用的;用户环境将设置访问此内存的权限。
初始化内核地址空间
设置UTOP
上面的地址空间:也就是虚拟地址空间的内核部分映射。
mem_init()
void
mem_init(void)
{
uint32_t cr0;
size_t n;
// Find out how much memory the machine has (npages & npages_basemem).
i386_detect_memory();
// Remove this line when you're ready to test this function.
// panic("mem_init: This function is not finished\n");
//
// create initial page directory.建立页目录
kern_pgdir = (pde_t *) boot_alloc(PGSIZE);//4K
memset(kern_pgdir, 0, PGSIZE);
//
// Recursively insert PD in itself as a page table, to form
// a virtual page table at virtual address UVPT.
// (For now, you don't have understand the greater purpose of the
// following line.)
// Permissions: kernel R, user R
// 递归地将PD自身作为页表插入,形成
// 虚拟地址 UVPT 处的虚拟页表。
//(目前,您还没有理解以下行的更大目的。
// 权限:内核R,用户R
kern_pgdir[PDX(UVPT)] = PADDR(kern_pgdir) | PTE_U | PTE_P;
//
// Allocate an array of npages 'struct PageInfo's and store it in 'pages'.
// The kernel uses this array to keep track of physical pages: for
// each physical page, there is a corresponding struct PageInfo in this
// array. 'npages' is the number of physical pages in memory. Use memset
// to initialize all fields of each struct PageInfo to 0.
// Your code goes here:
//struct PageInfo pages[npages];//内核使用这个数组来跟踪物理页面
pages=(struct PageInfo*)boot_alloc(npages*sizeof(struct PageInfo));//分配一个数组
memset(pages,0,sizeof(pages));
//
// 现在已经分配了初始内核数据结构,我们设置空闲物理页面。 一旦我们这样做了,所有进一步
// 内存管理将通过 page_* 函数进行。 在
// 特别是,我们现在可以使用 boot_map_region 映射内存
// 或 page_insert
page_init();
check_page_free_list(1);
check_page_alloc();
check_page();
//
// Now we set up virtual memory
//建立虚拟内存
//
// Map 'pages' read-only by the user at linear address UPAGES 在线性地址 UPAGES 处映射用户只读的“页面”
// boot_map_region(kern_pgdir,UPAGES,PTSIZE,page2pa(pages),PTE_U|PTE_P);
boot_map_region(kern_pgdir,UPAGES,(size_t)PTSIZE,PADDR(pages),PTE_U);//用户只读
//
// Use the physical memory that 'bootstack' refers to as the kernel
// stack. The kernel stack grows down from virtual address KSTACKTOP.
// We consider the entire range from [KSTACKTOP-PTSIZE, KSTACKTOP)
// to be the kernel stack, but break this into two pieces:
// * [KSTACKTOP-KSTKSIZE, KSTACKTOP) -- backed by physical memory
// * [KSTACKTOP-PTSIZE, KSTACKTOP-KSTKSIZE) -- not backed; so if
// the kernel overflows its stack, it will fault rather than
// overwrite memory. Known as a "guard page".
// Permissions: kernel RW, user NONE
// Your code goes here:
// boot_map_region(kern_pgdir,KSTACKTOP-KSTKSIZE,(size_t) KSTKSIZE,PADDR(bootstack),PTE_U|PTE_P);
boot_map_region(kern_pgdir,KSTACKTOP-KSTKSIZE,(size_t) KSTKSIZE,PADDR(bootstack),PTE_W);//用户没有权限
//
// Map all of physical memory at KERNBASE.
// Ie. the VA range [KERNBASE, 2^32) should map to
// the PA range [0, 2^32 - KERNBASE)
// We might not have 2^32 - KERNBASE bytes of physical memory, but
// we just set up the mapping anyway.
// Permissions: kernel RW, user NONE
// Your code goes here:
boot_map_region(kern_pgdir,KERNBASE,(2^32) - (size_t)KERNBASE,(physaddr_t)0,PTE_W);//用户没有权限
// Check that the initial page directory has been set up correctly.
check_kern_pgdir();
// Switch from the minimal entry page directory to the full kern_pgdir
// page table we just created. Our instruction pointer should be
// somewhere between KERNBASE and KERNBASE+4MB right now, which is
// mapped the same way by both page tables.
//
// If the machine reboots at this point, you've probably set up your
// kern_pgdir wrong.
lcr3(PADDR(kern_pgdir));
check_page_free_list(0);
// entry.S set the really important flags in cr0 (including enabling
// paging). Here we configure the rest of the flags that we care about.
cr0 = rcr0();
cr0 |= CR0_PE|CR0_PG|CR0_AM|CR0_WP|CR0_NE|CR0_MP;
cr0 &= ~(CR0_TS|CR0_EM);
lcr0(cr0);
// Some more checks, only possible after kern_pgdir is installed.
check_page_installed_pgdir();
}
Question2:
info pg:可以查看有效的页目录项。
包括3BC,映射的虚拟地址范围[ef000-ef3ff],UPAGES开始,因此映射的是pages数组。
3BD,映射的虚拟地址范围[ef400-ef7ff],UVPT,当前页表的映射,也就算kernerl_pgdir。
3BF,映射的虚拟地址范围[efc00-effff],KERNELBASE,kernerl_stack的映射,映射到物理地址bootstack。
3C0,映射的虚拟地址范围[f0000-f03ff],kernel的映射。
还有3C1-3DF,3E0-3FF两个页目录项,都是KERNELBASE之上的虚拟地址映射,具体不详。
Question3:
不同虚拟地址中用户访问的权限不同,用户对
ULIM
上面的任何内存都没有权限,而内核将能够读写这块内存。具体是通过页表项中PTE_U=0实现的。
Question4:
最大的物理内存:物理内存页放在pages数组中,数组可以使用的地址空间为PTSIZE=4M,可以存放结构体pageinfo(4byte指针+4byte整数)个数为512K,因此最大的物理内存为:512K*4K=2G。
Question5:
pages数组:4M+kernel_pgdir:4K+当前页表开销???(一共512K个页面*4字节的指针=2M),因此一共6M+2K??
Question6:
# Now paging is enabled, but we're still running at a low EIP # (why is this okay?). Jump up above KERNBASE before entering # C code. mov $relocated, %eax f0100028: b8 2f 00 10 f0 mov $0xf010002f,%eax jmp *%eax f010002d: ff e0 jmp *%eax
在这里eax寄存器的值被设置为0xf010002f,jmp指令后EIP指向0xf010002f,KERNBASE之上。因为entry_pgdir中仍有将[0,4M]虚拟地址映射到[0,4M]物理地址的页表,因此仍可以运行低EIP程序。[0,4M]中存放的是IDT与BIOS,在之后的运行中可能需要访问,因此保留下来。
Question参考:https://www.cnblogs.com/fatsheep9146/p/5324692.html