MIT 6.828 | JOS | 关于虚拟空间和物理空间的总结
Question:
做lab过程中越来越迷糊,为什么一会儿虚拟地址是4G 物理地址也是4G ,那这有什么作用呢?
解决途径:
停下来,根据当前lab的进展,再回头看上学期操作系统的ppt & 上网冲浪查资料!意识到自己对于分段机制理解不够。
最强总结:https://www.cnblogs.com/tolimit/p/4775945.html?utm_source=tuicool&utm_medium=referral
解决过程:
分段机制主要功能只有两点:
- 将物理内存划分为多个段,让操作系统可以使用大于其地址线对应的物理内存(比如正常情况下32位地址线可以访问4G大小的内存,但是有分段后则可访问大于4G的内存)。
- 权限控制,将每个段设置权限位,让不同的程序访问不同的段。
linux内核只用到了权限控制的功能,但是我一直将其理解为第一个功能!!!
注意:JOS中的分段功能并没有实现
正常的操作系统通常采用两个部件来完成对内核地址的保护,一个是通过段机制来实现的,但是JOS中的分段功能并没有实现。二就是通过分页机制来实现,通过把页表项中的 Supervisor/User位置0,那么用户态的代码就不能访问内存中的这个页。
物理地址空间
+------------------+ <- 0xFFFFFFFF (4GB)
| 32-bit |
| memory mapped |
| devices |
| |
/\/\/\/\/\/\/\/\/\/\
/\/\/\/\/\/\/\/\/\/\
| |
| Unused |
| |
+------------------+ <- depends on amount of RAM
| |
| |
| Extended Memory |
| |
| |
+------------------+ <- 0x00100000 (1MB)
| BIOS ROM |
+------------------+ <- 0x000F0000 (960KB)
| 16-bit devices, |
| expansion ROMs |
+------------------+ <- 0x000C0000 (768KB)
| VGA Display |
+------------------+ <- 0x000A0000 (640KB)
| |
| Low Memory |
| |
+------------------+ <- 0x00000000
GDT/LDT && 分段机制
采用的方式就是引入了数据结构GDT,即全局描述符表,不知道有多少人听过?说简单点,GDT就是一个数组,每一个元素就是一个描述符,多个组合一起就构成了全局描述符表。而每一个描述符共64位,包含了以下的这些信息:段基址、段长度、属性。原来的段寄存器,比如CS,DS等存的值则不是段偏移了,而是GDT的索引,通过该索引就可以找到对应的描述符。现在是不是明白了呢?接下来我们再详细的解释一下一个描述符中各个位的意义吧。
LDT 可以看作是多级索引的第二级GDT。


![]()

Linux 4种段寄存器取值
也就是linux只有4种段的选择。

![]()

![]()

保护模式地址映射

从实模式切换到保护模式
1、准备GDT
2、用lgdt指令加载gdtr
3、打开A20地址线
4、将cr0寄存器的最后一位(PE位)置1:
PE位为0时,CPU运行在实模式下;
PE位为1时,CPU运行在保护模式下.
5、跳转,进入保护模式

bootloader为止的模型

物理内存的分布
Lab1进入内存后,开启分页模式,将虚拟地址[0, 4MB)映射到物理地址[0, 4MB),[0xF0000000, 0xF0000000+4MB)映射到[0, 4MB)(/kern/entry.S)
lab1 结束后物理分布如下:

- lab2 中mem_init()结束之后,物理地址分布如下:

终极问题:物理空间和虚拟空间都是4G,为什么分布不一样?
这个问题困扰了我很久,并且在迷迷糊糊做lab的时候没有搞明白。
在总结梳理了前面的知识之后,终于可以解决这个问题了!
参考:
MIT 6.828 Lab Guide
CPU访问内存之后,就可以向其他kernel的其它模块、内核线程、用户空间进程、等等)提供服务,主要包括:
- 以虚拟地址(VA)的形式,为应用程序提供远大于物理内存的虚拟地址空间(Virtual Address Space)
- 每个进程都有独立的虚拟地址空间,不会相互影响,进而可提供非常好的内存保护(memory protection)
- 提供内存映射(Memory Mapping)机制,以便把物理内存、I/O空间、Kernel Image、文件等对象映射到相应进程的地址空间中,方便进程的访问
- 提供公平、高效的物理内存分配(Physical Memory Allocation)算法
- 提供进程间内存共享的方法(以虚拟内存的形式),也称作Shared Virtual Memory
在提供这些服务之前需要对内存进行合理的划分和管理。
物理地址空间布局
Linux系统在初始化时,会根据实际的物理内存的大小,为每个物理页面创建一个page对象,所有的page对象构成一个mem_map数组。进一步,针对不同的用途,Linux内核将所有的物理页面划分到3类内存管理区中,如图,分别为ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。

- ZONE_DMA 的范围是 0~16M,该区域的物理页面专门供 I/O 设备的 DMA 使用。之所以需要单独管理 DMA 的物理页面,是因为 DMA 使用物理地址访问内存,不经过 MMU,并且需要连续的缓冲区,所以为了能够提供物理上连续的缓冲区,必须从物理地址空间专门划分一段区域用于 DMA。
- ZONE_NORMAL 的范围是 16M~896M,该区域的物理页面是内核能够直接使用的。
- ZONE_HIGHMEM 的范围是 896M~结束,该区域即为高端内存,内核不能直接使用。
Linux内核空间虚拟地址分布

在 Kernel Image 下面有 16M 的内核空间用于 DMA 操作。位于内核空间高端的 128M 地址主要由3部分组成,分别为 vmalloc area、持久化内核映射区、临时内核映射区。
由于 ZONE_NORMAL 和内核线性空间存在直接映射关系,所以内核会将频繁使用的数据如 Kernel 代码、GDT、IDT、PGD、mem_map 数组等放在 ZONE_NORMAL 里。而将用户数据、页表(PT)等不常用数据放在 ZONE_HIGHMEM 里,只在要访问这些数据时才建立映射关系(kmap())。比如,当内核要访问 I/O 设备存储空间时,就使用 ioremap() 将位于物理地址高端的 mmio 区内存映射到内核空间的 vmalloc area 中,在使用完之后便断开映射关系。
Linux用户空间虚拟地址分布

用户进程的代码区一般从虚拟地址空间的 0x08048000 开始,这是为了便于检查空指针。代码区之上便是数据区,未初始化数据区,堆区,栈区,以及参数、全局环境变量。
Linux物理地址和虚拟地址的关系

Linux 将 4G 的线性地址空间分为2部分,0~3G 为 user space,3G~4G 为 kernel space。
【超级重要】
由于开启了分页机制,内核想要访问物理地址空间的话,必须先建立映射关系,然后通过虚拟地址来访问。为了能够访问所有的物理地址空间,就要将全部物理地址空间映射到 1G 的内核线性空间中,这显然不可能。于是,内核将 0~896M 的物理地址空间一对一映射到自己的线性地址空间中,这样它便可以随时访问 ZONE_DMA 和 ZONE_NORMAL 里的物理页面;此时内核剩下的 128M 线性地址空间不足以完全映射所有的 ZONE_HIGHMEM,Linux 采取了动态映射的方法,即按需的将 ZONE_HIGHMEM 里的物理页面映射到 kernel space 的最后 128M 线性地址空间里,使用完之后释放映射关系,以供其它物理页面映射。虽然这样存在效率的问题,但是内核毕竟可以正常的访问所有的物理地址空间了。
总结如下:

总结JOS 的虚拟空间和物理空间对应关系
做完实验后,我将与虚拟空间和物理空间有关的内容进行了梳理,画出了下图:

1、[UPAGES, UVPT]: 在虚拟地址中的这段内存就是对应的在实际物理地址里 pages 数组的存储位置。可以看到这段地址在ULIM之下,也就是说操作系统开放pages数组便于让用户程序可以访问。为什么呢?比如说假如有的程序想要知道一个物理页面被引用了多少次,那么根据相应的pages[i].pp_ref就可以知道了。我们看到这个区域在布局中分配了 PTSIZE 的大小,那么这个大小够么?因为我们知道在物理页面中pages所占用的大小为npage × sizeof (struct PageInfo) =npage ×8B(使用sizeof()查看,一个指针占4字节,uint16_t是2个字节,可是struct PageInfo占8字节,有些迷惑,可借鉴uint16_t类型在 Bochs 模拟出来的 x86 平台上是4字节理解)。我们看看PTSIZE的空间可以装struct Page的个数为1024 ×4KB / 8B = 4MB / 8B = 1/2M。一个Page结构对应一个实际大小为4KB的物理页面,所以这个PTSIZE大小的虚拟地址空间能够管理 1/2 M × 4KB =2GB的物理内存,这个对于我们的QEMU模拟器来讲应该还是足够了。
2、[UVPT, ULIM]: 这部分是一个系统页目录——kern_pgdir。开放给用户只读,可以使用户得到当前内存中某个虚拟地址对应的物物理页面地址是多少。关于这部分,我们需要看一下:
kern/pmap.c

kern_pgdir这部分实际紧接在kernel 的 end 后面,也即在实际的物理地址里,上图最下面一句话使以物理地址 PADDR(kern_pgdir) 开始的一页成为了虚拟地址的 PDX = PDX(UVPT) 的页表。这部分实际占物理空间 PGSIZE, 但是在虚拟地址上却分配了 PTSIZE 大小。
注意 UVPD 的虚拟地址为 PDX(UVPT)<<22 | PDX(UVPT)<<12 | 0000000000 。

详细的 UVPT 介绍见:Lecture 5
3、[KERNBASE, 4G] : 此部分映射实际物理内存中从0开始的中断向量表IDT、BIOS程序、IO端口以及操作系统内核等,该部分虚拟地址 - KERNBASE 就是物理地址。
从这个布局结构我们就可以看出来,实际上虚拟内存中分配给用户的只有[0, ULIM)大概3.7GB而非4GB的空间(ULIM = 0xef800000),其余的空间需要分配给操作系统。
MIT 6.828 | JOS | 关于虚拟空间和物理空间的总结的更多相关文章
- MIT 6.828 JOS学习笔记2. Lab 1 Part 1.2: PC bootstrap
Lab 1 Part 1: PC bootstrap 我们继续~ PC机的物理地址空间 这一节我们将深入的探究到底PC是如何启动的.首先我们看一下通常一个PC的物理地址空间是如何布局的: ...
- MIT 6.828 JOS学习笔记17. Lab 3.1 Part A User Environments
Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建 ...
- MIT 6.828 JOS学习笔记15. Lab 2.1
Lab 2: Memory Management lab2中多出来的几个文件: inc/memlayout.h kern/pmap.c kern/pmap.h kern/kclock.h kern/k ...
- MIT 6.828 JOS学习笔记0. 写在前面的话
0. 简介 操作系统是计算机科学中十分重要的一门基础学科,是一名计算机专业毕业生必须要具备的基础知识.但是在学习这门课时,如果仅仅把目光停留在课本上一些关于操作系统概念上的叙述,并不能对操作系统有着深 ...
- MIT 6.828 JOS学习笔记16. Lab 2.2
Part 3 Kernel Address Space JOS把32位线性地址虚拟空间划分成两个部分.其中用户环境(进程运行环境)通常占据低地址的那部分,叫用户地址空间.而操作系统内核总是占据高地址的 ...
- MIT 6.828 JOS学习笔记12 Exercise 1.9
Lab 1中Exercise 9的解答报告 Exercise 1.9: 判断一下操作系统内核是从哪条指令开始初始化它的堆栈空间的,以及这个堆栈坐落在内存的哪个地方?内核是如何给它的堆栈保留一块内存空间 ...
- MIT 6.828 JOS学习笔记10. Lab 1 Part 3: The kernel
Lab 1 Part 3: The kernel 现在我们将开始具体讨论一下JOS内核了.就像boot loader一样,内核开始的时候也是一些汇编语句,用于设置一些东西,来保证C语言的程序能够正确的 ...
- MIT 6.828 JOS学习笔记7. Lab 1 Part 2.2: The Boot Loader
Lab 1 Part 2 The Boot Loader Loading the Kernel 我们现在可以进一步的讨论一下boot loader中的C语言的部分,即boot/main.c.但是在我们 ...
- MIT 6.828 JOS学习笔记18. Lab 3.2 Part B: Page Faults, Breakpoints Exceptions, and System Calls
现在你的操作系统内核已经具备一定的异常处理能力了,在这部分实验中,我们将会进一步完善它,使它能够处理不同类型的中断/异常. Handling Page Fault 缺页中断是一个非常重要的中断,因为我 ...
随机推荐
- idea 导入eclipse play1.2.7项目
1.play eclipsify #myapp 转为eclipse目录结构 2.导入eclipse,一路next. 3.新增个Application -Xms1536m-Xmx2048m-Xdebug ...
- finalize()和四种引用的一点思考
一次对ThreadLocal的学习引发的思考 ThreadLocal对Entry的引用是弱引用,于是联想到四种引用的生命周期. 强引用,不会进行垃圾回收 软引用,JVM内存不够,进行回收 弱引用,下次 ...
- .net core options 依赖注入的方式
options 依赖注入的方式 public class JwtSettingsOptions { public const string JwtSettings = "JwtSetting ...
- Python 数字类型转换
Python数字类型转换: int(x):将 x 转换为一个整数 float(x):将 x 转换为一个浮点数 complex(x,y):将 x 和 y 转换为一个复数.x 为复数的实部,y 为复数的虚 ...
- Python for循环通过序列索引迭代
Python for 循环通过序列索引迭代: 注:集合 和 字典 不可以通过索引进行获取元素,因为集合和字典都是无序的. 使用 len (参数) 方法可以获取到遍历对象的长度. 程序: strs = ...
- Python 实现分层聚类算法
''' 1.将所有样本都看作各自一类 2.定义类间距离计算公式 3.选择距离最小的一堆元素合并成一个新的类 4.重新计算各类之间的距离并重复上面的步骤 5.直到所有的原始元素划分成指定数量的类 程序要 ...
- 部分浏览器 set-cookie 不成功踩坑记录
事件起因: 公司正在做一个sso的单点登录的项目,做完之后,在测试阶段,不同的终端的兼容测试时候,好几个不同的浏览器出现了不同的问题,有登录之后自动退出,有登陆不成功等问题. 在 pc 端只有 uc ...
- PHP date_sun_info() 函数
------------恢复内容开始------------ 实例 返回有关 2013 年 1 月 1 日,纬度 31.7667,经度 35.2333 的日出/日落和黄昏开始/黄昏结束的信息: < ...
- [草稿]Skill 中如何读取一个文件并打印出来
https://www.cnblogs.com/yeungchie/ path = "~/hello" file = infile(path) while(gets(x file) ...
- IOFFSETOF ICONTAINEROF IQUEUE_ENTRY
#include <iostream> #define IOFFSETOF(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) #de ...