操作系统实现之内存分页机制.虚拟空间
来源:互联网 发布:wifi打不开修复软件 编辑:程序博客网 时间:2024/06/05 18:16
内存虚拟存储主要是为了将一个进程分为不同页.存储到不同物理页中.然而不同进程的虚拟地址是可以相同的.因为MMU把进程的虚拟地址映射到各个不同的物理地址中.
这里我们将把页目录表放在0x100000处.页表也挨着页目录表放在0x101000处(第二个页表.当然在此之前应该把物理内存给算出来.这里可以使用bios中断来获取物理内存
以下操作系统采用二级分页.一开始CS:IP寄存器.将CS的基地址跟IP的偏移地址进行相加.得到线性地址.接着.线性地址的高10位用于当作页目录表的索引.页目录表保存的是页表的物理地址.接着.线性地址的低10位用于当作页表的索引.页表保存的是4k大小的页块.线性地址低10位那就是页块的偏移地址.由此虚拟地址->物理地址转换结束.当然操作系统在创建页目录表的时候.要把页目录表的物理地址给加载到cr3寄存器中.
开启分页机制分为3件事.
(1)设置页目录表跟页表
(2)把页目录表的地址加载到cr3寄存器中
(3)将寄存器的cr0的PG位置为1表示开启了分页机制
规划页目录表跟页表位置
在用户进程.高3G部分给操作系统.而0~3G是用户的空间.每个进程都有页表.然后一个用户进程的功能完成是需要内核配合的.于是所有用户进程应该共享内核.也就是等于3G-4g的所有用户进程空间指向的都是同一个物理页地址
- %include "boot.inc"
- section loader vstart=loader_base_addr
- ;------------全局描述符表的定义
- gdt_base:
- dd 0x00000000 ;全局描述表.第一个描述符要为空
- dd 0x00000000
- code_base:
- dd 0x0000FFFF
- dd desc_code_high4
- data_stack_desc:
- dd 0x0000ffff
- dd desc_data_high4
- video_desc:
- dd 0x80000007 ;段界限 limit=0x7fff.基址位于0xb8000
- dd desc_video_high4
- ;---------GDT的属性
- gdt_size equ $-gdt_base ;GDT大小
- gdt_limit equ gdt_size ;GDT限制
- times 60 dq 0 ;预留60个描述符的空位置
- ;以下定义一个数用来保存笔记本的内存大小,以上一共0x200个字节(64*8=0x200)
- total_mem_bytes dd 0
- ;----------定义段选择子-------------
- selector_code equ (0x0001<<3)+ti_gdt+rpl0
- selector_data equ (0x0002<<3)+ti_gdt+rpl0
- selector_video equ (0x0003<<3)+ti_gdt+rpl0
- ;定义gdt的指针,前2个字节为gdt界限,后4个字节为gdt起始地址
- gdt_ptr dw gdt_limit
- dd gdt_base
- ;-----定义ards结构体数量-------------
- ards_buf times 244 db 0
- ards_nr dw 0 ;用于记录ards结构体数量
- ;以上一共0x300个字节------------------
- loader_start:
- ;------- int 15h eax = 0000E820h ,edx = 534D4150h ('SMAP') 获取内存布局 -------
- xor ebx, ebx ;第一次调用时,ebx值要为0
- mov edx, 0x534d4150 ;edx只赋值一次,循环体中不会改变
- mov di, ards_buf ;ards结构缓冲区
- .e820_mem_get_loop: ;循环获取每个ARDS内存范围描述结构
- mov eax, 0x0000e820 ;执行int 0x15后,eax值变为0x534d4150,所以每次执行int前都要更新为子功能号。
- mov ecx, 20 ;ARDS地址范围描述符结构大小是20字节
- int 0x15
- jc .e820_failed_so_try_e801 ;若cf位为1则有错误发生,尝试0xe801子功能
- add di, cx ;使di增加20字节指向缓冲区中新的ARDS结构位置
- inc word [ards_nr] ;记录ARDS数量
- cmp ebx, 0 ;若ebx为0且cf不为1,这说明ards全部返回,当前已是最后一个
- jnz .e820_mem_get_loop
- ;在所有ards结构中,找出(base_add_low + length_low)的最大值,即内存的容量。
- mov cx, [ards_nr] ;遍历每一个ARDS结构体,循环次数是ARDS的数量
- mov ebx, ards_buf
- xor edx, edx ;edx为最大的内存容量,在此先清0
- .find_max_mem_area: ;无须判断type是否为1,最大的内存块一定是可被使用
- mov eax, [ebx] ;base_add_low
- add eax, [ebx+8] ;length_low
- add ebx, 20 ;指向缓冲区中下一个ARDS结构
- cmp edx, eax ;冒泡排序,找出最大,edx寄存器始终是最大的内存容量
- jge .next_ards
- mov edx, eax ;edx为总内存大小
- .next_ards:
- loop .find_max_mem_area
- jmp .mem_get_ok
- ;------ int 15h ax = E801h 获取内存大小,最大支持4G ------
- ; 返回后, ax cx 值一样,以KB为单位,bx dx值一样,以64KB为单位
- ; 在ax和cx寄存器中为低16M,在bx和dx寄存器中为16MB到4G。
- .e820_failed_so_try_e801:
- mov ax,0xe801
- int 0x15
- jc .e801_failed_so_try88 ;若当前e801方法失败,就尝试0x88方法
- ;1 先算出低15M的内存,ax和cx中是以KB为单位的内存数量,将其转换为以byte为单位
- mov cx,0x400 ;cx和ax值一样,cx用做乘数
- mul cx
- shl edx,16
- and eax,0x0000FFFF
- or edx,eax
- add edx, 0x100000 ;ax只是15MB,故要加1MB
- mov esi,edx ;先把低15MB的内存容量存入esi寄存器备份
- ;2 再将16MB以上的内存转换为byte为单位,寄存器bx和dx中是以64KB为单位的内存数量
- xor eax,eax
- mov ax,bx
- mov ecx, 0x10000 ;0x10000十进制为64KB
- mul ecx ;32位乘法,默认的被乘数是eax,积为64位,高32位存入edx,低32位存入eax.
- add esi,eax ;由于此方法只能测出4G以内的内存,故32位eax足够了,edx肯定为0,只加eax便可
- mov edx,esi ;edx为总内存大小
- jmp .mem_get_ok
- ;----------------- int 15h ah = 0x88 获取内存大小,只能获取64M之内 ----------
- .e801_failed_so_try88:
- ;int 15后,ax存入的是以kb为单位的内存容量
- mov ah, 0x88
- int 0x15
- jc .error_hlt
- and eax,0x0000FFFF
- ;16位乘法,被乘数是ax,积为32位.积的高16位在dx中,积的低16位在ax中
- mov cx, 0x400 ;0x400等于1024,将ax中的内存容量换为以byte为单位
- mul cx
- shl edx, 16 ;把dx移到高16位
- or edx, eax ;把积的低16位组合到edx,为32位的积
- add edx,0x100000 ;0x88子功能只会返回1MB以上的内存,故实际内存大小要加上1MB
- .mem_get_ok:
- mov [total_mem_bytes], edx ;将内存换为byte单位后存入total_mem_bytes处。
- ;-----进入保护模式----------
- in al,0x92
- or al,0000_0010B
- out 0x92,al
- ;----------加载GDT----------
- lgdt [gdt_ptr]
- ;---------cr0第0位置为1,表示打开保护模式-----------
- mov eax,cr0
- or eax,0x00000001
- mov cr0,eax
- jmp dword selector_code:p_mode_start
- .error_hlt: ;出错则挂起
- hlt
- [bits 32]
- p_mode_start:
- mov ax,selector_data
- mov ds,ax
- mov es,ax
- mov ss,ax
- mov esp,loader_stack_top
- mov ax,selector_video
- mov gs,ax
- ;创建页目录表
- call set_page
- sgdt [gdt_ptr] ;加载GDT
- mov ebx,[gdt_ptr+2]
- or dword [ebx+0x18+4] ,0xc0000000 ;+4是因为.段描述的高4字节的段基址是31-24.
- ;将gdt的基址加上0xc00000000使其成为内核的高地址
- add dword [gdt_ptr+2],0xc0000000
- add esp,0xc0000000
- ;-----页目录表赋给cr3-----
- mov eax,page_dir_table_pos
- mov cr3,eax
- ;打开cr0的pg位(31位)
- mov eax,cr0
- or eax,0x80000000
- mov cr0, eax
- lgdt[gdt_ptr]
- mov byte [gs:160],'V'
- jmp $
- ;------------创建页目录表以及页表
- set_page:
- mov ecx,4096
- mov esi,0
- .clear_page_dir:
- mov byte [page_dir_table_pos +esi],0
- inc esi
- loop .clear_page_dir
- ;-------创建PDE-------
- create_pde:
- mov eax,page_dir_table_pos
- add eax,0x1000 ;页表地址
- mov ebx,eax ;ebx指向第一个页表地址
- or eax, pg_us_u | pg_rw_w | pg_p
- mov [page_dir_table_pos +0x0],eax ;将第一个页表写到页目录表的第一项
- mov [page_dir_table_pos+0xc00],eax ;一个页表项占用4字节,0xc00表示第768个页表占用的目录项,0xc00以上的目录项用于内核空间
- sub eax,0x1000
- mov [page_dir_table_pos+4092],eax ;页目录表的最后一个页目录项指向它自己
- ;---------创建页表项pte-------------
- mov ecx,256 ;1M低端内存/4k=256
- mov esi,0
- mov edx,pg_us_u | pg_rw_w |pg_p
- .create_pte:
- mov [ebx+esi*4],edx
- add edx,4096
- inc esi
- loop .create_pte
- ;--------创建内核跟页表的pde
- mov eax,page_dir_table_pos
- add eax,0x2000 ;指向第二个页表
- or eax,pg_us_u | pg_rw_w | pg_p
- mov ebx,page_dir_table_pos
- mov esi,769
- mov ecx,254
- .create_kernel_pde:
- mov [ebx+esi*4],eax
- inc esi
- add eax,0x1000
- loop .create_kernel_pde
- ret
0 0
- 操作系统实现之内存分页机制.虚拟空间
- 操作系统实现之内存分页机制.虚拟空间
- 操作系统之内存管理
- 操作系统之内存管理
- 操作系统之内存
- 操作系统之内存管理
- 操作系统之内存管理
- 操作系统之内存管理
- java之内存机制
- 操作系统之内存与进程
- 操作系统实践之内存布局
- 操作系统之内存管理1
- 操作系统之内存管理2
- 计算机操作系统之内存管理
- 操作系统之内存管理详解
- 操作系统之内存管理科普
- 操作系统之内存管理(一)
- 操作系统之内存管理(未完成)
- 快速排序以及输出展示
- Java NIO:浅析I/O模型
- java之Iterator迭代器
- Poj 1631 Bridging signals (LIS, nlog(n))
- python挑战之level5
- 操作系统实现之内存分页机制.虚拟空间
- vector C++
- [jenkins工具]之三:jenkins上传android/ios项目到fir.im/蒲公英
- oracle表分区详解
- JAVA SE 学习第二天
- HTML语言及其背景
- 动态网站服务器架构解决方案简要归类
- 移动互联网中的网络小结
- 搜索硬盘下目录文件