首页 > 编程语言 >java开发操作系统内核:由实模式进入保护模式之32位寻址

java开发操作系统内核:由实模式进入保护模式之32位寻址

时间:2023-06-14 11:08:15浏览次数:47  
标签:保护模式 java 字节 CODE32 32 mov LABEL eax ax


从时模式到保护模式,是计算法技术跨时代的发展。大家想想笨拙的Dos界面,黑底白字的那种冷漠界面到win95各种色彩斑斓的窗口,两者之间的区别其实就是实模式和保护模式的天壤之别。

保护模式中,最重要的一个概念莫过于”保护”二字,有了“保护”功能后,CPU为软件提供了很多的功能,当然也有了更多的限制。要详细解析保护机制,没有几千字的“鸿篇巨制”是不可能的,但我想你和我一样不会有那样的耐心,由此话不多说,我们先将代码跑起来再说:

%include "pm.inc"

org   0x9000

jmp   LABEL_BEGIN

[SECTION .gdt]
 ;                                  段基址          段界限                属性
LABEL_GDT:          Descriptor        0,            0,                   0  
LABEL_DESC_CODE32:  Descriptor        0,      SegCode32Len - 1,       DA_C + DA_32
LABEL_DESC_VIDEO:   Descriptor     0B8000h,         0ffffh,            DA_DRW

GdtLen     equ    $ - LABEL_GDT
GdtPtr     dw     GdtLen - 1
           dd     0

SelectorCode32    equ   LABEL_DESC_CODE32 -  LABEL_GDT
SelectorVideo     equ   LABEL_DESC_VIDEO  -  LABEL_GDT

[SECTION  .s16]
[BITS  16]
LABEL_BEGIN:
     mov   ax, cs
     mov   ds, ax
     mov   es, ax
     mov   ss, ax
     mov   sp, 0100h

     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_GDT
     mov   dword  [GdtPtr + 2], eax

     lgdt  [GdtPtr]

     cli   ;关中断

     in    al,  92h
     or    al,  00000010b
     out   92h, al

     mov   eax, cr0
     or    eax , 1
     mov   cr0, eax

     jmp   dword  SelectorCode32: 0

     [SECTION .s32]
     [BITS  32]
LABEL_SEG_CODE32:
    mov   ax, SelectorVideo
    mov   gs, ax
    mov   si, msg
    mov   ebx, 10
    mov   ecx, 2
showChar:
    mov   edi, (80*11)
    add   edi, ebx
    mov   eax, edi
    mul   ecx
    mov   edi, eax
    mov   ah, 0ch
    mov   al, [si]
    cmp   al, 0
    je    end
    add   ebx,1
    add   si, 1
    mov   [gs:edi], ax
    jmp    showChar
end: 
    jmp   $
    msg:
    DB     "Protect Mode", 0

SegCode32Len   equ  $ - LABEL_SEG_CODE32

我们再看看pm.inc的内容:

%macro Descriptor 3
    dw    %2  &  0FFFFh
    dw    %1  &  0FFFFh
    db   (%1>>16) & 0FFh
    dw   ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
    db   (%1 >> 24) & 0FFh
%endmacro


DA_32       EQU 4000h   ; 32 位段
DA_C        EQU 98h ; 存在的只执行代码段属性值
DA_DRW      EQU 92h ; 存在的可读写数据段属性值

把上面的代码通过以下命令编译:
nasm -o kernel.bat kernel.asm

在目录下会生成一个内核文件,kernel.bat,运行java代码,程序会将生成的内核写入到虚拟软盘中,将虚拟软盘加载到虚拟机中,运行后会得到如下结果:

我们看看代码,从LABEL_SEG_CODE32:这一部分开始,代码就执行在保护模式下,这段代码的作用就是显示一串字符,gs是计算机的一个寄存器,它跟eax,ebx这些寄存器差不多,但作用更为单一,主要用来指向显存,当我们将信息写入gs指向的内存后,信息会显示到屏幕上。用于显示字符的显存,内存地址从0XB800h开始,从该地址开始,每两个字节用来在屏幕上显示一个字符,这两个字节中,第一个字节的信息用来表示字符的颜色,第二个字节用来存储要显示的字符的ASCII值,屏幕一行能显示80个字符,大家看到代码中有语句:
mov edi, (80*11)
这表明我们要从第11行开始显示字符,接下来又有语句:
add edi, ebx
其中,ebx的值是11,这表明我们要从第11行的第10列开始显示字符串,接下来的语句是:
mov eax, edi
mul ecx

ecx的值是2,这个2就是我们前面说过的显示一个字符需要两个字节,上面几句汇编语句的作用是:
eax = ((80*11) + 10) * 2
这样eax就指向了第11行第11列所在的显存位置,接下来语句:
mov ah, 0ch
它的作用是在用来显示字符的两字节中,对第一个字节放入数值0ch,也就是设置字符的颜色,接下来的语句:

mov al, [si]
将寄存器si指向的字符的ascii值写入到第二个字节,这样,字符就显示到屏幕上了。大家注意寄存器si的用法:[si]. si相当于C语言中的一个指针,指向内存某个地址,[si]就是读取si指向的内存地址的信息,等同于 c语言中的*(si)

以上都是小细节,真正的要点是,我们要理解什么叫保护模式。我们先看保护模式的两个显著特点:

1.寻址空间从时模式的1M增强到4G
2.不同的代码拥有不同的优先级,优先级高的能够执行特殊指令,优先级低的,某些重要指令就无法执行。

于是进入保护模式,我们需要解决两个问题,一是如何获取超过1M以上的内存地址,第二是如何设置不同代码所具有的优先级。我们先看看寻找能力的变化,在实模式下,cpu是16位的,寄存器16位,数据总线16位,地址总线20位,于是寻找的范围必然受限于20位的地址总线,所以寻找范围无法超过1M(2^20).要想实现4GB的寻址,我们必须使用32位来表示地址,intel是这么解决这个问题的,他们用连续的8个字节组成的结构体来解决一系列问题:
byte0
byte1
…..
byte7

其中,字节2,3,4以及字节7,这四个字节合在一起总共有32位,这就形成了一个32位的地址。同时把字节0,字节1,以及将字节6的拆成两部分,各4个bits,前4个bits跟字节0,字节1合在一起,形成一个20个bit的数据,用来表示要访问的内存长度。这样,我们就解决了内存寻址的问题。

大家或许猜到,pm.inc里面的宏定义就是我们说的7字节数据结构,
%macro Descriptor 3
表示要初始化该数据结构,需要传入3个参数,%1表示引用第一个参数,%2表示引用第二个参数。初始化该结构时,输入的一个参数是内存的地址,大家看语句:
dw %1 & 0FFFFh
db (%1>>16) & 0FFh
这两句就是把内存地址的头三个字节放入到byte2,byte3,byte4,最后一句:
db (%1 >> 24) & 0FFh
就是讲地址的第4个字节放入到byte7. 初始化数据结构的第二个参数表示的是要访问的内存的长度,大家看语句:
dw %2 & 0FFFFh
就是把内存长度的头两个字节写入byte0,byte1,语句:
dw ((%2 >> 8) & 0F00h) | (%3 & 0F0FFh)
中的((%2 >> 8) & 0F00h)就是把内存长度的第16-19bit写入到byte6的前4个bit.由此要访问的内存和内存的长度就都设置好了,由于保护模式是一个非常复杂的逻辑,为了掌握它,我们一次只吃透一点,这样才好掌握,本章,我们只要了解如何进行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

LABEL_SEG_CODE32是一段代码的起始地址,上面的语句就是将这个起始地址写入到byte2,byte3,byt4,和byte7.大家是否会疑惑,为什么不在初始化时将这个地址直接传进去呢,例如:

LABEL_DESC_CODE32: Descriptor LABEL_SEG_CODE32, SegCode32Len - 1, DA_C + DA_32

这是因为,结构体初始化时只能传入常量,LABEL_SEG_CODE32所代表的数值需要编译器在将代码编译完后才能计算出来,所以LABEL_SEG_CODE32一开始的值还不能确定,因此不能直接用于初始化结构体。

以上的代码大家可以通过命令获取:
git clone https://github.com/wycl16514/OS-Kernel-from-real-to-protected-mode.git

大家看完这一章后,可能有一点感性认识,但更有可能是落入到一种云里雾里,不知所措的状态,这不要紧,在后面,我还会有很多篇幅来解释保护模式,大家的迷惑会在后面的阅读中一点一点的给驱散掉,面对一个复杂棘手的问题,将其分解,各个击破是最好的策略,只要我们有耐心,一步一个脚印,我们就会经历一个山穷水尽疑无路,柳暗花明又一村的体验。


标签:保护模式,java,字节,CODE32,32,mov,LABEL,eax,ax
From: https://blog.51cto.com/u_16160261/6476202

相关文章

  • java开发C语言解释器:数组元素的读取和赋值
    本节技术内容难度较大,请结合视频对代码的讲解和调试来理解本节内容:用java开发编译器一个成熟的编译器或解释器,要能够解析和执行目标语言开发的逻辑复杂的程序代码,我们用java开发的C语言解释器,能够执行用C语言开发的较为复杂的程序时,才称得上是合格的,从本节开始,我们致力于C语言解......
  • 保护模式超强的寻址功能:天空任鸟飞
    X86架构下,cpu的运行模式分两种,一种是实模式,像早期Dos那种黑底白字的命令行操作界面,可以说是实模式最好表现形式,在实模式下也只能产生这种冰冷,呆板,机械的用户体验。后来Intel的CPU进一步发展,引入了保护模式,由此,操作系统的发展进入了新的时代,在保护模式下,CPU功能进一步增强,进而支撑......
  • java开发C编译器:把函数调用编译成字节码
    本节,我们研究如何把函数声明和函数调用转换成可执行的java字节码,在完成本节代码后,我们的编译器能把下面代码编译成可被java虚拟机执行的字节码,示例代码如下:voidf(){printf("executefunctionf()");}voidmain(){f();}假设java一个类含有如下方法:publicfloatco......
  • java开发操作系统内核:让内核突破512字节的限制
    我们当前的系统内核,必须包含在虚拟软盘的第1扇区,由于一个扇区只有512字节,因此,系统内核的大小不可能超过512字节。但是,一个拥有完善功能的内核不可能只有512字节,因此要想越过512字节的限制,具体的做法就是做一个内核加载器,放入到第一扇区,加载器加载如内存后,再将内核从软盘加载到系统......
  • java开发编译器:LR 状态机的缺陷与改进
    前两节我们构造的状态机有些缺陷,当我们进入某个状态节点时,根据该节点的特性,我们需要产生一些动作,根据上两节的有限状态机图,当我们进入节点5,我们发现,符号”.”为位于表达式的最右边,在.后面不再有其他非终结符或终结符,进入这样的节点时,我们要根据表达式做一次reduce操作,例如在节点5......
  • java开发C编译器:结构体的解析和执行
    更详细的讲解和代码调试演示过程,请参看视频用java开发C语言编译器结构体是C语言中,最为复杂的原生数据结构,它把多种原生结构结合在一起,形成一个有特点含义的数据结构,要实现一个完整的C语言编译器或解释器,就必须要拥有对结构体的解析能力,本节,我们在当前解释器的基础上,增加结构体的解......
  • 用java做操作系统内核:软盘读写
    在前两节,我们将一段代码通过软盘加载到了系统内存中,并指示cpu执行加入到内存的代码,事实上,操作系统内核加载也是这么做的。只不过我们加载的代码,最大只能512byte,一个操作系统内核,少说也要几百兆,由此,系统内核不可能直接从软盘读入系统内存。通常的做法是,被加载进内存的512Byte程......
  • java开发系统内核:caps 按键处理
    更详细的讲解和代码调试演示过程,请参看视频LinuxkernelHacker,从零构建自己的内核上一节,我们成功实现了对shift按键的处理,这一节,我们看看如何处理caps按键,当该键按下时,输入系统的字符在大小写间切换。由于我们系统启动后,默认输入是大写字符,完成本节后,我们把系统的默认字符改成......
  • java开发系统内核:像Linux一样使用中断实现内核API
    我们当前提供的内核API有个问题,就是每次使用时,需要计算API函数在内核中的位置,一旦内核代码改变,API接口的位置也会改变,同时调用API的应用程序也必须跟着改变,显然这种限制是不可接受的。为了突破当前缺陷,我们必须想出新的API提供办法。常用的做法是,仿照Linux将API当做一个中断调用,由......
  • java开发系统内核:使用C语言开发系统应用程序
    更详细的讲解和代码调试演示过程,请参看视频用java开发C语言编译器更详细的讲解和代码调试演示过程,请参看视频如何进入google,算法面试技能全面提升指南如果你对机器学习感兴趣,请参看一下链接:机器学习:神经网络导论更详细的讲解和代码调试演示过程,请参看视频LinuxkernelHacker,......