MIT6.828——Lab2内存管理准备知识

保护模式内存管理机制

MIT6.828——Lab1 PartA

MIT6.828——Lab1 PartB

分段机制的问题

​ 分段的主要问题,出现在内存不足或者内存碎片过多的情况下。对于一个程序而言,例如其代码段长度就和其代码的长度直接相关,各个段的大小是不固定的,不能拆分的,要装入内存便一次性将一个整段都装入,因此在内存紧张时,就会出现问题。可以想象到的解决方法有这样一种: 将段换出到磁盘上,从而空出一部分的物理内存空间。但是同样的,如果段长度过长,内存过小,在频繁的换入换出也无济于事。

​ 问题的本质在于,分段机制下产生的 连续线性地址,被认为在 物理内存上也是连续的,线性地址就是物理地址。但是我们 可用的物理地址并不是连续的,因此就会产生冲突。所以解决这个问题的关键在于,是否可以: 线性地址连续,对应的物理地址不连续?为了解除这种一一映射的关系,便可以通过地址映射。

​ 即 线性地址(虚拟地址)——页表——> 物理地址

MIT6.828——Lab2内存管理准备知识

​ 为了效率的问题,这种映射关系写在页表里,页表在内存中,查表的工作由硬件完成。

分页机制的思想:通过映射,可以使得连续的线性地址与任意的物理地址相关联,逻辑上连续的线性地址对应的物理地址可以不连续。

分段机制的作用:将线性地址转化为物理地址;用大小相等(4KB)的页代替大小不等的段。

MIT6.828——Lab2内存管理准备知识

页表结构

一级页表

首先,对于一级页表,寻址过程可以用下图来表示

[En]

First of all, for the first-level page table, the addressing process can be represented by the following figure

MIT6.828——Lab2内存管理准备知识

可以看到,分页机制仍旧是基于分段基础上的。将分段形成的线性地址,进行划分,利用高20位作为在页表内寻址的偏移量,寻址页表项,其中页表项的基址(物理地址)放在CR3寄存器中。再利用低12位,结合页表项给出的基址,合成物理地址,送上地址总线,即可寻址物理内存单元。可以看到,因为划分了低12位为页内偏移,因此页表的大小也就是4KB,这是一个常用的页大小值。而高20位,则说明,页表中含1M项页表项,占内存位4MB。

二级页表

​ 在一级页表的铺垫下,便有了另一个问题。页表的大小为4M,且必须提前建立好,每个进程都有自己的页表,如果进程数很多,页表的内存开销便很可观,因此是否可以动态的创建页表项呢?解决这个问题的答案就是二级页表。二级页表的思想是,将1M页平均放到1K个页表中,每个页表1K个页表项,占据内存位4KB,刚好为一页的大小。为了存储这些页表,引入了页目录。每个页表的物理地址,都在页目录中以页目录项的形式存储。因为最多1K页表,因此页目录大小也为4KB,一页的大小。

​ 二级页表下的寻址过程如下:

MIT6.828——Lab2内存管理准备知识

​ 现在需要了解一下,页表项和页目录项的详细信息了。这部分信息,可以在Intel系统开发手册上得到详细说明。

MIT6.828——Lab2内存管理准备知识

这里的直接拦截部分显示:

[En]

Here the direct intercept section shows:

MIT6.828——Lab2内存管理准备知识

开启分页机制

要打开分页机制,您需要做三件事:

[En]

To turn on the paging mechanism, you need to do three things:

  • 准备好页表和页目录
  • 将页目录的物理地址写入CR3
  • 寄存器CR0的PG位置1

编程实例

在之前的 mit6.828实验1中,已经看到了一个比较基本的实例,如何进入保护模式,并进行分页操作。为了对于 lab2有一个更好的理解,这里截取一部分《操作系统真象还原》的代码进行解释说明。这里可以和 lab1部分结合来看

建立GDT进入保护模式

GDT_BASE:   dd    0x00000000
            dd    0x00000000

CODE_DESC:  dd    0x0000FFFF
            dd    DESC_CODE_HIGH4

DATA_STACK_DESC:dd    0x0000FFFF
                dd    DESC_DATA_HIGH4

VIDEO_DESC: dd    0x80000007
            dd    DESC_VIDEO_HIGH4

GDT_SIZE    equ   $ - GDT_BASE
GDT_LIMIT   equ   GDT_SIZE -1

gdt_ptr  dw  GDT_LIMIT
         dd  GDT_BASE

;-----------------  打开A20  ----------------
in al,0x92
or al,0000_0010B
out 0x92,al
;-----------------  加载GDT  ----------------
lgdt [gdt_ptr]
;-----------------  cr0第0位置1  ----------------
mov eax, cr0
or eax, 0x00000001
mov cr0, eax

jmp dword SELECTOR_CODE:p_mode_start         ; 刷新流水线
[bits 32]
p_mode_start:
   mov ax, SELECTOR_DATA
   mov ds, ax
   mov es, ax
   mov ss, ax
   mov esp,LOADER_STACK_TOP
   mov ax, SELECTOR_VIDEO
   mov gs, ax

按约定,GDT的第一个段描述符为空,这里建立了三个段,都是按照段描述符的规格进行性质填充,对照相关位的含义既可以知道段的信息。为了装载GDT,使用命令 lgdt即可。进入保护模式后,寻址就需要使用段选择子,这在之前的 lab1中也说到了。

开启分页

首先,规划好内存的整体布局,可以先画下图

[En]

First of all, plan the overall layout of the memory, you can first draw the following picture

MIT6.828——Lab2内存管理准备知识

人为规定的,将页目录放在了物理地址 0x100000处,将第一个页表,放在了物理地址 0x101000处。同时划分进程的虚拟地址空间位高端 1GB内核空间和低端 3GB用户控件。首先需要注意的是,在虚拟内存空间中,将高端 1GB完全分给了内核。这对于每个进程都是一样的,为了实现所有进程的内核共享,这部分空间固定占据了页目录项的第 0xc00项至第 1023项。对于页目录而言,第 0项存储了第 0个页表的位置,最后一项存储了页目录自身在物理内存中的位置。

值得注意的是,页表 0和页表 c00都映射到了物理内存的低端 1MB。这么做的原因是,在内核加载到内存空间之前,运行的一直是 loader程序,它运行在低端 1MB。为了保证之前段机制下的地址和现在分页后的地址一致,内核的前 1MB也需要映射到物理内存低端 1MB空间。低端的 1MB=256*4KB,因此占据了 256页,需要 256个页表项。

在这一节的解释之后,具体实现如下:

[En]

After the explanation in this section, here is the specific implementation:

setup_page:
;先把页目录占用的空间逐字节清0
   mov ecx, 4096
   mov esi, 0
.clear_page_dir:
   mov byte [PAGE_DIR_TABLE_POS + esi], 0
   inc esi
   loop .clear_page_dir

;开始创建页目录项(PDE)
.create_pde:                     ; 创建Page Directory Entry
   mov eax, PAGE_DIR_TABLE_POS
   add eax, 0x1000               ; 此时eax为第一个页表的位置及属性
   mov ebx, eax                  ; 此处为ebx赋值,是为.create_pte做准备,ebx为基址。

;   下面将页目录项0和0xc00都存为第一个页表的地址,
;   一个页表可表示4MB内存,这样0xc03fffff以下的地址和0x003fffff以下的地址都指向相同的页表,
;   这是为将地址映射为内核地址做准备
   or eax, PG_US_U | PG_RW_W | PG_P          ; 页目录项的属性RW和P位为1,US为1,表示用户属性,所有特权级别都可以访问.

   mov [PAGE_DIR_TABLE_POS + 0x0], eax       ; 第1个目录项,在页目录表中的第1个目录项写入第一个页表的位置(0x101000)及属性
   mov [PAGE_DIR_TABLE_POS + 0xc00], eax     ; 一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间,
                                             ; 也就是页表的0xc0000000~0xffffffff共计1G属于内核,0x0~0xbfffffff共计3G属于用户进程.

   sub eax, 0x1000
   mov [PAGE_DIR_TABLE_POS + 4092], eax      ; 使最后一个目录项指向页目录表自己的地址

;下面创建页表项(PTE)
   mov ecx, 256                              ; 1M低端内存 / 每页大小4k = 256
   mov esi, 0
   mov edx, PG_US_U | PG_RW_W | PG_P         ; 属性为7,US=1,RW=1,P=1
.create_pte:                                 ; 创建Page Table Entry
   mov [ebx+esi*4],edx                       ; 此时的ebx已经在上面通过eax赋值为0x101000,也就是第一个页表的地址
   add edx,4096
   inc esi
   loop .create_pte

;创建内核其它页表的PDE
   mov eax, PAGE_DIR_TABLE_POS
   add eax, 0x2000           ; 此时eax为第二个页表的位置
   or eax, PG_US_U | PG_RW_W | PG_P  ; 页目录项的属性RW和P位为1,US为0
   mov ebx, PAGE_DIR_TABLE_POS
   mov ecx, 254              ; 范围为第769~1022的所有目录项数量
   mov esi, 769
.create_kernel_pde:
   mov [ebx+esi*4], eax
   inc esi
   add eax, 0x1000
   loop .create_kernel_pde
   ret

在建立虚拟内存布局后,可以正式打开分页机制。

[En]

After the layout of the virtual memory is established, the paging mechanism can be officially turned on.

call setup_page

;要将描述符表地址及偏移量写入内存gdt_ptr,一会用新地址重新加载
sgdt [gdt_ptr]        ; 存储到原来gdt所有的位置

;将gdt描述符中视频段描述符中的段基址+0xc0000000
mov ebx, [gdt_ptr + 2]
or dword [ebx + 0x18 + 4], 0xc0000000      ;视频段是第3个段描述符,每个描述符是8字节,故0x18。
;段描述符的高4字节的最高位是段基址的31~24位

;将gdt的基址加上0xc0000000使其成为内核所在的高地址
add dword [gdt_ptr + 2], 0xc0000000

add esp, 0xc0000000        ; 将栈指针同样映射到内核地址

; 把页目录地址赋给cr3
mov eax, PAGE_DIR_TABLE_POS
mov cr3, eax

; 打开cr0的pg位(第31位)
mov eax, cr0
or eax, 0x80000000
mov cr0, eax

;在开启分页后,用gdt新的地址重新加载
lgdt [gdt_ptr]             ; 重新加载

Original: https://www.cnblogs.com/oasisyang/p/15421981.html
Author: OasisYang
Title: MIT6.828——Lab2内存管理准备知识

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/524441/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • [云计算]TCA云架构-思维导图

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/Skybiubiu/p/15962992.htmlAut…

    Linux 2023年6月13日
    0135
  • springboot JDBC整合

    1、建立一个springboot项目,并导包:JDBC API 与 MySQL Driver 2、项目建好之后,发现自动帮我们导入了如下的启动器: <dependency&g…

    Linux 2023年6月14日
    0124
  • 防火墙NAT配置与DHCP下发

    该实验如果有做的不足的地方请见谅 实验目标: 按要求划分区域,公司内部办公区为trust,服务器区为dmz,外部网络为untrust。 PC1和PC2为公司内部办公区,需要从防火墙…

    Linux 2023年6月7日
    0115
  • ShardingSphere-proxy-5.0.0企业级分库分表、读写分离、负载均衡、雪花算法、取模算法整合(八)

    一、简要说明 以下配置实现了: 1、分库分表 2、每一个分库的读写分离 3、读库负载均衡算法 4、雪花算法,生成唯一id 5、字段取模 6、解决笛卡尔积问题 7、设置默认所有表不进…

    Linux 2023年6月14日
    095
  • Linux 配置Java环境变量

    前言:请各大网友尊重本人原创知识分享,谨记本人博客: 南国以南i 注:目前在官网下载的时候需要登陆,这边分享一个账号,方便下载 账号:2696671285@qq.com密码:Ora…

    Linux 2023年6月14日
    093
  • 2021年度总结 2022年度规划

    2021年 计划 1、学习更多的知识😁 2、学习408的知识,至少能熟悉计算机组成原理、操作系统、计算机网络、算法这几个的联系,区别等。😁 3、整理408的知识到博客上。 (一篇未…

    Linux 2023年6月13日
    097
  • Linux高可用之Keepalived

    注意: 各节点时间必须同步 确保各节点的用于集群服务的接口支持MULTICAST通信(组播); 安装 从CentOS 6.4开始keepalived随系统base仓库提供,可以使用…

    Linux 2023年5月27日
    0144
  • grafana+prometheus如何查看tcp连接数量

    最后解决方案 经过和负责监控的大佬了解,获得了一个可行的方案:在每个pod中新增一个sidecar容器,在容器中部署node_exporter,或者在容器中放个自动查看端口连接数并…

    Linux 2023年6月13日
    0113
  • Redis基础

    1.简介 Redis (远程字典服务器)是一 个开源的、使用C语言编写的NoSQL数据库。Redis基于内存运行并支持持久化,采用 key-value (键值对)的存储形式,是目前…

    Linux 2023年6月13日
    092
  • 博客园装饰——(一)置顶菜单栏

    功能描述:当页面向下滚动到菜单栏上边沿触碰到浏览器窗口上边沿时,菜单栏会固定地显示在浏览器窗口上方(贴紧),即达到了置顶菜单栏的效果。而当页面向上滚动到原来的位置时,菜单栏又会自动…

    Linux 2023年6月14日
    0118
  • Linux之vim编辑器

    1.vim三种模式 模式 操作 可视模式 可查看内容 编辑模式 可查看可修改内容 命令行模式 给vim发送控制命令,可查看内容 注:打开文件,默认是可视模式 2.三种模式的切换 可…

    Linux 2023年6月6日
    0102
  • 【论文笔记】(模型压缩)Do Deep Nets Really Need to be Deep?

    摘要 作者通过模型压缩(model compression)使浅层的网络学习与深层网络相同的函数,以达到深层网络的准确率(accuracy)。当与深浅模型的参数量相同时,浅层模型可…

    Linux 2023年6月7日
    0113
  • 接口压测提示redis获取不到连接数,出现timeout waiting for idle object异常问题定位

    博客园 :当前访问的博文已被密码保护 请输入阅读密码: Original: https://www.cnblogs.com/qmfsun/p/11583355.htmlAuthor…

    Linux 2023年5月28日
    099
  • 对脱壳脚本的一些改进–识别出目标DEX

    一、前言 通常对于加壳的程序,第一步的操作通常是脱壳,而现在脱壳一般都选择利用 frida 来进行 hook 进行脱壳,不谈其他脱壳方式,利用 frida 脱壳原理大致分为两种: …

    Linux 2023年6月8日
    0106
  • Netty-如何写一个Http服务器

    前言 动机 最近在学习Netty框架,发现Netty是支持Http协议的。加上以前看过Spring-MVC的源码,就想着二者能不能结合一下,整一个简易的web框架(PS:其实不是整…

    Linux 2023年6月7日
    0109
  • 实验一 密码引擎-3-电子钥匙功能测试

    任务详情 1 解压”龙脉密码钥匙驱动实例工具等”压缩包2 在Ubuntu中运行 “龙脉密码钥匙驱动实例工具等\mToken-GM3000\skf…

    Linux 2023年6月8日
    0111
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球