dream在一个月之内完成一个简易的linux x86系统,博客会慢慢更新,先说下用的环境配置:vmvare+ubunbtu18+gcc-i686-linux-gnu+qemu+gdb+vscode。
首先从一些前置知识开始,因为本来就学过数电、模电、微机原理这些,所以对这一块就简单回顾一下,在参考中列出了后续主要参考的项目源码和书籍,希望能早点看完这几本书。
前置知识
计算机的启动流程
- 上电启动后,CPU处于16位运行的实模式,分页机制禁止,此时只有1MB内存可用,没有特权级
- CPU跳转到BIOS的入口 (地址为0XFFFF0,也就是说,BIOS程序的第一条指令就设计在这个位置!) 开始执行
- BIOS进行硬件自检(POST),主板、硬盘、显卡、内存等自检,并保存一些配置数据到特定内存地址处(如硬盘的数量)
- 根据配置的启动顺序(光驱、U盘、硬盘等),加载引导代码运行。例,从硬盘启动时,将硬盘的第1扇区(主引导纪录)读取到0x7c00处并且跳转到该地址处运行
- 引导代码对操作系统的运行初始环境进行配置,并加载操作系统到内存中
- 跳转到操作系统运行。
实模式
什么是实模式:
Real Mode is a simplistic 16-bit mode that is present on all x86 processors.
Real Mode was the first x86 mode design and was used by many early operating systems before the birth of Protected Mode.
For compatibility purposes, all x86 processors begin execution in Real Mode.
——出自:实模式的解释
实模式的特性是一个20位的存储器地址空间(即1MB的可寻址空间),可以直接软件访问BIOS和周边硬件,没有硬件支持的分页机制和实时多任务概念。
——出自《Linux 内核设计的艺术》
使用的扇区形式
我们需要实现的是自己的引导代码和操作系统,为了简便,和参考的项目源码中一致,没有考虑磁盘、磁头这些,扇区用的是下面这种形式,每个扇区大小是512字节。而Linux0.11中是将软驱0号磁头的0磁道1扇区加载到0x07c00处。
BIOS会把磁盘的第0个扇区加载到0x7c00处运行,所以考虑直接把程序放在第0个扇区,就会被自动加载到内存中运行。
同时在检查引导标志的时候会看第0个扇区的最后两个字节(1FE和1FF)是不是0x55和0xaa,有效再进行加载。
整体的工作流程
- 在VSCode中编写C源文件、汇编文件、链接脚本;
- CMake根据配置脚本,调用GCC对源文件进行编译和汇编,调用LD进行链接生成可执行的ELF文件;
- CMake还会调用OBJCOPY将ELF文件进行缩小,或者转换成BIN文件;
- 调试前,Visual Studio Code调用一些小工具将ELF、BIN文件写入磁盘映像;
- 调试时,QEMU加载磁盘映像文件,然后等待GDB连接,这里主要是在
tasks.json
和launch.json
中进行编写; - GDB连接上QEMU,开始正式的调试过程。
引导程序的作用如下:这里采用的是二级加载,boot只完成loader的加载工作,再由loader完成具体的初始化工作和内核加载,也就是Boot->loader->Kernel
。
x86包含很多寄存器,8位、16位、32位寄存器这些都是以前微机原理学过的,在CPU启动进入16位实模式后,EAX/EBX/ECX/EDX 仅能使用低16位,即AX/BX/CX/DX
。
为了方便,这里用的不是段地址+偏移量的地址访问方式,而是直接将段地址都置为0,那么只需要一个偏移地址(比如 0:0x7c00 )
x86的存储映射
因为实模式只能访问1MB以内的内存,具体的内存映射如下:其中引导程序在内存中加载并运行,物理地址为0x7C00到 0x7DFF: