保护模式——从分段到分页

时间:2021-05-24 16:46:07

保护模式——从分段到分页

  

其实很早以前就想写这个关于分页模式的了,可是自己还是有点东西没弄的太明白,今天也是终于写出来了,关于上一篇讲分段机制的,一博友还夸了我一下,另我有点小激动,开博客三年来第一次啊!谢谢那位博友,也同时给我很大鼓励,觉得就是每天抱着一大堆天书,没人指导的也值了!

当然也免不了会有错误,但是以后会开一个错误更正章,专门负责更正错误。

关于分段模式和分页模式的关系,分页是在分段的基础上进行的,具体关系请见:内存管理--三个地址,分页与分段的关系http://blog.sina.com.cn/s/blog_6730a3aa01010z8m.html

大家都知道,我们现在的内存现在都是几个GB,上一章也说了是为了提高寻址范围而有了保护模式,那么在保护模式下分段模式下总感觉内存定位不是很好,其实单单是使用分段的话也能实现应有的功能。而分页模式就为内存提供了更加细致的划分,每个页是4KB,每个也都能准确的寻址定位到具体的字节。这样细致的划分也就很啥啥啥了。

那么首先来介绍下分页机制的原理,分页机制采用的是三级寻址,为了好理解,我们用一个比喻:

我们平时看书的时候要想看哪个具体内容,也就是哪一页,我们首先找到目录里面的哪一章,然后在找到的章中找到哪一节,然后我们再到找到的节中具体的找到哪一页。那么,这其中就用到了三个“地址”,章、节和页。

类似的,在内存分页机制也是这样的(引用老师说的话就是当计算机的东西困扰人的时候,计算机工作者就会想到实际生活,看,这个就是实际生活的例子),我们把一页内存(4KB)比喻成书的一节(注意不是一页,而是一节,可以把具体的字节(具体地址)看成是书的一页),保存这些内存页的地址的地方我们成为页表,实际是内存里面的一张表(先记住!!!),我们把保存所有页表地址的地方比喻成为章,我们成为页目录表,也就是说页目录表里面存的是很多页表的地址,页表里面存的是很多页地址(你明白了吗?:-)),实际上页目录表和页表都是内存里面的表,是实际存在内存里面的。那么现在你就会问了,页目录表是怎么寻到的?其实页目录表的首地址是在一个固定的寄存器中存存着的,硬件会自动识别:-)。那么,现在寻到4KB的页了以后是不是还是需要一个地址来寻到具体的地址,也就是定位到哪一个字节?是的:-)。现在来理清一下寻址方式:

保护模式——从分段到分页


好了,了解了分页机制的大致过程之后我们再来看点专业的东西:

首先,我们来看看保存页目录表的寄存器:

CR3控制寄存器:

还记得上一章说的CR0吗?跟这个差不多,80X86一共有五个控制寄存器:CR0~CR4,现在我们看看前四个:

保护模式——从分段到分页

前三个暂时不说,看 CR3,CR3的后20位保存的是页目录表的地址的高20位,也就是说页目录表的地址的低12位必须位0。

然后再来说说传说中的线性地址和物理地址,还有一种地址怕弄乱在此不说,线性地址说通俗点就是我们程序里面写的地址(事实上线性地址是由分段模式翻译过来的地址,具体请见:内存管理--三个地址,分页与分段的关系http://blog.sina.com.cn/s/blog_6730a3aa01010z8m.html,物理地址就是内存里面的实际地址,保护分页模式下线性地址经过一系列翻译后翻译成物理地址,然后才可访问,那么我们在程序里面写的线性地址格式如下:

22~31位:A段

12~21位:B段

0~11位:C段

为了防止头晕,我们不使用那些专业术语,我们直接使用A、B、C段来表示,A段存的数据指示的是页目录表里面的哪一项,单位为(个),注意,不是字节!!!B段存的数据指示的是页表里面的哪一项,单位为(个),C段指示的是页内的偏移量,单位为(字节)。

值得说明的是:内存里面可以有很多页目录表,一般4GB以内,一个页目录表就够用了,一个页目录表指向很多页表,一个页表指向很多页,从上面可以看出,一个页目录表总共最多可以指向2^10=1024个页表,一个页表最多可以指向2^10=1024个页,每个页有2^12=4KB.实际上,一个页目录表里面的项有4字节,一个页表里面的项也是4个字节。所以一页内存可以存储一个页目录表或者一个页表。

我们再来看看页目录表里面的一项,我们就称为页表项,一个页表项占4个字节,格式如下:

保护模式——从分段到分页

其中 12~31位存的是页表的基地址,其他的下面介绍。

页表里面的一项称为页表项,格式如下:

保护模式——从分段到分页

其中 12~31位存的是页的基地址,其他的位同页表项一样:

如下摘自《Linux0.11源码分析》:

 保护模式——从分段到分页
保护模式——从分段到分页

保护模式——从分段到分页


下面就总结看看寻址:

从上面可以知道,基地址位20位,就是说基地址后面20位为0.

延续上面的A、B、C段,我们称页目录项的物理地址部分位X1,页表项的物理地址部分为X2,那么:

页表基地址位:

                                  X1*8+A*4,

因为A的单位为个数,一个位4字节,所以要乘以4.

页基地址位:

                                  X2*8+B*4

物理地址位

                                  页基地址+C

                            即:

                                  X2*8+B*4+C

设置好这些后我们再设置一个东西,那就是CR0的pg位,pg=0时不启用分页机制,当pg=1时才启用分页机制。

设置好分页机制以后,内存地址从线性地址到物理地址的转换是由硬件负责的,我们不用管,剩下的就是软件控制内存什么的了,现在不知道该写什么了,等想到了再继续写。

 

下面是代码:代码说明后来加上(电脑没电了。。。。。)

boot.asm:

     org 0x7c00

     jmp start

 

     SETUPSEG equ 0x9000

     DATASEG        equ 0x8000

     ROOT_DEV db  0

 

start:

     mov      ax,cs

     mov      ds,ax

     mov      es,ax

     mov      ss,ax

     mov      [ROOT_DEV],dl

     call load_msg

     mov      ax,DATASEG

     mov      ds,ax

     mov      [0],dl

     mov      ax,cs

     mov      ds,ax

      

load_setup:

     mov      ax,SETUPSEG

     mov      es,ax

     xor bx,bx

     mov      ax,0x0202

     mov      dl,[ROOT_DEV]

     mov      dh,0

     mov      cx,0x0002

     int  0x13

     jnc  ok_load_setup

     mov      ax,0

     mov      dx,0

     int  0x13

     jmp load_setup

      

ok_load_setup:

     jmp SETUPSEG:0x0000

 

load_msg:   

     call load_curser

     mov      ax,loadmessage

     mov      cx,18

     mov      bp,ax

     mov      ax,0x1301

     mov      bx,0x000c

     int  0x10

     ret

      

load_curser:

     mov      ah,0x03

     xor bh,bh

     int  0x10

     ret

      

loadmessage      db  "Booting diers..."

           db  13,10

     times    510-($-$$)  db  0

     dw  0xaa55

 

setup.asm:

     org 0x0000

     jmp start

 

     SETUPSEG equ 0x9000

     DATASEG        equ 0x8000

     SETSEG           equ 0x0400

gdtaddr:

     dw  40

     dw  0x3000

     dw  0x0000

gdt:

null_table:

     dw  0,0,0,0

system_code_table:

     dw  0x2000

     dw  0x0000

     dw  0x9a00

     dw  0x00c0

system_data_table:

     dw  0x2000

     dw  0x0000

     dw  0x9200

     dw  0x00c0

user_code_table:

     dw  0xffff

     dw  0x0000

     dw  0xfa00

     dw  0x02cf

user_data_table:

     dw  0xffff

     dw  0x0000

     dw  0xf200

     dw  0x02cf

      

idtaddr:

     dw  0x0

     dw  0x0

     dw  0x0

      

start:

     call load_msg

load_jump:

     mov      ax,DATASEG

     mov      ds,ax

     mov      dl,[0]

     mov      ax,SETUPSEG

     mov      es,ax

     mov      bx,SETSEG

     mov      ah,0x02

     mov      al,1

     mov      dh,0

     mov      cx,0x0004

     int  0x13

      

     jnc  load_set

     mov      ax,0

     mov      dx,0

     int  0x13

     jmp start

 

load_set:

     mov      ax,0x1100

     mov      es,ax

     xor bx,bx

     mov      dl,[0]

     mov      ah,0x02

     mov      al,0x08

     mov      dh,0

     mov      cx,0x0005

     int  0x13

      

     jnc  ok_load

     mov      ax,0

     mov      dx,0

     int  0x13

     jmp load_set

 

ok_load:

;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

;;; hadware read here.

     ;; get memory size.

     mov      ax,DATASEG

     mov      ds,ax

     mov      ah,0x88

     int  0x15

     mov      [1],ax

     ;; set curser show message.

     mov      al,'a'

     mov      [3],al

     mov      al,0x04

     mov      [4],al

;;; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

     ;; open A20.

     call open_a20

     ;; no interrupts allowed.

     cli

     ;; mov set file to 0x0000:0x0000.

     call move_jump

     ;; move gdt to 0x00003000

     call move_gdt

     ;; set gdt.

     lgdt [gdtaddr]

     ;; set idt empty.

     lidt [idtaddr]

     ;; init 8259A.

     call init_8259a

     ;; set cr0

     call set_cr0

     ;; jump to set program.

     jmp 0x8:0x0

 

move_gdt:

     cld

     mov      ax,0x0000

     mov      es,ax

     mov      di,0x3000

     mov      ax,cs

     mov      ds,ax

     mov      si,gdt

     mov      cx,20

     rep movsw

     ret

 

load_msg:

     call load_curser

     mov      ax,cs

     mov      es,ax

     mov      ax,loadmessage

     mov      cx,16

     mov      bp,ax

     mov      ax,0x1301

     mov      bx,0x000c

     int  0x10

     ret

      

load_curser:

     mov      ah,0x03

     xor bh,bh

     int  0x10

     ret

 

open_a20:

o:   mov      dx,0x92

     mov      al,02

     out dx,al

t:   mov      dx,92h

     in   al,dx

     and al,02

     jz   o

     ret

 

move_jump:

     cld

     mov      ax,SETUPSEG

     mov      ds,ax

     mov      si,SETSEG

     mov      ax,0x0

     mov      es,ax

     xor di,di

     mov      cx,256

     rep movsw

     ret

      

init_8259a:

     mov      al,0x11

     out 0x20,al

     call io_delay

 

     out 0xa0,al

     call io_delay

 

     mov      al,0x20

     out 0x21,al

     call io_delay

 

     mov      al,0x28

     out 0xa1,al

     call io_delay

 

     mov      al,0x04

     out 0x21,al

     call io_delay

 

     mov      al,0x02

     out 0xa1,al

     call io_delay

 

     mov      al,0x01

     out 0x21,al

     call io_delay

 

     out 0xa1,al

     call io_delay

 

     mov      al,0xff

     out 0x21,al

     call io_delay

 

     out 0xa1,al

     call io_delay

     ret

      

io_delay:

     mov      bx,bx

     mov      bx,bx

     mov      bx,bx

     ret

      

set_cr0:

     mov      eax,cr0

     or   eax,1

     mov      cr0,eax

     ret

      

loadmessage:

     db  "Setup diers..."

     db  13,10

 

     times    1024-($-$$) db  0

 

 

jump.asm:

     [BITS 32]

     org 0x0

set_cr0:

     mov      eax,cr0

     and eax,0x7fffffff

     mov      cr0,eax

     jmp start

start:

     mov      ax,0x10

     mov      ds,ax

 

show:

     mov      al,[0x80003]

     mov      ah,[0x80004]

     mov      [0xb8000],al

     mov      [0xb8001],ah

     inc  al

     inc  ah

     mov      [0x80003],al

     mov      [0x80004],ah

      

     jmp 0x8:0x11000

     times    512-($-$$)  db  0

 

set.asm:

     [BITS 32]

     jmp start

 

     PAGEDIR   equ 0x00001000

     PAGES equ 0x00100000

     INT_DESC_TABLE   equ 0x00002000

     GLOBAL_DESC_TABLE equ 0x00003000

 

_idt:

     dw  0x0800

     dw  0x2000

     dw  0x0000

 

_gdt:

     dw  40

     dw  0x3000

     dw  0x0000

start:

     mov      ax,0x10

     mov      ds,ax

     mov      es,ax

     mov      ss,ax

set_page_dir:

     mov      esi,0

     mov      eax,0

s1:

     mov      ebx,PAGES+7

     add ebx,eax

     mov      [PAGEDIR+esi],ebx

     add eax,4096

     add esi,4

     cmp esi,4096

     jz   end_set_page_dir

     jmp s1

end_set_page_dir:

     mov      esi,0

     mov      ecx,0

     mov      ebx,0x00008000

     mov      eax,0x00008e00

set_int_table:

     mov      [INT_DESC_TABLE+esi],ebx

     add esi,4

     mov      [INT_DESC_TABLE+esi],eax

     add esi,4

     inc  ecx

     cmp ecx,512

     jz   set_int_end

     jmp set_int_table

set_int_end:

     lidt [_idt]

open_pages:

set_cr3:

     mov      eax,0x00001000

     mov      cr3,eax

set_cr0:

     mov      eax,cr0

     or   eax,0x80000000

     mov      cr0,eax

open_pages_end:

     jmp $

 

操作系统文章更正地址:http://blog.sina.com.cn/s/blog_6730a3aa01010xrc.html


前一篇:操作系统引导--从实模式到保护模式后一篇:文章更正篇