首页
社区
课程
招聘
汇编语言程序设计--笔记
发表于: 2006-3-24 17:20 21265

汇编语言程序设计--笔记

2006-3-24 17:20
21265
收藏
免费 0
支持
分享
最新回复 (88)
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
51
**********此教材的重点*******
1.5   8086微处理器目录
1.5.1 8086的功能结构
1.5.2 8086的寄存器组
1.5.3 存储组织与段寄存器
----1. 存储器组织(位、字节、地址、字单元。。。)
----2.分段管理(分段、逻辑地址、物理地址。。。)
----3.段寄存器(段寄存器、段超越。。。)

附图略省,以后补充。
2006-3-25 22:29
0
雪    币: 124
活跃值: (70)
能力值: ( LV4,RANK:50 )
在线值:
发帖
回帖
粉丝
52
偶像啊,我都不知道怎么佩服你好
2006-3-25 22:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
53
1.5.1 8086的功能结构
附图:

***(此文转载而来,作者不详,如侵犯了你的版权,请予以说明)***
通用寄存器
下面介绍通用寄存器及其习惯用法。顾名思义,通用寄存器是那些你可以根据自己的意愿使用的寄存器,修改他们的值通常不会对计算机的运行造成很大的影响。通用寄存器最多的用途是计算。

EAX
32-bit宽
通用寄存器。相对其他寄存器,在进行运算方面比较常用。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)
EBX
32-bit宽
通用寄存器。通常作为内存偏移指针使用(相对于EAX、ECX、EDX),DS是默认的段寄存器或选择器。在保护模式中,同样可以起这个作用。
ECX
32-bit宽
通用寄存器。通常用于特定指令的计数。在保护模式中,也可以作为内存偏移指针(此时,DS作为 寄存器或段选择器)。
EDX
32-bit宽
通用寄存器。在某些运算中作为EAX的溢出寄存器(例如乘、除)。在保护模式中,也可以作为内存偏移指针(此时,DS作为段 寄存器或选择器)。

上述寄存器同EAX一样包括对应的16-bit和8-bit分组。

用作内存指针的特殊寄存器

ESI
32-bit宽
通常在内存操作指令中作为“源地址指针”使用。当然,ESI可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。
EDI
32-bit宽
通常在内存操作指令中作为“目的地址指针”使用。当然,EDI也可以被装入任意的数值,但通常没有人把它当作通用寄存器来用。DS是默认段寄存器或选择器。
EBP
32-bit宽
这也是一个作为指针的寄存器。通常,它被高级语言编译器用以建造‘堆栈帧’来保存函数或过程的局部变量,不过,还是那句话,你可以在其中保存你希望的任何数据。SS是它的默认段寄存器或选择器。

注意,这三个寄存器没有对应的8-bit分组。换言之,你可以通过SI、DI、BP作为别名访问他们的低16位,却没有办法直接访问他们的低8位。

段寄存器和选择器

实模式下的段寄存器到保护模式下摇身一变就成了选择器。不同的是,实模式下的“段寄存器”是16-bit的,而保护模式下的选择器是32-bit的。

CS 代码段,或代码选择器。同IP寄存器(稍后介绍)一同指向当前正在执行的那个地址。处理器执行时从这个寄存器指向的段(实模式)或内存(保护模式)中获取指令。除了跳转或其他分支指令之外,你无法修改这个寄存器的内容。
DS 数据段,或数据选择器。这个寄存器的低16 bit连同ESI一同指向的指令将要处理的内存。同时,所有的内存操作指令 默认情况下都用它指定操作段(实模式)或内存(作为选择器,在保护模式。这个寄存器可以被装入任意数值,然而在这么做的时候需要小心一些。方法是,首先把数据送给AX,然后再把它从AX传送给DS(当然,也可以通过堆栈来做).
ES 附加段,或附加选择器。这个寄存器的低16 bit连同EDI一同指向的指令将要处理的内存。同样的,这个寄存器可以被装入任意数值,方法和DS类似。
FS F段或F选择器(推测F可能是Free?)。可以用这个寄存器作为默认段寄存器或选择器的一个替代品。它可以被装入任何数值,方法和DS类似。
GS G段或G选择器(G的意义和F一样,没有在Intel的文档中解释)。它和FS几乎完全一样。
SS 堆栈段或堆栈选择器。这个寄存器的低16 bit连同ESP一同指向下一次堆栈操作(push和pop)所要使用的堆栈地址。这个寄存器也可以被装入任意数值,你可以通过入栈和出栈操作来给他赋值,不过由于堆栈对于很多操作有很重要的意义,因此,不正确的修改有可能造成对堆栈的破坏。

* 注意 一定不要在初学汇编的阶段把这些寄存器弄混。他们非常重要,而一旦你掌握了他们,你就可以对他们做任意的操作了。段寄存器,或选择器,在没有指定的情况下都是使用默认的那个。这句话在现在看来可能有点稀里糊涂,不过你很快就会在后面知道如何去做。

特殊寄存器(指向到特定段或内存的偏移量):

EIP 这个寄存器非常的重要。这是一个32位宽的寄存器 ,同CS一同指向即将执行的那条指令的地址。不能够直接修改这个寄存器的值,修改它的唯一方法是跳转或分支指令。(CS是默认的段或选择器)
ESP 这个32位寄存器指向堆栈中即将被操作的那个地址。尽管可以修改它的值,然而并不提倡这样做,因为如果你不是非常明白自己在做什么,那么你可能造成堆栈的破坏。对于绝大多数情况而言,这对程序是致命的。(SS是默认的段或选择器)

IP: Instruction Pointer, 指令指针
SP: Stack Pointer, 堆栈指针

好了,上面是最基本的寄存器。
2006-3-25 22:32
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
54
1.5.2 8086寄存器组
附图:


1.5.2.1 例图

附整理后的表图:






(转自 www.77169.com  DJ管理 收集整理)
汇编语言的准备知识--给初次接触汇编者(1)
 汇编语言和CPU以及内存,端口等硬件知识是连在一起的. 这也是为什么汇编语言没有通用性的原因. 下面简单讲讲基本知识(针对INTEL x86及其兼容机)
            ============================
    x86汇编语言的指令,其操作对象是CPU上的寄存器,系统内存,或者立即数. 有些指令表面上没有操作数, 或者看上去缺少操作数, 其实该指令有内定的操作对象, 比如push指令, 一定是对SS:ESP指定的内存操作, 而cdq的操作对象一定是eax / edx.
在汇编语言中,寄存器用名字来访问. CPU 寄存器有好几类, 分别有不同的用处:
            
1. 通用寄存器:
EAX,EBX,ECX,EDX,ESI,EDI,EBP,ESP(这个虽然通用,但很少被用做除了堆栈指针外的用途)这些32位可以被用作多种用途,但每一个都有专长.
EAX 是累加器(accumulator), 它是很多加法乘法指令的缺省寄存器.
EBX 是基地址(base)寄存器, 在内存寻址时存放基地址.
ECX 是计数器(counter), 是重复(REP)前缀指令和LOOP指令的内定计数器.
EDX是数据寄存器,但它总是被用来放整数除法产生的余数.
这4个寄存器的低16位可以被单独访问,分别用AX,BX,CX和DX. AX又可以单独访问低8位(AL)和高8位(AH), BX,CX,DX也类似. 函数的返回值经常被放在EAX中.
ESI/EDI分别叫做源/目标索引寄存器(source/destination index),因为在很多字符串操作指令中, DS:ESI指向源串,而ES:EDI指向目标串.
EBP是基址指针(BASE POINTER), 它最经常被用作高级语言函数调用的框架指针(frame pointer). 在破解的时候,经常可以看见一个标准的函数起始代码:
push ebp ;保存当前ebp
mov ebp,esp ;EBP设为当前堆栈指针
sub esp, xxx ;预留xxx字节给函数临时变量.
这样一来,EBP 构成了该函数的一个框架, 在EBP上方分别是原来的EBP, 返回地址和参数. EBP下方则是临时变量. 函数返回时作 mov esp,ebp/pop ebp/ret 即可.
ESP 专门用作堆栈指针.
2. 段寄存器:
CS(Code Segment,代码段) 指定当前执行的代码段. EIP (Instruction pointer, 指令指针)则指向该段中一个具体的指令. CS:EIP指向哪个指令, CPU 就执行它. 一般只能用jmp, ret, jnz, call 等指令来改变程序流程,而不能直接对它们赋值.
DS(DATA SEGMENT, 数据段) 指定一个数据段. 注意:在当前的计算机系统中, 代码和数据没有本质差别, 都是一串二进制数, 区别只在于你如何用它. 例如, CS 制定的段总是被用作代码, 一般不能通过CS指定的地址去修改该段. 然而,你可以为同一个段申请一个数据段描述符别名而通过DS来访问/修改. 自修改代码的程序常如此做.
ES,FS,GS 是辅助的段寄存器, 指定附加的数据段.
SS(STACK SEGMENT)指定当前堆栈段. ESP 则指出该段中当前的堆栈顶. 所有push/pop 系列指令都只对SS:ESP指出的地址进行操作.
3. 标志寄存器(EFLAGS):
该寄存器有32位,组合了各个系统标志. EFLAGS一般不作为整体访问, 而只对单一的标志位感兴趣. 常用的标志有:
进位标志C(CARRY), 在加法产生进位或减法有借位时置1, 否则为0.
零标志Z(ZERO), 若运算结果为0则置1, 否则为0
符号位S(SIGN), 若运算结果的最高位置1, 则该位也置1.
溢出标志O(OVERFLOW), 若(带符号)运算结果超出可表示范围, 则置1.
JXX 系列指令就是根据这些标志来决定是否要跳转, 从而实现条件分枝. 要注意,很多JXX 指令是等价的, 对应相同的机器码. 例如, JE 和JZ 是一样的,都是当Z=1是跳转. 只有JMP 是无条件跳转. JXX 指令分为两组, 分别用于无符号操作和带符号操作. JXX 后面的XX 有如下字母:
无符号操作:                       带符号操作:
A = ABOVE, 表示高于               G = GREATER, 表示大于
B = BELOW, 表示低于               L = LESS, 表示小于
C = CARRY, 表示进位或借位         O = OVERFLOW, 表示溢出
S = SIGN, 表示负
通用符号:
E = EQUAL 表示等于, 等价于Z (ZERO)
N = NOT 表示非, 即标志没有置位. 如JNZ 如果Z没有置位则跳转
Z = ZERO, 与E同.
如果仔细想一想,就会发现 JA = JNBE, JAE = JNB, JBE = JNA, JG = JNLE, JGE= JNL, JL= JNGE, ....
4. 端口            
端口是直接和外部设备通讯的地方。外设接入系统后,系统就会把外设的数据接口映射到特定的端口地址空间,这样,从该端口读入数据就是从外设读入数据,而向外设写入数据就是向端口写入数据。当然这一切都必须遵循外设的工作方式。端口的地址空间与内存地址空间无关,系统总共提供对64K个8位端口的访问,编号0-65535. 相邻的8位端口可以组成成一个16位端口,相邻的16位端口可以组成一个32位端口。端口输入输出由指令IN,OUT,INS和OUTS实现,具体可参考汇编语言书籍。
2006-3-25 23:12
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
55
1.5.2.2 标志寄存器
附图:

2006-3-25 23:27
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
56
1.5.2.2.1 状态标志
附图:

2006-3-25 23:28
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
57
1.5.2.2.2 控制标志位
附图:

2006-3-25 23:30
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
58
1.5.3   存储器组织与段寄存器
1.5.3.1 数据的存储格式
附图:

2006-3-25 23:38
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
59
1.5.3.2   存储器的分段管理
1.5.3.2.1 为何分段
1.5.3.2.2 地址的表示方法
附图:

     (此文转载而来,作者不详,如侵犯了你的版权,请予以说明)
我将分别介绍如何在保护模式和实模式操作内存,然而在此之前,我们先熟悉一下这两种模式中内存的结构。
3.1 实模式
事实上,在实模式中,内存比保护模式中的结构更令人困惑。内存被分割成段,并且,操作内存时,需要指定段和偏移量。不过,理解这些概念是非常容易的事情。请看下面的图:
|-------|      |--------|        |--------|
|内存   |      |        |        |        |
|       |      |--------|        |--------|段
|       |      | 段     |        |        |
|       |      |        |        | <------|----偏移地址
|       |      |--------|        |--------|        
|       |      |        |        |        |
---------      |--------|        |--------|

段-寄存器这种格局是早期硬件电路限制留下的一个伤疤。地址总线在当时有20-bit。
然而20-bit的地址不能放到16-bit的寄存器里,这意味着有4-bit必须放到别的地方。因此,为了访问所有的内存,必须使用两个16-bit寄存器。
这一设计上的折衷方案导致了今天的段-偏移量格局。最初的设计中,其中一个寄存器只有4-bit有效,然而为了简化程序,两个寄存器都是16-bit有效,并在执行时求出加权和来标识20-bit地址。
偏移量是16-bit的,因此,一个段是64KB。下面的图可以帮助你理解20-bit地址是如何形成的:

|-------16bit---------|
|----|----|-----|-----|-----|
|  - | -  |  -  |    -| 0000| 段
|----|----|-----|-----|-----|

     |-------16bit---------|
|----|----|-----|-----|-----|
|0000| -  |  -  |    -| -   | 偏移地址
|----|----|-----|-----|-----|

|-----------20位地址--------|
|----|----|-----|-----|-----|
|  - | -  |  -  |    -| -   |物理地址
|----|----|-----|-----|-----|

段-偏移量标识的地址通常记做 段:偏移量 的形式。
由于这样的结构,一个内存有多个对应的地址。例如,0000:0010和0001:0000指的是同一内存地址。又如,
0000:1234 = 0123:0004 = 0120:0034 = 0100:0234
0001:1234 = 0124:0004 = 0120:0044 = 0100:0244
作为负面影响之一,在段上加1相当于在偏移量上加16,而不是一个“全新”的段。反之,在偏移量上加16也和在段上加1等价。某些时候,据此认为段的“粒度”是16字节。
关于保护模式中内存操作的一点说明
正如3.2节提到到的那样,保护模式中,你可以使用32位的线性地址,这意味着直接访问4GB的内存。由于这个原因,选择器不用像实模式中段寄存器那样频繁地修改。顺便提一句,这份教程中所说的保护模式指的是386以上的保护模式,或者,Microsoft通常称为“增强模式”的那种。

在为选择器装入数值的时候一定要非常小心。错误的数值往往会导致无效页面错误(在Windows中经常出现:)。同时,也不要忘记你的地址是32位的,这也是保护模式的主要优势之一。
2006-3-25 23:43
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
60
1.5.3.2.3 逻辑地址和物理地址
1.5.3.2.4 例题
附图:


段地址和偏移地址的概念:
由于8086 CPU的寄存器只有16位,而地址线有20根,为使用16位的寄存器访问20位的内存地址(访问内存时,大多情况下需将内存地址放到寄存器中),8086 CPU将1M内存分成多个段(Segment)。每个段有自己的段起始地址(简称段地址,16位宽),每个段的最大长度为64K。当我们访问一个内存单元时,必须告诉CPU所访问的内存单元的段地址以及该单元距段首的距离(多少字节),该距离称为段内偏移地址(简称偏移量,Offset,16位宽)。离开段地址,孤立地谈偏移地址是没有任何意义的。在同一个段内,偏移量的变化范围是0000H~FFFFH。
为表示方便,8086规定,内存地址的表示方法为:16位段地址:16位偏移地址。其转换为20位物理地址的公式规定如下:
20位物理地址=16位段地址左移4位 + 16位偏称地址
注:由于左移4位后,段地址的低4位一定是0H,所以一个段的起始物理地址一定是xxxx0H的形式。
例如1A2B:5DF2对应的20位物理地址是200A2H:
1A2BH->左移4个二进制位->1A2B0H
                     +   5DF2H
                     =  200A2H                        

显然,表示同一个物理地址时可以有近64K种段地址和偏移量的组合。例如2000:00A2和200A:0002都表示同一个物理地址200A2H。
(此文出自Write An OS In A Week(2) 萝卜甲@ZJU 2004 badturnip@163.com)
经过昨天的热身准备,今天可以进入一些比较细节的内容了。在开始设计前首先要熟悉一下x86的硬件体系,怎么说在Intel的平台下混只好这样啦。
在Intel PC处理器80386之前的都是属于玩具型的,没有大地址寻址,没有硬件保护,没有虚拟存储,没有分页机制,不可能支持现代操作系统。所以我们的目标是80386以上的CPU,从80386到现在最新的Pentium IV,体系结构都是差不多的,无非是速度快了,缓存加了,修修补补,增加了了一些新的功能。下面我们就来看一下Intel的保护模式机制,对了,在介绍Intel保护模式之前,你最好能搞到一份Intel IA-32体系结构手册(IA-32 Intel Architechure Software Developer's Manual),三卷装,第三卷System Programming对Intel的IA-32机制讲得很详细。不过想想Intel用整整780页描述的内容我们今天一天要把它搞定,真是。。。。。很寒啊。没有接触过保护模式的xdjm们准备好了吗?一定要坚定信念,顽强拼搏,义无反顾。。。。来吧!

保护模式对于没有接触过的人来说可能第一次接触很是让人困惑,他的核心机制其实是一个二级地址转换机制。在IA-32体系结构下存在的有三个地址空间的概念: 虚拟地址空间(又称逻辑地址空间),线性地址空间,物理地址空间。还有两个转换机制:段机制和分页机制。虚拟地址通过段机制转换成线性地址,线性地址通过分页机制转换成物理地址。如下:

         虚拟地址 ---------> 线性地址 ---------> 物理地址
                    段机制 分页机制

先来看看最简单也最容易理解的物理地址空间吧,物理地址空间所对应的地址就是真正的物理内存空间,IA-32体系结构的物理地址是一个32-bit的数值,所以他的最大物理地址寻址就是0 到 2^32 - 1 共4G空间,比如你有一个物理地址0x00010000,这个地址就是你内存中1M的那个位置。

线性地址空间是一个想象的地址空间,32位,从0 到 2^32 - 1,即 0x00000000 到 0xFFFFFFFF,跟物理地址空间是一样的性质。线性地址空间通过分页转换机制转换成物理地址。

最后来看看虚拟地址空间,虚拟地址就是程序给出的地址,在IA-32,一个虚拟地址是48-bit的数值,其中16位为段地址,32位为段内偏移地址,比如有一个mov eax,ds:[eax],16位的ds就是段地址(其实是段选择子Selector,下面详细介绍),eax里面给出的就是32-bit偏移。这里顺便说一下386以后的Intel CPU寄存器的设置,8086下16-bit的ax,bx,cx,dx,si,di,bp,sp,ip,flag被扩展成了32-bit的eax,ebx,ecx,edx,esi,edi,ebp,esp,eip,eflag。cs,ds,es,ss的还是保持了16-bit,但是他们的意义已经变了,还增加了两个段寄存器fs和gs。另外IA-32下还有很多其他的寄存器,比如控制寄存器CR0,CR1,CR2,CR3,CR4用来控制CPU的工作状态,调试寄存器DR0 - DR7用来调试等。

OK,下面进入了比较实质性的内容,让我们来看看比较容易头晕的段机制,看看一个虚拟地址是怎么被转换成线性地址的。在8086下写过汇编的应该知道, 虚拟地址(20-bit) = 段的基地址(16-bit 左移4位,为20-bit) + 段内偏移(16-bit), 但是8086没有任何地址转换机制,所以这个20-bit的虚拟地址就是物理地址了。保护模式下的线性地址是由段的线性基地址(32-bit) + 段内偏移(32-bit)形成的,但是注意,这个段的线性基地址(Base Address)不是由段寄存器直接给出的,而是段寄存器指出一个特定的8个字节的一个结构,由这个结构给出该段的线性基地址,Intel把该结构称为段描述符(Segment Descriptor)。

这个8个字节的段描述符结构到底里面保存了哪些东西呢?第一,该段的线性基地址,就是说这个段在线性空间中从哪个位置开始,相当于8086下的段地址,只不过这个地址是32位的。第二,该段的长度,这个应该很好理解,如果一个偏移地址超过了段的长度,那么该偏移地址会被CPU判断为无效,CPU会产生一个异常(exception),相当于一个中断,然后操作系统会捕获这个异常,然后OS该怎么处置产生这个异常的程序就是OS的事了,我们这里先跳过了。第三,这个描述符里面还保存了该段的属性,比如该段的权限设置啊,怎么样的权限才能访问这个段,这个涉及到8086没有的权限问题,我们下面会提到,还有该段是什么内容啊,市数据段呢,还是代码段呢,还是其他呢,这个其他里面包含了很多类型,Intel共定义了16种类型,包括一些门啊,还有任务描述段啦等等一堆,这个门又分中断们,陷阱门....任务描述符里面是....好....慢点慢点,有的观众已经开始撤离了,那我们暂且把他们放在一边,否则就真的是无穷无尽的地狱了,阿弥陀佛!

除了这些以外,8个字节的描述符还保存了一些其他相关的信息,我们暂且不理他。这些描述符位于什么地方呢?这些描述符本身是位于线性地址空间中的,是由程序员设置的,也就是说我们作为OS开发者要干的活。我们在整个OS中可能需要设置很多个这样的描述符,比如要为内核的数据(堆栈也被认为是数据)设置一个描述符,为内核的代码设置一个描述符,为用户程序的数据设置一个,还要为用户的代码设置一个。看过OS理论的都知道,OS内核和用户程序永远是对立统一的,就是说OS既要为用户程序服务,又要限制用户的权限,管理和分配用户程序的资源,从这一点上来看得话,OS内核很像政府机构,用户程序很想人民大众,政府要为人民服务,又要限制,管理和分配人民的资源。从这个比喻来讲,单一内核很像计划经济,OS内核分配和管理一切,看似效率比较高,其实整体结构不佳。而微内核像市场经济,OS内核只起到监督和协调的作用,很多政府机构变成了服务者(相当于微内核中的Server).....

咳咳,貌似扯远了.....继续回到原来的内容,嗯.....刚才讲什么来着? 对!描述符,为什么那个该死的Intel要搞那么一套复杂的古怪的东西来折磨我们呢?其实据我猜测,其中原因是很多的,我把它归结如下:
1 向上兼容。 因为8086有段机制,留下了很多段寄存器,所以总不能空着不用吧,所以到了386就成了段机制。
2 段机制的保护作用。 刚才提到的段描述符里面保存了关于段的长度以及段的属性以及段的访问权限,这个就是为了保护OS内核不受外界非法访问的一个机制。
3 段机制在程序数据共享和重定位方面有优势。 这个就比较麻烦了,撤开去又是很大的一个话题,不讲了 :)
4 Intel纯粹耍酷,存心要玩死OS developer -_-!b,看了那个描述符的结构真是想杀到Intel总部把那帮家伙咔咔了。因为在设计80286这个不完全变态产品的时候,Intel的设计人员没有想到为80386留条后路,所以留下了一系列的古怪结构,就像古代女人的小脚 ---- 蹩足得很啊!

这里既然提到了保护,就说边说说Intel的保护机制吧。在IA-32下,所有的代码和数据共分4个级别的权限,0,1,2,3,也就是常说的Ring 0, Ring 1, Ring 2, Ring 3。数字越小,权限越大。权限大的能访问全县小的,反之不成立,同级能互访。由于4级的权限太多,一般很少都被用到,一般的OS只用到了Ring 0和Ring 3。把内核的代码和数据放在Ring 0下,把用户的代码和数据放在Ring 3下是很常见的一种做法。如果权限低的代码试图访问权限高的数据,CPU就会产生一个exception,CPU会自动把控制权交给处于Ring 0的OS kernel,由OS kernel来处置越权访问的代码(这就是很常见的General protection error)。至于为什么要保护就不用我在这里罗嗦了吧,你总不想你的Kernel数据和代码随便用户程序改动吧?否则还有快感?

看到这里,我做个调查:各位讨不讨厌那个复杂古怪的段机制?十有八九,我敢肯定,十分讨厌,至少我个人是不太喜欢。不过我不得不宣布一个好消息,那就是:我们可以跳过段机制! (西瓜皮,西红柿,烂橘子马上向我飞来: 不早说!害我死亡了那么多脑细胞!)只要我们恰当地设置好描述符,我们就可以将段机制的影响减到最少,可以让你几乎感觉不到他的存在。设置方法如下:设置4个描述符,OS kernel data, OS kernel code, User data, User code。访问权限分别为 0, 0, 3, 3。每个段的线性基地址都为0,这样的话,每个虚拟地址给出的偏移就是线性地址,这就是传说中的flat mode。怎么样,够简单吧?如果你还不是很明白的话我们来看一个简单的例子:

你写了一个程序,里面有
char * p;
p = (char *)(0x00010000);
*p = 'A';
经过编译以后变成了这样:
mov eax,0x00010000
mov [eax],65 ; 65是'A'的ASCII码

这里你没有看到段地址,是因为Intel规定使用eax寄存器寻址的话如果不指定段寄存器,则默认为ds,就像esp默认为ss一样,Intel有一套段寄存器默认规则。所以这个程序给出的虚拟地址其实是ds:eax即ds:0x00010000。下面我们来看看这个虚拟地址如何被转换成线性地址。首先CPU根据ds的内容,去查找相应的描述符,这些描述符都放在一些表中,成为描述符表(Description Table),就像描述符数组一样,很多描述符都放在那些表中,Intel规定有三种表:全局描述符表GDT, 局部描述符表LDT和中断描述符表IDT,这些表的本身都是放在线性地址空间中的,这些表的具体在线性空间中的位置是这样的:GDT和IDT分别由CPU的寄存器GDTR和IDTR指向,而LDT由GDT中的描述符指向。一般数据段或者代码段的描述符都放在GDT或者LDT中,而具体从这两个表中哪一个中取出来则取决于段寄存器中的bit0。反正就是由ds可以得到一个描述符,然后我们根据先前设置的描述符,取得该段的线性基地址。还记得刚才我们想跳过那该死的段机制吗?OK!如果刚才你的那几句代码是出自于你的OS KERNEL的话,那么我们从ds得到的应该就是OS kernel data描述符,然后还记得我们把所有描述符的线性基地址设为0了么?所以最后的线性地址为 0 + eax 即 0x00010000, omg!千万里我追寻着你~~~终于得到线性地址了,其中还涉及到许多寄存器设置以及映射寄存器等概念,我们暂且跳过了,如果想看得仔细的话就看一下那本Intel 圣经吧!

里程碑啊!绝对是里程碑,我们终于到了线性地址了!下面就要从线性地址转换成为物理地址了。

嗯,是不是已经头晕脑涨了呢?先休息一会儿吧,20分钟后我们再继续。(20分钟休息)

OK!休息完了我们继续。不要紧张,分页机制比分段机制简单得多,而且你应该也在很多OS理论书或者计算机组成里面看到过。Intel的分页机制就是把物理内存分割为一个一个的页,每个页的大小为4K(4096 bytes),成为页框(Page frame),从0 - 4K为一页,4K - 8K, 8K - 12K .... 一直到物理内存结束。线性地址空间也被分割为一个一个页,每个大小也是4K,跟物理内存一样。分页的实质其实是为了完成线性地址空间中的页到物理内存中的页的映射,从而完成虚拟存储以及数据保护和共享等。线性空间的页只能被映射到唯一的一个页框,而一个页框可以同时被多个线性空间中页映射,这样就有利于数据共享。

分页映射是怎么实现的呢?我们来看一下一个很简单的映射方案:线性空间中的每个页由一个数据结构描述其对应于物理空间中的哪个页框,由于物理空间最多可能有 2^32 / 4096 = 2^20 个页框,所以这个数据结构至少20-bit,在加上一些页的属性啊,访问权限等描述,一般都取32-bit,也就是4个字节。所以一个线性空间到物理空间的映射需要 2^32 / 4096 * 4 = 4M 的空间来描述映射函数。由于现代操作系统需要做到地址空间隔离,就是每个进程都有一个独立的线性空间,以便于数据隔离,这样的话这些映射函数的空间消耗十分大。

所以Intel采用了2级映射机制,首先每个线性空间有一个4K字节的页目录(Page directory),页目录里面存放有1K个页表指针,每个指针4个字节,这些指针可以指向一些称为页表(Page Table)的结构,每个页表也是4K字节大小,里面放着也是同样的指针,这些指针指向真正的物理内存的页框。不像刚才第一个方案一样,这个方案中每个线性空间必须有一个页目录,但页表的数量可以动态改变。我们来看看一个线性地址是怎么被转换成物理地址的。一个线性地址有32-bit,首先取高最高的10-bit,也就是bit 22 到 bit 31,以这10-bit的数字为索引,在页目录中找到相应的页表地址,然后取中间10-bit,也就是bit 12 到 bit 21,以这10-bit为索引找到刚才页表中的页框地址,然后将这页框地址 + 这个线性地址的低12位就是物理地址了。整个-过程如下:

线性地址:
31 22 21 12 11 0
-------------------------------------------------
| 10 bits | 10 bits | 12 bits |
-------------------------------------------------
       | | |
       | | |
       | | |
       | | |
       | | |
       | | | | | | |
       | |--------| | |--------| |
       | | | -->| XXXX ------+------> 物理地址
       | |--------| |--------|
       --->| XXXX ----| | |
           |--------| | |--------|
           | | | | |
           |--------| | | |
           | | --->|--------|
           | | page table
page dir ->|--------|

这些页目录啊,页表啊都是OS developer设置的,放在物理空间中。为什么要采取这样一种2级映射的方案呢?原因是为了节省内存的开销。本来一个线性地址要消耗4M的空间来映射,现在的方案是每个线性地址空间只需要一个页目录(4K),而页表则可以按需添加和减少,比如线性地址0x80000000 - 0xFFFFFFFF都是没有映射的,则这些地址对应在页目录的位置中的第512项到1023项都可以设为空,就不需要页表,所以如果一个线性空间只映射了4M,则整个映射函数只要占用一个页目录和一个页表也就是总共(8K)的空间,大大节省了。

大家还记不记得刚才讲段模式的提到分页机制也可以有保护?大家有没有发现页目录和页表的每一项都是指向相应的页框的(页目录,页表本身都占用一个页框),但是每个页框的地址都是可以被4K整除的,所以每个指针的低12位都是0。但是页目录和页表的每一项都有4个字节,所以这4个字节的低12位就用来放一些其他的信息,比如访问权限啦,页属性啊等等。其中有一个属性很重要那就是页的Present属性。如果这个位为1的话说明这个页存在于物理内存中,如果是0的话则不在内存中。当页不在内存中时,如果程序访问到这个页,CPU就会产生一个exception叫page fault,然后OS捕获到这个fault后,判断为缺页错误,经过一系列的合法性检查之后,如果是进程缺页错误的话,OS就把程序的相应的页调入到内存中,然后让进程恢复执行,这就是请页机制(demanding page)。

呼~~~终于一口气讲完了IA-32的核心机制,看到这里大家一定快疯了吧,其实我也是....,把Intel的780页的文档压缩到这么短,真是很有挑战啊!

OK,温习一下今天的内容!准备明天的挑战吧!!'
2006-3-25 23:46
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
61
1.5.3.2.5 地址的唯一性
1.5.3.2.6 涉及分段的其他问题
附图:


段地址和偏移地址的概念:
由于8086 CPU的寄存器只有16位,而地址线有20根,为使用16位的寄存器访问20位的内存地址(访问内存时,大多情况下需将内存地址放到寄存器中),8086 CPU将1M内存分成多个段(Segment)。每个段有自己的段起始地址(简称段地址,16位宽),每个段的最大长度为64K。当我们访问一个内存单元时,必须告诉CPU所访问的内存单元的段地址以及该单元距段首的距离(多少字节),该距离称为段内偏移地址(简称偏移量,Offset,16位宽)。离开段地址,孤立地谈偏移地址是没有任何意义的。在同一个段内,偏移量的变化范围是0000H~FFFFH。
为表示方便,8086规定,内存地址的表示方法为:16位段地址:16位偏移地址。其转换为20位物理地址的公式规定如下:
20位物理地址=16位段地址左移4位 + 16位偏称地址
注:由于左移4位后,段地址的低4位一定是0H,所以一个段的起始物理地址一定是xxxx0H的形式。
例如1A2B:5DF2对应的20位物理地址是200A2H:
1A2BH->左移4个二进制位->1A2B0H
                     +     5DF2H
                     =    200A2H                        

显然,表示同一个物理地址时可以有近64K种段地址和偏移量的组合。例如2000:00A2和200A:0002都表示同一个物理地址200A2H。
2006-3-25 23:50
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
62
1.5.3.3   段寄存器
1.5.3.3.1 段寄存器的作用
1.5.3.3.2 段超越
附图:
2006-3-27 17:33
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
63
1.5.3.3 的段寄存器使用规定
附图:
2006-3-27 17:35
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
64
1.5.3.3.3 使用段寄存器涉及的其他问题
附图:
2006-3-27 17:37
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
65
1.5.3.3.3 的段大于64K示意图
附图:
2006-3-27 17:38
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
66
偶像,我严重支持你~
2006-3-27 17:53
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
67
学习之中!!!!!!!
2006-3-27 18:31
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
68
我在学习的过程中遇到了和大家同样的问题,一个很普遍的问题---记忆力偏差。对于想要记住的东西,需要好多遍来强行记忆,可过些天记忆又开始模糊不清了,可总是反复记忆也不很有效的办法,所以我决定去寻找一种科学的增强记忆力的方法,终于找到了!现把方法提供给大家,做以参考:
(摘自http://ajy.ssjy.cn 作者不详,请来信通知)
*******************大脑记忆力训练法********************   
据意大利《晚邮报》报道,意大利一所大学三名教授进行了这样的一项实验:他们挑选了一位记忆中等的青年学生,让他每星期接受三至五天,每天一小时,背诵由三个数至四个数组成的数字训练。每次训练前,他如果能一字不差地背诵前次所记的训练。就让他再增加一组数字。经过二十个月约二百三十小时的训练,他起初能熟记七个数,以后增加到八十个互不相关的数,而且在每次练习时几乎能记住80%的新数字,使得他的记忆力能同一些具有特殊记忆力的专家媲美。
  可见,记忆力通过训练的确可以提高。事实上,古今中外的许多名人学者都通过各种方法来锻炼自己的记忆力。马克思从少年时代开始,坚持不断地用一种自己不太熟悉的外语去背诵诗歌,有意识地锻炼记忆力;列夫.托尔斯泰也是采用背诵的方式锻炼记忆力。他说:"背诵是记忆力的体操。"每天早晨,他都严格要求自己强记一些单词或其他方面的东西,以增强记忆力。宋代词人李清照采用与丈夫比赛竞猜某典故出自某书的方式,在兴趣盎然的娱乐中,巩固了知识,增强了记忆。
      专门研究锻炼记忆力方法的美国学者布鲁诺.弗斯特说:"要具备一个可靠的记忆力,必须每天费一刻钟到半个小时的时间,做一套有计划的脑力练习,复杂的或简单的均可,只要能迫使口口你去动脑筋。"
  下面给大家介绍几种行之有效的记忆力训练方法:
  (1) 积极暗示法
  许多人之所以记忆力不佳,是由于对自己的记忆力缺乏自信。在面对一个要记的材料时,这些人常常想:"多难记啊!","这么多,我能记住吗?"这种想法是提高记忆力的最大障碍。
  美国心理学家胡德华说:"凡是记忆力强的人,都必须对自己的记忆充满信心。"要想树立起这种信心就要进行积极的自我暗示,经常在心中默念:"我一定能记住!"。当你对能否记住缺乏信心时,也可以回忆自己过去的成功经验,如"我曾在全班各科考试成绩排前五名""我几岁的时候就能背许多唐诗"。当这些过去良好的记忆形象再次浮现时,会增强你"一定能记住"的信心。
  (2) 精细回忆法
  我们在平时的学习和生活中,识记了很多东西,却很少去回忆。识记和回忆之间的不平衡,使我们的记忆变得十分模糊。
  经常回忆,回忆得尽可能精细,是锻炼记忆力的好方法。比如:
  回忆一间你非常熟悉的房间,想一想房间里都有什么?门窗朝哪开?家具都摆放在哪里?墙上挂有哪些装饰品?暖气片和电灯开关在什么地方?等等。要回忆得尽量完整无缺。当你再次回到到间时,检查一下你遗漏了什么。
  想一想一小时前你在做什么?你在哪里?和什么人在一起?你们在一起都说了什么?那个人长得什么样?你如何向别人描述他的长相?
  回忆一下你最近看过的电影,电影里都有哪些主要人物?发生了什么事?他们都做了什么?结局如何?要尽可能回想电影中每一个镜头。
  回忆一下你童年的伙伴,你们在一起都做过什么?还能记起他们的名字吗?他们的家都住在什么地方?
      (3) 奇特联想法
  联想是促进记忆的一种方式。比如,我们遇到一个生字:咩。该字由口和羊组成,口即嘴,羊的嘴,除了吃草,还会叫。羊怎么叫"咩……"字义出来了,字音也知道。咩,羊叫之声,读MIE。
  奇特联想是联想的一种则将要记的东西在头脑中人为地形成一定衡奇古怪的联想,从而帮助记忆。比如,要想记住"狗--自行车"这对词,我们可以想象"狗骑着自行车在马路上逛来逛去"。有人要记"火车、河流、风筝、大炮、鸭梨、黄狗、闪电、街道、松树、高粱"共十个词,他形成如下奇特联想:一个人登上了高速的火车,火车在河流上奔驰,河流上飘来一个大风筝,风筝上架着一门大炮,大炮的炮筒里打出来一个大风筝,风筝上架着陆门大炮,大炮的炮筒里打出来一只鸭梨,鸭梨打进黄狗的嘴里,黄狗像一道闪电,迅速地路过街道,爬上一棵老松树,咬住了老松树上长着的一棵高粱。
  (4) 限时强记法
  在规定的时间里去背诵一些数字、人名、单词等等,可以锻炼博闻强记的能力。比如:
  在3分钟内,背诵圆周率()小数点后30位数字:3.141592653589793238462643383279;
  在2分钟内,背诵十个陌生的人名;
  在10分钟内,背诵十个外文生词。
  (5) 记忆保健操
  在头颈后部找到"天柱"、"风池"二穴,将两手交凡于脑后,用拇指的指腹腔按压这两个穴位,每次按压5秒钟,突然加压,然后将拇指移开,按压5至10次后,会感到头脑清醒。

(摘自:北京晚报 http://www.lzisti.net.cn/)
************多吃苹果西红柿增强大脑记忆力****************  
  据法新社报道,英国伦敦大学的伦敦国王学院科学家今天在曼彻斯特举行的英国皇家制药学会学术会议上发表论文指出,吃花椰菜、西红柿、柑橘、苹果和萝卜等5种水果蔬菜可能对增强脑力、防止阿尔茨海默氏病即早老性痴呆症有利。
  目前治疗阿尔茨海默氏病的大多数药品中都含有能抑制乙酰胆碱脂酶的物质,乙酰胆碱脂酶能破坏人脑神经信息传送。伦敦国王学院科学家发现,从这5种水果蔬菜中提取出来的物质含有抗乙酰胆碱脂酶的物质,其中花椰菜含量最多,科学家正准备对花椰菜进行深入的研究。

2006-3-27 18:32
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
69
(作者不详 由诸暨人民广播电台供稿www.zhuji.net )
           ******怎么开发大脑记忆力(全像思维)******
    在过去的二、三十年中, 有大量的实践研究企图解开人类左右两边脑部分别掌管的是怎样的功能的秘密.结果证明左
半部是语言控制中心,以逻辑性,时间性的思考运用;而右半部似乎掌管着意念、印象等整体图形等直觉的反应思考模式
.基本上这个二分法的大脑运作,在近年西方全像的理论是早己被推翻了,但是,为了让学习者了解一些理论性的探讨,
也只好将左,右脑分别来讨论其功能。 __让我们举一个生活例子来说:不知您是否有过如此的经验,某天在百货公司
或是街头上闲逛或是shopping时,突然一个熟悉的身影从旁而过,一时之间,你怎么了吗?是不是想不起他的名字,但
是你确定自己一定认识他;另外,我们常参加高中,国中时期的同学会时,有没有这样的经验了?一时之间又叫不出同
学的名字,但是那似曾相识的脸蛋,让你肯定知道,他是你的同学。 __这代表什么意思吗?右脑掌管着图像记忆功能,
所以你很有效率的搜寻到过去十几年前同学的画面(长像),但一时却又找不到左脑掌管的逻辑,推理的庞杂的数据库
中的同学名字;从例子中,可以很明确的知道,右脑的图像学习的效应是十分快而不易忘记.所以,从坊间的许多的学
习法中,而窥伺得知,皆是利用右脑的理论来利用图形及图片的记忆上或是透过情境的想象连接来帮助学习上的记忆效
果,尤其是在英文与幼儿教育领域中的应用范围更是广泛。 __但是实际上,这不过是利用右脑原本的记忆功能来应用,
只是人们大都不知道如何来强化及使用我们自己的大脑的应用吧了!而且,就以全像的理论来说,这也只是一小部分的
应用于记忆的一个方法,而非做到全脑的真正开发及使用。 __{因为,大脑的应用发展是不该区分于左右两脑的差异,
而应该是如何整合其全脑的应用,而图像思维就是如此,透过右脑的图像记忆效应来思索左脑的分析和决策,这才是全
像思维,而非仅是记忆效果。}
2006-3-27 18:57
0
雪    币: 208
活跃值: (376)
能力值: ( LV12,RANK:330 )
在线值:
发帖
回帖
粉丝
70
佩服佩服!
2006-3-27 23:45
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
71
有如此毅力,楼主的汇编大成指日可待
2006-3-28 00:30
0
雪    币: 9583
活跃值: (1935)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
72
支持学习,这么硬的骨头现在还是有许多人啃。
2006-3-28 09:05
0
雪    币: 200
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
73
1.6 8086的寻址方式目录


***************天生笨才也有聪明方法********************
我又重新温习了前面的东西,学习的过程真的很累,略显疲惫了。尤其是在“数字和公式”面前,显得力不从心,甚至要发疯。
这个问题和很多朋友遇到的情况一样,但我只能说明一点(和我同类型的朋友)---即使再累再疲惫,也千万别放弃,以后会轻松很多。
先 实践 后理论 ;先理论 后实践;边实践边理论。。。。(它们既有先后顺序、也可并行操作、更可多种组合)
实践和理论相结合是最好的回答!因为这两个词不论你是如何组合,最终的目的是为了体现学习的过程实现学习的最终目标。因人而宜、因人而定、因人而制(治)。
而我呢,文字图形逻辑脑细胞数量相对于其他脑细胞数量而言,文字图形逻辑脑细胞数多些,接受理论知识(概念、分类、表现形式、作用、图形、列表等等)的基本组成结构,快些。
所以我选择的是“理论、实践、理论、再理论、实践、再实践、理论”。
两词的组合结构里,“理论”一词多些,所以在实践的过程中,我出错的机会小,而接受深的东西就显得容易了。因为我追求准确无误的高标准,所以我在学习的过程中,耐心、毅力也随时间变化而变化。
缺点:效率低,准确的说在学习的一开始是这样的。(因为我1。没有相应的工具2.实际操作水平也太差了。3.没人带我)
2006-3-29 02:42
0
雪    币: 196
活跃值: (135)
能力值: ( LV10,RANK:170 )
在线值:
发帖
回帖
粉丝
74
楼上老兄,真是厉害啊!
2006-3-29 02:47
0
雪    币: 201
活跃值: (10)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
75
同步学习!!
2006-3-29 07:39
0
游客
登录 | 注册 方可回帖
返回
//