MIT6.828-Lab2-Part3-内核地址空间

 

内核地址空间 Kernel Address Space

JOS把32-bit 线性地址分成了两个部分,其中用户环境(进行运行环境)通常占据低地址空间,称作用户地址空间。而操作系统内核总是占据高地址的部分,即内核地址空间。两部分的分界线是定义在 inc/memlayout.h 中的宏 ULIM。JOS为内核保留了接近256M的虚拟地址空间,这也就解释了为什么需要给kernel一个如此高的link address,否则的话kernel的虚拟地址空间就没有足够的空间用来映射用户地址空间。

为什么上面说内核地址空间还要保留一部分用于用户地址空间呢?

一个简单的例子是某程序需要打开某个文件,由于程序只能够读写自己的虚拟地址空间,是无法直接访问位于磁盘或其他存储设备上面的文件的,它会发起一个open系统调用,产生一个异常,将控制权交给内核。

内核地址由于和用户地址是相互隔离的,因此二者之间传递信息需要先进行一个操作:将用户地址空间映射到内存地址空间。

这之后内核便能够正确地读取用户传递给系统调用的参数,打开正确的文件;而后将返回值正确地返回到用户空间。

memlayout.h

/*
 * Virtual memory map:                                Permissions
 *                                                    kernel/user
 *
 *    4 Gig -------->  +------------------------------+
 *                     |                              | RW/--
 *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *                     :              .               :
 *                     :              .               :
 *                     :              .               :
 *                     |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~| RW/--
 *                     |                              | RW/--
 *                     |   Remapped Physical Memory   | RW/--
 *                     |                              | RW/--
 *    KERNBASE, ---->  +------------------------------+ 0xf0000000      --+
 *    KSTACKTOP        |     CPU0's Kernel Stack      | RW/--  KSTKSIZE   |
 *                     | - - - - - - - - - - - - - - -|                   |
 *                     |      Invalid Memory (*)      | --/--  KSTKGAP    |
 *                     +------------------------------+                   |
 *                     |     CPU1's Kernel Stack      | RW/--  KSTKSIZE   |
 *                     | - - - - - - - - - - - - - - -|                 PTSIZE
 *                     |      Invalid Memory (*)      | --/--  KSTKGAP    |
 *                     +------------------------------+                   |
 *                     :              .               :                   |
 *                     :              .               :                   |
 *    MMIOLIM ------>  +------------------------------+ 0xefc00000      --+
 *                     |       Memory-mapped I/O      | RW/--  PTSIZE
 * ULIM, MMIOBASE -->  +------------------------------+ 0xef800000
 *                     |  Cur. Page Table (User R-)   | R-/R-  PTSIZE
 *    UVPT      ---->  +------------------------------+ 0xef400000
 *                     |          RO PAGES            | R-/R-  PTSIZE
 *    UPAGES    ---->  +------------------------------+ 0xef000000
 *                     |           RO ENVS            | R-/R-  PTSIZE
 * UTOP,UENVS ------>  +------------------------------+ 0xeec00000
 * UXSTACKTOP -/       |     User Exception Stack     | RW/RW  PGSIZE
 *                     +------------------------------+ 0xeebff000
 *                     |       Empty Memory (*)       | --/--  PGSIZE
 *    USTACKTOP  --->  +------------------------------+ 0xeebfe000
 *                     |      Normal User Stack       | RW/RW  PGSIZE
 *                     +------------------------------+ 0xeebfd000
 *                     |                              |
 *                     |                              |
 *                     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 *                     .                              .
 *                     .                              .
 *                     .                              .
 *                     |~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~|
 *                     |     Program Data & Heap      |
 *    UTEXT -------->  +------------------------------+ 0x00800000
 *    PFTEMP ------->  |       Empty Memory (*)       |        PTSIZE
 *                     |                              |
 *    UTEMP -------->  +------------------------------+ 0x00400000      --+
 *                     |       Empty Memory (*)       |                   |
 *                     | - - - - - - - - - - - - - - -|                   |
 *                     |  User STAB Data (optional)   |                 PTSIZE
 *    USTABDATA ---->  +------------------------------+ 0x00200000        |
 *                     |       Empty Memory (*)       |                   |
 *    0 ------------>  +------------------------------+                 --+
 *
 * (*) Note: The kernel ensures that "Invalid Memory" is *never* mapped.
 *     "Empty Memory" is normally unmapped, but user programs may map pages
 *     there if desired.  JOS user programs map pages temporarily at UTEMP.
 */

Permissions and Fault Isolation

在每个进程的地址空间中,都包含了用户内存和内核内存2。(这是因为进程的执行会遇到内核态和用户态的切换,如果没有在连续空间内,就要涉及切换页表的操作,是很耗时的)。这就使得我们需要控制页表中的访问权限标志位(permission bits)来实现用户进程的代码只能访问用户地址空间。

注意可写标志位(PTE_W)同时影响用户代码和内核代码

  1. 地址 > ULIM:用户进程不能够访问任何,但是内核可读可写。
  2. UTOP <= 地址 < ULIM:用户进程和内核都是只读。这部分地址是暴露一些只读的内核数据结构给用户进程。
  3. 地址 < UTOP:用户进程可以访问,修改这部分地址空间的内容。

Initializing the Kernel Address Space

现在设置一下 UTOP 之上的地址空间:这也是整个虚拟地址空间中的内核地址空间部分。inc/memlayout.h 文件中已经展示了这部分地址空间的布局。使用之前编写的函数来设置这些地址的布局。

Exercise5:

  1. 完善调用 cehck_page() 之后的代码,使其能够通过 check_kern_pgdir() and check_page_installed_pgdir() 的验证

Question

Q:到目前为止页目录表中已经包含多少有效页目录项?他们都映射到哪里?换句话说就是尽可能完善下面这个表(ps: 页目录的每一项指示一个页表,一个页表项管理4KB的页面,所以一个页目录项管理1024 * 4KB = 4MB的地址空间)

Entry Base Virtual Address Points to (logically):
1023=0x3ff ? Page table for top 4MB of phys memory
1022 ? ?
. ? ?
. ? ?
960=0x3c0 0xf0000000=KERNBASE 指向pyhs=0x00000000,和上面所有直到1023的表项存储了256MB的kernel
. ? ?
0x3BF 0xefc00000 指向bootstack
0x3BE ? ?
0x3BD 0xef400000 指向kern_pgdir
0x3BC 0xef000000 指向pages数组
. ? ?
. ? ?
2 0x00800000 ?
1 0x00400000 ?
0 0x00000000 [see next question]

Q:我们把kernel和user environment放在一个相同的地址空间中。为什么用户程序不能读取,写入内核的内存空间?用什么机制保护内核的地址范围。

A:通过设置权限标志位控制了每段内存空间的访问权限。

Q:这个操作系统最多可以挂载多大的物理内存,为什么?3存疑

A:每个struct PageInfo的大小是8B,所以存放它们的大小为4MB的UPAGES可以存放512个此struct。对应512个页面共512 * 4KB = 2GB,所以可以挂载的物理内存大小是2GB。ps:从KERNBASE开始,一共有256MB的虚拟地址空间留给物理地址一一映射,所以内核最多能使用的大小是256MB。

Q:如果挂载了最大数量的物理内存,那么管理内存有多少空间开销?这个开销是如何分解的?3

A:2GB的内存,共可以划分为2GB/4KB=512K个页面。总共需要 6MB + 4KB。具体分析如下:

  • 一个页面需要一个struct PageInfo:共需 512K * 8B(sizeof(struct PageInfo)) = 4MB
  • 512K个页面需要512个页表:共需 512 * 4KB(一个页表1024 * 32bits) = 2MB
  • 512个页表需要1个页目录:共需 1 * 4KB(一个页目录1024 * 32bits) = 4KB

Q:回顾entry.S文件中,当分页机制开启时,寄存器EIP的值仍旧是一个小的值。在哪个位置代码才开始运行在高于KERNBASE的虚拟地址空间中的?当程序位于开启分页之后到运行在KERNBASE之上这之间的时候,EIP的值是小的值,怎么保证可以把这个值转换为真实物理地址的?1

A:在entry.S文件中有一个指令 jmp *%eax,这个指令要完成跳转,就会重新设置EIP的值,把它设置为寄存器eax中的值,而这个值是大于KERNBASE的,所以就完成了EIP从小的值到大于KERNBASE的值的转换。在 kern/entrypgdir.c/[entry_pgdir] 这个页目录中,0号页目录项把虚拟地址空间[0, 4MB)映射到物理地址空间[0, 4MB)上,所以当访问位于[0, 4MB)之间的虚拟地址时,可以把它们转换为物理地址。开启分页机制到跳转到高地址的过程可以看lab1-exe9-entry.S

参考资料

  1. cnblog–fatsheep9146
  2. 一文解析为什么进程地址空间中包括操作系统?
  3. Ynagbo Long