操作系统真象-开启分页&加载内核

操作系统真象-开启分页&加载内核

Tags: learning experience, 操作系统

开启分页:

ps. 下面的内容都是基于二级页表的, 毕竟大伙都是用的二级页表, 而且动态规划的二级页表空间利用率高于一级页表哇

为什么要开启分页:

最大化利用物理内存, 避免物理内存啥的, 以及加速内外存交换?(毕竟书上方案B在面对需要大空间进程时, 对内外存交换速度的打击是毁灭性的)

如何开启分页机制:

ps. 这三个步骤是有顺序的

  1. 准备好页目录及页表
  2. 将页表目录的地址写入cr3
  3. 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

おすすめ

転載: blog.csdn.net/eeeeeight/article/details/120591253