oslab_在开始之前

实验内容

1-3:了解 Linux 的启动过程、用户空间是如何与内核空间进行交互的。
4-5:了解进程的执行原理、状态的转换过程,及进程之间是如何转换的。
6:通过信号量机制了解内核是如何实现同步的。
7、9:了解内核的内存和文件系统的实现原理。
8:了解内核如何实现设备管理,明白驱动是如何实现的。
10:图形界面是开课老师提到的扩展实验。

背景

在开始实验之前当然必须要先搭建好实验环境了,不过操作系统作为最复杂的软件,它的安装也显得那么不容易。

x86 的初始化(三种处理器模式的转换)

其实称为 i386 初始化更恰当,因为这里的讨论涉及到保护模式,而 80386 是第一块支持保护模式的 32 位芯片,也是 i386 的第一块芯片。

Reset

  1. 复位信号(RESET)到来后,CPU 会执行复位操作,包括起电自检(power-up self test),自检完毕后会将结果设置到 EAX 寄存器中(若复位后不进行自检操作,EAX 的值是未定义的),0 表示无异常,非 0 值代表对应的 80386 单元出了故障。
  2. 其他操作主要是初始化一些寄存器,见 i386 手册 10.1。

实模式下的初始化


  1. 因为很多指令使用到了栈,所以先将栈寄存器指向内存(RAM)的某个位置。
  2. 中断向量表
    虽然在 RESET 时已经设置了忽略中断(如何设置?),但这时遇到异常(软中断 0-15)或不可屏蔽中断(2)仍然会访问中断向量表,所以要先设置中断向量表。
  3. 第一条指令
    RESET 时已经设置了 CS:IP,此时将会执行位于这个位置(FFFFFFF0H)的第一条指令,是一条控制转移指令(跳转)。

实模式->保护模式

实模式下的线性地址到了保护模式下仍然是合法的,只是保护模式下的地址空间更大。

  1. 设置 cr0 中 MSW 的 PE 位
    cr0 是 80386 中的一个控制寄存器(其他还有 cr1、cr2、cr3),MSW 是 80286 中的控制寄存器,其原本的功能由 cr0 的低 16 位实现。
    MSW 的位 0 是启用保护模式(Protection Enable)标志。当设置该位时开启保护模式;清零时进入实模式。
  2. 清空处理器的预读取指令队列
    执行一条跳转指令,跳过预读取的指令。
    因为 80386 在执行指令前会先对指令和地址进行解码,这会导致在实模式下获取的指令无法在保护模式下执行

保护模式下的初始化

注意此时不能使用保护模式的一些特性(有哪些?),因为还未初始化。

  1. 中断描述符表
    重新设置,因为保护模式下的中断向量表和实模式下的格式不同(不同在哪?)。

  2. 如果已经在实模式下设置好了栈寄存器,那现在就不需要再改了,因为转换到了保护模式,栈指针仍会指向相同的线性地址。
  3. 全局描述符表
    设置 GDT 和 LDT 用于保存段描述符、任务描述符等。
  4. 页表
    设置 CR0 的 PG 位激活分页功能,具体的页表功能和 CR3 有关(CR3 是 80386 引入的控制寄存器之一,但具体的功能我不明白)。
  5. 第一个任务
    包括任务寄存器和 TSS 的初始化

初始化例子

。。。

编译 linux 0.11

在自己的机器上因为 gcc 的版本问题,很多库文件不对,最后 lgcc 的版本不对也没有解决,因为需要重新装一个 32 位的 ubuntu 或着装兼容库 ia32-libs(各种原因安不上)。
装了一个 32 位运行库就好了:

1
sudo aptitude install gcc-5-multilib

接下来在运行 bochs 的时候又遇到了动态链接库找不到的问题,折腾了半天,发现照着网上的心得装的一堆库都没用,因为链接库肯定是装好了,只是找不到,比如要到/usr/lib/i386-linux-gnu/下找 libX11.so.6,但该文件本身在/usr/lib/x86_64-linux-gnu/,就得用ln -s source target命令建立软链接。
还有一个问题,链接库的版本,我最后从安装 Stream 的文件夹下找到了 i386 的版本,直接复制到了/usr/lib/i386-linux-gnu/,狗急跳墙了。
最后运行 bochs 停在Loading System界面不动了,估计和编译的 gcc 版本有关,赵炯博士是使用 gcc-4.3 编译的,但是我是使用 gcc-5 编译的,实在不清楚哪里出了问题。
最后,没办法还是老实在实验楼环境里干吧,代码直接提交到 github 上好了。

  1. 编译内核
    1
    2
    3
    make all
    make -j 2 # 多处理器系统上实现并行编译
    make clean # 删除上一次编译生成的所有中间文件和目标文件,确保在全新的状态下编译整个工程
    在 linux 目录下 make all,在 linux-0.11 目录下会生产一个名为 Image 的文件,它就是编译之后的目标文件。该文件内已经包含引导和所有内核的二进制代码。
    oslab 采用 bochs 模拟器加载这个 Image 文件,模拟执行 Linux 0.11,这样省却了重新启动计算机的麻烦(用 vmware 应该也没问题)。
  2. 运行
    1
    ./run
    run 是运行 bochs 的脚本命令。运行后 bochs 会自动在它的虚拟软驱 A 和虚拟硬盘上各挂载一个镜像文件,软驱上挂载是 linux-0.11/Image,硬盘上挂载的是 hdc-0.11.img。
    因为 bochs 配置文件中的设置是从软驱 A 启动,所以 Linux 0.11 会被自动加载。
    而 Linux 0.11 会驱动硬盘,并 mount 硬盘上的文件系统,也就是将 hdc-0.11.img 内镜像的文件系统挂载到 0.11 系统内的根目录——“/”。在 0.11 下访问文件系统,访问的就是 hdc-0.11.img 文件内虚拟的文件系统。
  3. 调试
    汇编级调试
    1
    ./dbg-asm
    C 语言级调试
    1
    2
    ./dbg-c
    ./rungdb # 重开终端运行
  4. 加载文件系统
    oslab 下的 hdc-0.11.img 是 0.11 内核启动后的根文件系统镜像文件,相当于在 bochs 虚拟机里装载的硬盘。
    Ubuntu 上可以直接装载
    1
    2
    sudo ./mount-hdc
    sudo unmount hdc # 卸载
    hdc-0.11.img 文件格式 是 Minix 文件系统的镜像。Linux 所有版本都支持这种格式的文件系统,所以可以直接在宿主 Linux 上通过 mount 命令访问此文件内的文件,达到宿主系统和 bochs 内运行的 Linux 0.11 之间交换文件的效果。
    Windows 下目前没有(或者是还没发现)直接访问 Minix 文件系统的办法,所以要借助于 fdb.img,这是一个 1.44M 软盘的镜像文件,内部是 FAT12 文件系统。将它挂载到 bochs 的软驱 B,就可以在 0.11 中访问它。而通过 filedisk 或者 WinImage,可以在 Windows 下访问它内部的文件。
    hdc-0.11.img 内包含有:
  • Bash shell
  • 一些基本的 Linux 命令、工具,比如 cp、rm、mv、tar。
  • vi 编辑器
  • gcc 1.4 编译器,可用来编译标准 C 程序
  • as86 和 ld86
  • Linux 0.11 的源代码,可在 0.11 下编译,然后覆盖现有的二进制内核
  1. 注意点
  • 不要在 0.11 内核运行的时候 mount 镜像文件,否则可能会损坏文件系统。同理,也不要在已经 mount 的时候运行 0.11 内核。
  • 在关闭 Bochs 之前,需要先在 0.11 的命令行运行“ sync ”,确保所有缓存数据都存盘后,再关闭 Bochs。
  • Linux0.11 版本集成的 gcc 版本较老,不支持很多新的 C 语言语法,比如’//‘注释、for 语句内声明变量等。

QA

参考

  1. i386 手册
  2. 编译过程一些错误的解决办法 http://blog.csdn.net/DeltaForce_eagle/article/details/50109303
  3. http://www.linuxidc.com/Linux/2012-04/58644.htm
  4. Linux 中 x86 的内联汇编
  5. https://www.ibm.com/developerworks/cn/linux/sdk/assemble/inline/index.html
  6. http://www.cnblogs.com/cassvin/archive/2011/07/24/Linux_Qtopia_firstBlogOncnblogs.html
  7. http://www.cnblogs.com/tradoff/p/5693710.html
  8. https://stackoverflow.com/questions/4492799/undefined-reference-to-stack-chk-fail
  9. Linux 0.11 在 Ubuntu-11 和 gcc-4.6.1 下编译调试至正常运行的过程详解 http://www.linuxidc.com/Linux/2012-04/58644.htm