博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASM:《X86汇编语言-从实模式到保护模式》第16章:Intel处理器的分页机制和动态页面分配...
阅读量:4314 次
发布时间:2019-06-06

本文共 49832 字,大约阅读时间需要 166 分钟。

  第16章讲的是分页机制和动态页面分配的问题,说实话这个一开始接触是会把人绕晕的,但是这个的确太重要了,有了分页机制内存管理就变得很简单,而且能直接实现平坦模式。

★PART1:Intel X86基础分页机制

1. 页目录、页表和页

  首先先要明白分页是怎么来的,简单来讲,分页其实就是内存块的映射管理。在我们之前的章节中,我们都是使用的分段管理模式,处理器中负责分段的部件是段部件,段管理机制是Intel处理器最基本的处理机制,在任何时候都是无法关闭的。而当开启了分页管理之后,处理器会把4GB的内存分成长度相同的段,也就是说用长度固定的页来代替长度不一的段。页的分配由处理器固件来进行,可以实现非常高效的操作。页的最小单位是4KB,也就是说4GB的内存可以分成1048576个页(页的物理地址的低12位全是0)。

  在分页模式下,操作系统可以创建一个为所有任务共用的4GB虚拟内存空间。也可以为每一个任务创建一个独立的4GB虚拟内存空间,当一个程序加载的时候,操作系统既要在虚拟内存空间中分配空间,而且也要在物理内存中分配相应的也页面。另外,如果允许页共享,多个段或者多个程序可以用同一个页来存放各自的数据。4GB虚拟内存空间只是一个用来指示内存使用状况的一个机制,当操作系统加载一个程序并且创建为任务时,操作系统在虚拟内存空间中寻找空闲的段,并映射到空闲的页中取去。然后,到真正加载程序的时候,再把原本属于段的数据按页的尺寸拆分,分开写入相应的页中。页最大的好处就是方便操作系统进行内存管理,特别是虚拟内存管理,每个任务都可以有4GB虚拟内存,但是假如机器没有那么大的内存,操作系统也可以认为确实存在那么大的内存,当一个程序使用的内存超过了实际的物理内存,那么操作系统就会搜索那些暂时用不到的页,并且把他们转移到磁盘中,并且调入马上要使用的页。(当然这种操作非常地花费时间,这就是为什么小内存的电脑会有很严重的卡顿现象)。

  Intel处理器的最基础的分页管理机制就是二级管理机制。(当然现在更新的处理器支持更复杂的分页操作,但是教材也没有提及。)如果每个操使用直接分页管理(也就是把4GB的内存直接分成1048576个页),这需要1048576个表项,每个表项是4字节,所以映射表的总大小是4MB。事实上程序往往用不到4GB的内存,所以1级映射是一个很严重的浪费。但是如果采用层次化分页管理,那么就会产生巨大的内存节约。所谓Intel的分页管理,其实就是把页拆成3个部分(页目录,页表和页)。

  页目录(Page Directory Table,PDT)的物理地址由PDBR(Page Directory Base Register,也就是CR3寄存器)指定,每个任务都可以有自己的页目录。每个任务的TSS段就有自己的CR3的物理地址。每次进行任务切换的时候,CR3的内容都会被替换改为新任务的CR3域中的物理地址。页目录也是一个的页。但是他里面存放的是页表的地址,所以页目录可以指向1024个页表。

  页表也可以指向1024个页,每个项也是4个字节,同样页表的大小也是页的大小。

  页目录,页表和页的关系:(图片出处看水印)

  

  处理器有页部件,专门负责线性地址到物理地址的转换工作。它首先将段部件送来的32位线性地址截成3段,分别是高10位(页目录的索引),中间的10位(页表的索引)和低12位(页内偏移)。操作系统要负责填写页目录和页表地址,然后程序的内存访问就可以像上图那样进行转换了。

2. 页目录、页表和CR3的填写

  

  P(Present)是存在位,为“1”时,表示页表或者页位于内存中,否则,表示页表或者页不在内存中,必须先予以创建,或者从磁盘调入内存后方可使用。

  RW(Read/Write)是读/写位,为“0”时表示这样的页只能读取,为“1”时,可读可写。

  US(User/Supervisor)是用户/管理位。为“1”时,允许所有特权级别的程序访问;为“0”时,只允许特权级为0,1,2的程序访问,特权级为3的程序不能访问。

  PWT(Page-Level Write-Through)是页级通写位,和高速缓存有关,“通写”是处理器高速缓存的一种工作方式,这意味用来间接决定是否采用此种方式来改善页面的访问效率。

  A(Accessed)是访问位。该位由处理器固件来设置,用来指示此表项所指向的页是否被访问过。(这个坑爹的属性将会在练习题中体现)。

  D(Dirty)是脏位。该位由处理器固件来设置,用来指示此表项所指向的页是否被写入数据。(和A位一样,练习题会有体现)。

  PAT(Page Attribute Table)页属性表支持位。此位涉及更为复杂的分页系统。和页高速缓存有关。

  G(Global)是全局位,用来只是该表项指向的页是否为全局属性。如果页是全局属性的,那么,他将一直在高速缓存中一直保存(地址转换速度会加快)。因为页高速缓存的缓存容量有限,只能存放频繁使用的那些表项。而且,当因为任务切换等原因改变CR3寄存器的内容时,整个页高速缓存的内容都会被刷新。

  AVL位被处理器忽略,软件可以使用。

  进入分页模式之后,所有东西的地址都变成了虚拟线性地址了,包括GDT,LDT和TSS的地址等等。需要注意的是,页目录和页表在内存中的位置必须是在有效的可用内存范围(比如2GB内存只能在2GB内存中设置页目录和页表)。注意页目录必须进行初始化,主要是对P位置零,如果P位等于0,而处理器又执行了一个对这个页表的访问,那么就会引发异常中断。

       清空页表以后就是要对页目录进行初始化了,在教材的“系统”中,把页目录设置在0x00020000这个物理地址中。然后在页目录中的最后一项,指向页目录自己。然后只进行最底下1MB内存的页面设置(所以只需要一个页表),然后就可以进行页表的初始化了,初始化过后。就可以直接进行CR3的设置,CR3的设置如下图:

  

  其实CR3的设置和页表和页的填写差不多,只是除了PCD位和PWT位外,其他都不需要填写。

       最后一步就是打开分页了,这个开关也是在CR0那里,称为PG位,在CR0的最高位31位上,注意这个功能只有在保护模式下才能开启(也就是CR0的PE位(0位)要是1),当PG位为1的时候,段部件传来的内存地址必须要经过页部件的转换才能得到真实的内存地址。

       有关CR0-3这4个寄存器的控制可以看:

  

  

  以上代码就就是开启页功能的一个演示。

3. 任务全局空间和局部空间的页面演示,页面查找的例子

       在15章我们说过,任务的4GB地址空间包括两个部分:局部空间和全局空间,全局空间是所有任务共用的,内核就是所有任务共用的,它属于每个任务的全局空间。在教材的系统中,规定4GB的虚拟高2GB空间就是全局空间,地址范围是0x80000000-0xFFFFFFFF,局部空间使用低2GB空间,地址范围是0x00000000-0x7FFFFFFF,在任何时候,如果段部件发出的线性地址高于等于0x80000000,指向和访问的就是全局地址空间,或者说是内核。另外在上面我说过,我们把页目录的最后一项指向自己页目录的页,把页目录看成页表,再把页目录看成页表,刚好这个页表的最后一项也是指向页目录,那样我们就可以实现对页目录的内容进行访问,否则我们将无法访问页目录,因为处理器不允许访问一个没有登记的页(访问了会引发异常中断)。

       但是为了保证内核的全局部分可以被其他程序使用,要创建内核的映射,内核应该具有两个部分,一个是和他的物理地址一样的对应(低地址)部分,另一个是映射到高地址的对应部分。这样说可能会比较抽象,我们直接看代码就好了。然后接下来就是把GDT的内容全部重定位了(GDT页必须使用虚拟线性地址,已经在前面把内核映射到高地址空间了),当然我们这里的全局空间映射位置取的比较好,是0x80000000,所以只要往GDT的最高4位or一个8就可以了。

   

  注意 mov dword[es:ebx+esi],PDT_Mem_Address,add dword[es:ebx+esi],0x00001003这两句话,ebx的内容是0XFFFFF000,ESI的内容是0x00000200,因此段部件发出的线性地址是0XFFFFF200,现在举这个访问页目录的例子,以更加清楚了解如何访问页:

   

  

  所以现在位于物理地址0x00021000这个地方的页被页目录中两个项所指向,但是这两个项所映射的物理地址时不一样的!!!!,页目录的第0项对应的映射是0x00000000-0x000FFFFF,0x800项对应的是0x80000000-0x800FFFFF。这也是一个分级管理有效地缩减表的占用的一个证据:因为全局空间总是映射到高区间,所以如果采用单映射的话,必须先准备2MB的表项先把局部空间描述完了才能到全局空间。

  注意因为段部件的内容不会因为你做好了映射而自动把自己的内容改变,所以需要显式切换,那就是上面代码的后面的部分。

★PART2:保护模式页管理模式下的内核任务的创建

1. 内核的虚拟内存的分配

  说实话教材说了那么多,其实就是最重要的是讲明白一个东西就行了,那就是页的分配。把这个搞明白了其他东西都是一个套路。

  现代操作系统可以跟踪所有页的分配状况。内存空间来自于插在主板上的内存条,按照新的工业标准,每个内存条上焊有一个很小的只读存储器,用于标明该内存条的容量和工作参数,作为一个PCI(E)设备,软件可以读取它,以获得计算机上的物理内存容量。然后简历上述的页分配表。但是由固件创建的表是每个表项占1个字节的,如果有4GB内存,则最多分220个页,要占用1MB的内存。但是如果以比特来指示页的使用状况,那么最多使用1048576个比特(128KB),将会产生巨大的内存节约。我们的系统为了简单,假定我们的系统只有2MB的内存,2MB的内存,可以分512个页,需要512个比特,我们可以在内核区定义比特串:

   

  我们可以看到底下的1MB已经被内核和ROM-BIOS分配的差不多了。但是页的分配可以不连续的,接下来我们就可以在代码中看到这个问题。

 

 

注意代码中我们使用了bts指令,这个指令用于测试串中的某个比特位。用该比特的值设置EFLAGS寄存器的CF标志位,然后将这个标志置“1”,他的最基本两种形式为:

                     bts r/m16,r16

                     bts r/m32,r32

  目的操作数可以是16/32位的通用寄存器,或者指向一个包含了16/32位实际操作数的内存党员,用于指定位串;源操作数可以是16/32位的通用寄存器,用于指定待测试的比特在位串的索引 (位置)。其他类似的指令还有:btr,btc和bt,他们的区别如下:

  例程首先做的事情就是在页目录看一下相应的页表是否已经在页目录中登记了,如果没有登记,则分配一个新的页作为页表然后写入页目录相应位置,否则就直接使用这个页表(当然这个管理程序非常粗糙,会有非常严重的问题,但是现在我们要把事情简单化)。

2. 任务程序的加载

  其实也是一堆套路,只不过我们需要注意的是,因为我们现在用的是页管理模式,所以要使用平坦模式。平坦模式是现代操作系统流行的管理模式,抛弃段管理模式冗杂的段的管理,可以大大简化代码的编写难度(注意的是页也有一点特权控制)。

  教材上加载程序的思路其实和前面几章是一样的,但是使用的是平坦模式来加载,并且对TCB进行了一些改变,而且使用了向上拓展的栈段(其实向上拓展的栈段只是段界限的检查会不一样,那是处理器的事情,push和pop指令的操作是一样的)。TCB变成了下图这个样子:

  最后要说明的是,分页机制下,和分段机制是一样的,内存也是先登记后使用的。程序在编写和编译之后,都是连续的,在加载后不能保证这一点。页的分配是随机的,尽管页不是连续的,但是线性地址是连续的就足够了。处理器访问数据,取指令,用的是线性地址。教材把所有的用户程序都从0x00000000开始加载,其实是不合理的,多段模型之下段内元素的偏移量都是相对于段而言的,在程序加载之后,段的描述符的基地址,就是段实际加载的位置。也就是这样,多段模型下,不管段加载到哪里,都不会影响段内元素的访问,这就是多段模型下程序可以重定位和浮动的根本原因。

  而在平坦模式下,程序的重定位和浮动实现比较复杂,在现代流行的操作系统中,编写的程序必须符合一定的规范才能重定位,比如很多系统要求用户提供一个标准的重定位表,列出所有需要动态加载的元素,程序加载后,操作系统会找到这个表,用实际的加载地址修正每一个表项。(非常复杂的一个过程)。

★PART3:本章的课后练习题

1. 显示当前任务的当前任务的虚拟地址的前55个双字,前50个页面的物理地址

       显示虚拟地址前55个双字书上是有例程的,就是物理地址这个比较麻烦一点,其实也不麻烦,主要是要把页目录指向也目录自己和指向内核的那两个页表项修改为特权级3的程序也能访问。说下几个坑:

       1. 之前几章的PrintDword过程都是有问题的,因为我忽略了我的put_char过程和书上的不一样,要把put_char压栈的那几个操作改成pushad,出栈改为popad

       2. 页目录/页表内登记的物理地址的低12位都是有含义的!页表/页的物理地址的31-12位才会出现在项中,低12位是属性!特别是A和D位,处理器会自动将他们设置。显示物理内存的时候低12位都是0(and一下,页都是4KB对齐的)。

1 ;===============================内核程序=================================   2         ;定义内核所要用到的选择子   3         All_4GB_Segment         equ 0x0008        ;4GB的全内存区域   4         Stack_Segement             equ 0x0018        ;内核栈区   5         Print_Segement            equ 0x0020        ;显存映射区   6         Sys_Routine_Segement     equ 0x0028        ;公用例程段   7         Core_Data_Segement        equ 0x0030        ;内核数据区   8         Core_Code_Segement        equ 0x0038        ;内核代码段   9         ;----------------------------------------------------------------  10         User_Program_AddressA    equ 50            ;用户程序所在逻辑扇区  11         User_Program_AddressB    equ 80            ;用户程序所在逻辑扇区  12         Switch_Stack_Size        equ 4096        ;切换栈段的大小  13         PDT_Mem_Address            equ 0x00020000    ;PDT在内存加载的地址  14         Global_Page_Directory    equ 0x80000000     ;给全局空间的映射地址  15 ;=============================内核程序头部===============================  16 SECTION header vstart=0  17         Program_Length             dd    Program_end                    ;内核总长度  18         Sys_Routine_Seg         dd  section.Sys_Routine.start    ;公用例程段线性地址  19         Core_Data_Seg             dd  section.Core_Data.start        ;内核数据区线性地址  20         Core_Code_Seg             dd  section.Core_Code.start        ;内核代码区线性地址  21         Code_Entry                dd    start                        ;注意偏移地址一定是32位的  22                                 dw  Core_Code_Segement  23     ;----------------------------------------------------------------  24                             [bits 32]  25 ;=========================================================================  26 ;============================公用例程区===================================  27 ;=========================================================================  28 SECTION Sys_Routine align=16 vstart=0  29     ReadHarddisk:                            ;push1:28位磁盘号(esi)  30                                             ;push2:应用程序数据段选择子(ax->ds)  31                                             ;push3: 偏移地址(ebx)  32                                             ;push4: 应用程序代码段选择子(dx)  33         pushad  34         push ds  35         push es  36           37         mov ebp,esp  38           39         mov esi,[ebp+15*4]  40         movzx eax,word[ebp+14*4]  41         mov ebx,[ebp+13*4]  42         movzx edx,word[ebp+12*4]  43           44         arpl ax,dx  45         mov ds,ax  46           47         mov dx,0x1f2  48         mov al,0x01        ;读一个扇区                                  49         out dx,al  50           51         inc edx            ;0-7位  52         mov eax,esi  53         out dx,al  54           55         inc edx            ;8-15位  56         mov al,ah  57         out dx,al  58           59         inc edx            ;16-23位  60         shr eax,16  61         out dx,al  62           63         inc edx            ;24-28位,主硬盘,LBA模式  64         mov al,ah  65         and al,0x0f  66         or al,0xe0  67         out dx,al  68           69         inc edx  70         mov al,0x20  71         out dx,al  72           73         _wait:  74             in al,dx  75             and al,0x88  76             cmp al,0x08  77             jne _wait  78           79         mov dx,0x1f0  80         mov ecx,256  81         _read:  82             in ax,dx  83             mov [ebx],ax  84             add ebx,2  85         loop _read  86           87         pop es  88         pop ds  89         popad  90         retf 16        ;4个数据  91     ;----------------------------------------------------------------  92     put_string:                                                    ;ebx:偏移地址  93         pushad  94         push ds  95         push es  96           97         _print:  98             mov cl,[ebx]  99             cmp cl,0 100             je _exit 101             call put_char 102             inc ebx 103             jmp _print 104         _exit: 105             pop es 106             pop ds 107             popad 108             retf            ;段间返回 109         ;--------------------------------------------------------------     110         put_char:            ;cl就是要显示的字符 111             pushad 112             push es 113             push ds 114              115             mov dx,0x3d4 116             mov al,0x0e        ;高8位 117             out dx,al 118             mov dx,0x3d5 119             in al,dx 120             mov ah,al        ;先把高8位存起来 121             mov dx,0x3d4 122             mov al,0x0f        ;低8位 123             out dx,al 124             mov dx,0x3d5 125             in al,dx        ;现在ax就是当前光标的位置 126              127             _judge: 128                 cmp cl,0x0a 129                 je _set_0x0a 130                 cmp cl,0x0d 131                 je _set_0x0d 132             _print_visible: 133                 mov bx,ax 134                 mov eax,Print_Segement 135                 mov es,eax 136                 shl bx,1     ;注意这里一定要把ebx变成原来的两倍,实际位置是光标位置的两倍 137                 mov [es:bx],cl            ;注意这里是屏幕! 138                 mov byte[es:bx+1],0x07         139                 add bx,2 140                 shr bx,1 141                 jmp _roll_screen 142             _set_0x0d:        ;回车 143                 mov bl,80 144                 div bl 145                 mul bl 146                 mov bx,ax 147                 jmp _set_cursor 148             _set_0x0a:        ;换行 149                 mov bx,ax 150                 add bx,80 151                 jmp _roll_screen 152             _roll_screen: 153                 cmp bx,2000 154                 jl _set_cursor 155                 mov eax,Print_Segement 156                 mov ds,eax 157                 mov es,eax 158                  159                 cld 160                 mov edi,0x00 161                 mov esi,0xa0 162                 mov ecx,1920 163                 rep movsw 164             _cls: 165                 mov bx,3840 166                 mov ecx,80 167                 _print_blank: 168                     mov word[es:bx],0x0720 169                     add bx,2 170                     loop _print_blank     171                 mov bx,1920    ;别总是忘了光标的位置! 172             _set_cursor:        ;改变后的光标位置在bx上 173             mov dx,0x3d4 174             mov al,0x0f        ;低8位 175             out dx,al 176              177             mov al,bl 178             mov dx,0x3d5 179             out dx,al 180              181             mov dx,0x3d4 182             mov al,0x0e     ;高8位 183             out dx,al 184              185             mov al,bh 186             mov dx,0x3d5 187             out dx,al 188              189             pop ds 190             pop es 191             popad 192             ret 193     ;----------------------------------------------------------------         194     Make_Seg_Descriptor:                    ;构造段描述符 195                                             ;输入: 196                                             ;eax:线性基地址 197                                             ;ebx:段界限 198                                             ;ecx:属性 199                                             ;输出: 200                                             ;eax:段描述符低32位 201                                             ;edx:段描述符高32位 202         mov edx,eax 203         and edx,0xffff0000 204         rol edx,8 205         bswap edx 206         or edx,ecx 207          208         shl eax,16 209         or ax,bx 210         and ebx,0x000f0000 211         or edx,ebx 212         retf                 213     ;----------------------------------------------------------------         214     Make_Gate_Descriptor:                    ;构造门描述符 215                                             ;输入: 216                                             ;eax:段内偏移地址 217                                             ;bx: 段的选择子 218                                             ;cx: 段的属性 219                                             ;输出: 220                                             ;eax:门描述符低32位 221                                             ;edx:门描述符高32位 222         push ebx 223         push ecx 224          225         mov edx,eax 226         and edx,0xffff0000                    ;要高16位 227         or dx,cx 228          229         shl ebx,16 230         and eax,0x0000ffff 231         or eax,ebx 232          233         pop ecx 234         pop ebx 235          236         retf                 237     ;---------------------------------------------------------------- 238     Set_New_GDT:                            ;装载新的全局描述符 239                                             ;输入:edx:eax描述符 240                                             ;输出:cx选择子 241         push ds 242         push es 243          244         mov ebx,Core_Data_Segement 245         mov ds,ebx 246          247         mov ebx,All_4GB_Segment 248         mov es,ebx 249          250         sgdt [pgdt_base_tmp] 251          252         movzx ebx,word[pgdt_base_tmp] 253         inc bx                                ;注意这里要一定是inc bx而不是inc ebx,因为gdt段界限初始化是0xffff的 254                                             ;要用到回绕特性 255         add ebx,[pgdt_base_tmp+0x02]        ;得到pgdt的线性基地址 256          257         mov [es:ebx],eax 258         mov [es:ebx+0x04],edx                ;装载新的gdt符 259                                             ;装载描述符要装载到实际位置上 260          261         add word[pgdt_base_tmp],8            ;给gdt的段界限加上8(字节) 262          263         lgdt [pgdt_base_tmp]                ;加载gdt到gdtr的位置和实际表的位置无关 264          265         mov ax,[pgdt_base_tmp]                ;得到段界限 266         xor dx,dx 267         mov bx,8                            ;得到gdt大小 268         div bx 269         mov cx,ax 270         shl cx,3                            ;得到选择子,ti=0(全局描述符),rpl=0(申请特权0级) 271          272         pop es 273         pop ds 274         retf 275     ;---------------------------------------------------------------- 276     Set_New_LDT_To_TCB:                        ;装载新的局部描述符 277                                             ;输入:edx:eax描述符 278                                             ;     : ebx:TCB线性基地址 279                                             ;输出:cx选择子 280          281         push edi 282         push eax 283         push ebx 284         push edx 285         push ds 286          287         mov ecx,All_4GB_Segment 288         mov ds,ecx 289          290         mov edi,[ebx+0x0c]                    ;LDT的线性基地址 291         movzx ecx,word[ebx+0x0a] 292         inc cx                                ;得到实际的LDT的大小(界限还要-1) 293          294         mov [edi+ecx+0x00],eax 295         mov [edi+ecx+0x04],edx 296          297         add cx,8 298         dec cx 299          300         mov [ebx+0x0a],cx 301          302         mov ax,cx 303         xor dx,dx 304         mov cx,8 305         div cx 306          307         shl ax,3 308         mov cx,ax 309         or cx,0x0004                        ;LDT,第三位TI位一定是1 310          311         pop ds 312         pop edx 313         pop ebx 314         pop eax 315         pop edi 316         retf 317     ;----------------------------------------------------------------     318     PrintDword:                                ;显示edx内容的一个调试函数 319         pushad 320         push ds 321          322         mov eax,Core_Data_Segement 323         mov ds,eax 324          325         mov ebx,bin_hex 326         mov ecx,8 327          328         _query: 329             rol edx,4 330             mov eax,edx 331             and eax,0x0000000f 332             xlat 333              334             push ecx 335             mov cl,al 336             call put_char 337             pop ecx 338              339         loop _query 340              341         pop ds 342         popad 343          344         retf 345     ;---------------------------------------------------------------- 346     allocate_4KB_page:                            ;输入:无 347                                                 ;输出eax:页的物理地址 348                                                 ;注意这个是近调用 349         push ebx 350         push ecx 351         push edx 352         push ds 353          354         mov eax,Core_Data_Segement 355         mov ds,eax 356          357         xor eax,eax 358          359         _search_pages: 360             bts [page_bit_map],eax 361             jnc _found_not_uesd 362             inc eax 363             cmp eax,page_map_len*8 364         jl _search_pages 365          366         mov ebx,No_More_Page 367         call Sys_Routine_Segement:put_string 368         hlt                                     ;无可用页,直接停机 369          370         _found_not_uesd: 371         shl eax,12                                ;eax相当于是选择子,乘以一个4KB得到物理地址 372          373         pop ds 374         pop edx 375         pop ecx 376         pop ebx 377         ret 378     ;---------------------------------------------------------------- 379     alloc_inst_a_page:                            ;分配一个页,并安装在当前活动的层级分页结构中 380                                                 ;输入:EBX=页的线性地址 381                                                 ;输出:无 382         push eax 383         push ebx 384         push edi 385         push esi 386         push ds 387          388         mov eax,All_4GB_Segment                    ;转平坦模式 389         mov ds,eax 390          391         _test_P:                                ;在页目录中看是否存在这个页表 392             mov esi,ebx 393             and esi,0xffc00000 394             shr esi,20                                          395             or esi,0xfffff000                    ;指向页目录本身 396             test dword[esi],0x00000001 397             jnz _get_page_and_create_new_page 398         _create_new_page_directory: 399             call allocate_4KB_page 400             or eax,0x00000007                    ;存在于主存,可读可写,允许特权级3程序访问 401             mov [esi],eax         402         _get_page_and_create_new_page:     403             mov esi,ebx 404             shr esi,10                            ;页表在页目录的偏移项 405             and esi,0x003ff000                    ;得到页表的偏移地址 406             or esi,0xffc00000                    ;指向页目录 407              408             and ebx,0x003ff000 409             shr ebx,10                            ;中间10位是页目录-页表-表内偏移量(注意这里的层次理解) 410             or esi,ebx                            ;esi就是页的对应线性地址 411             call allocate_4KB_page 412             or eax,0x00000007                    ;存在于主存,可读可写,允许特权级3程序访问 413             mov [esi],eax 414              415         pop ds 416         pop esi 417         pop edi 418         pop ebx 419         pop eax 420         retf 421     ;---------------------------------------------------------------- 422     Copy_Page:                                    ;把在创建的包含全局和私有部分的页表复制一份给用户程序用 423                                                 ;输入:无 424                                                 ;输出eax:页的物理地址 425         push ds 426         push es 427         push edi 428         push esi 429         push ebx 430         push ecx 431         push edx 432          433         mov eax,Core_Data_Segement 434         mov ds,eax 435         mov edx,[task_pos] 436         sub edx,4 437         add edx,[page_header] 438         mov edi,[page_soft_header] 439         sub edi,0x1000 440         mov esi,[page_header]                        ;指向全局页目录 441         mov [es:0x16],edi 442          443         mov eax,All_4GB_Segment 444         mov ds,eax 445         mov es,eax 446          447         call allocate_4KB_page 448         mov ebx,eax 449         or ebx,0x00000007 450         mov [edx],ebx 451                              452         mov ecx,1024 453         cld 454         repe movsd 455          456         pop edx 457         pop ecx 458         pop ebx 459         pop esi 460         pop edi 461         pop es 462         pop ds 463          464         retf 465     ;---------------------------------------------------------------- 466 ;========================================================================= 467 ;===========================内核数据区==================================== 468 ;========================================================================= 469 SECTION Core_Data align=16 vstart=0 470 ;------------------------------------------------------------------------------- 471         pgdt_base_tmp:          dw  0                              472                                 dd  0 473  474         salt: 475         salt_1:                    db    '@Printf'                    ;@Printf函数(公用例程) 476         times 256-($-salt_1)    db    0 477                                 dd    put_string 478                                 dw    Sys_Routine_Segement 479                                 dw  0                            ;参数个数 480                                  481         salt_2:                    db    '@ReadHarddisk'                ;@ReadHarddisk函数(公用例程) 482         times 256-($-salt_2)    db    0 483                                 dd    ReadHarddisk 484                                 dw    Sys_Routine_Segement 485                                 dw  4                            ;参数个数 486                                  487         salt_3:                    db    '@PrintDwordAsHexString'    ;@PrintDwordAsHexString函数(公用例程) 488         times 256-($-salt_3)    db    0 489                                 dd    PrintDword 490                                 dw    Sys_Routine_Segement 491                                 dw  0                            ;参数个数 492                                  493         salt_length:            equ    $-salt_3 494         salt_items_sum            equ    ($-salt)/salt_length        ;得到项目总数 495          496         salt_tp:                dw    0                            ;任务门,专门拿来给程序切换到全局空间的 497          498         message_1                db  '   If you seen this message,that means we ' 499                                 db  'are now in protect mode,and the system ' 500                                 db  'core is loaded,and the video display ' 501                                 db  'routine works perfectly.',0x0d,0x0a,0 502  503         message_2                db  '   Loading user program...',0 504  505         do_status                db  'Done.',0x0d,0x0a,0 506  507         message_3                db  0x0d,0x0a,0x0d,0x0a,0x0d,0x0a 508                                 db  '   User program terminated,control returned.' 509                                 db  0x0d,0x0a,0x0d,0x0a,0 510         message_4                db  '   We have been backed to kernel.',0x0d,0x0a,0 511         message_5                db  '   The GDT and memory have benn recycled.',0 512         message_6                db  '   From the system wide gate:',0x0d,0x0a,0 513         message_7                db  '   Setting the gate discriptor...',0 514         message_In_Gate            db  '   Hi!My name is Philip:',0x0d,0x0a,0 515         message_page            db  '  Paging is enabled.System core is mapped to' 516                                 db  ' address 0x80000000.',0x0d,0x0a,0 517         core_stop                db  0x0d,0x0a,'  Processor HALT.',0 518         No_More_Page            db  '********No more pages********',0 519         task_switch                db  0x0d,0x0a,'  Task switching...@_@',0x0d,0x0a,0 520  521         bin_hex                  db '0123456789ABCDEF' 522                                                                 ;put_hex_dword子过程用的查找表 523         core_buf        times 2048 db  0                             ;内核用的缓冲区(2049个字节(2MB)) 524  525         esp_pointer             dd  0                              ;内核用来临时保存自己的栈指针 526  527         cpu_brnd0                db  0x0d,0x0a,'  ',0 528         cpu_brand         times 52 db  0 529         cpu_brnd1                db  0x0d,0x0a,0x0d,0x0a,0 530         core_ss                    dw  0 531         core_sp                    dd  0 532         ;程序管理器的任务信息  533         prgman_tss               dd  0                             ;程序管理器的TSS基地址 534                                 dw  0                             ;程序管理器的TSS描述符选择子  535         ;假设只有2MB内存可以用的意思,正确的做法应该先读PCI(E),然后再分配! 536         page_bit_map             db  0xff,0xff,0xff,0xff,0xff,0x55,0x55,0xff 537                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff 538                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff 539                                 db  0xff,0xff,0xff,0xff,0xff,0xff,0xff,0xff 540                                 db  0x55,0x55,0x55,0x55,0x55,0x55,0x55,0x55 541                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 542                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 543                                 db  0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 544         page_map_len             equ $-page_bit_map 545         core_next_laddr          dd  0x80100000                    ;内核空间中下一个可分配的线性地址 546         task_pos                dd  0x00000ffc                     ;任务程序的页表在全局页目录的偏移 547         page_header                dd  0xfffff000                    ;全局页目录 548         page_soft_header        dd  0xfffff000                     ;加载页目录地址 549           550         tcb_chain                dd  0                            ;任务控制块链头指针 551 ;========================================================================= 552 ;===========================内核代码区==================================== 553 ;========================================================================= 554 SECTION Core_Code align=16 vstart=0         555     ;--------------------------------------------------------------------- 556     append_to_tcb:                        ;写入新的TCB链 557                                         ;输入:ecx新的TCB线性基地址 558         pushad 559          560         push ds 561         push es 562          563         mov eax,All_4GB_Segment 564         mov es,eax 565          566         mov eax,Core_Data_Segement 567         mov ds,eax 568          569         mov dword[es:ecx+0x00],0 570         mov eax,[tcb_chain] 571         cmp eax,0x00 572         je _notcb 573          574         _search_tcb: 575             mov edx,[tcb_chain+0x00] 576             mov eax,[es:edx] 577             cmp eax,0x00 578         jne _search_tcb 579          580         mov [es:edx+0x00],ecx 581         jmp _out_tcb_search 582          583         _notcb: 584         mov [tcb_chain],ecx 585          586         _out_tcb_search: 587         pop es 588         pop ds 589          590         popad 591         ret 592     ;---------------------------------------------------------------------     593     load_program:                        ;输入push1:逻辑扇区号 594                                         ;     push2:    线性基地址 595         pushad 596         push ds 597         push es 598          599         mov ebp,esp                        ;别忘了把参数传给ebp 600          601         mov eax,Core_Data_Segement 602         mov ds,eax                        ;切换到内核数据段 603          604         mov eax,All_4GB_Segment 605         mov es,eax 606          607         mov ebx,0xfffff000 608         xor esi,esi 609         _flush_private: 610             mov dword[es:ebx+esi*4],0x00000000 611             inc esi 612             cmp esi,512 613         jl _flush_private 614          615         mov edi,[ebp+11*4]                ;获取tcb的线性基地址,别忘了调用相对近调用还要有1个push 616         mov esi,[ebp+12*4]                ;esi必须是逻辑扇区号 617         mov ebx,core_buf                ;ebx要在内核数据缓冲区(先读取头部在缓冲区,esi已经是有扇区号了) 618          619         push esi 620         push ds 621         push ebx 622         push cs 623         call Sys_Routine_Segement:ReadHarddisk 624          625         mov eax,[core_buf]                ;读取用户程序长度 626         mov ebx,eax                         627         and ebx,0xfffff000                ;清空低12位(强制对齐4096:4KB) 628         add ebx,4096                         629         test eax,0x00000fff                 630         cmovnz eax,ebx                    ;低12位不为0则使用向上取整的结果 631          632         mov ecx,eax 633         shr ecx,12                        ;看占了几页 634         mov eax,All_4GB_Segment            ;切换到4GB段区域(平坦模式) 635         mov ds,eax 636         mov edi,[ebp+11*4]                ;获取tcb的线性基地址 637         mov esi,[ebp+12*4]                ;esi必须是逻辑扇区号 638          639         _loop_read_@1: 640             mov ebx,[es:edi+0x06]        ;从TCB取得下一个可用的虚拟线性地址 641             add dword[es:edi+0x06],0x1000 642             call Sys_Routine_Segement:alloc_inst_a_page 643              644             push ecx 645             mov ecx,8                    ;512*8==4096 646             _loop_read_@2: 647                 push esi 648                 push ds 649                 push ebx 650                 push cs 651                 call Sys_Routine_Segement:ReadHarddisk    ;esi还是User_Program_Address 652                  653                 mov eax,[es:0xffc00000] 654                 mov eax,[es:0xffc00004] 655                 mov eax,[es:0xffc00008] 656                  657                 inc esi 658                 add ebx,512 659             loop _loop_read_@2 660             pop ecx 661         loop _loop_read_@1 662          663         mov eax,Core_Data_Segement        ;把数据段切回来 664         mov ds,eax 665         mov esi,edi                        ;esi: TCB的线性基地址 666          667         mov ebx,[core_next_laddr]        ;TSS必须在全局空间中进行 668         call Sys_Routine_Segement:alloc_inst_a_page 669         add dword[core_next_laddr],4096 670          671         mov [es:esi+0x14],ebx            ;填写TSS的线性基地址 672         mov word[es:esi+0x12],103        ;无I/O映射 673          674         mov ebx,[es:esi+0x06]            ;从用户私有空间建立LDT 675         add dword[es:esi+0x06],0x1000     676         call Sys_Routine_Segement:alloc_inst_a_page 677         mov [es:esi+0x0c],ebx            ;填写LDT的线地址 678          679         mov edi,[es:esi+0x14]            ;edi就是TSS的线性地址 680         ;接下来就是一堆套路了,这里和15章14章的程序不一样,这里直接填TSS,比较直观 681         ;不用给用户程序头回填段的选择子了,现在是平坦模式的演示 682         ;代码段 683         mov eax,0x00000000 684         mov ebx,0x000fffff 685         mov ecx,0x00c0f800 686         call Sys_Routine_Segement:Make_Seg_Descriptor 687         mov ebx,esi 688         call Sys_Routine_Segement:Set_New_LDT_To_TCB 689         or cx,0x0003                    ;特权级为3 690         mov [es:edi+76],cx                ;CS域 691          692         ;数据段 693         mov eax,0x00000000 694         mov ebx,0x000fffff 695         mov ecx,0x00c0f200 696         call Sys_Routine_Segement:Make_Seg_Descriptor 697         mov ebx,esi 698         call Sys_Routine_Segement:Set_New_LDT_To_TCB 699         or cx,0x0003                    ;特权级为3 700         mov [es:edi+72],cx                ;ES域,已经映射到全局空间了 701         mov [es:edi+84],cx                ;DS域,已经映射到全局空间了 702         mov [es:edi+88],cx                ;FS域,已经映射到全局空间了 703         mov [es:edi+92],cx                ;GS域,已经映射到全局空间了 704          705         ;创建一系列栈 706         ;创建自身特权级为3的栈 707         mov ebx,[es:esi+0x06]             708         add dword[es:esi+0x06],0x1000     709         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了 710         mov [es:edi+80],cx                                ;cx是数据段的选择子 711         mov edx,[es:esi+0x06]                            ;向上拓展的ESP的初始值 712         mov [es:edi+56],edx             713          714         ;创建特权级0的栈 715         mov ebx,[es:esi+0x06]             716         add dword[es:esi+0x06],0x1000     717         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了 718          719         mov eax,0x00000000 720         mov ebx,0x000fffff 721         mov ecx,0x00c09200                                 ;4KB粒度的堆栈段描述符,特权级0 722         call Sys_Routine_Segement:Make_Seg_Descriptor 723         mov ebx,esi 724         call Sys_Routine_Segement:Set_New_LDT_To_TCB 725         or cx,0x0000                                    ;选择子特权级为0 726          727         mov [es:edi+8],cx                                ;cx是数据段的选择子 728         mov edx,[es:esi+0x06]                            ;向上拓展的ESP0的初始值 729         mov [es:edi+4],edx             730          731         ;创建特权级1的栈 732         mov ebx,[es:esi+0x06]             733         add dword[es:esi+0x06],0x1000     734         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了 735          736         mov eax,0x00000000 737         mov ebx,0x000fffff 738         mov ecx,0x00c0b200                                 ;4KB粒度的堆栈段描述符,特权级1 739         call Sys_Routine_Segement:Make_Seg_Descriptor 740         mov ebx,esi 741         call Sys_Routine_Segement:Set_New_LDT_To_TCB 742         or cx,0x0001                                    ;选择子特权级为1 743          744         mov [es:edi+16],cx                                ;cx是数据段的选择子 745         mov edx,[es:esi+0x06]                            ;向上拓展的ESP1的初始值 746         mov [es:edi+12],edx         747          748         ;创建特权级2的栈 749         mov ebx,[es:esi+0x06]             750         add dword[es:esi+0x06],0x1000     751         call Sys_Routine_Segement:alloc_inst_a_page        ;自动在用户私有空间的页表登记了 752          753         mov eax,0x00000000 754         mov ebx,0x000fffff 755         mov ecx,0x00c0d200                                 ;4KB粒度的堆栈段描述符,特权级2 756         call Sys_Routine_Segement:Make_Seg_Descriptor 757         mov ebx,esi 758         call Sys_Routine_Segement:Set_New_LDT_To_TCB 759         or cx,0x0002                                    ;选择子特权级为2 760          761         mov [es:edi+24],cx                                ;cx是数据段的选择子 762         mov edx,[es:esi+0x06]                            ;向上拓展的ESP2的初始值 763         mov [es:edi+20],edx         764          765         ;现在开始重定位API符号表 766         ;--------------------------------------------------------------------- 767         mov eax,All_4GB_Segment            ;因为这个时候用户头部在LDT,而LDT还没有被加载,只能通过4GB空间访问 768         mov es,eax 769         mov eax,Core_Data_Segement 770         mov ds,eax 771          772         cld 773         mov ecx,[es:0x0c] 774         mov edi,[es:0x08] 775  776         _loop_U_SALT:                     777             push edi 778             push ecx 779              780             mov ecx,salt_items_sum 781             mov esi,salt 782              783             _loop_C_SALT: 784                 push edi 785                 push esi 786                 push ecx 787                  788                 mov ecx,64                ;比较256个字节 789                 repe cmpsd 790                 jne _re_match            ;如果成功匹配,那么esi和edi刚好会在数据区之后的 791                  792                 mov eax,[esi]            ;偏移地址 793                 mov [es:edi-256],eax    ;把偏移地址填入用户程序的符号区 794                 mov ax,[esi+0x04]        ;段的选择子 795                  796                 or ax,0x0002            ;把RPL改为3,代表(内核)赋予应用程序以特权级3 797                 mov [es:edi-252],ax        ;把段的选择子填入用户程序的段选择区 798                  799                 _re_match: 800                 pop ecx 801                 pop esi 802                 add esi,salt_length 803                 pop edi 804             loop _loop_C_SALT 805              806             pop ecx 807             pop edi 808             add edi,256 809         loop _loop_U_SALT 810         ;--------------------------------------------------------------------- 811         ;----------------------填入临时中转任务门选择子----------------------- 812         mov ax,[salt_tp] 813         mov [es:0x14],ax            ;填充任务门选择子 814         ;--------------------------------------------------------------------- 815         mov esi,[ebp+11*4]                        ;重新获得TCB的线性基地址 816          817         ;在GDT中存入LDT信息 818         mov eax,[es:esi+0x0c] 819         movzx ebx,word[es:esi+0x0a] 820         mov ecx,0x00408200                        ;LDT描述符,特权级0级 821         call Sys_Routine_Segement:Make_Seg_Descriptor 822         call Sys_Routine_Segement:Set_New_GDT 823         mov [es:esi+0x10],cx                    ;在TCB放入LDT选择子 824          825         ;构建TSS剩下的信息表 826         mov ebx,[es:esi+0x14] 827         mov [es:ebx+96],cx                        ;TSS中LDT选择子 828          829         mov word[es:ebx+0],0                    ;填充反向链(任务切换的时候处理器会帮着填的,不用操心) 830         mov dx,[es:esi+0x12]                    ;TSS段界限 831         mov [es:ebx+102],dx 832         mov word[es:ebx+100],0                    ;T=0 833        834         mov eax,[es:0x04]                          ;从任务的4GB地址空间获取入口点  835         mov [es:ebx+32],eax                        ;填写TSS的EIP域  836          837         pushfd 838         pop edx 839         mov [es:ebx+36],edx                           ;EFLAGS  840          841         ;在GDT中存入TSS信息 842         mov eax,[es:esi+0x14] 843         movzx ebx,word[es:esi+0x12] 844         mov ecx,0x00408900 845         call Sys_Routine_Segement:Make_Seg_Descriptor 846         call Sys_Routine_Segement:Set_New_GDT 847         mov [es:esi+0x18],cx 848          849         ;复制一份页表 850         call Sys_Routine_Segement:Copy_Page 851         mov ebx,[es:esi+0x14] 852         mov [es:ebx+28],eax                        ;填写PDBR(CR3) 853          854         pop es 855         pop ds 856         popad 857         ret 8                                    ;相当于是stdcall,过程清栈 858         ;--------------------------------------------------------------------- 859     start: 860         mov eax,Core_Data_Segement 861         mov ds,eax 862         mov eax,All_4GB_Segment 863         mov es,eax 864          865         mov ebx,message_1 866         call Sys_Routine_Segement:put_string 867          868         ;下面准备开启页管理 869         mov ecx,1024 870         mov ebx,PDT_Mem_Address 871         xor esi,esi 872          873         _flush_PDT:                                ;清空页表 874             mov dword[es:ebx+esi*4],0x00000000     875             inc esi 876         loop _flush_PDT 877          878         ;下面的代码会非常绕,注意观察 879         ;页目录的最后一个32字节是指向自己的页表(这个页表就是页目录) 880         mov dword[es:ebx+4092],PDT_Mem_Address 881         or dword[es:ebx+4092],0x00000007        ;属性:存在于物理内存,可写可读,啥程序都能访问 882          883         ;页目录的第一个页表指示最底下1MB内存的4KB页(内核代码,必须虚拟地址和物理地址一致) 884         mov dword[es:ebx+0],PDT_Mem_Address 885         or dword[es:ebx+0],0x00000007            ;属性:存在于物理内存,可写可读,啥程序都能访问 886         or dword[es:ebx+0],0x00001000            ;注意是这个页表是放在目录的后一个页! 887          888         ;现在0x00020000的页是第0个页表(指示页目录),0x00021000是第一个页表(指示底下1MB的东西) 889         mov ebx,PDT_Mem_Address 890         or ebx,0x00001000 891         xor eax,eax 892         xor esi,esi 893          894         _make_page: 895             mov edx,eax 896             or edx,0x00000007                    ;属性:存在于物理内存,可写可读,啥程序都能访问 897             mov [es:ebx+esi*4],edx                ;物理位置 898             add eax,0x1000 899             inc esi 900             cmp esi,256 901         jl _make_page 902          903         _make_page_last: 904             mov dword[es:ebx+esi*4],0x00000000    ;标记页为无效 905             inc esi 906             cmp esi,1024 907         jl _make_page_last 908          909         mov eax,PDT_Mem_Address 910         mov cr3,eax                                ;把页目录基地址放在cr3,准备开启页功能 911          912         mov eax,cr0 913         or eax,0x80000000 914         mov cr0,eax                                ;置PG位,开启页功能 915          916         ;--------------------------已开启页功能-------------------------------- 917         ;------------------开始映射高端内存区到页目录表中---------------------- 918         mov ebx,0xfffff000                                    ;表示页表指向页目录自己 919         mov esi,Global_Page_Directory                        ;映射的起始地址 920         shr esi,22 921         shl esi,2 922         mov dword[es:ebx+esi],PDT_Mem_Address                 923         add dword[es:ebx+esi],0x00001003 924          925         sgdt [pgdt_base_tmp] 926         mov ebx,[pgdt_base_tmp+2]                            ;GDT线性基地址 927          928         or dword[es:ebx+0x10+4],Global_Page_Directory        ;0x10是刚好是64个字节,忽略0字串 929         or dword[es:ebx+0x18+4],Global_Page_Directory 930         or dword[es:ebx+0x20+4],Global_Page_Directory 931         or dword[es:ebx+0x28+4],Global_Page_Directory 932         or dword[es:ebx+0x30+4],Global_Page_Directory 933         or dword[es:ebx+0x38+4],Global_Page_Directory 934          935         add dword[pgdt_base_tmp+2],Global_Page_Directory    ;线性基地址也要变 936          937         lgdt [pgdt_base_tmp] 938         jmp Core_Code_Segement:_flush                        ;强制刷新代码段,映射到高端内存区 939          940         _flush: 941         mov eax,Stack_Segement 942         mov ss,eax 943          944         mov eax,Core_Data_Segement 945         mov ds,eax 946          947         mov ebx,message_page 948         call Sys_Routine_Segement:put_string 949         ;---------------------------------------------------------------------- 950          951         _@load: 952         ;----------------------------安装门------------------------------------ 953         mov edi,salt 954         mov ecx,salt_items_sum 955         _set_gate: 956             push ecx 957             mov eax,[edi+256] 958             mov bx,[edi+260]        ;选择子 959             mov cx,0xec00            ;门是特权级是3的门,那么任何程序都能调用 960             or cx,[edi+262]            ;加上参数个数 961              962             call Sys_Routine_Segement:Make_Gate_Descriptor 963             call Sys_Routine_Segement:Set_New_GDT 964             mov [edi+260],cx        ;回填选择子 965             add edi,salt_length 966             pop ecx 967         loop _set_gate 968         ;---------------------------------------------------------------------- 969         mov ebx,message_In_Gate 970         call far [salt_1+256]            ;调用门显示字符信息(忽略偏移地址(前4字节)) 971          972         mov ebx,[core_next_laddr] 973         call Sys_Routine_Segement:alloc_inst_a_page 974         add dword[core_next_laddr],4096        ;指向下一个页 975          976         mov word[es:ebx+100],0             ;TI=0 977         mov word[es:ebx+102],103        ;任务管理器不需要I/O映射,要大于等于界限 978         mov word[es:ebx+96],0            ;任务允许没有自己的LDT 979         mov eax,cr3 980         mov dword[es:ebx+28],eax        ;设置CR3,注意不是0了!     981         mov word[es:ebx+0],0            ;没有前一个任务 982          983         mov eax,ebx 984         mov ebx,103                        ;TSS段界限 985         mov ecx,0x00408900 986         call Sys_Routine_Segement:Make_Seg_Descriptor 987         call Sys_Routine_Segement:Set_New_GDT 988         mov [prgman_tss+0x04],cx 989          990         ltr    cx                            ;启动任务 991         ;------------------安装用户管理程序的临时返回任务门--------------------         992         mov eax,0x0000                    ;TSS不需要偏移地址 993         mov bx,[prgman_tss+0x04]        ;TSS的选择子 994         mov cx,0xe500 995          996         call Sys_Routine_Segement:Make_Gate_Descriptor         997         call Sys_Routine_Segement:Set_New_GDT 998         mov [salt_tp],cx                ;填入临时中转任务门选择子,注意不需要加260了 999         ;----------------------------------------------------------------------1000         mov ebx,[core_next_laddr]1001         call Sys_Routine_Segement:alloc_inst_a_page1002         add dword[core_next_laddr],4096        ;指向下一个页1003         1004         mov dword[es:ebx+0x06],0         ;用户程序从0位置开始分配1005         mov word[es:ebx+0x0a],0xffff    ;LDT初始界限1006         mov ecx,ebx                        ;添加到TCB链中1007         call append_to_tcb1008         1009         push dword User_Program_AddressA1010         push ecx1011         1012         call load_program1013         1014         mov ebx,task_switch1015         call Sys_Routine_Segement:put_string1016         1017         jmp far [es:ecx+0x14]1018         ;call far [es:ecx+0x14]1019         1020         mov ebx,core_stop1021         call Sys_Routine_Segement:put_string1022         1023         hlt1024         ;----------------------------------------------------------------------1025 ;=========================================================================1026 SECTION core_trail1027 ;----------------------------------------------------------------1028 Program_end:
1 ;================================用户程序======================================= 2         program_length               dd program_end          ;程序总长度#0x00 3         entry_point                  dd start                ;程序入口点#0x04 4         salt_position                dd salt_begin           ;SALT表起始偏移量#0x08  5         salt_items                   dd (salt_end-salt_begin)/256  6                                                             ;SALT条目数#0x0C 7         TpBack:                         dd  0                    ;任务门的偏移地址没用,直接填充就可以了 8                                     dw    0                    ;任务门的选择子#0x14 9         Own_Page                    dd    0                    ;自己页面的物理地址#0x1610 ;-------------------------------------------------------------------------------11         ;符号地址检索表12         salt_begin:                                     13         PrintString                  db  '@Printf'14                             times 256-($-PrintString) db 015         TerminateProgram:            db  '@TerminateProgram'16                             times 256-($-TerminateProgram) db 017 ;-------------------------------------------------------------------------------18         reserved              times 256*500 db 0            ;保留一个空白区,以演示分页19 ;-------------------------------------------------------------------------------20         ReadDiskData                 db  '@ReadHarddisk'21                             times 256-($-ReadDiskData) db 022         PrintDwordAsHex              db  '@PrintDwordAsHexString'23                             times 256-($-PrintDwordAsHex) db 024         salt_end:25         message_0                      db  0x0d,0x0a,26                                     db  '  ............User task is running with '27                                     db  'paging enabled!............',0x0d,0x0a,028         message_1                      db  0x0d,0x0a,29                                     db  '  ..........,,,..The address of the user'30                                     db  ' task(first 50)!...........',0x0d,0x0a,0                            31         space                         db  0x20,0x20,0 32         next_line:                    db  0x0d,0x0a,033         Own_Page_Message            db  0x0d,0x0a,34                                     db    0x20,0x20,'Task Page: ',035 ;-------------------------------------------------------------------------------36       [bits 32]37 ;-------------------------------------------------------------------------------38 start:39         ;--------------------------显示50个页内容------------------------40         mov ebx,message_041         call far[PrintString]42          43         xor esi,esi44         mov ecx,5045         .b1:46             mov ebx,space47             call far[PrintString] 48          49             mov edx,[esi*4]50             call far[PrintDwordAsHex]51             inc esi52         loop .b153     54         ;--------------------显示新任务页目录地址--------------------------55         mov ebx,Own_Page_Message56         call far[PrintString]     57         mov ebx,space58         call far [PrintString]59         mov ebx,[Own_Page]60         mov edx,[es:ebx]61         and edx,0xfffff000            ;处理器会设置A和D位,直接忽略得到物理地址了62         call far[PrintDwordAsHex]63         64         mov ebx,next_line65         call far [PrintString]66         ;--------------------显示任务前50个页物理地址----------------------67         mov ebx,message_168         call far[PrintString]         69         mov edi,0xffc00000        70         mov ecx,5071         xor esi,esi72                             73         _show:74             mov ebx,space75             call far [PrintString]76             77             mov edx,[es:edi+esi*4]78             and edx,0xfffff000            ;处理器会设置A和D位,直接忽略得到物理地址了79             call far[PrintDwordAsHex]80             inc esi81         loop _show82         jmp far [fs:TpBack]83         ;iretd84 ;-------------------------------------------------------------------------------85 program_end:

 

转载于:https://www.cnblogs.com/Philip-Tell-Truth/p/5317979.html

你可能感兴趣的文章
9、接口和抽象类
查看>>
timeStamp和GMT时间的转换
查看>>
探索J2ME应用:如何用GCF通信
查看>>
jquery ajaxform上传文件返回不提示信息的问题
查看>>
实现一个2008serve的IIS的虚拟目录(通过网络路径(UNC)的形式,共享在另外一个2008服务器上...
查看>>
适配器
查看>>
c#截取字符串
查看>>
VS2005中配置 ScriptManager,UpdatePanel,UpdateProgress 等AJAX控件 .
查看>>
使用logback实现http请求日志导入mongodb
查看>>
【 2017 Multi-University Training Contest - Team 9 && hdu 6162】Ch’s gift
查看>>
redis在php中的应用(Hash篇)
查看>>
Docker系列之Docker镜像(读书笔记)
查看>>
Scrapy 多url爬取、爬取post请求、更换代理ip、指定日志等级
查看>>
phpExcel实现excel文件导出
查看>>
Pandas中dataframe以及spark中rdd使用groupByKey进行合并
查看>>
简单字符串处理应避免使用正则表达式
查看>>
了解正则表达式操作符的优先级
查看>>
Spring框架集成FreeMarker
查看>>
用 async/await 来处理异步
查看>>
app开发-1
查看>>