others linux服务器运维 django3 监控 k8s golang 数据库 大数据 前端 devops 理论基础 java oracle 运维日志

简单操作系统内核开发2

访问量:1432 创建时间:2021-01-15

32位x86架构处理器编程架构

32处理器起源于1985年10月推出的80386,寄存器,数据总线,算数逻辑单元都是32位,提供32位地址线,可以访问4G内存。80386兼容8086指令。

保护模式:将内存的每个程序隔离,避免程序之间互相影响。

x86 : Intel公司86系列处理器的统称。

IA-32,x86-32: 英特尔32位架构

从16位到32位处理器寄存器的扩展和扩充(32位处理器高16位不能独立使用):

8086 16位通用寄存器 80386 32位通用寄存器
AX16位{AH高8,Al低8} EAX 32位
BX16位{BH高8,Bl低8} EBX 32位
CX16位{CH高8,Cl低8} ECX 32位
DX16位{DH高8,Dl低8} EDX 32位
SI16位 ESI 32位
DI EDI
BP EBP
SP ESP

寄存器混合使用案例:

mov edx ,0xf000c000
mov dx,0        ;edx=0xf000 0000
add edx,0xcccc  ;edx=0xf000cccc
实模式 保护模式
只和真实内存地址进行交互,程序内存布局是真实物理内存布局 内存的真实布局进行了转换

32位指令指针寄存器的扩展: EIP(保护模式下才会用EIP)32位{IP低16位,在实模式下只用ip处理指令},标志位寄存器扩展:EFLAGS{低16位FLAGS}

32位新增加的段寄存器FS,GS,总的段寄存器CS\DS\ES\FS\GS\SS,段寄存器长度未变16位;实模式下段寄存器用法不变(用来保存逻辑段地址,逻辑段地址左移4位加偏移地址,形成20位地址来寻址),保护模式下,段寄存器用法发生变化。

32位处理器内存访问概况:

程序所使用的段需要提前分配,登记在一个表中叫做描述符表,记录了每个段的起始地址,段的长度等基本信息,段的长度没有限制,描述符内段的起始地址可以是32位的任意地址,段的长度最大可以使4G字节(假如段起始是0,段长度是4G); 在保护模式下段寄存器不在用于保存逻辑段地址,而是描述符的选择子,用来从描述符中选择一个描述符,通过描述符,处理器取出段基地址+程序内给出的段内偏移地址形成完整的内存地址,偏移地址32位没有限制。

32处理器寻址概述:

寄存器寻址:

mov ah,dh ; 寄存器寻址,寄存器宽度8位,在32位处理器依然有效 mov ax,dx ; 寄存器寻址,寄存器宽度16位,在32位处理器依然有效 mov eax,edx ;寄存器寻址,寄存器宽度32位

内存寻址,如果指令中包含了内存寻址,一定是一个段内偏移地址:有效地址(Effective address :EA),默认段地址ds

32位:基址寄存器+变址寄存器*(1、2、4、8比例因子)+8位或32位偏移量,无论是16位还是32位,不允许使用栈指针寄存器sp提供有效地址(ESP可以),下图mov ax,[sp]非法

案例:16位写显存显示字符:

mov ax,0xb000
mov ds,ax

mov bx,0x8000

mov byte [bx+0x00],';'
mov byte [bx+0x01],0x07
mov byte [bx+0x02],')'
mov byte [bx+0x03],0x07

jmp $
  times 510-($-$$) db 0
  db 0x55,0xaa

使用eax代替bx(16位处理器不能使用ax形成有效地址,32位处理器可以使用eax形成有效地址)

mov ax,0xb000
mov ds,ax

mov eax,0x8000

mov byte [eax+0x00],';'
mov byte [eax+0x01],0x07
mov byte [eax+0x02],')'
mov byte [eax+0x03],0x07

jmp $
  times 510-($-$$) db 0
  db 0x55,0xaa

x86流水线技术,pipline

下图,每条指令执行都要3个步骤,假设每个步骤需要一个时钟周期,3个指令执行需要9个周期,流水线执行需要5个周期。

高速缓存技术

乱序执行技术 out of order execution

寄存器重命名技术

分支目标预测技术branch Prediction (当处理器遇到转移指令时,需要清空flush流水线),分支目标缓存器BTB

保护模式

保护模式下每个程序有自己的特权级0,1,2,3 ;0级最高-操作系统,3级最低-用户程序,因为操作系统权限最高,可以分配内存、加载用户程序、设置用户程序特权级别、调度用户程序执行等等。程序由代码段、数据段、栈段组成(包括操作系统程序,用户程序),操作系统会等级每个程序所使用的段,包括段的起始内存位置、段的长度等信息,如果一个程序的特权级很低,它只能访问自己的段

后面的内容包括:

全局描述符表GDT 段描述符的各个组成部分以及他们的含义和作用 认识描述符表寄存器GDTR、控制器CR0和段选择子 进入32位保护模式的方法和步骤 保护模式下的程序调试 x86新指令lgdt(load GDT)

和一个段有关的信息需要8个字节来描述,称为段描述符Segment Descriptor,每个段需要一个段描述符,需要在内存开辟一段空间存储段描述符,当所有段描述符在这里集中存放形成的表格叫做描述符表,用来管理所有的用户程序,在进入保护模式前要先定义GDT,为了跟踪GDT,在处理器内有一个48位寄存器GDTR(保存了GDT的起始线性地址和GDT的界限值,界限值是表内最后一个字节相对于起始值的偏移量=<表的总字节数减1>,所以表最大为65536个字节即64kB,一个描述符8个字节,所以可以在GDT中定义8192个描述符),由于实模式下只能访问1M内存,GDT通常定义下0-1M内的内存空间。

描述符分类:一类--存储器的段描述符,用于描述一般的代码段或者数据段(栈段也是数据段),另一类是系统描述符,用于描述系统运行控制相关的内容,包括:系统的段描述符、门描述符(与程序运行有关的逻辑结构,过程调用、中断处理等)

描述符的作用:每个描述符用于描述一个代码段,或者数据段,或者过程入口信息等,描述符是这些对象的索引标签,通过描述符可以定位找到这些对象.

如上图所示,每个描述符占8字节64位,从0-63,描述符位44是S位,S=0是系统描述符,type段用来指定系统段类型或者门的类型。

存储器的段描述符-段的类型和段的特权级:

在S=1时,当前描述符是存储器的段描述符,TYPE(决定存储器的段类型)的位11是执行位X(executable),X=0表示不可执行是数据段。E(expand)扩展位,表示扩展方向E=0,表示段向上扩展,是普通数据段,E=1从高地址向低地址扩展是栈段;W位是段的读写位,是否可写,W=0不允许写入,只能读,W=1表示段可以正常写入。

X=1,该描述符表示是一个可执行的段,即是代码段,在这种情况下另外三位叫做C、R、A位,C是特权级依从的段,C=0表示非依从的代码段,只有特权级相同的程序才能直接转移到这个段内执行;C=1表示依从的代码段,特权级低的程序可以直接转移到段内执行。没有特殊要求C位通常清零。R位指示代码段是否可以被读出,为了防止程序被破坏代码段是不能被写入的,R决定代码段是否能像数据一样被读取,R=0不能读出,R=1可以读出。A位accessed是否已访问过,无论X是0、1,该描述符所描述的段是否使用过,描述符创建后A位清零,访问后置1,清零由操作系统负责,内存紧张时操作系统将不经常用的段退避到磁盘上,从而实现虚拟内存管理。如下图所示:

描述符的高双字,位13-14两表示段的特权级,组合是00 01 10 11,代表 0,1,2,3 四种特权级别,如下图所示:

描述符中有20位的段界限(段边界),低双字的位的0-15 ,高双字的位 16-19,20位的段界限限制段的扩展范围。对于向上扩展的段,段界限是最大偏移量,超过最大界限会被处理器阻止出现异常中断,段的大小是段界限+1 ; X=0且E=1向下扩展的数据段(通常用作栈段),使用SP时,最大值FFFF,使用ESP时ESP最大值FFFFFFFF,此时段界限是指针所不允许的最小值,等于或小于这个值会引发处理器中断,如果指定的段界限是0,并不是至段大小是0,相反段此时是最大值

G位,段界限的单位,G=0是段界限以字节为单位,G=1段界限以4K字节为单位(G=1时 ,实际使用的段界限= 描述符中的段界限+0x1000+0xFFF)

P位,段存在位,P=0 表示段不存在(当内存资源紧张时,将不常用的段换出内存放入磁盘),当通过描述符访问内存中的段时,如果p=0,产生异常中断,该中断处理过程由操作系统提供,负责从硬盘将段换回内存,并将p位置1,在多用户多任务系统中是常用任务策略。

L位: 64位代码标段志位,保留此位给64位处理器使用,用于指示64位的代码段,(32位处理器)目前将这一位置0即可

描述符D/B位(操作尺寸/栈上部边界) ,高双字的位22,用于说明段时按16位(B/D位=0)进行操作还是按32位(B/D位=1)进行操作,例如对于向下扩展的段界限来说,用于指示0xffff还是0xffffffff,使用SP进行操作还是ESP进行操作

AVL位(Available可自由使用的保留位),高双字的20位。

在全局描述符表GDT中安装存储器的段描述符

下图段的基地址是0x000B 8000,段界限是0x0FFFF=64k,P位1,说明段存在,DPL=0特权级,S=1说明是存储器的段,X=0说明是数据段,E=0向上扩展,W=1这个段可写,A初始化为0, ,

此描述符在内存中的分布:

通过上面的代码可以创建GDT表,下面要加载全局描述符表线性地址和全局描述符表边界到48位的GDTR寄存器,使用lgdt指令

lgdt m ; m的起始地址往后是一个48位的操作数,这个操作数分为2部分:第一部分是GDT的界限值,长度1个字;第二部分是GDT线性地址,长度是2个字

开启处理器的第21根地址线A20

         in al,0x92                         ;南桥芯片内的端口 
         or al,0000_0010B     ;二进制数据,将al位1置1
         out 0x92,al                        ;会写数据,打开A20

通过设置控制寄存器CR0(Control Register:CR,因为处理器还有CR1,cr2,cr3等,所以命名CR0)的PE位,开启保护模式,(在调试器中观察gdtr和cr0). CR0是32位,位0是PE位,PE=1进入保护模式。

         cli                                ;保护模式下中断机制尚未建立,应 
                                            ;禁止中断 
         mov eax,cr0
         or eax,1
         mov cr0,eax                        ;设置PE位

段寄存器的描述符高速缓存器,用于保护模式下得内存访问。在保护模式下处理器的很多工作方式发生变化,在保护模式下CS\DS\ES\FS\GS\SS不再叫段寄存器,而叫段选择器,这6个寄存器各自增加了1个不可见的部分,叫做描述符高速缓存器。描述符高速缓存器存放段的线性基地址、段界限和访问控制属性。在实模式下段选择器内容是逻辑段地址,在保护模式下,段选择器内容是段描述符的选择子,简称选择子。

段选择子 (Segment Selector:SS),由三部分组成,第一部分 描述符索引用来在描述符表中选择一个段描述符,2的13次方=8192,可以选择8192个段描述符;第二部分TI,Table Indicator:TI表指示器 ,描述符表的指示器,TI=0时表示要选择的描述符在全局描述符表GDT中,TI=1描述符在LDT中,第三部分位0和位1是RPL,请求特权级。为了访问一个段,要将段选择子传送到段选择器,一旦用段选择子改变了段选择器,处理器会立刻用段选择子去描述符表中取出描述符,将描述符的内容传送到描述符高速缓存器中,从此处理器就用高速缓存器中的线性基地址+段内偏移来访问内存,而不是用段选择器。

加载数据段选择子的案例:

;选择GDT的第二个描述符
mov cx,00000000000_01_000B
mov ds,cx

加载段选择子处理器处理过程如下:

80286的16位保护模式

80286寄存器基本与8086相同,AX\BX\CX\DX\Si\DI\SP\BP\IP\CS\DS\ES\SS,但是80286由24根地址线,为了支持超过1M内存,支持多用户多任务,加强对程序的保护,80286引入了保护模式

下图显示描述符基地址是24位,80286的段寄存器只有4个,但是80286增加了描述符高速缓存器(包含段的线性基地址、界限和属性),

16位处理器指令操作尺寸8、16,32位处理器指令操作尺寸8、16、32

x86处理器机器指令格式,操作码1-3个字节

F4 hlt FA cli FD std C3 ret CC int3

操作码1-3个字节+立即数1/2/4个字节(注意立即数低端字节序)

B0 03 mov al,3 B9 0300 mov cx,3 BA 03000000 mov edx,3

操作码参考x86架构软件开发手册卷一-基本架构,卷二-指令集参考,卷三-系统编程指南

x86处理器指令格式,寻址方式ModR/M和偏移量部分。

有些指令比较复杂,如需要在2个寄存器之间或者寄存器内存之间进行操作,此时在操作码的后面需要一个寻址方式部分ModR/M(长度1个字节)

88C8 mov al,cl 8B07 mov ax,[bx] 8B08 mov cx,[bx+si]

ModR/M 的组成(mod和r/m字段配合使用)

67位 mod 345位 reg/opcode 012位 r/m

x86处理器指令格式--寻址方式SIB

32位寻址方式如下图:

上图寻址方式的指令格式 操作码1-3+ modR/m 1个字节 + SIB 1个字节

SIB字段的格式:

x86处理器指令格式--前缀部分(如段超越前缀,一个指令最多可以有4个前缀,每个前缀1个字节)

机器码 指令
8917 mov [bx],dx
2E8917 mov [cs:bx],dx
A5 movsw
F3A5 rep movsd

16位操作尺寸,32位操作尺寸,处理器默认操作尺寸和相关指令前缀

不同的指令可能有相同的机器码(如果解决这个问题,通过默认操作尺寸增加机器码前缀)如下图:

伪指令bits生成16位和32位模块

bits 16 ;(可以用[]括起来[bits 16]) 告诉编译器假定后面的操作尺寸是多少(同一条指令不通的bits下编译出来的操作码不同)。要求程序开发人员清除程序在什么情况下执行,处理器的默认操作尺寸是多少。

描述符和段描述符高速缓存器的D位。

如何知道处理器当前默认的操作尺寸,如何改变处理器的默认操作尺寸,在段描述符高双字22位,位D/B位。对于S=1,x=1 这一位是D位,D=0是16位操作尺寸,D=1表示32位操作尺寸。通过高速缓存器的内容D位,可以知道处理器当前默认的操作尺寸。

进入保护模式后,立即切换到32位模块并使用32位默认操作尺寸。 直接绝对远转移指令:在实模式下与保护模式下不同

         ;文件说明:硬盘主引导扇区代码 
         ;设置堆栈段和栈指针 
         mov ax,cs      
         mov ss,ax
         mov sp,0x7c00

         ;计算GDT所在的逻辑段地址 
         mov ax,[cs:gdt_base+0x7c00]        ;低16位 
         mov dx,[cs:gdt_base+0x7c00+0x02]   ;高16位 
         mov bx,16        
         div bx            
         mov ds,ax                          ;令DS指向该段以进行操作
         mov bx,dx                          ;段内起始偏移地址 

         ;创建0#描述符,它是空描述符,这是处理器的要求
         mov dword [bx+0x00],0x00
         mov dword [bx+0x04],0x00  

         ;创建#1描述符,保护模式下的代码段描述符
         mov dword [bx+0x08],0x7c0001ff     
         mov dword [bx+0x0c],0x00409800     

         ;创建#2描述符,保护模式下的数据段描述符(文本模式下的显示缓冲区) 
         mov dword [bx+0x10],0x8000ffff     
         mov dword [bx+0x14],0x0040920b     

         ;创建#3描述符,保护模式下的堆栈段描述符
         mov dword [bx+0x18],0x00007a00
         mov dword [bx+0x1c],0x00409600

         ;初始化描述符表寄存器GDTR
         mov word [cs: gdt_size+0x7c00],31  ;描述符表的界限(总字节数减一)   

         lgdt [cs: gdt_size+0x7c00]

         in al,0x92                         ;南桥芯片内的端口 
         or al,0000_0010B     ;二进制数据,将al位1置1
         out 0x92,al                        ;会写数据,打开A20

         cli                                ;保护模式下中断机制尚未建立,应 
                                            ;禁止中断 
         mov eax,cr0
         or eax,1
         mov cr0,eax                        ;设置PE位

         ;以下进入保护模式... ...
         jmp dword 0x0008:flush             ;16位的描述符选择子:32位偏移
                                            ;清流水线并串行化处理器 
         [bits 32] 

    flush:
         mov cx,00000000000_10_000B         ;加载数据段选择子(0x10)
         mov ds,cx

         ;以下在屏幕上显示"Protect mode OK." 
         mov byte [0x00],'P'  
         mov byte [0x02],'r'
         mov byte [0x04],'o'
         mov byte [0x06],'t'
         mov byte [0x08],'e'
         mov byte [0x0a],'c'
         mov byte [0x0c],'t'
         mov byte [0x0e],' '
         mov byte [0x10],'m'
         mov byte [0x12],'o'
         mov byte [0x14],'d'
         mov byte [0x16],'e'
         mov byte [0x18],' '
         mov byte [0x1a],'O'
         mov byte [0x1c],'K'

         ;以下用简单的示例来帮助阐述32位保护模式下的堆栈操作 
         mov cx,00000000000_11_000B         ;加载堆栈段选择子
         mov ss,cx
         mov esp,0x7c00

         mov ebp,esp                        ;保存堆栈指针 
         push byte '.'                      ;压入立即数(字节)

         sub ebp,4
         cmp ebp,esp                        ;判断压入立即数时,ESP是否减4 
         jnz ghalt                          
         pop eax
         mov [0x1e],al                      ;显示句点 

  ghalt:     
         hlt                                ;已经禁止中断,将不会被唤醒 

;-------------------------------------------------------------------------------

         gdt_size         dw 0
         gdt_base         dd 0x00007e00     ;GDT的物理地址 

         times 510-($-$$) db 0
                          db 0x55,0xaa

存储器的保护

mov ds,ax ; 此指令任何时候(16位和32位)机器码都一样 mov ds,eax

保护模式下修改段寄存器的保护机制:1、将一个描述符选择子带入段选择器,此时要检查带入值的合法性,2、用选择子选择一个描述符并传送到高速缓存器,此时要检查描述符的正确性和完成性。

xchg al,ah ;交换指令 exchange,xchg r/m ,r/m ,两个操作数不能同时为内存地址。操作数长度一致。 xchg al,ah xchg ecx,edx xchg xc,[0x7e00]

字符串跑冒泡排序并在屏幕显示:

         ;文件说明:硬盘主引导扇区代码 
         ;设置堆栈段和栈指针 
         mov eax,cs      
         mov ss,eax
         mov sp,0x7c00

         ;计算GDT所在的逻辑段地址
         mov eax,[cs:pgdt+0x7c00+0x02]      ;GDT的32位线性基地址 
         xor edx,edx
         mov ebx,16
         div ebx                            ;分解成16位逻辑地址 

         mov ds,eax                         ;令DS指向该段以进行操作
         mov ebx,edx                        ;段内起始偏移地址 

         ;创建0#描述符,它是空描述符,这是处理器的要求
         mov dword [ebx+0x00],0x00000000
         mov dword [ebx+0x04],0x00000000  

         ;创建1#描述符,这是一个数据段,对应0~4GB的线性地址空间
         mov dword [ebx+0x08],0x0000ffff    ;基地址为0,段界限为0xfffff
         mov dword [ebx+0x0c],0x00cf9200    ;粒度为4KB,存储器段描述符 

         ;创建保护模式下初始代码段描述符
         mov dword [ebx+0x10],0x7c0001ff    ;基地址为0x00007c00,512字节 
         mov dword [ebx+0x14],0x00409800    ;粒度为1个字节,代码段描述符 

         ;创建以上代码段的别名描述符
         mov dword [ebx+0x18],0x7c0001ff    ;基地址为0x00007c00,512字节
         mov dword [ebx+0x1c],0x00409200    ;粒度为1个字节,数据段描述符

         mov dword [ebx+0x20],0x7c00fffe    ;基地址0x00007c00 ,向下扩展,界限0xffffe,粒度4KB,数据段描述符
         mov dword [ebx+0x24],0x00cf9600

         ;初始化描述符表寄存器GDTR
         mov word [cs: pgdt+0x7c00],39      ;描述符表的界限   

         lgdt [cs: pgdt+0x7c00]

         in al,0x92                         ;南桥芯片内的端口 
         or al,0000_0010B
         out 0x92,al                        ;打开A20

         cli                                ;中断机制尚未工作

         mov eax,cr0
         or eax,1
         mov cr0,eax                        ;设置PE位

         ;以下进入保护模式... ...
         jmp dword 0x0010:flush             ;16位的描述符选择子:32位偏移,jmp会隐式修改段寄存器cs

         [bits 32]                          
  flush:                                     
         mov eax,0x0018                      
         mov ds,eax

         mov eax,0x0008                     ;加载数据段(0..4GB)选择子
         mov es,eax
         mov fs,eax
         mov gs,eax

         mov eax,0x0020                     ;0000 0000 0010 0000
         mov ss,eax
         xor esp,esp                        ;ESP <- 0

         mov dword [es:0x0b8000],0x072e0750 ;字符'P''.'及其显示属性
         mov dword [es:0x0b8004],0x072e074d ;字符'M''.'及其显示属性
         mov dword [es:0x0b8008],0x07200720 ;两个空白字符及其显示属性
         mov dword [es:0x0b800c],0x076b076f ;字符'o''k'及其显示属性

         ;开始冒泡排序 
         mov ecx,pgdt-string-1              ;遍历次数=串长度-1 
  @@1:
         push ecx                           ;32位模式下的loop使用ecx 
         xor bx,bx                          ;32位模式下,偏移量可以是16位,也可以 
  @@2:                                      ;是后面的32位 
         mov ax,[string+bx] 
         cmp ah,al                          ;ah中存放的是源字的高字节 
         jge @@3 
         xchg al,ah 
         mov [string+bx],ax 
  @@3:
         inc bx 
         loop @@2 
         pop ecx 
         loop @@1

         mov ecx,pgdt-string
         xor ebx,ebx                        ;偏移地址是32位的情况 
  @@4:                                      ;32位的偏移具有更大的灵活性
         mov ah,0x07
         mov al,[string+ebx]
         mov [es:0xb80a0+ebx*2],ax          ;演示0~4GB寻址。
         inc ebx
         loop @@4

         hlt 

;-------------------------------------------------------------------------------
     string           db 's0ke4or92xap3fv8giuzjcy5l1m7hd6bnqtw.'
;-------------------------------------------------------------------------------
     pgdt             dw 0
                      dd 0x00007e00      ;GDT的物理地址
;-------------------------------------------------------------------------------                             
     times 510-($-$$) db 0
                      db 0x55,0xaa

保护模式程序的动态加载和运行

bswap r ;反转寄存器的操作数 cpuid 指令

条件传送指令:

cmovne 不相等则传送(cmovcc指令族) cmov.. r, r/m

SGDT m ;SGDT指令 Store Global Descriptor Table register,将全局描述符寄存器GDTR内容读出的内存M, m必须有6个字节的空间.

movzx movsx

字符串比较指令:

cmpsb 按字节进行比较 cmpsw 按字进行比较 cmpsd 按双字进行比较 cmpsq 按四字进行比较

当前处理器默认的操作尺寸 目的串 源串
16 ES:DI DS:SI
32 ES:EDI DS:ESI
64 ES:RDI DS:RSI

串比较指令的重复前缀,串比较的方向(cmpsb从源串地址和目的串地址各取出一个字节比较)

rep cmpsb rep cmpsw rep cmpsd rep cmpsq

前缀rep的重复次数

默认操作尺寸 rep前缀使用的计数器
16 cx
32 ecx
64 rcx

rep cmpsb 必须将cx/ecx/rcx设置为串的总字节数 rep cmpsw 必须将cx/ecx/rcx设置为串的总字节数/2 rep cmpsd 必须将cx/ecx/rcx设置为串的总字节数/4 rep cmpsq 必须将cx/ecx/rcx设置为串的总字节数/8

repe 相等则重复 repz 为0则重复 repne 不相等则重复 repnz 不为0则重复

重复前缀 终止条件
rep cx/ecx/rcx=0
repe/repz cx/ecx/rcx=0 并且zf=0
repne/repnz cx/ecx/rcx=0 并且zf=1

案例: repe cmpsw ,如果比较结果标志位ZF=1,说明相等,(cx/ecx/rcx 减1)则再次执行repe cmpsw,如果比较结束cx/ecx/rcx=0且ZF=1,说明2个字符串相等;再重复执行的过程中有2个字符串不相等,ZF=0,比较过程提前终止,两个串不同。

串的比较方向取决于标志寄存器DF位。

串比较的过程:

  1. 用CLD或STD清除或者置方向位DF;
  2. 根据处理器的默认操作尺寸和方向标志,设置DS和SI/ESI/RSI以指向源串。
  3. 根据处理器的默认操作尺寸和方向标志,设置ES和DI/EDI/RDI以指向目的串。
  4. 根据处理器的默认操作尺寸以及选择的串比较指令,将比较的次数传送到CX/ECX/RCX;
  5. 根据实际情况,选择以下串比较指令中的一个:CMPSB/SMPSW/SMPSD/SMPSQ;
  6. 根据实际情况,为上述串比较指令指定一个前缀,可以是repz/repe/repnz/repne;
  7. 重复比较结束后,根据零标志ZF最后的状态0还是1,判断两个串是否相同。

以十六进制形式显示一个双字、PUSHAD\POPAD和XLAT指令

pushad 指令意思是 push all double ,压入全部的双字寄存器,压栈顺序EAX-ECX-EDX-EBX-ESP-EBP-ESI-EDI

popad 指令的意思是pop all double,从栈中弹出双字寄存器,出栈顺序 EDI-ESI-EBP-废弃-EBX-EDX-ECX-EAX

为了是数值转换成可显示的字符编码,可使用xlat查表指令:

任务与任务的创建

操作系统有自己的数据段-栈段-代码段,有全局GDT;每个用户程序有自己的数据段-栈段-代码段,有自己独立的LDT。每个用户程序还要有一个特殊的内存段TSS任务状态段(task status segment),用来保存改程序的相关信息(多任务操作系统,从当前执行的程序切换到其他程序需要保存当前任务的状态信息【段寄存器的值、指令指针寄存器的值、通用寄存器的值等等】,等下次程序运行时,再从tss中恢复)

任务控制块=Task Control Block :TCB

push imm8 ;push byte 0x55 push imm16 ;push word 0x55 push imm32 ;push dword 0x55

带参数的ret 指令返回调用者

登陆评论: 使用GITHUB登陆