操作系统实现之内存分页机制.虚拟空间

来源:互联网 发布:wifi打不开修复软件 编辑:程序博客网 时间:2024/06/05 18:16
内存虚拟存储主要是为了将一个进程分为不同页.存储到不同物理页中.然而不同进程的虚拟地址是可以相同的.因为MMU把进程的虚拟地址映射到各个不同的物理地址中.

以下操作系统采用二级分页.一开始CS:IP寄存器.将CS的基地址跟IP的偏移地址进行相加.得到线性地址.接着.线性地址的高10位用于当作页目录表的索引.页目录表保存的是页表的物理地址.接着.线性地址的低10位用于当作页表的索引.页表保存的是4k大小的页块.线性地址低10位那就是页块的偏移地址.由此虚拟地址->物理地址转换结束.当然操作系统在创建页目录表的时候.要把页目录表的物理地址给加载到cr3寄存器中.
开启分页机制分为3件事.
(1)设置页目录表跟页表
(2)把页目录表的地址加载到cr3寄存器中
(3)将寄存器的cr0的PG位置为1表示开启了分页机制

规划页目录表跟页表位置
在用户进程.高3G部分给操作系统.而0~3G是用户的空间.每个进程都有页表.然后一个用户进程的功能完成是需要内核配合的.于是所有用户进程应该共享内核.也就是等于3G-4g的所有用户进程空间指向的都是同一个物理页地址

这里我们将把页目录表放在0x100000处.页表也挨着页目录表放在0x101000处(第二个页表.当然在此之前应该把物理内存给算出来.这里可以使用bios中断来获取物理内存

[cpp] view plain copy 在CODE上查看代码片派生到我的代码片
  1. %include "boot.inc"  
  2. section loader vstart=loader_base_addr  
  3. ;------------全局描述符表的定义   
  4. gdt_base:   
  5.      dd 0x00000000    ;全局描述表.第一个描述符要为空  
  6.      dd 0x00000000  
  7. code_base:   
  8.      dd 0x0000FFFF  
  9.      dd desc_code_high4  
  10. data_stack_desc:  
  11.     dd 0x0000ffff  
  12.     dd desc_data_high4  
  13. video_desc:   
  14.     dd 0x80000007   ;段界限 limit=0x7fff.基址位于0xb8000  
  15.     dd desc_video_high4  
  16. ;---------GDT的属性  
  17. gdt_size  equ $-gdt_base  ;GDT大小  
  18. gdt_limit equ gdt_size    ;GDT限制  
  19. times 60 dq 0  ;预留60个描述符的空位置  
  20. ;以下定义一个数用来保存笔记本的内存大小,以上一共0x200个字节(64*8=0x200)  
  21. total_mem_bytes  dd 0  
  22. ;----------定义段选择子-------------  
  23. selector_code equ (0x0001<<3)+ti_gdt+rpl0  
  24. selector_data equ (0x0002<<3)+ti_gdt+rpl0  
  25. selector_video equ (0x0003<<3)+ti_gdt+rpl0  
  26. ;定义gdt的指针,前2个字节为gdt界限,后4个字节为gdt起始地址  
  27. gdt_ptr dw gdt_limit  
  28.         dd gdt_base  
  29. ;-----定义ards结构体数量-------------  
  30.  ards_buf times 244 db 0  
  31.  ards_nr dw 0             ;用于记录ards结构体数量  
  32.    
  33. ;以上一共0x300个字节------------------  
  34. loader_start:  
  35. ;-------  int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局  -------  
  36.    xor ebx, ebx           ;第一次调用时,ebx值要为0  
  37.    mov edx, 0x534d4150        ;edx只赋值一次,循环体中不会改变  
  38.    mov di, ards_buf       ;ards结构缓冲区  
  39. .e820_mem_get_loop:       ;循环获取每个ARDS内存范围描述结构  
  40.    mov eax, 0x0000e820        ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。  
  41.    mov ecx, 20            ;ARDS地址范围描述符结构大小是20字节  
  42.    int 0x15  
  43.    jc .e820_failed_so_try_e801   ;若cf位为1则有错误发生,尝试0xe801子功能  
  44.    add di, cx             ;使di增加20字节指向缓冲区中新的ARDS结构位置  
  45.    inc word [ards_nr]         ;记录ARDS数量  
  46.    cmp ebx, 0             ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个  
  47.    jnz .e820_mem_get_loop  
  48. ;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。  
  49.    mov cx, [ards_nr]          ;遍历每一个ARDS结构体,循环次数是ARDS的数量  
  50.    mov ebx, ards_buf   
  51.    xor edx, edx           ;edx为最大的内存容量,在此先清0  
  52. .find_max_mem_area:       ;无须判断type是否为1,最大的内存块一定是可被使用  
  53.    mov eax, [ebx]         ;base_add_low  
  54.    add eax, [ebx+8]       ;length_low  
  55.    add ebx, 20            ;指向缓冲区中下一个ARDS结构  
  56.    cmp edx, eax           ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量  
  57.    jge .next_ards  
  58.    mov edx, eax           ;edx为总内存大小  
  59. .next_ards:  
  60.    loop .find_max_mem_area  
  61.    jmp .mem_get_ok  
  62. ;------  int 15h ax = E801h 获取内存大小,最大支持4G  ------  
  63. ; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位  
  64. ; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。  
  65. .e820_failed_so_try_e801:  
  66.    mov ax,0xe801  
  67.    int 0x15  
  68.    jc .e801_failed_so_try88   ;若当前e801方法失败,就尝试0x88方法  
  69. ;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位  
  70.    mov cx,0x400      ;cx和ax值一样,cx用做乘数  
  71.    mul cx   
  72.    shl edx,16  
  73.    and eax,0x0000FFFF  
  74.    or edx,eax  
  75.    add edx, 0x100000 ;ax只是15MB,故要加1MB  
  76.    mov esi,edx       ;先把低15MB的内存容量存入esi寄存器备份  
  77. ;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量  
  78.    xor eax,eax  
  79.    mov ax,bx          
  80.    mov ecx, 0x10000 ;0x10000十进制为64KB  
  81.    mul ecx      ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.  
  82.    add esi,eax      ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可  
  83.    mov edx,esi      ;edx为总内存大小  
  84.    jmp .mem_get_ok  
  85. ;-----------------  int 15h ah = 0x88 获取内存大小,只能获取64M之内  ----------  
  86. .e801_failed_so_try88:   
  87.    ;int 15后,ax存入的是以kb为单位的内存容量  
  88.    mov  ah, 0x88  
  89.    int  0x15  
  90.    jc .error_hlt  
  91.    and eax,0x0000FFFF  
  92.         
  93.    ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中  
  94.    mov cx, 0x400     ;0x400等于1024,将ax中的内存容量换为以byte为单位  
  95.    mul cx  
  96.    shl edx, 16       ;把dx移到高16位  
  97.    or edx, eax       ;把积的低16位组合到edx,为32位的积  
  98.    add edx,0x100000  ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB  
  99. .mem_get_ok:  
  100.    mov [total_mem_bytes], edx    ;将内存换为byte单位后存入total_mem_bytes处。  
  101. ;-----进入保护模式----------  
  102. in al,0x92  
  103. or al,0000_0010B  
  104. out 0x92,al  
  105. ;----------加载GDT----------  
  106. lgdt [gdt_ptr]  
  107. ;---------cr0第0位置为1,表示打开保护模式-----------  
  108. mov eax,cr0  
  109. or  eax,0x00000001  
  110. mov cr0,eax  
  111. jmp dword selector_code:p_mode_start  
  112. .error_hlt:           ;出错则挂起  
  113.    hlt  
  114. [bits 32]  
  115. p_mode_start:  
  116.     mov ax,selector_data  
  117.     mov ds,ax  
  118.     mov es,ax  
  119.     mov ss,ax  
  120.     mov esp,loader_stack_top  
  121.     mov ax,selector_video  
  122.     mov gs,ax  
  123.       
  124. ;创建页目录表  
  125. call set_page  
  126. sgdt [gdt_ptr]  ;加载GDT  
  127. mov ebx,[gdt_ptr+2]  
  128. or  dword [ebx+0x18+4] ,0xc0000000  ;+4是因为.段描述的高4字节的段基址是31-24.  
  129. ;将gdt的基址加上0xc00000000使其成为内核的高地址  
  130. add dword [gdt_ptr+2],0xc0000000  
  131. add esp,0xc0000000  
  132. ;-----页目录表赋给cr3-----  
  133. mov eax,page_dir_table_pos  
  134. mov cr3,eax  
  135. ;打开cr0的pg位(31位)  
  136. mov eax,cr0  
  137. or eax,0x80000000  
  138. mov cr0, eax  
  139. lgdt[gdt_ptr]   
  140. mov byte [gs:160],'V'  
  141. jmp $  
  142. ;------------创建页目录表以及页表  
  143. set_page:  
  144.     mov ecx,4096  
  145.     mov esi,0  
  146. .clear_page_dir:  
  147.     mov byte [page_dir_table_pos +esi],0  
  148.     inc esi  
  149.     loop .clear_page_dir  
  150. ;-------创建PDE-------  
  151. create_pde:  
  152.     mov eax,page_dir_table_pos  
  153.     add eax,0x1000  ;页表地址  
  154.     mov ebx,eax   ;ebx指向第一个页表地址  
  155.       
  156.     or eax, pg_us_u | pg_rw_w | pg_p  
  157.       
  158.     mov [page_dir_table_pos +0x0],eax ;将第一个页表写到页目录表的第一项  
  159.     mov [page_dir_table_pos+0xc00],eax ;一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间  
  160.     sub eax,0x1000   
  161.     mov [page_dir_table_pos+4092],eax ;页目录表的最后一个页目录项指向它自己  
  162.       
  163.       
  164. ;---------创建页表项pte-------------  
  165. mov ecx,256            ;1M低端内存/4k=256  
  166. mov esi,0  
  167. mov edx,pg_us_u | pg_rw_w |pg_p  
  168. .create_pte:  
  169.   mov [ebx+esi*4],edx  
  170.   add edx,4096  
  171.   inc esi  
  172.   loop .create_pte  
  173. ;--------创建内核跟页表的pde  
  174. mov eax,page_dir_table_pos  
  175. add eax,0x2000 ;指向第二个页表  
  176. or eax,pg_us_u | pg_rw_w | pg_p  
  177. mov ebx,page_dir_table_pos  
  178. mov esi,769  
  179. mov ecx,254  
  180. .create_kernel_pde:  
  181.     mov [ebx+esi*4],eax  
  182.     inc esi  
  183.     add eax,0x1000  
  184.     loop .create_kernel_pde  
  185.     ret  



0 0
原创粉丝点击