linux2.6 和 unix-v6 源码实验
《计算机操作系统》、《Operating Systems: Three Easy Pieces》、《UNIX环境高级编程》
MIT6.828: Operating System Engineering
https://pdos.csail.mit.edu/6.828/2019/schedule.html
基于 Linux 内核(如 2.6.11.12 或 2.6.39)做学习或者实验,想从内核启动的源代码开始分析整个启动过程,我可以帮你从入口函数逐步展开。
🧠 一、内核启动流程总览(x86 架构)
对于 QEMU 启动的 x86 架构内核(bzImage),内核的启动过程主要分以下阶段:
Bootloader → Real Mode Entry → Protected Mode Setup → Paging → start_kernel → rest_init → kernel_thread(init)
🧩 二、代码入口文件和路径说明
阶段 | 文件位置 | 关键函数 |
---|---|---|
实模式入口 | arch/x86/boot/header.S | _start (由 bootloader 跳转) |
保护模式设置 | arch/x86/boot/compressed/head_32.S | startup_32 |
解压完成跳转 | arch/x86/kernel/head.S | startup_32 (再次) |
内核 C 语言入口 | init/main.c | start_kernel 🟢 |
最终用户态进程 | init/main.c | init() → /init 程序 |
🚀 三、最核心起点:start_kernel
路径:init/main.c
asmlinkage void __init start_kernel(void)
这个函数是内核初始化最重要的部分,从这个函数开始,Linux 内核进入了 C 语言世界。
🧩 四、start_kernel()
内部主要步骤(精简版)
- 设置中断、禁用中断
- early_printk 初始化(早期调试信息输出)
- 设置内存页管理
- 初始化调度器、时间子系统
- 初始化中断控制器
- 初始化文件系统、字符设备、网络协议栈
- 调用
rest_init()
→ 进入内核线程
🔁 五、从 start_kernel
到用户空间
路径(重要流程)如下:
start_kernel()└── rest_init()├── kernel_thread(kernel_init)│ ├── kernel_init_freeable()│ │ ├── prepare_namespace()│ │ └── init_post() → 启动 /init└── cpu_idle() // 空转CPU
🧪 六、你可以重点阅读的源码文件(按顺序):
文件路径 | 用途说明 |
---|---|
arch/x86/boot/header.S | 实模式入口,设置堆栈、跳转到保护模式 |
arch/x86/kernel/head.S | 保护模式下的入口,设置分页等 |
init/main.c | start_kernel() 是所有 C 代码的起点 |
kernel/sched.c | 调度器初始化 |
init/do_mounts.c | 挂载根文件系统 |
fs/exec.c / init/initramfs.c | 加载 init 程序 |
📌 建议阅读顺序(推荐)
arch/x86/kernel/head.S
→ 看startup_32
init/main.c
→ 看start_kernel()
- 跟踪调用
rest_init()
→kernel_thread()
→kernel_init()
→init_post()
- 查看
init/do_mounts.c
→ 如何挂载rootfs
- 查看
fs/exec.c
→ 如何加载/init
用户态程序(或 busybox)
🧰 想动手调试?
你可以使用 QEMU 启动内核,加上 GDB 调试:
qemu-system-i386 \-kernel bzImage \-initrd initramfs.cpio.gz \-append "console=ttyS0" \-nographic \-s -S
然后用 GDB 连接:
gdb vmlinux
(gdb) target remote :1234
这样可以在 start_kernel
或 startup_32
处下断点单步分析。
下面是一个完整的 Dockerfile,用于在 Docker 容器中编译 Linux 2.6.39 内核并支持使用 QEMU
启动调试:
✅ Dockerfile(x86_64 架构,适用于 QEMU 调试)
FROM ubuntu:20.04ENV DEBIAN_FRONTEND=noninteractive# 安装依赖
RUN apt-get update && \apt-get install -y \build-essential \gcc-4.6 g++-4.6 \libncurses5-dev \bc \kmod \cpio \flex \bison \libelf-dev \qemu qemu-system-x86 \busybox \initramfs-tools \wget \git \vim \gdb \python3 \ca-certificates && \rm -rf /var/lib/apt/lists/*# 设置默认 gcc 为 4.6
RUN update-alternatives --install /usr/bin/gcc gcc /usr/bin/gcc-4.6 60 && \update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-4.6 60WORKDIR /root# 可选:自动下载 Linux 内核源码(也可以手动挂载)
# RUN wget https://cdn.kernel.org/pub/linux/kernel/v2.6/linux-2.6.39.tar.xz && \
# tar -xf linux-2.6.39.tar.xz && \
# mv linux-2.6.39 linux# 拷贝本地源码挂载路径(外部 -v 映射)
VOLUME ["/root/linux"]CMD ["/bin/bash"]
✅ 构建镜像
docker build -t linux2639-env .
✅ 运行容器(挂载你的源码目录)
假设你已经解压了内核源码到当前目录:
docker run -it --rm -v $PWD/linux-2.6.39:/root/linux linux2639-env
✅ 容器内操作步骤
cd linux# 使用默认配置
make defconfig# 编译内核(vmlinux 和 bzImage)
make -j$(nproc)
编译静态链接的 busybox
bash
git clone -b 1_21_stable https://github.com/mirror/busybox.git
cd busybox
make defconfig
# 设置为静态链接
sed -i 's/^# CONFIG_STATIC is not set/CONFIG_STATIC=y/' .config
make -j$(nproc)
✅ 构建最小 rootfs
mkdir -p initramfs/{bin,sbin,etc,proc,sys,dev}
cp busybox initramfs/bin/
chmod +x initramfs/bin/busybox
cd initramfs/bin
for cmd in $(./busybox --list); do ln -s busybox $cmd; done# 创建 /init 脚本
cat > initramfs/init << 'EOF'
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
echo "Welcome to Linux 2.6.39!"
exec /bin/sh
EOF
chmod +x initramfs/init# 创建 /dev/console
sudo mknod -m 600 initramfs/dev/console c 5 1# 打包成 initramfs
cd initramfs
find . | cpio -o --format=newc | gzip > ../initramfs.cpio.gz
✅ 启动内核(QEMU)
qemu-system-x86_64 \-kernel /root/linux/arch/x86/boot/bzImage \-initrd /root/initramfs.cpio.gz \-append "root=/dev/ram rdinit=/init console=ttyS0" \-nographic
✅ 使用 GDB 远程调试(可选)
qemu-system-x86_64 \-kernel /root/linux/arch/x86/boot/bzImage \-initrd /root/initramfs.cpio.gz \-append "root=/dev/ram rdinit=/init console=ttyS0" \-nographic -s -S
然后在另一个终端:
docker exec -it <容器名或ID> bash
cd /root/linux
gdb vmlinux
(gdb) set architecture i386:x86-64
(gdb) target remote :1234
(gdb) b start_kernel
(gdb) c
✅ 小贴士
vmlinux
:用于调试(含符号)bzImage
:用于启动- 如果需要支持 ARM、MIPS 等架构,需要补充交叉工具链 + QEMU 环境
注意事项
内核的符号表文件一般是编译时生成的 vmlinux,而不是压缩过的 bzImage。确保你用 GDB 载入的是 vmlinux。
确保内核配置中开启了调试符号(编译内核时加 CONFIG_DEBUG_INFO=y)。
你可以在 make menuconfig 里配置或直接在 .config 里加。
✅ VSCode + GDB 可以可视化调试 Linux 内核源码,并能看到源代码、变量、寄存器、内存等信息。前提是你的 vmlinux
文件必须是 带调试信息的未剥离(not stripped) 文件 —— 你已经满足了这一点:
vmlinux: ELF 64-bit LSB executable, x86-64, ... not stripped
🔍 你能看到什么?
使用 VSCode + GDB(通过 launch.json 远程调试) 时,你可以看到:
项目 | 是否能看到 | 说明 |
---|---|---|
源代码行号 + 断点 | ✅ | 高亮当前执行行,支持设置断点 |
寄存器值 | ✅ | 显示 CPU 通用寄存器 |
当前栈帧 | ✅ | 显示函数调用栈 |
局部变量、参数 | ✅ | 自动解析变量名和值 |
内存内容(hex) | ✅ | 支持查看/编辑内存 |
汇编指令视图 | ✅ | 支持 |
VSCode 会在调试面板中提供图形方式查看这些内容,非常直观。
🧩 准备工作
确保你已经有这些东西:
✅ 1. 带调试信息的 vmlinux
你已经有了:
vmlinux: ELF 64-bit ... not stripped
✅ 2. 启动 QEMU 并开放 gdb 端口
你需要这样启动 QEMU:
qemu-system-x86_64 \-kernel arch/x86/boot/bzImage \-append "console=ttyS0" \-nographic \-s -S
解释:
-s
=-gdb tcp::1234
(监听端口)-S
= 上电暂停,等 GDB 连接
⚙️ 3. 创建 VSCode 的调试配置
在你的 VSCode 项目目录中,添加 .vscode/launch.json
文件:
{"version": "0.2.0","configurations": [{"name": "Debug Linux Kernel","type": "cppdbg","request": "launch","program": "${workspaceFolder}/linux-2.6.39/vmlinux","miDebuggerServerAddress": "localhost:1234","miDebuggerPath": "/usr/bin/gdb","stopAtEntry": true,"cwd": "${workspaceFolder}/linux-2.6.39","externalConsole": false,"MIMode": "gdb","setupCommands": [{"description": "Enable pretty-printing","text": "-enable-pretty-printing","ignoreFailures": true},{"text": "set architecture i386:x86-64"}],"sourceFileMap": {"/home/you/linux-2.6.39": "${workspaceFolder}/linux-2.6.39"}}]
}
说明:
字段 | 含义 |
---|---|
program | 你的 vmlinux 文件路径 |
miDebuggerPath | GDB 路径(确保支持多架构) |
miDebuggerServerAddress | QEMU 提供的 gdb 端口 |
stopAtEntry | 调试时暂停在入口函数 |
set architecture | 设置为 64 位架构 |
🚀 调试步骤
- 编译 Linux 内核(保留符号)
- 用 QEMU
-s -S
启动内核(等 GDB) - 在 VSCode 中按
F5
启动调试 - 看到源码、调试面板、可视化变量/堆栈
📷 效果图预览(预期)
👉 如果设置正确,你会看到 VSCode 显示如下内容:
▶ start_kernel() at init/main.c:457printk(KERN_INFO "Linux version %s\n", linux_banner);
左侧:变量、栈帧、寄存器;
中间:源码窗口 + 当前行;
右侧:控制台调试信息。
❓可能踩坑提示
问题 | 原因 |
---|---|
GDB 无法定位源码 | 你的 vmlinux 被 strip 掉了,或源码路径不匹配 |
VSCode 看不到变量 | 编译器优化过强,-O2/-O3 会内联和移除变量 |
无法连接 | QEMU 没用 -s -S ,或端口不对 |
启动就崩溃 | bzImage 和 vmlinux 不匹配或命令行出错 |
✅ 总结
你可以通过 VSCode + GDB 实现 Linux 2.6.x 的源码级图形调试,能看到源码、变量、寄存器,非常适合研究内核启动过程。
同理,可以编译 unix-v6 (https://github.com/mit-pdos/xv6-public.git)
以下是使用 xv6‑public(xv6 的 x86 版本)在 Ubuntu/Linux 上安装、编译、运行、调试的详细中文指南。如果你想使用 RISC‑V 版本(xv6‑riscv),后续内容里也有说明。
1. 安装前提
- 操作系统:Ubuntu 18.04/20.04 或其他主流 Linux 发行版
- 工具集:Git、GNU C/C++ 编译器、
make
、qemu-system-i386
(x86 版本) - 若系统为 64 位,且构建结果为 32-bit binary,则需安装多重架构支持(
gcc-multilib
)
可使用以下命令批量安装:
sudo apt update
sudo apt install git build-essential qemu qemu-system-i386 gcc-multilib
确保 qemu-system-i386
可用于运行 xv6(x86 ELF 格式)([博客园][1], [GitHub][2], [GitHub][3])。
2. 获取源代码
从 MIT 官方仓库克隆 xv6-public:
git clone https://github.com/mit-pdos/xv6-public.git
cd xv6-public
该版本对应 MIT 6.828 课程使用版本,是学习 Unix V6 系统设计的现代实现镜像([pdos.csail.mit.edu][4])。
⚠️ MIT 已停止维护 x86 分支,建议转向 xv6‑riscv。如你坚持使用该版本,仍可继续使用,但未来建议迁移平台。
3. 编译 xv6
在 xv6-public 根目录下运行:
make
此命令执行以下工作:
- 使用当前系统的
gcc
编译汇编文件与 C 源码 - 链接生成 32-bit ELF 格式的 xv6 内核镜像:
kernel
文件 - 最终调用
qemu-system-i386
启动模拟器(若指定为make qemu
)([GitHub][2], [知乎专栏][5])
若系统默认执行不了 make qemu
,请检查并使用 make qemu-nox
(无图形 GUI)运行。
4. 启动 xv6
编译完成后,使用以下命令启动:
make qemu-nox
会在终端进入 xv6 shell 环境,如下所示:
init: starting sh
$
在 shell 中可以执行命令,例如 ls
会列出初始文件系统中的内容(README、各示例程序)([博客园][1])。
退出模拟器,可以按 Ctrla
然后 x
。
5. 常用命令对照表
操作 | 命令示例 |
---|---|
编译系统(清理后重编译) | make clean && make |
仿真启动 xv6 | make qemu 或 make qemu-nox |
启动 shell 并交互运行 | make qemu (默认进入 shell) |
调试 / 设置断点(QEMU + GDB) | make qemu-gdb + i386-elf-gdb |
查看源代码打印版 PDF | make pdf , 编译成可打印的源码列表 |
6. 调试技巧
- 使用
CTRL‑P
在 xv6 shell 打印进程状态(相当于ps
) - 若想调试流程可在
kernel/
下设置断点,法国 QEMU 可附带 GDB:
make qemu-gdb
然后使用 i386-elf-gdb
连接:
i386-elf-gdb kernel
(gdb) target remote localhost:26000
可以观察寄存器、设置断点、单步执行等([pdos.csail.mit.edu][6], [GitHub][7], [知乎专栏][5], [zhayujie.com][8], [博客园][1])。
7. xv6‑public vs xv6‑riscv 差异介绍
-
xv6‑public(x86 版本):
- 面向 Intel x86 架构,使用 ELF 格式
- 已停止 MIT 官方维护,功能稳定
- 可在 Ubuntu 直装,编译方式以上说明
-
xv6‑riscv(RISC‑V 版本):
- 用于 MIT 6.1810 课程,适用于 RISC‑V 指令集
- 编译需安装
riscv-gnu-toolchain
和针对 RISC‑V 的qemu
(qemu-system-riscv64) - 运行方式略同:
git clone https://github.com/mit-pdos/xv6-riscv.git
make
make qemu
运行前请确保已安装 riscv64-linux-gnu-gcc
、qemu-system-riscv64
等([bilibili.com][9], [pdos.csail.mit.edu][4], [GitHub][10])。
8. 教学资源与实验模板支持
- MIT 6.828 提供一系列基于 xv6 的实验,包括 system calls、文件系统、调度器等
- 学习资源包括 xv6-book(Rev11 版本 ~99 页)、源码手册、实验要求等
- 推荐通过分支
xv6-labs-2020
来获取实验材料,并使用make grade
和make handin
提交练习作业([pdos.csail.mit.edu][6], [维基百科][11])。
✅ 总结建议
-
项目:xv6-public
- 最基本的 MIT xv6 内核,在多核 x86 环境下静态编译、运行
- 使用
make
编译、make qemu
运行,适合初步学习 Unix 系统机制
-
工业路径建议
- 若你落脚于现代机器,建议使用 xv6-riscv(支持 MIT 官方维护,也便于迁移和扩展)
- 体验实验任务时结合
xv6-labs-2020
的指令完成课堂作业
无论你是想 阅读 xv6 官方注释书、深入理解 Unix V6 源码逻辑、还是 实现 kid‑OS 实验系统功能,xv6-public 和 xv6-riscv 都是很好的起点!
xv6‑RISC‑V 是什么?🔧
特性 | 描述 |
---|---|
目标 | 一个基于 RISC‑V 平台重写的教学操作系统,源自 MIT 的 xv6(x86 分支已于 2018 年停止维护)([pdos.csail.mit.edu][1]) |
灵感来源 | 模仿 UNIX 第六版的内部设计风格,使用 ANSI C 编写,面向现代多核 RISC‑V 架构 |
教学用途 | 支持 MIT 操作系统课程(6.828 / 6.1810),配有精炼的教材,包括进程、中断、分页、文件系统、并发控制等主题 |
一、准备环境(以 Ubuntu 22.04 为例)
-
安装 RISC‑V 工具链
包括riscv64-unknown-elf-gcc
,riscv64-unknown-elf-ld
,libc
等。可通过源码编译(推荐)或使用预编译版本:sudo apt-get update sudo apt-get install autoconf automake gawk libmpc-dev libmpfr-dev \libgmp-dev build-essential bison flex texinfo gperf libtool patchutils zlib1g-dev git clone --depth 1 https://github.com/riscv/riscv-gnu-toolchain cd riscv-gnu-toolchain ./configure --prefix=$HOME/riscv make linux -j$(nproc) export PATH=$HOME/riscv/bin:$PATH
-
安装 QEMU 的 RISC‑V 模拟器,至少支持
riscv64-softmmu
架构:sudo apt-get install libglib2.0-dev libpixman-1-dev ninja-build git clone --depth 1 https://github.com/qemu/qemu.git cd qemu ./configure --target-list=riscv64-softmmu --disable-docs make -j$(nproc) sudo make install
-
获取 xv6‑RISC‑V 源码并运行:
git clone https://github.com/mit-pdos/xv6-riscv.git cd xv6-riscv make qemu
— 如果希望连带调试支持,可使用:
make qemu-gdb riscv64-unknown-elf-gdb
构建完成后你将看到
xv6 kernel is booting
、多个hart
启动日志,并进入类似init: starting sh
的 shell 界面,支持基本 UNIX 命令(如ls
,echo
,cat
等)([pdos.csail.mit.edu][1])
二、xv6‑RISC‑V 与原始 Unix V6 的相似与创新
-
相似点:继承了 Unix V6 的系统调用接口和设计风格(进程调度、fork, exec, pipe, open, read/write 等)([pdos.csail.mit.edu][1])
-
不同点:现代化实现(ANSI C + RISC‑V),支持 copy-on-write、日志文件系统、多核处理、页面映射、虚拟内存等概念;
- fork 的实现由 Unix V6 的整块复制变为 按需复制(COW);
- 文件系统设计加入了 logging 和 crash recovery;
- 多核支持由原本 XX Uni-core PDP‑11 → RISC‑V 多核 SoC。
三、为什么选择 xv6‑RISC‑V?
- x86 分支(
xv6-public
)已于 2018 年停止更新,而 RISC‑V 分支从 2019 年起持续维护,更适合现代教学用途; - RISC‑V 是开源 ISA,让你无需购买特定硬件,就能在任何 Linux/macOS 上模拟和调试 xv6;
- 学术资源丰富:MIT 官方发布教材(
book-riscv-rev3.pdf
),并提供配套实验题与 GitHub 教学仓库 ([pdos.csail.mit.edu][1])
四、如何深入学习与实验
-
阅读教材:
xv6 book for RISC‑V
(可从 MIT 教材或 GitHub 下载)覆盖系统调用、内存管理、中断机制、文件系统与调度算法;
-
阅读关键模块代码:
kernel/trap.c
,proc.c
,vm.c
,fs.c
,syscall.c
,swtch.S
等源码 README 有模块简介;
-
逐章修改并执行实验:
- 可以仿照 MIT 课程的模块自行修改 / 添加实验,例如修改
fork
为多页 COW、加入线程机制、扩展 shell;
- 可以仿照 MIT 课程的模块自行修改 / 添加实验,例如修改
-
逐步测试与调试:
- 使用
make CPUS=1 qemu-gdb
或在 shell 内加断点,结合riscv64-unknown-elf-gdb
调试机制。
- 使用
五、常见问题提示 🔍
问题 | 解决方案 |
---|---|
make qemu 显示 “command not found” | 检查 $PATH 是否包含 riscv‑gnu‑toolchain 的 bin 目录;确认 qemu-system-riscv64 已安装并在 PATH 中 |
构建 riscv‑toolchain 时间很久(数小时) | 硬件较慢或并行编译时平均时间较长,可使用 --prefix 更改安装位置,或换用预编译版本 |
GDB 上提示关于 qemu 连接失败 | 尝试 make qemu-gdb CPUS=1 ,并确认防火墙未阻止 localhost:26000 |
使用 macOS 的用户提示 Case‑insensitive filesystem 错误 | 建议使用 HFS+(区分大小写)或在外接设备上创建 case‑sensitive 分区 ([pdos.csail.mit.edu][1]) |
六、回顾与建议
- xv6‑RISC‑V 不是为了性能或完整性设计,而是为了教育目的:代码量小(不过仍包含操作系统的主要概念与结构),易于阅读和扩展;
- 它精炼体现了 UNIX 的“简单规范 + 可组合性”哲学(如系统调用、管道、环回 shell),同时引入现代操作系统技术;
- 适合初学者、新手系统工程师,以及操作系统课程的作业与实验平台。
https://pdos.csail.mit.edu/6.S081/2023/xv6/book-riscv-rev3.pdf?utm_source=chatgpt.com “xv6: a simple, Unix-like teaching operating system”