内容概述

主要介绍了一些和 Lab1 相关的内容。(对 C++ 一窍不通。。羞耻,只是大致了解一下,有时间和精力要对照试验内容认真补一下。。。

  • 系统启动过程
    • BIOS
    • BootLoader
      • 段机制
      • 操作系统的加载
  • C 语言的一些相关的只是
    • 函数调用过得实现(略)
    • GCC 内联汇编(略)
  • x86 架构下的中断处理过程

    系统启动过程

BIOS

BIOS 的工作过程已经在第三讲中详细说过了,在此不再重复。唯一值得注意的是,虽然实模式下的寻址方式是 Base (16 位寄存器 CS)*16+Offset (16位寄存器IP) = 线性地址( 20 位),但这并不是段机制。

bootloader

BIOS 将控制权转交给bootloader。它的工作内容主要包括:

  • 使能保护模式 ( protection mode ) 和段机制 (segment level protection),切换回 32 位 4G 的寻址空间,对段机制进行初始化
  • 从硬盘上读取 ELF 格式的 ucore kernel (位于 MBR 后面的扇区) 并放到内存中固定的位置。
  • 跳转到 ucore OS 的入口点 (entry point),将控制权转交给 ucore OS

使能保护模式

将系统寄存器 CR0 的第 0 个 bit 置为 1,说明进入保护模式。当然,在此之前要开 A20,并准备好 GDT 表,将基址加载到 GDT 基址寄存器中。

段机制

保护模式下必须开启段机制。

总的来说,段机制其实是一种映射关系。一个段指向的是线性地址空间中一段连续的内存,有基址和 limit 。短与段之间是可以重叠的。

设置段机制的方法是,建立一个数组来存储段描述符表,称为全局描述符表 GDT (也称为段表,在 ucore 中是由 bootloader 建立的,因为开启保护模式之前就需要设置好 GDT ),其中包括描述符表的位置、大小等信息。这样 CPU 就可以找到段表了 (用 GDTR 寄存器保存表信息)。除了设置 GDT 之外,还要为 CS、DS 等段寄存器设置好对应的 index,使他们能够指向全局描述符表 GDT 对应的项,这可以在切换到保护模式之后进行。

硬件提供了一些段寄存器。这些段寄存器指向段描述符,比较重要的几个段寄存器包括:

  • CS : 代码段寄存器
  • DS : 数据段寄存器
  • SS : 堆栈段寄存器

段寄存器的结构是这样的:

  • 高 13 位:GDT index
  • 1 位:TI,一般设置为0,因为没有用到 LDT (本地描述符表)
  • 2位:RP,表明段优先级,有 4 个特权级,一般应用程序放在 3 , 操作系统放在 0

每个段寄存器指向一个 GDT 或者 LDT 中的段描述符。段描述符描述了一个段的起始地址和它的大小。(一个段描述符的大小是 8 字节,具体内容比较复杂。。。忽略)

uCore 中采用过的应该是 Intel 手册中提到的扁平保护模型。

在设置完所需的表和寄存器之后,段机制就可以完成从逻辑地址到线性地址(在页机制没有开启的时候,线性地址 = 物理地址)的翻译了。

  • 通过逻辑地址中的段选择子查找段描述符表项
  • 从表项中读出段基址和段的大小
  • 检查逻辑地址中的 offset 是否合法
  • 安全性检查
  • 段基址 (Base Address) + 段内偏移量 (offset) = 线性地址 (linear address)

代码实现过程

(略。。。)

x86 架构下的中断处理过程

此处的 “中断” 包括两类:

  • 中断 (Interrupts)
    • 外部中断 (External (hardware generated) interrupts):串口、硬盘、网卡、时钟
    • 软件产生的中断 (Software generated interrupts):INT n指令,通常用于系统调用
  • 异常 (Exceptions)
    • 程序错误
    • 软件产生的异常 (Software generated excetpion):INTO, INT 3 和 BOUND
    • 机器检查出的异常

通过中断号确定中断服务例程 ( ISR )

(略。。。)