操作系统实现

操作系统实现之BootLoader引导启动程序

1. 基本原理

  • 计算机启动流程

    1
    2
    3
    4
    5
    graph LR
    A[加电] --> B[BIOS自检]
    B --BIOS加载引导扇区--> C[Boot引导程序]
    C --Boot加载Loader--> D[Loader引导加载程序]
    D --Loader加载内核--> E[操作系统]
  • 为什么引导程序要分为bootloader,为什么BIOS不直接加载Loader?

    • 一个扇区的空间太小,所以需要两级接力进行初始化和引导。
  • 引导扇区是软盘的第0磁头0磁道1扇区,且以数值0x550xaa两字节结尾。BIOS程序即根据这两个特征识别启动设备。

  • BIOS识别到引导扇区后,会将此扇区数据加载到内存,然后跳转至0x7c00地址处执行。因此Boot程序必须以0x7c00为起始地址。

  • FAT12文件系统结构

2. 实现过程

简单的boot引导程序

  • 程序如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    org 0x7c00					;程序从0x7c00处开始
    BaseofStack equ 0x7c00 ;栈地址
    Label_Start:
    mov ax,cs
    mov ds,ax
    mov es,ax
    mov ss,ax
    mov sp,BaseofStack

    ;显示引导程序日志信息
    ;清屏
    mov ax,0600h
    mov bx,0700h
    mov cx,0
    mov dx,0184fh
    int 10h

    ;设置光标
    mov ax,0200h
    mov bx,0000h
    mov dx,0000h
    int 10h

    ;在屏幕上显示Start Boot
    mov ax,1301h
    mov bx,000fh
    mov dx,0000h
    mov cx,10
    push ax
    mov ax,ds
    mov es,ax
    pop ax
    mov bp,StartBootMessage
    int 10h

    ;操作磁盘驱动器

    ;软盘驱动器初始化,将软盘磁头移动至默认位置
    xor ah,ah
    xor dl,dl
    int 13h

    ;死循环
    jmp $

    StartBootMessage: db "Start Boot"

    ;用0填充剩余位置
    times 510 - ($-$$) db 0

    ;以0x55 0xaa结尾,表示扇区为引导扇区
    dw 0xaa55
  • 使用nasm编译boot.asm程序,得到二进制文件boot.bin

    1
    nasm boot.asm -o boot.bin
  • 使用bximage制作软盘boot.img

  • 使用dd命令将二进制文件boot.bin写入到软盘镜像的第一个扇区

    1
    dd if=boot.bin of=boot.img bs=512 count=1 conv=notrunc

    image-20220105213141735

  • 配置好bochs之后,启动bochs,出现如下界面则boot引导程序正常

    image-20220105213618969

加载Loader到内存

  • 编译boot.asmloader.asm

    1
    2
    nasm boot.asm -o boot.bin
    nasm loader.asm -o loader.bin
  • boot.bin写入镜像boot.img

    1
    dd if=boot.bin of=boot.img bs=512 count=1 conv=notrunc
  • 挂载boot.img镜像

    1
    2
    sudo mount boot.img ../boot/ -t vfat -o loop
    #-t指定文件系统,-o loop表示把此文件描述为磁盘分区
  • loader.bin复制到文件系统中

    1
    2
    sudo cp loader.bin ../boot/
    sync #磁盘同步命令,防止写入信息还在缓存中,没有真正写入磁盘
  • 卸载设备

    1
    sudo umount	../boot/
  • 运行结果

    image-20220106214735709

loader程序

基本原理

  • loader引导加载程序的功能——检测硬件信息、处理器模式切换、向内核传递数据
  • 检测硬件信息
    • 因为BIOS上电自检出的信息为实模式下的信息,而内核运行于非实模式下。需要在进入内核之前,将这些信息检测出来作为参数提供给内核程序使用。
    • 其中最重要的信息是物理地址空间信息,还有VBE功能获取的显示信息。
  • 处理器模式切换
    • Loader引导加载程序经历三个模式:实模式16位——保护模式32位——长模式IA-32e
    • 在各个阶段,程序要手动创建运行各膜式的临时数据,并实现切换。
  • 向内核传递数据
    • 传递两类信息:控制信息、硬件数据信息
    • 控制信息:控制内核启动流程或限制内核的某些功能,是纯软件控制逻辑。
    • 硬件数据信息:检测出的硬件信息,如内存信息,VBE信息等

内核

内核执行头程序

  • 一小段汇编代码,控制权从Loader程序移交到内核之后即先执行内核执行头程序。
  • 作用:为操作系统创建段结构和页表结构、设置某些结构的默认处理函数、配置关键寄存器。

内核主程序

  • 相当于应用程序的主函数,不会返回,因为内核执行头程序没有提供返回地址
  • 作用:调用各个系统模块的初始化函数,将这些模块初始化之后创建第一个进行init,然后将控制权移交给init进程

屏幕显示

编译链接时出现如下错误提示

image-20220202164040514

错误undefined reference to __stack_chk_fail是因为使用gcc编译源码到目标文件时,默认会调用__stack_chk_fail进行栈相关检查,但是使用ld进行链接时并不会自动链接到__stack_chk_fail所在的库文件,所以链接时一定会报错。此时在编译时添加-fno-stack-protector,强制gcc不进行栈检查即可。

错误undefined reference to strlen是由于strlen定义为内联函数,而内联函数嵌入到调用者代码中的操作是一种优化操作,只有进行优化编译时才会进行。如果不是优化编译,则内联函数的代码不会被嵌入到调用者的代码中,而是作为普通函数来处理。此时在编译时添加-O选项,打开优化编译即可解决问题。

image-20220202170113691

参考