1.操作系统的启动

  • 1、从BIOS到系统kernel
  • 1.1.开机取得BIOS
  • 1.2.BIOS干什么
  • 1.3. bootloader的512个字节做什么
  • 1.4.代码表现


1、从BIOS到系统kernel

1.1.开机取得BIOS

BIOS 是存在一段掉电不丢失的rom中,或者说是内存中。计算机约定,上电后再所有硬件稳定后,到某个地方去取得BIOS的程序进行执行。(这里有点模糊,按照我学硬件想法BIOS应该是存在EEPROM(特点的掉电不丢失rom)中而不是内存中,因为内存关闭了就是会消失的,但是说成掉电不丢失内存也是可以的)。具体的约束图如下

bios p0 bios p0 p1_linux

开机启动,计算器处于实模式,它要取得的代码指针地址 PC = 16*CS+IP ,我们约定CS:IP = 0xf000:fff0 。就是1M的那个地方,开始读取BIOS;

1.2.BIOS干什么

当然BIOS会干很多事,要查看详细开发文档。但是总结我们需要关注的是

第一:它会把硬件检测好,将显示器,输入输出设备带入到一种正常的状态。

第二:它会读第一个扇区(一般是第一个)的512个字节(bootloader),放到内存中(约定0x7c00)。

bios p0 bios p0 p1_linux_02


这个两点大概是BIOS主要做的。最后BIOS通过JMP(每个cpu不同)指令,跳到0x7c00,以后就没有BIOS什么事情了。

1.3. bootloader的512个字节做什么

  • 第一:使能段模式也就是保护模式,
  • 第二:继续读取512个字节,也就是跟在自己屁股后面的kernel。它是个elf格式的文件,有文件头信息,可以知道代码偏移。可以知道数据段等。

把kernel放到内存中。最后还做JMP跳转到kernel放的那个地址。bootloader就没什么事了。所有的事都交给kernel。

kernel起来后需要做很多事情,需要,中断异常,系统调用,内存管理,进程管理等等很多事情。

1.4.代码表现

上面讲到从 BIOS --》bootloader --》kernel
从中说切换时都是JMP 跳转指令。这个切换只是说用JMP大概的概括,确实任何程序间的跳转都是可以用JMP来概括。我们来看下系统代码里的表现,
BIOS 到BootLoader 就不说了 最终会调用到 bootloader.S 然后切换到 BootLoader.c

在 bootloader汇编里面 直接调用 下面的指令

call bootmain

该函数在 BootLoader.c

void
bootmain(void) {

    // call the entry point from the ELF header
    // note: does not return                init()
    ((void (*)(void))(ELFHDR->e_entry & 0xFFFFFF))();

bad:
    outw(0x8A00, 0x8A00);
    outw(0x8A00, 0x8E00);

    /* do nothing */
    while (1);
}

看注释 也是直接调用( ELFHDR->e_entry)而这个 e_entry 就是 kern_init
然后就是 while (1);死等这边。
其实注释也说了 init也别返回 如下

void
kern_init(void){
    extern char edata[], end[];
    memset(edata, 0, end - edata);

    cons_init();                // init the console

    const char *message = "(THU.CST) os is loading ...";
    cprintf("%s\n\n", message);

    print_kerninfo();

    grade_backtrace();

    pmm_init();                 // init physical memory management

    pic_init();                 // init interrupt controller
    idt_init();                 // init interrupt descriptor table

    clock_init();               // init clock interrupt
    intr_enable();              // enable 
    lab1_switch_test();

    /* do nothing */
    while (1);
}

它就是内核了代码了,while(1)
就是前面的代码应该是一直有线程吧,中断响应啊等等。
这样一个操作系统就开始起来了。