• 分页机制总结


    【0】写在前面(分页机制)

    • 0.0) source code from orange’s implemention of a os and text description from Zhaojiong’s perfect analysis of Linux kernel and for complete code ,please visit https://github.com/pacosonTang/Orange-s-OS/blob/master/p67.asm
    • 0.1)本代码旨在演示 怎样开启分页机制 + 怎样构建页目录和页表
    • 0.3)本文 只对 与 分页机制的 代码进行简要注释,言简意赅;
    • 0.4) 为了让广大小白(像我一样对os分页机制不觉明里的小白)真真切切了解分页机制,即使这篇文章是转载自 “0.0” 中的两本,但我还是将本文归为原创以推荐到博客首页;(版权,我已在0.0中声明了)

    ; ==========================================
    ; pmtest6.asm
    ; 编译方法:nasm pmtest6.asm -o pmtest6.com
    ; ==========================================
    
    %include    "pm.inc"    ; 常量, 宏, 以及一些说明
    
    PageDirBase  equ    200000h ; 页目录开始地址: 2M
    PageTblBase  equ    201000h ; 页表开始地址: 2M+4K
    
    org 0100h
     jmp    LABEL_BEGIN
    
    [SECTION .gdt]
    ; GDT
    ;                            段基址,       段界限, 属性
    LABEL_GDT:           Descriptor 0,              0, 0     ; 空描述符
    LABEL_DESC_NORMAL:   Descriptor 0,         0ffffh, DA_DRW    ; Normal 描述符
    

    ; 页目录描述符 [add]

    LABEL_DESC_PAGE_DIR: Descriptor PageDirBase, 4095, DA_DRW;Page Directory
    

    ; 页表描述符 [add]

    LABEL_DESC_PAGE_TBL: Descriptor PageTblBase, 1023, DA_DRW|DA_LIMIT_4K;Page Tables
    
    LABEL_DESC_CODE32:   Descriptor 0, SegCode32Len-1, DA_C+DA_32    ; 非一致代码段, 32
    LABEL_DESC_CODE16:   Descriptor 0,         0ffffh, DA_C  ; 非一致代码段, 16
    LABEL_DESC_DATA:     Descriptor 0,      DataLen-1, DA_DRW    ; Data
    LABEL_DESC_STACK:    Descriptor 0,     TopOfStack, DA_DRWA + DA_32  ; Stack, 32 位
    LABEL_DESC_VIDEO:    Descriptor 0B8000h,   0ffffh, DA_DRW    ; 显存首地址
    ; GDT 结束
    
    GdtLen   equ    $ - LABEL_GDT  ; GDT长度
    GdtPtr   dw GdtLen - 1  ; GDT界限
      dd    0    ; GDT基地址
    
    ; GDT 选择子
    SelectorNormal   equ    LABEL_DESC_NORMAL   - LABEL_GDT
    SelectorPageDir  equ    LABEL_DESC_PAGE_DIR - LABEL_GDT
    SelectorPageTbl  equ    LABEL_DESC_PAGE_TBL - LABEL_GDT
    SelectorCode32   equ    LABEL_DESC_CODE32   - LABEL_GDT
    SelectorCode16   equ    LABEL_DESC_CODE16   - LABEL_GDT
    SelectorData     equ    LABEL_DESC_DATA  - LABEL_GDT
    SelectorStack    equ    LABEL_DESC_STACK    - LABEL_GDT
    SelectorVideo    equ    LABEL_DESC_VIDEO    - LABEL_GDT
    ; END of [SECTION .gdt]
    
    [SECTION .data1]     ; 数据段
    ALIGN   32
    [BITS   32]
    LABEL_DATA:
    SPValueInRealMode   dw  0
    ; 字符串
    PMMessage:   db "In Protect Mode now. ^-^", 0   ; 进入保护模式后显示此字符串
    OffsetPMMessage  equ    PMMessage - $$
    DataLen  equ    $ - LABEL_DATA
    ; END of [SECTION .data1]
    
    
    ; 全局堆栈段
    [SECTION .gs]
    ALIGN   32
    [BITS   32]
    LABEL_STACK:
     times 512 db 0
    
    TopOfStack  equ $ - LABEL_STACK - 1
    
    ; END of [SECTION .gs]
    
    
    [SECTION .s16]
    [BITS   16]
    LABEL_BEGIN:
     mov    ax, cs
     mov    ds, ax
     mov    es, ax
     mov    ss, ax
     mov    sp, 0100h
    
     mov    [LABEL_GO_BACK_TO_REAL+3], ax
     mov    [SPValueInRealMode], sp
    
     ; 初始化 16 位代码段描述符
     mov    ax, cs
     movzx  eax, ax
     shl    eax, 4
     add    eax, LABEL_SEG_CODE16
     mov    word [LABEL_DESC_CODE16 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE16 + 4], al
     mov    byte [LABEL_DESC_CODE16 + 7], ah
    
     ; 初始化 32 位代码段描述符
     xor    eax, eax
     mov    ax, cs
     shl    eax, 4
     add    eax, LABEL_SEG_CODE32
     mov    word [LABEL_DESC_CODE32 + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_CODE32 + 4], al
     mov    byte [LABEL_DESC_CODE32 + 7], ah
    
     ; 初始化数据段描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_DATA
     mov    word [LABEL_DESC_DATA + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_DATA + 4], al
     mov    byte [LABEL_DESC_DATA + 7], ah
    
     ; 初始化堆栈段描述符
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_STACK
     mov    word [LABEL_DESC_STACK + 2], ax
     shr    eax, 16
     mov    byte [LABEL_DESC_STACK + 4], al
     mov    byte [LABEL_DESC_STACK + 7], ah
    
     ; 为加载 GDTR 作准备
     xor    eax, eax
     mov    ax, ds
     shl    eax, 4
     add    eax, LABEL_GDT   ; eax <- gdt 基地址
     mov    dword [GdtPtr + 2], eax ; [GdtPtr + 2] <- gdt 基地址
    
     ; 加载 GDTR
     lgdt   [GdtPtr]
    
     ; 关中断
     cli
    
     ; 打开地址线A20
     in al, 92h
     or al, 00000010b
     out    92h, al
    
     ; 准备切换到保护模式
     mov    eax, cr0
     or eax, 1
     mov    cr0, eax
    
     ; 真正进入保护模式
     jmp    dword SelectorCode32:0  ; 执行这一句会把 SelectorCode32 装入 cs, 并跳转到 Code32Selector:0  处
    
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    LABEL_REAL_ENTRY:    ; 从保护模式跳回到实模式就到了这里
     mov    ax, cs
     mov    ds, ax
     mov    es, ax
     mov    ss, ax
    
     mov    sp, [SPValueInRealMode]
    
     in al, 92h  ; ┓
     and    al, 11111101b   ; ┣ 关闭 A20 地址线
     out    92h, al  ; ┛
    
     sti     ; 开中断
    
     mov    ax, 4c00h   ; ┓
     int    21h  ; ┛回到 DOS
    ; END of [SECTION .s16]
    
    
    [SECTION .s32]; 32 位代码段. 由实模式跳入.
    [BITS   32]
    
    LABEL_SEG_CODE32:
    

    ; 开启分页机制的初始化工作,该指令执行后,cpu即可开启分页机制[add]

     call   SetupPaging
    
     mov    ax, SelectorData
     mov    ds, ax   ; 数据段选择子
     mov    ax, SelectorVideo
     mov    gs, ax   ; 视频段选择子
    
     mov    ax, SelectorStack
     mov    ss, ax   ; 堆栈段选择子
    
     mov    esp, TopOfStack
    
    
     ; 下面显示一个字符串
     mov    ah, 0Ch  ; 0000: 黑底    1100: 红字
     xor    esi, esi
     xor    edi, edi
     mov    esi, OffsetPMMessage    ; 源数据偏移
     mov    edi, (80 * 10 + 0) * 2  ; 目的数据偏移。屏幕第 10 行, 第 0 列。
     cld
    .1:
     lodsb
     test   al, al
     jz .2
     mov    [gs:edi], ax
     add    edi, 2
     jmp    .1
    .2: ; 显示完毕
    
     ; 到此停止
     jmp    SelectorCode16:0
    

    ; [add]启动分页机制 ————————————————————–

    SetupPaging:
    

    ; 为简化处理, 所有线性地址对应相等的物理地址.
    ; 首先初始化页目录

     mov    ax, SelectorPageDir ; 此段首地址为 PageDirBase
     mov    es, ax
     mov    ecx, 1024    ; 共 1K 个表项
     xor    edi, edi
     xor    eax, eax
     mov    eax, PageTblBase | PG_P  | PG_USU | PG_RWW ; PageTblBase equ 201000h 页表开始地址: 2M+4K
    

    ; 这里是在初始化页目录中项的内容,即对应页表的内存地址

    .1: 
     stosd 
    

    ;stosb, stosw, stosd 把al/ ax/ eax的内容存储到 es:edi 指向的内存单元中, 该指令执行后,edi自增1

     add    eax, 4096    
    

    ; 为了简化, 所有页表在内存中是连续的.,每个页表占用4k字节空间

     loop   .1
    

    ; 初始化页目录的项内容 over
    ; 再初始化所有页表 (1K 个, 4M 内存空间)的项内容,即页表的项存储的是内存地址的高20位地址;

     mov    ax, SelectorPageTbl ; 此段首地址为 PageTblBase
     mov    es, ax
     mov    ecx, 1024 * 1024    ; 共 1M 个页表项, 也即有 1M 个页 (因为1k个页目录项,每个目录项映射到1k个页表项)
     xor    edi, edi
     xor    eax, eax
     mov    eax, PG_P  | PG_USU | PG_RWW
    .2:
     stosd
     add    eax, 4096    ; 每一页指向 4K 的空间
     loop   .2
    

    ; 初始化页表的项内容 over

     mov    eax, PageDirBase
    

    ; 加载页目录的基地址到 cr3

     mov    cr3, eax
    

    ; 设置cr0的PG位=1,开启分页机制

     mov    eax, cr0
     or eax, 80000000h
     mov    cr0, eax
     jmp    short .3
    .3:
     nop
    
     ret
    

    ; 分页机制启动完毕 ———————————————————-

    SegCode32Len    equ $ - LABEL_SEG_CODE32
    ; END of [SECTION .s32]
    
    
    ; 16 位代码段. 由 32 位代码段跳入, 跳出后到实模式
    [SECTION .s16code]
    ALIGN   32
    [BITS   16]
    LABEL_SEG_CODE16:
     ; 跳回实模式:
     mov    ax, SelectorNormal
     mov    ds, ax
     mov    es, ax
     mov    fs, ax
     mov    gs, ax
     mov    ss, ax
    
     mov    eax, cr0
     and    eax, 7FFFFFFEh   ; PE=0, PG=0
     mov    cr0, eax
    
    LABEL_GO_BACK_TO_REAL:
     jmp    0:LABEL_REAL_ENTRY  ; 段地址会在程序开始处被设置成正确的值
    
    Code16Len   equ $ - LABEL_SEG_CODE16
    
    ; END of [SECTION .s16code]
    

    3.3 页式存储总结

    分段机制将逻辑地址转换成线性地址,线性地址通过分页机制转换成物理地址。
    这里写图片描述
    为什么使用分页?(干货)
    分页管理机制的目的在于实现虚拟存储器,线性地址中的任意一个页都能映射到物理地址中的任何一个页,使得内存管理机制特别灵活。
    分页和分段的最大不同之处在于?(干货)

    • 1)段的长度不固定: 段的长度通常与存放在其中的代码和数据结构具有相同的长度;
    • 2)页面长度固定:页面具有固定的长度;
    • 3)如果仅使用分段地址转换:那么存储在物理内存中的一个数据结构将包含所有的部分;
    • 4)如果使用了分页,那么一个数据结构就可以一部分存储在物理内存中,一部分存储在磁盘上(虚拟存储器);
    • 5)为了减少地址转换所要求的总线总起数量,最近访问的页目录和页表会被存放在处理器的缓冲器件中,该缓冲器件被称为TLB(translation lookaside buffer,翻译后备缓冲寄存器),提高访存效率;

    3.3.1 分页机制概述

    这里写图片描述
    Why-为什么使用两极页表结构?

    • w1)页表含有2^20(1M)个表项,每项占4字节。如果用一个表来存储的话,将最多占用4M;为减少内存占用量,X86使用了两极页表;
    • w2)每个,仅有一个页目录占用4k, 每个页表占用4k,而要知道页目录一定常驻内存,而页表是在需要的时候才占用内容空间,当然常用的页表会存储在TLB中,这在一定程度上减少了
      页表机制进行线性地址与物理地址映射所占用内存空间;(干货)

    • 1)第一级页表——页目录(bit31~22):页目录项存储的内容是页表的基地址(高20位存储页表基地址,低12位存储所指向的页表属性)(干货); 它被存放在1页4k页面中, 含有1k个4字节长度的表项,通过线性地址的bit31~22进行索引页目录表项;

    • 2)第二级页表——页表(bit21~12):页表项存储的内容是物理页的基地址(高20位存储物理页基地址,低12位存储物理页属性)(干货); 它被存放在1页4k页面中,含有1k个4字节长度的表项,而该页表的表项由 bit21~12进行定位;

    • 3)偏移地址(bit11~0):它存储的是物理页的第12位地址(干货); 把页表项存储的高20位地址作为物理页的高20位地址,而偏移地址12位作为物理页的低12位地址,这样就得到了32位的物理地址;

    • 4)映射到的物理地址空间:页长4k,总共有1k个页目录项,每个页目录项映射一张页表,每张页表有1k个页表项;故总共1k * 1k =1M个页表项;所以物理地址空间=1M * 4k = 4G

    (you should know):
    Y1)页目录的基地址存储在cr3中;
    Y2)开启分页机制,要设置cr0的最高位PG位=1;

    版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 相关阅读:
    win10+vs2010 安装Silverlight 安装说明
    常用小方法
    .net 技术学习进阶
    NetMQ——推拉模式 Push-Pull
    NetMQ使用——发布订阅模式 Publisher-Subscriber
    NetMQ使用——请求响应模式 Request-Reply
    跨终端Web
    七大排序算法
    JDK线程池
    Redis为什么这么快
  • 原文地址:https://www.cnblogs.com/pacoson/p/4893158.html
Copyright © 2020-2023  润新知