操作系统真象-开启分页&加载内核
Tags: learning experience, 操作系统
开启分页:
ps. 下面的内容都是基于二级页表的, 毕竟大伙都是用的二级页表, 而且动态规划的二级页表空间利用率高于一级页表哇
为什么要开启分页:
最大化利用物理内存, 避免物理内存啥的, 以及加速内外存交换?(毕竟书上方案B在面对需要大空间进程时, 对内外存交换速度的打击是毁灭性的)
如何开启分页机制:
ps. 这三个步骤是有顺序的
- 准备好页目录及页表
- 将页表目录的地址写入cr3
- cr0的PG位置零
二级页表的结构:
原谅我实在懒得自己画图与找pdf
看5-20, 页表目录本身是占4kb, 其中是可以有空目录的, 要用上了再加嘛, 而每个目录项所指的地址上也会先占用4kb来记录1024个物理地址, 其中也是可以有空物理地址, 还是要用上了再加呗~
虚拟地址到物理地址的转换:
虚拟地址的高10位是在页表目录的索引, 中间10位是在选定目录中的二级目录的索引(索引得*4才能获得在目录上的地址哦)
这样经过两级目录的寻找就能找到物理地址了, 而虚拟地址的低12位是在物理地址中的偏移
而机器内部的转换是cpu中的部件自带支持的嘞
书上的例子:
一些特殊的映射:
第0个, 咱自己在源码中这样设置的
第1个, 第768个页目录, 是1g系统内存的开始, loader其实是属于系统的, 所以被丢到了系统内存了, 但是loader又是之前我们运行的程序, 所以存在了0号一级目录和768号一级目录映射向了同一块物理地址
第345个出现的原因都是因为我们把第1023个一级目录指向了页目录表本身的地址, 因为大伙看它们的高10位都是0x3ff, 在一级目录表中指向了最后一个一级目录, 就是俺们一级页目录表本身的地址为0x100000, 之后看3, 4个, 一个中间10位是0, 另一个十进制是768, 所以映射值相等原因就是和前面0, 1俩的原因相同(请把第四个看作0xfff0000-0xfff00fff → 0x101000-0x101fff, 即与第三个相同), 而它们的值是因为我们0号一级目录是这样映射的: 0x100000(一级目录表地址)→0x101000(0号一级目录上记录的二级目录地址)→0(0号一级目录0号二级目录的真实地址), 然而我们映射只到第二步就停止了, 所以映射范围是0x101000~0x101fff, 话说第四个之所以变成很大, 是因为我们在.create_kernel_pde中已经循环初始化了属于内核空间的一级目录
第5个再拿出来单独说一下, 高10位是0x3ff, 中间也是0x3ff, 则利用高20位, 最后还是指向了页目录表本身, 则我们可以利用最后的12位=索引*4, 获得所有页目录表上的二级目录的地址啦~
ps. 想达到书上第3, 4个地址映射相同的话, 需要注释掉整个.create_kernel_pde函数
一级目录即PDE, 二级目录即PTE
加载内核:
elf的结构:
这玩意…感觉不是很重要, 主要就讲一下在稍微高版本一些的gcc中会自动加入名为eh_frame的segment, 所以会导致elf结构不是和书上完全一致, 但是好像不影响使用
其次64位下要编译链接32位程序得这样写
gcc -m32 -c main.c -o main.o
ld -m elf_i386 -Ttext 0xc0001500 -e main -o kernel.bin main.o
为什么要先加载内核进内存:
作者说是缓冲区的概念啥的, 先把内核加载进内存, 使得操作系统运行更加高速
如何加载内核:
先利用中断, 将内核从外存(这里是硬盘)读进内存, 之后再根据elf的结构找到.text segment, 把它的内容用cld与movsb的组合读进指定虚拟地址就行了
最后的loader.s:
%include "boot.inc"
section loader vstart=LOADER_BASE_ADDR
LOADER_STACK_TOP equ LOADER_BASE_ADDR
jmp loader_start
db 0
dd 0, 0, 0
GDT_BASE: dd 0x00000000
dd 0x00000000
CODE_DESC: dd 0x0000ffff
dd DESC_CODE_HIGH4
DATA_STACK_DESC: dd 0x0000ffff
dd DESC_DATA_HIGH4
VIDEO_DESC: dd 0x80000007
dd DESC_VIDEO_HIGH4
GDT_SIZE equ $ - GDT_BASE
GDT_LIMIT equ GDT_SIZE - 1
times 60 dq 0
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_ptr dw GDT_LIMIT
dd GDT_BASE
loader_start:
.LoaderStart_E801FailedRetry:
mov ax, 0xe801
int 0x15
jc .LoaderStart_E801FailedRetry
; calculate low 15mb mem
mov cx, 0x400
mul cx
shl edx, 16
and eax, 0xffff
or edx, eax
add edx, 0x100000
mov esi, edx
xor eax, eax
mov ax, bx
mov ecx, 0x10000
mul ecx
add esi, eax
mov [TOTAL_MEM_BYTES], esi
;ready to go to protect
;open A20
in al, 0x92
or al, 0000_0010B
out 0x92, al
;load GDT
lgdt [gdt_ptr]
;change cr0
mov eax, cr0
or eax, 0x00000001
mov cr0, eax
jmp dword SELECTOR_CODE:p_mode_start
[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
mov byte [gs:400], 'E'
;-----load kernel-----
mov eax, KERNEL_START_SECTOR
mov ebx, KERNEL_BIN_BASE_ADDR
mov ecx, 200
call rd_disk_m_32 ;read into disk
;-----create PDE and PTE-----
call setup_page
;-----flush GDT-----
sgdt [gdt_ptr]
mov ebx, [gdt_ptr + 2]
or dword [ebx + 0x18 + 4], 0xc0000000
add dword [gdt_ptr + 2], 0xc0000000
add esp, 0xc0000000
mov eax, PAGE_DIR_TABLE_POS
mov cr3, eax
mov eax, cr0
or eax, 0x80000000
mov cr0, eax
lgdt [gdt_ptr]
mov byte [gs:398], 'N'
mov byte [gs:402], 'W'
jmp SELECTOR_CODE:enter_kernel
enter_kernel:
call kernel_init
mov esp, 0xc009f000
jmp KERNEL_ENTRY_POINT
setup_page:
;-----set space of PDE to 0-----
mov ecx, 0x1000
mov esi, 0
.clear_page_dir:
mov byte [PAGE_DIR_TABLE_POS + esi], 0
inc esi
loop .clear_page_dir
;-----create PDE-----
.create_pde:
mov eax, PAGE_DIR_TABLE_POS
add eax, 0x1000
mov ebx, eax
;set directory 0 and 0xc00 with addr of the 1st table, every directory stores 4MB mem
;perpare for mapping addr of kernel
;directory 0xc00 and above 0xc00 used to kernel
;0x0~0xbfffffff: user, 0xc0000000~0xffffffff: kernel
or eax, PG_US_U | PG_RW_W | PG_P
mov [PAGE_DIR_TABLE_POS + 0x0], eax
mov [PAGE_DIR_TABLE_POS + 0xc00], eax
;set last directory(0xfff) to PDE itself
sub eax, 0x1000
mov [PAGE_DIR_TABLE_POS + 4092], eax
;-----create PTE-----
;we want to make pyhsical addr 0x101000 -> 0x101000 * 0x1000*256(1MB) to be directory 0: 1->256 table
;because our kernel is small about 70kb, so 1mb is enough to kernel
mov ecx, 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
;create kernel other PDE
;because kernel is shared to all process, so we need to set kernel PDE firstly
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 ecx, 254 ;count between directory 769->1022
mov esi, 769
.create_kernel_pde:
mov [ebx+esi*4], eax
inc esi
add eax, 0x1000
loop .create_kernel_pde
ret;
;function to read kernel.bin into memory
rd_disk_m_32:
mov esi, eax
mov di, cx
mov dx, 0x1f2
mov al, cl
out dx, al; out port 0x1f2 with sector count
mov eax, esi
mov dx, 0x1f3
out dx, al
mov cl, 8
shr eax, cl
mov dx, 0x1f4
out dx, al
shr eax, cl
mov dx, 0x1f5
out dx, al; out port 0x1f3-0x1f5 with LOADER_START_SECTOR(LBA num)
shr eax, cl
and al, 0x0f
or al, 0xe0
mov dx, 0x1f6
out dx, al; out port 0x1f6 to set lba mode
mov dx, 0x1f7
mov al, 0x20
out dx, al
.not_ready:
nop
in al, dx
and al, 0x88
cmp al, 0x08
jnz .not_ready; judge sector status
mov ax, di
mov dx, 256
mul dx
mov cx, ax; set read word count
mov dx, 0x1f0
.go_on_read:
in ax, dx
mov [ebx], ax
add ebx, 2
loop .go_on_read
ret
;-----copy segment in kernel.bin into address of compiling
kernel_init:
xor eax, eax
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov dx, [KERNEL_BIN_BASE_ADDR + 42]
mov ebx, [KERNEL_BIN_BASE_ADDR + 28]
add ebx, KERNEL_BIN_BASE_ADDR
mov cx, [KERNEL_BIN_BASE_ADDR + 44]
.each_segment:
cmp byte[ebx + 0], PT_NULL
je .PTNULL
push dword[ebx + 16]
mov eax, [ebx + 4]
add eax, KERNEL_BIN_BASE_ADDR
push eax
push dword[ebx + 8]
call mem_cpy
add esp, 12
.PTNULL:
add ebx, edx
loop .each_segment
ret
mem_cpy:
cld
push ebp
mov ebp, esp
push ecx
mov edi, [ebp + 8]
mov esi, [ebp + 12]
mov ecx, [ebp + 16]
rep movsb
pop ecx
pop ebp
ret
最后的boot.inc
;-----about loader and kernel-----
LOADER_BASE_ADDR equ 0x900
LOADER_START_SECTOR equ 0x2
DESC_G_4K equ 1_000_000_000_000_000_000_000_00b
DESC_D_32 equ 1_000_000_000_000_000_000_000_0b
DESC_L equ 0_000_000_000_000_000_000_000b
DESC_AVL equ 0_000_000_000_000_000_000_00b
DESC_LIMIT_CODE2 equ 1111_0000_0000_0000_0000b
DESC_LIMIT_DATA2 equ DESC_LIMIT_CODE2
DESC_LIMIT_VIDEO2 equ 0000_000_000_000_000_000b
DESC_P equ 1_000_000_000_000_000b
DESC_DPL_0 equ 00_000_000_000_000_0b
DESC_DPL_1 equ 01_000_000_000_000_0b
DESC_DPL_2 equ 10_000_000_000_000_0b
DESC_DPL_3 equ 11_000_000_000_000_0b
DESC_S_CODE equ 1_000_000_000_000b
DESC_S_DATA equ DESC_S_CODE
DESC_S_sys equ 0_000_000_000_000b
DESC_TYPE_CODE equ 1000_0000_0000b
DESC_TYPE_DATA equ 0010_0000_0000b
DESC_CODE_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_CODE2 + \
DESC_P + DESC_DPL_0 + DESC_S_CODE + \
DESC_TYPE_CODE + 0x00
DESC_DATA_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_DATA2 + \
DESC_P + DESC_DPL_0 + DESC_S_DATA + \
DESC_TYPE_DATA + 0x00
DESC_VIDEO_HIGH4 equ (0x00 << 24) + DESC_G_4K + DESC_D_32 + \
DESC_L + DESC_AVL + DESC_LIMIT_VIDEO2 + DESC_P + \
DESC_DPL_0 + DESC_S_DATA + DESC_TYPE_DATA + 0x0b
RPL0 equ 00b
RPL1 equ 01b
RPL2 equ 10b
RPL3 equ 11b
TI_GDT equ 000b
TI_LDT equ 100b
KERNEL_START_SECTOR equ 0x9
KERNEL_BIN_BASE_ADDR equ 0x70000
PT_NULL equ 0
KERNEL_ENTRY_POINT equ 0xc0001500
;-----about memory page-----
PAGE_DIR_TABLE_POS equ 0x100000
PG_P equ 1b
PG_RW_R equ 00b
PG_RW_W equ 10b
RG_US_S equ 000b
PG_US_U equ 100b