MIT 6.828 JOS学习笔记6. Appendix 1: 实模式(real mode)与保护模式(protected mode)
在我们阅读boot loader代码时,遇到了两个非常重要的概念,实模式(real mode)和保护模式(protected mode)。
首先我们要知道这两种模式都是CPU的工作模式,实模式是早期CPU运行的工作模式,而保护模式则是现代CPU运行的模式。
但是为什么现代CPU在运行boot loader时仍旧要先进入实模式呢?就是为了实现软件的向后兼容性不得已才这样的。
下面我们分别看下这两种工作模式的基本原理。
实模式(real mode)
实模式出现于早期8088CPU时期。当时由于CPU的性能有限,一共只有20位地址线(所以地址空间只有1MB),以及8个16位的通用寄存器,以及4个16位的段寄存器。所以为了能够通过这些16位的寄存器去构成20位的主存地址,必须采取一种特殊的方式。当某个指令想要访问某个内存地址时,它通常需要用下面的这种格式来表示:
(段基址:段偏移量)
其中第一个字段是段基址,它的值是由段寄存器提供的。段寄存器有4种,%cs,%ds,%ss,%es。具体这个指令采用哪个段寄存器是由这个指令的类型来决定的。比如要取指令就是采用%cs寄存器,要读取或写入数据就是%ds寄存器,如果要对堆栈操作就是%ss寄存器。总之,不管什么指令,都会有一个段寄存器提供一个16位的段基址。
第二字段是段内偏移量,代表你要访问的这个内存地址距离这个段基址的偏移。它的值就是由通用寄存器来提供的,所以也是16位。那么问题来了,两个16位的值如何组合成一个20位的地址呢?这里采用的方式是把段寄存器所提供的段基址先向左移4位。这样就变成了一个20位的值,然后再与段偏移量相加。所以算法如下:
物理地址 = 段基址<<4 + 段内偏移
所以假设 %cs中的值是0xff00,%ax = 0x0110。则(%cs:%ax)这个地址对应的真实物理地址是 0xff00<<4 + 0x0110 = 0xff110。
上面就是实模式访问内存地址的原理。
保护模式(protected mode)
但是随着CPU的发展,CPU的地址线的个数也从原来的20根变为现在的32根,所以可以访问的内存空间也从1MB变为现在4GB,寄存器的位数也变为32位。所以实模式下的内存地址计算方式就已经不再适合了。所以就引入了现在的保护模式,实现更大空间的,更灵活的内存访问。
在介绍保护模式的工作原理之前,我们必须先清楚以下几个容易混淆的概念。逻辑地址(logical address),虚拟地址(virtual address),线性地址(linear address),物理地址(physical address)。
我们都知道,如今在编写程序时,程序时运行在虚拟地址空间下的,也就是说,在程序员编写程序时指令中出现的地址并不一定时这个程序在内存中运行时真正要访问的内存地址。这样做的目的就是为了能够让程序员在编程时不需要直接操作真实地址,因为当它在真实运行时,内存中各个程序的分布情况是不可能在你编写程序时就知道的。所以这个程序的这条指令到底要访问哪个内存单元是由操作系统来确定的。所以这就是一个从虚拟地址(virtual address)到真实主存中的物理地址(physical address)的转换。
那么逻辑地址(logical address)又是什么呢?根据上面一段文字我们知道,程序员编写时看到的是虚拟地址,但是并不是说程序员是直接把这个虚拟地址写到指令中的。它是由逻辑地址推导得到的。所以指令中真实出现的是逻辑地址。一个逻辑地址是由两部分组成的,一个段选择子(segment selector),一个段内偏移量(offset),通常被写作segment:offset。而且采用哪个段选择子通常也是在指令中隐含的,程序员通常只需要指明段内偏移量。然后分段管理机构(segmentation hardware)将会把这个逻辑地址转换为线性地址(linear address)。如果该机器没有采用分页机制(paging hardware)的话,此时linear address就是最后的主存物理地址。但是如果机器中还有分页设备的话,比如内存大小实际只有1G,但是根据前面我们知道可访问的空间有4G。所以此时还需要分页机构(paging hardware)把这个线性地址转换为最终的真实物理地址。所以可见虚拟地址和线性地址的含义是差不多的。我们可以再下图中看到我们上面叙述的地址转换过程。在boot loader中,并没有开启分页机构。所以计算出来的线性地址就是真实要访问的主存地址。

那么在保护模式下,我们是如何通过segment:offset最终得到物理地址的呢?
首先,在计算机中存在两个表,GDT,LDT。它们两个其实是同类型的表,前者叫做全局段描述符表,后者叫做本地段描述符表。他们都是用来存放关于某个运行在内存中的程序的分段信息的。比如某个程序的代码段是从哪里开始,有多大;数据段又是从哪里开始,有多大。GDT表是全局可见的,也就是说每一个运行在内存中的程序都能看到这个表。所以操作系统内核程序的段信息就存在这里面。还有一个LDT表,这个表是每一个在内存中的程序都包含的,里面指明了每一个程序的段信息。我们可以看一下这两个表的结构,如下图所示:

我们从图中可以看到,无论是GDT,还是LDT。每一个表项都包括三个字段:
Base : 32位,代表这个程序的这个段的基地址。
Limit : 20位,代表这个程序的这个段的大小。
Flags :12位,代表这个程序的这个段的访问权限。
当程序中给出逻辑地址 segment:offset时,他并不是像实模式那样,用segment的值作为段基址。而是把这个segment的值作为一个selector,代表这个段的段表项在GDT/LDT表的索引。比如你当前要访问的地址是segment:offset = 0x01:0x0000ffff,此时由于每个段表项的长度为8,所以此时应该取出地址8处的段表项。然后首先根据Flags字段来判断是否可以访问这个段的内容,这样做是为了能够实现进程间地址的保护。如果能访问,则把Base字段的内容取出,直接与offset相加,就得到线性地址(linear address)了。之后就是要根据是否有分页机构来进行地址转换了。
比如当前Base字段的值是0x00f0000,则最后线性地址的值为0x00f0ffff。
如上所述就是保护模式下,内存地址的计算方法。
综述,通过上面的叙述可见,保护模式还是要比实模式的工作方式灵活许多,可以在以下几个方面看出来:
1. 实模式下段基地址必须是16的整数倍,保护模式下段基地址可以是4GB空间内的任意一个地址。
2. 实模式下段的长度是65536B,但是保护模式下段的长度也是可以达到4GB的。
3. 保护模式下可以对内存的访问多加一层保护,但是实模式没有。
有什么问题,大家可以给我发邮件~
zzqwf12345@163.com
MIT 6.828 JOS学习笔记6. Appendix 1: 实模式(real mode)与保护模式(protected mode)的更多相关文章
- 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学习笔记4. Lab 1 Part 2.1: The Boot Loader
Part 2: The Boot Loader 对于PC来说,软盘,硬盘都可以被划分为一个个大小为512字节的区域,叫做扇区.一个扇区是一次磁盘操作的最小粒度.每一次读取或者写入操作都必须是一个或多个 ...
- MIT 6.828 JOS学习笔记0. 写在前面的话
0. 简介 操作系统是计算机科学中十分重要的一门基础学科,是一名计算机专业毕业生必须要具备的基础知识.但是在学习这门课时,如果仅仅把目光停留在课本上一些关于操作系统概念上的叙述,并不能对操作系统有着深 ...
- 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学习笔记5. Exercise 1.3
Lab 1 Exercise 3 设置一个断点在地址0x7c00处,这是boot sector被加载的位置.然后让程序继续运行直到这个断点.跟踪/boot/boot.S文件的每一条指令,同时使用boo ...
- 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学习笔记1. Lab 1 Part 1: PC Bootstrap
Lab 1: Booting a PC Part 1: PC Bootstrap 介绍这一部分知识的目的就是让你能够更加熟悉x86汇编语言,以及PC启动的整个过程,而且也会首次学习使用QEMU软件来仿 ...
- MIT 6.828 JOS学习笔记18. Lab 3.2 Part B: Page Faults, Breakpoints Exceptions, and System Calls
现在你的操作系统内核已经具备一定的异常处理能力了,在这部分实验中,我们将会进一步完善它,使它能够处理不同类型的中断/异常. Handling Page Fault 缺页中断是一个非常重要的中断,因为我 ...
- MIT 6.828 JOS学习笔记17. Lab 3.1 Part A User Environments
Introduction 在这个实验中,我们将实现操作系统的一些基本功能,来实现用户环境下的进程的正常运行.你将会加强JOS内核的功能,为它增添一些重要的数据结构,用来记录用户进程环境的一些信息:创建 ...
随机推荐
- 关于WEB项目的一点想法
有点失落.迷茫,差点在上班的时候发了火.原因是之前离职的一位同事,在代码里不加注释,而且百般偷懒,致使很多应该的验证没有验证,很多应该考虑到的情况没有考虑.因为是老员工,我相比他来说是新员工.气势上总 ...
- 轻松理解spring IOC
spring IOC(Inversion of control)即控制反转 概念:一,spring框架的核心之一 二,控制权由对象本身转向容器:由容器根据配置文件去创建实例并创建各个实例之间的依赖关系 ...
- API接口:分页
// 查询满足要求的总记录数 $count = M("back")->where($back_map)->count(); $pagecount = ceil($cou ...
- C++程序设计——知识点总结
C++程序设计课程的总结,方便以后快速查阅和复习 Week 2 从C走进C++ 函数指针 函数名是函数的入口地址,指向函数的指针称为"函数指针". 比如,qsort库函数: voi ...
- ThinkPHP3.2.3整合smarty模板(一)
一.php模板引擎有哪些? 1.1 PHPLIB:一套古老且主流的模板引擎,直接在html中使用PHP变量进行编程: 1.2 Template Blocks:一款轻巧且速度非常快的PHP模板引擎,支持 ...
- PHP合并2个数字键数组的值
先要了解一个基础知识点:PHP数组合并+与array_merge的区别分析 & 对多个数组合并去重技巧 <?php /** * PHP合并2个数字键数组的值 * * @param arr ...
- 机器学习 k-临近算法
程序清单一: from numpy import * import operator def creatDataSet(): group = array([[1.0,1.1],[1.0,1.0],[0 ...
- 使用php+swoole对client数据实时更新(上)
如果想对一个列表做实时的更新,传统的做法是采用轮询的方式.以web为例,通过Ajax定时请求服务端然后获取数据显示在页面.这种方式实现简单,缺点就是浪费资源. HTTP1.1新增加了对websocke ...
- codeigniter钩子的使用
CodeIgniter 的钩子功能,使得我们可以在不修改系统核心文件的基础上,来改变或增加系统的核心运行功能.可是钩子究竟该怎么用呢?虽然不是很难,不过很多刚用ci的朋友可能还是不明白怎么用. 通过本 ...
- Collection接口
Collection接口所定义的方法: clear:清空 retainAll 求一个Collection和另一个 Collection的交集. object[] toArray() 把里面的各个对象 ...