关于lab2中对内存的分配:
示意图如下:
/*
* Virtual memory map: Permissions
* kernel/user
*
* 4 Gig --------> +------------------------------+
* | | RW/--
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* : . :
* : . :
* : . :
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/--
* | | RW/--
* | Physical Memory | RW/--
* | | RW/--
* KERNBASE -----> +------------------------------+
* | Kernel Virtual Page Table | RW/-- PDMAP
* VPT,KSTACKTOP--> +------------------------------+ --+
* | Kernel Stack | RW/-- KSTKSIZE |
* | - - - - - - - - - - - - - - -| PDMAP
* | Invalid memory | --/-- |
* ULIM ------> +------------------------------+ --+
* | R/O User VPT | R-/R- PDMAP
* UVPT ----> +------------------------------+
* | R/O PAGES | R-/R- PDMAP
* UPAGES ----> +------------------------------+
* | R/O ENVS | R-/R- PDMAP
* UTOP,UENVS -------> +------------------------------+
* UXSTACKTOP -/ | user exception stack | RW/RW BY2PG
* +------------------------------+
* | Invalid memory | --/-- BY2PG
* USTACKTOP ----> +------------------------------+
* | normal user stack | RW/RW BY2PG
* +------------------------------+
* | |
* | |
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* . .
* . .
* . .
* |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
* | |
* UTEXT -------> +------------------------------+
* | | 2 * PDMAP
* 0 ------------> +------------------------------+
*/
具体的意义:
内核与用户的分界:ULIM
注意在UTOP-ULIM这段用户与内核的权限是一样的,都只有读的权限。在这里放着一些核心的数据结构。这样做可以让用户读取这些结构。
可以看到,在i386_vm_init(void)中我们完成了下面这些布局:
[KSTACKTOP-PDMAP, KSTACKTOP) kernel stack,部分无用 到bootstack
[KERNBASE, 2^32 - 1] physical memory 到[0,2^32-1]
[UPAGES,UPAGES+ ROUND(npage*sizeof(struct Page), BY2PG)] user page 到pages
[UENVS,UENVS+ ROUND(NENV*sizeof(struct Env), BY2PG)] user envs 到envs
IA-32结构采用段页式的内存管理,而页式管理是可选的。
通过段式管理把一个虚拟地址(virtual address)转换成一个线性地址(liner address)。如果没有页式管理,那么把这个线性地址直接转换到物理地址。否则通过页面映射到一个物理地址。
通过设置PG (paging) flag. Bit 31 of CR0 (available in all IA-32 processors beginning with the
Intel386 processor).来开启页面映射。
在这个试验中所做的是一开始并没有打开页面映射机制,通过把段寄存器设置成-KERNBASE完成了从一个内核的链接地址(0xf0100000开始)到物理地址的一个映射的过程(就像我们在试验一中所做的那样)。完成了页表的初始化后开始打开页面映射机制,把段寄存器都清零,从而实现了KERNBASE+x => KERNBASE+x => x这样似乎关掉了段映射机制的地址映射过程。
关于页面的访问权限:
x86的结构是通过设置页表项的相关位来决定页的访问权限的:
下面是在/inc/mmu.h中定义的访问权限的宏定义:
/* Page Table/Directory Entry flags
* these are defined by the hardware
*/
#define PTE_P 0x1 /* Present */
#define PTE_W 0x2 /* Writeable */
#define PTE_U 0x4 /* User */
#define PTE_PWT 0x8 /* Write-Through */
#define PTE_PCD 0x10 /* Cache-Disable */
#define PTE_A 0x20 /* Accessed */
#define PTE_D 0x40 /* Dirty */
#define PTE_PS 0x80 /* Page Size */
#define PTE_MBZ 0x180 /* Bits must be zero */
#define PTE_USER 0xe00 /* Bits for user processes */
#define PTE_FLAGS 0xfff /* All flags */
在这个试验中我们需要设置的位只是PTE_P, PTE_W, PTE_U这三个位
PTE_P: 该页是否存在。由操作系统或者应用程序来设置
PTE_W: 0代表只读,1代表可读写。
PTE_U: 0需要supervisor权限,1只需user权限
If the processor is currently operating at a CPL of 0, 1, or 2, it is in supervisor mode; if it is operating at a CPL of 3, it is in user mode. When the processor is in supervisor mode, it can access all pages; when in user mode, it can access only user-level pages. -- System Programming Guide
这样就可以理解为什么要像注释中给出的那样设置权限了,也就可以理解为什么linux只是使用了CPL为0和3这两个级别了
例如
Permissions: kernel RW, user NONE
就是置PTE_W和PTE_P位就可以了
而对于下面这段代码就应该这样写:
//
// Make 'pages' point to an array of size 'npage' of 'struct Page'.
// Map this array read-only by the user at UPAGES (ie. perm = PTE_U | PTE_P)
// Permissions:
// - pages -- kernel RW, user NONE
// - the image mapped at UPAGES -- kernel R, user R
// Your code goes here:
pages = alloc ( ROUND(npage*sizeof(struct Page), BY2PG), BY2PG , 1 ) ;
boot_map_segment ( pgdir, pages , ROUND(npage*sizeof(struct Page), BY2PG), PADDR(pages), PTE_W ) ;
boot_map_segment ( pgdir, UPAGES , ROUND(npage*sizeof(struct Page), BY2PG), PADDR(pages) , PTE_U ) ;