Pintos Kernel 启动详解(零):系列概述
Pintos 操作系统内核启动代码的详细分析文档系列概述,包括文档列表、阅读顺序和先决知识。
Pintos Kernel 启动详解(零):系列概述
概述
这是一个针对 Pintos 操作系统内核启动代码的详细分析文档系列,力求用简单易懂的方式解释每一个概念和代码片段。
本系列是 Loader 系列的延续——Loader 将内核加载到内存后,跳转到内核的入口点 start(位于 start.S),接着内核开始自己的初始化过程,最终调用 C 语言编写的 pintos_init() 函数。
从 Loader 到 Kernel 的过渡
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
┌─────────────────────────────────────────────────────────────────┐
│ 引导过程流程 │
├─────────────────────────────────────────────────────────────────┤
│ │
│ BIOS │
│ │ │
│ ▼ │
│ loader.S (0x7C00) ← 实模式 (16-bit) │
│ │ 加载内核到 0x20000 │
│ │ 跳转到 start │
│ ▼ │
│ start.S (0x20000) ← 实模式 → 保护模式 (32-bit) │
│ │ 获取内存大小 │
│ │ 启用 A20 地址线 │
│ │ 设置临时页表 │
│ │ 切换到保护模式 │
│ │ 启用分页 │
│ │ 调用 pintos_init() │
│ ▼ │
│ init.c:pintos_init() ← C 语言内核主函数 │
│ │ 初始化 BSS 段 │
│ │ 解析命令行参数 │
│ │ 初始化线程系统 │
│ │ 初始化内存系统 │
│ │ 初始化中断系统 │
│ │ 启动调度器 │
│ │ 执行用户任务 │
│ ▼ │
│ 正常运行... │
│ │
└─────────────────────────────────────────────────────────────────┘
文档列表
本系列包含以下文档:
Part 1: start.S 汇编启动代码
| 序号 | 主题 | 简介 |
|---|---|---|
| 1 | start.S 概述与初始段寄存器设置 | 内核入口点和基本环境设置 |
| 2 | 内存大小检测 | 使用 BIOS 中断获取物理内存大小 |
| 3 | A20 地址线启用 | 解决历史遗留的 1MB 内存限制 |
| 4 | 临时页表创建 | 创建页目录和页表实现虚拟内存映射 |
| 5 | 保护模式切换 | 从 16 位实模式切换到 32 位保护模式 |
| 6 | GDT 全局描述符表 | GDT 的结构和作用详解 |
Part 2: init.c 内核初始化
| 序号 | 主题 | 简介 |
|---|---|---|
| 7 | pintos_init 主函数 | 内核 C 语言入口和初始化流程 |
| 8 | BSS 段初始化 | 清零未初始化的全局变量区域 |
| 9 | 命令行解析 | 读取和解析内核启动参数 |
| 10 | 内存系统初始化 | palloc 和 malloc 内存分配器初始化 |
| 11 | 永久页表建立 | 替换临时页表建立完整的虚拟内存映射 |
| 12 | 线程系统初始化 | 初始化线程调度器和主线程 |
| 13 | 中断系统初始化 | IDT、PIC 和中断处理框架 |
| 14 | 设备初始化 | 定时器、键盘、串口等设备初始化 |
| 15 | 启动完成与任务执行 | 启动调度器并执行用户指定的任务 |
阅读顺序
建议按照文档编号顺序阅读,因为:
- 文档按执行顺序组织:代码是按顺序执行的,文档也按这个顺序排列
- 概念层层递进:后面的文档会引用前面介绍的概念
- 从汇编到 C:先理解底层汇编代码,再学习 C 语言初始化
如果你已经阅读过 Loader 系列,可以直接从本系列开始。如果没有,建议先阅读 Loader 系列了解引导过程。
每个文档的结构
每个文档都包含以下部分:
- 概述:本部分代码的作用和重要性
- 原始代码:完整的源代码
- 前置知识:理解代码所需的背景知识
- 逐行详解:每一行代码的详细解释
- 图解/流程图:可视化的执行过程和数据结构
- 常见问题:FAQ 解答常见疑惑
- 练习思考:帮助加深理解的思考题
先决知识
阅读这些文档前,最好了解:
必须了解
- Loader 系列文档:了解引导过程和实模式基础
- C 语言基础:函数、指针、结构体
- 十六进制:内存地址的表示
基本计算机概念
- 内存和 CPU 的关系
- 程序如何执行
- 堆栈的概念
可选但有帮助
- x86 汇编语言基础
- 虚拟内存的基本概念
- 操作系统原理
核心概念速览
实模式 vs 保护模式
| 特性 | 实模式 | 保护模式 |
|---|---|---|
| 位宽 | 16 位 | 32 位 |
| 最大内存 | 1 MB | 4 GB |
| 内存保护 | 无 | 有 |
| 多任务支持 | 无 | 有 |
| 地址模型 | 段:偏移 | 线性地址 + 分页 |
关键内存地址
| 地址 | 内容 |
|---|---|
| 0x7C00 | Loader 加载位置 |
| 0x20000 (128KB) | 内核加载位置 |
| 0xC0000000 (3GB) | 内核虚拟地址基址 |
| 0xF000 (60KB) | 初始栈顶/临时页目录 |
关键寄存器
| 寄存器 | 用途 |
|---|---|
| CR0 | 控制寄存器,包含 PE(保护模式)、PG(分页)位 |
| CR3 | 页目录基址寄存器 |
| GDTR | 全局描述符表寄存器 |
| IDTR | 中断描述符表寄存器 |
术语表
| 术语 | 解释 |
|---|---|
| GDT | 全局描述符表,定义内存段 |
| IDT | 中断描述符表,定义中断处理程序 |
| PDE | 页目录项 |
| PTE | 页表项 |
| A20 | 地址线 20,启用后可访问超过 1MB 的内存 |
| BSS | 未初始化数据段,程序启动时需要清零 |
| PIC | 可编程中断控制器 |
| TSS | 任务状态段 |
相关文件
汇编文件
src/threads/start.S- 内核汇编入口点src/threads/intr-stubs.S- 中断桩代码
C 源文件
src/threads/init.c- 内核主初始化函数src/threads/thread.c- 线程管理src/threads/interrupt.c- 中断处理src/threads/palloc.c- 页面分配器src/threads/malloc.c- 内核内存分配器
头文件
src/threads/loader.h- 引导相关常量src/threads/thread.h- 线程结构定义src/threads/vaddr.h- 虚拟地址操作宏src/threads/pte.h- 页表项操作宏
学习建议
- 边读边画:画出内存布局和执行流程图
- 对照代码:打开源文件对照阅读
- 动手调试:使用 GDB 单步执行,观察寄存器和内存变化
- 做笔记:记录自己的理解和疑问
- 多问为什么:理解每行代码存在的原因
调试技巧
使用 GDB 调试内核启动
1
2
3
4
5
6
7
8
# 在 Pintos 目录下
pintos --gdb -- run <test>
# 在另一个终端
pintos-gdb kernel.o
(gdb) target remote localhost:1234
(gdb) break start
(gdb) continue
有用的 GDB 命令
# 查看寄存器
info registers
# 查看内存
x/10x 0x20000
# 查看页表
monitor info pg
# 单步执行
stepi
下一步
准备好了吗?让我们开始第一篇文档:start.S 概述与初始段寄存器设置!
本文由作者按照 CC BY 4.0 进行授权