首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

详解操作系统分页机制与实战

32 位的地址总线提供了 4GB 内存的寻址能力,但程序的运行受限于实际的内存容量,同时,系统在启动时又很难预先定义每个进程究竟要分配多大的段空间来满足每一个应用程序的需要。...,让出内存供其他任务使用 虚拟化 — 离散化的解决方案引入了一个新的问题: 同一个物理地址在不同的时间可能载入不同任务所对应的内存,同一个任务在不同时间使用的相同变量又可能位于不同的物理地址中,要解决这些问题就必须要通过虚拟化的方式...从 80386 开始,内存被分为 4KB 固定大小的“页”,他们在需要使用时载入内存,不需要使用时可以被置换到磁盘上,由分页机制将程序持有的固定的线性地址动态映射到物理地址上。...为了以最快速度上手实战,我们这里不考虑线性地址与物理地址的映射关系,直接取线性地址 = 物理地址,同时假设内存地址空间足够容纳所有页面,并且页、页表在内存中均连续。...mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32

1.1K30

进军保护模式

,所以在进入保护模式前的最后一步,就是要置位 CR0 的 PE 位,来开启保护模式。...执行步骤 要进入保护模式,需要按照上述描述,按下列步骤进行一系列操作: 准备 GDT 通过 lgdt 指令加载 gdtr 通过 cli 指令关闭硬件中断 打开 A20 地址线 置 cr0 的 PE 位,...mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32:0 ; 执行这一句会把...代码的加载地址 可以看到,在程序起始处,我们定义了 org 0100h,这是 DOS 操作系统内存加载的地址,我们没有如同开始时那样,让程序被加载到 0c700h,来让虚拟机直接加载。...到保护模式的长跳转 代码中在进行上述一系列操作后,进行了一个跳转: jmp dword SelectorCode32:0 通过 jmp 命令,将段选择子 SelectorCode32 地址处的内存值载入

67120
  • 您找到你想要的搜索结果了吗?
    是的
    没有找到

    保护模式进阶 -- 再回实模式

    原理描述 还记得我们是怎么从实地址模式进入保护模式的吗: 准备 GDT 通过 lgdt 指令加载 gdtr 通过 cli 指令关闭硬件中断 打开 A20 地址线 置 cr0 的 PE 位,打开保护模式...在实地址模式下,除 CS 外所有的缓冲寄存器都必须拥有相同的段界限:0xffff,以及段属性:0x92,CS 对应的高速缓冲寄存器的段属性则必须为 0x98。...跳回实地址模式切换步骤 剩下的工作就比较简单了,只要按照跳转到保护模式时操作的反操作即可: 段描述符高速缓冲寄存器赋值 复位 PE 位,切换到实地址模式 跳转到实地址模式代码起始地址 关闭 A20 地址线...如果在触发 INT 21H 时,AH 值为 4CH,则退出当前程序。 4.2. 创建用于编写返回代码的代码段和 4.2.1....mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32

    64810

    写一个Loader引导加载程序

    然后我们的loader需要把处理器切换到保护模式,这样就有了32位的寻址空间。最终再切换到IA-32e模式(长模式),获得64位的寻址空间。...寄存器的第0位,开启保护模式 mov eax, cr0 or eax, 1 mov cr0, eax ; 为fs寄存器加载新的数据段的值 mov ax, SelectorData32...mov eax, cr0 and al, 11111110b ; 将第0位置0 mov cr0, eax sti ; 开启外部中断 jmp $ 接着我们在qemu中启动操作系统...大致流程如下 屏蔽外部中断 加载GDT的基地址和长度到GDTR寄存器 置位CR0的PE标志位 执行远跳转,切换到保护模式的代码段(将代码段寄存器更新为保护模式) 重新加载数据段选择子,或使用jmp/call...将页目录的物理基地址加载到CR3中 置位IA32_EFER寄存器的LME标志位,开启IA-32e模式 置位CR0的PG标志位,开启分页机制,此时处理器会自动置位IA32_EFER寄存器的LMA标志位 最后一个远跳转指令

    69420

    实战操作系统 loader 编写(上) -- 进入保护模式

    创建 GDT 及对应的段选择子 在段内编写保护模式代码 将 GDT 首地址通过 lgdt 指令载入 gdtr 关闭硬件中断 打开 A20 地址总线 置位 cr0 寄存器的保护模式标志位 长跳转进入保护模式...如今,我们自己编写的 boot 可以直接指定 loader 被载入内存的起始物理地址,这样,我们在代码编写时就可以计算出进入保护模式的起始位置,因此,再也不需要之前那种 treak 的方法,直接可以在代码中编写操作数实现上述操作了...总结 本文详细介绍了 loader 中关键性的两个步骤: 将内核载入内存 进入保护模式 正所谓“厚积薄发”,此前我们关于保护模式原理的一系列介绍和总结所积累的大量代码终于派上用场,本文的代码也就显得非常简单易懂了...mov eax, cr0 or eax, 1 mov cr0, eax ; 真正进入保护模式 jmp dword SelectorFlatC...cr3, eax mov eax, cr0 or eax, 80000000h mov cr0, eax jmp short .3 .3

    1.1K20

    进入Linux内核前的准备

    可以使用32位指令。32位的x86 CPU用做高速的8086。在实模式下,所有的段都是可以读、写和可执行的。...先让我们回忆一下在加载启动区时,为什么要给ds赋值0x07c0但是实际在内存中的基址是0x7c00,我们当时说,这是为了x86能够在16位实模式下访问20根地址线,会把给ds寄存器的值左移4位得到基址再加偏移地址来进行计算...而当CPU切换到32位保护模式后,内存地址的计算方式还会改变,首先ds寄存器里面的值在实模式下称为段基址,在保护模式下叫做段选择子,段选择子里存储着段描述符的索引。...(cs) 模式这个状态字保存在机器状态字寄存器cr0中,当我们准备好切换到保护模式时,前两行汇编做的就是将cr0这个寄存器的位0置1,我们就从实模式切换到保护模式了。...分段机制就是我们之前几个标题中讨论的,目的是为每个程序或者任务提供单独的代码段cs,数据段ds,栈段ss,使其不会互相干扰。分段机制在Intel的保护模式下是必须开启的。

    5.6K20

    操作系统开发:编写开机引导

    BOIS 是如何苏醒的 BIOS 基本输入输出系统,BIOS代码所做的工作是一成不变的,所以他是被固化到ROM中的一块只读区域中,在开机时此ROM会被映射到低端1MB内存的顶部,原因是系统在开启时默认是实地址模式...在实地址模式下,寻址是按照[段基址+段内偏移]的形式进行,而在保护模式下为了保证兼容性,其也必须遵循这一规范。...mov cr0, eax 最后还需要使用jmp SELECTOR_CODE:p_mode_start指令来实现刷新流水线。...由于实模式是16位的,而保护模式是32位,在切换时必须要清空当前流水线上面所有的16位指令集,以及错误的段属性,只有这样才能保证后面的32位指令能够被正确的执行。...此时我们既要改变代码段描述符缓冲寄存器的值,又要清空以前的流水线,使用JMP指令则可以达到这两种效果,JMP指令在执行无条件跳转时会自动的将所有段寄存器初始化并清空当前流水线上的指令集。

    66730

    xv6(2) 启动代码部分

    #将值写回到CR0 从此开始进入保护模式,16 位的 CPU 变成了 32 位的 CPU,此刻前后的指令格式也是不一样的,在此之前使用的 16 位指令,在此之后使用的 32 位指令,这里所说的多少位的指令不是说这个指令的长度...问题就出在这儿,进入保护模式后流水线上可能还存在 16位的指令,所以进入保护模式后需要清空流水线,无条件跳转 $jmp$ 指令可以用来清空流水线: ljmp $(SEG_KCODE的指令,而后面应该用32位保护模式下的指令 这里就是使用了一个长跳指令来刷新流水线,顺便设置 $CS$ 和 $EIP$ 寄存器,因为现在是保护模式了,段寄存器的可见部分应存放的是段选择子,...那为什么要使用页面大小扩展呢?...虚拟地址到物理地址需要转换,但又因为值是相同的,所以有了页表项: 虚拟地址空间的 $[0,4M)$ 映射到物理地址 $[0,4M)$ ④跳到 $main$ 函数执行时,为什么使用间接跳转 jmp *%eax

    39800

    实战分页机制实现 -- 通过实际内存大小动态调整页表个数

    如果内存总共只要 8MB,那上面的分页程序执行完,光是页表就占用了 4MB,空间已经所剩无几,可见,按需使用内存,合理规划页表的大小是非常重要的,而这一切的前提是必须要搞清楚内存总共有多少。...分配 ARDS 存储空间 我们需要在数据段中开辟出一块内存用来存储若干个 ARDS 结构,同时为了能够在保护模式下使用,需要创建一个存储偏移的指针。...打印函数定义 随着我们的程序越来越长,我们必须进行函数的封装和拆分,甚至进行文件的拆分,来让我们的程序可读性更强。 4.2.1....打印一个 dword 中的数字 在 32 位系统中,打印一个 32 位的数字是最为常用的功能,也是我们本次程序中所必须使用的。...mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32

    83020

    保护模式下的中断和异常(下) -- 软件实战篇

    引言 上一篇文章中,我们详细介绍了保护模式下的中断和异常以及他们的硬件基础结构 — 可编程中断控制器 8259A,以及他的初始化和中断的屏蔽与打开: 保护模式下的中断和异常(上) — 硬件原理篇 现在,...硬件已经完成初始化与设定,进入操作状态,一切就绪,只欠东风,我们如何在保护模式中通过程序实现中断与陷阱的设计和响应呢?...另外,虽然在实地址模式的默认情况下,程序只使用主 8259A 芯片,但仍然必须设置为级联模式,而不能将级联位设置为 1,虽然我在一些书中看到,在回跳时,主 ICW1 设置为了 17h,即 single...中断与异常 保护模式下的中断和异常(上) -- 硬件原理篇 14....mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword

    1.2K20

    Linux Rootkit如何避开内核检测的

    和杀毒软件打架一样,Rootkit和反Rootkit也是互搏的对象。无论如何互搏,其战场均在内核态。 很显然,我们要做的就是: 第一时间封堵内核模块的加载。...换句话说, 静态代码不能往动态内存进行直接的call/jmp(毕竟静态代码并不知道动态地址啊), 如果静态代码需要动态的函数完成某种任务,那么只能用 回调, 而回调函数在指令层面是要借助寄存器来寻址的,...如果我们在静态的代码中hack掉一条call/jmp指令,使得它以新的立即数作为操作数call/jmp到我们的动态代码,那么这就是一个奇技淫巧,这就是不正规的方式。...// 每当内核模块进行加载时,都会有消息在通知链上通知,我们只需要注册一个handler。 // 我们的handler让该模块“假加载”!...一切归于尘土。 然而,我们自己怎么办?这将把我们自己的退路也同时封死,只要使用电压冻结住内存快照,离线分析,真相必将大白!我们必须给自己留个退路,以便捣毁并恢复现场后,全身而退,怎么做到呢?

    1.3K10

    《一个操作系统的实现》笔记(2)--保护模式

    ---- 保护模式 什么实模式和保护模式 这是CPU的两种工作模式,解析指令的方式不同。 在实模式下,16位寄存器需要通过段:偏移的方法才能达到1MB的寻址能力。...指向LDT中描述符的选择子的T1 位必须置1,在运用它时,需要先用lldt指令加载ldtr,其操作数是GDT中用来描述LDT的描述符。LDT相当于是GDT的二级目录,增加了一个层次。...3、关于堆栈 短调用:在段内跳转 长调用:在段间跳转 call指令是会影响堆栈的,不同于jmp的是,call就像调用一个函数,也会返回的,长调用和短调用对堆栈的影响是不同的。...当使用分页时,每个段被划分成页面(通常每页为4KB大小),页面会被存储于物理内存中或硬盘上。操作系统通过维护一个页目录和一些页表(存放在物理内存的某个位置)来留意这些页面。...---- 中断和异常机制 在实模式下能用的BIOS中断在保护模式下已经不能用了,实模式下的中断向量表被保护模式下的IDT所代替。 IDT的作用是将每一个中断向量和一个描述符对应起来。

    1.5K80

    MIT 6.828 操作系统工程 lab1 2018 fall part1 & part2 笔记 and 中文注释源代码阅读

    PC从CS = 0xf000和IP = 0xfff0开始执行。 要执行的第一条指令是jmp指令,它跳转到分段地址 CS = 0xf000和IP = 0xe05b。...当BIOS找到可引导的软盘或硬盘时,它将512字节的引导扇区加载到物理地址0x7c00至0x7dff的内存中,然后使用jmp指令将CS:IP设置为0000:7c00,将控制权传递给引导程序装载机。...引导加载程序必须执行的两个主要功能: 将处理器从实模式切换到 32位保护模式; 通过x86的特殊I / O指令直接访问IDE磁盘设备寄存器,从硬盘读取内核; 引导加载程序的源代码: boot/boot.S...,gdb会提示:The target architecture is assumed to be i8086 切换到保护模式之后(ljmpl 0x8,0xfd18f指令后),提示: The target...在BIOS进入引导加载程序时检查0x00100000处的8个内存字,然后在引导加载程序进入内核时再次检查。

    2.1K50

    利用调用门实现特权级间跳转 -- 实战篇

    从 Ring0 到 Ring3 毋庸置疑,操作系统是启动在最高特权级的 Ring0 下的,那么,在操作系统中如何实现从 Ring0 特权级跳转到应用程序所在的 Ring3 特权级的呢?...但是,在跳转的过程中,CPU 自动进行了栈空间的切换,每个特权级都必须对应不同的栈基址与栈指针,通过 TSS 来进行描述,只要在门描述符中指定参数数量,CPU 会自动完成栈切换以及在这一过程中的参数复制工作...将显示函数放入一致代码段 页面展示函数要在本次的程序中被 Ring0、Ring3 程序分别调用,最简单的方法就是让他成为 Ring0 的一致代码段。 我们做如下修改。 5.1....mov eax, cr0 or eax, 1 mov cr0, eax ; 跳转进入保护模式 jmp dword SelectorCode32...ss, ax mov eax, cr0 and al, 0feh mov cr0, eax LABEL_GO_BACK_TO_REAL: jmp

    71220

    8种HOOK技术

    ,因此必须有一种新的策略。...我之前使用的跳转流程是: // MOV RAX, 绝对地址 // JMP RAX // 后来感觉修改 RAX 不太好(显然 RAX 是易失性寄存器),于是换了方式: // JMP QWORD PTR[本条指令结束后的地址...jmp地址上,第二个jmp到我写的函数,在我写的函数中调到我分配的地址中执行, 执行的代码先把原函数开头的15个字节执行,再jmp到原函数地址+15的位置执行,原函数返回后,继续执行我的代码 */...通过查看 IofCallDriver函数发现,在函数开头存在一个jmp指令。ff2500c85480其中ff25是jmp的机器码,后面的机器码是跳转的绝对地址。...; 写入新的数据 mov eax, oData;恢复cr0的数据 mov cr0, eax } KeLowerIrql(oldIrql); return

    3.2K10
    领券