哈工大操作系统实验从零构建Linux 0.11内核探索之旅对于许多计算机专业的学生和爱好者而言操作系统原理这门课既令人着迷又充满挑战。纸上得来终觉浅绝知此事要躬行——这句话在操作系统学习上体现得淋漓尽致。哈工大李治军老师开设的操作系统课程以其经典的Linux 0.11内核实验而闻名它就像一把钥匙为我们打开了理解现代操作系统核心机制的大门。然而当满怀热情地准备动手时第一个拦路虎往往是实验环境的搭建该从哪里获取代码虚拟机怎么配置编译环境如何搭建遇到各种报错怎么办这篇文章正是为你而来无论你是零基础的本科生还是希望重温经典内核设计的开发者我将带你一步步走过这段旅程不仅解决环境问题更深入理解每个步骤背后的意义。1. 实验环境的选择与准备本地还是云端在开始任何实验之前选择一个合适的“工作台”至关重要。对于Linux 0.11实验你主要有两种选择在本地计算机上搭建完整的开发环境或者使用在线的实验平台。这两种方式各有优劣选择哪一种很大程度上取决于你的个人习惯、网络条件以及对系统底层控制的需求。本地环境搭建意味着你需要在你的电脑上安装虚拟机软件如VirtualBox或VMware然后在虚拟机中安装一个较老版本的Linux发行版如Ubuntu 12.04或更早因为现代Linux系统的工具链可能无法直接编译三十年前的内核代码。这个过程听起来有些繁琐但它能给你带来最大的灵活性和控制权。你可以随时保存快照、自由修改系统配置、深入调试并且实验进度完全掌握在自己手中。对于希望深入学习、未来可能进行更多内核级探索的同学来说这是必经之路。提示如果你选择本地环境请务必为虚拟机分配足够的磁盘空间建议至少20GB和内存1GB以上编译内核和运行模拟器需要一定的资源。另一方面云实验平台如一些在线教育平台提供的实验环境提供了一种开箱即用的解决方案。你无需安装任何软件打开浏览器就能获得一个预配置好的实验环境。这种方式最大的优点是便捷特别适合只想快速完成实验、验证原理的同学。但其缺点也很明显环境通常是临时的无法保存个人定制化的配置网络延迟可能影响体验并且你无法深入了解环境本身是如何构建起来的失去了一个学习系统配置的机会。为了帮助你做出决策我整理了两种方式的对比对比维度本地虚拟机环境云实验平台上手速度较慢需自行安装配置极快即开即用控制自由度完全控制可任意修改受限仅限平台提供的功能数据持久性永久保存可随时继续通常为临时会话关闭后重置学习深度深入涵盖环境搭建本身较浅聚焦于实验本身网络依赖仅初次安装需要强依赖全程需要稳定网络适用人群希望全面学习、不惧挑战的探索者希望快速验证原理、完成作业的学习者我个人强烈建议如果你时间相对充裕并且对计算机系统抱有持续的热情那么从本地环境搭建开始。这个过程本身就是一个极佳的学习体验你会遇到并解决许多在云平台上被隐藏起来的问题这对你理解整个软件栈大有裨益。2. 本地开发环境搭建全流程假设你决定挑战自己搭建本地环境。别担心我会把每一步拆解清楚并附上我踩过坑后总结的解决方案。我们将以使用VirtualBox虚拟机 Ubuntu 12.04 32-bit系统为例因为这个组合的兼容性经过了大量验证。2.1 基础软件安装与系统准备首先去VirtualBox官网下载并安装最新版本的VirtualBox。接着你需要获取Ubuntu 12.04的安装镜像ISO文件。由于这是旧版本你可以在Ubuntu的官方归档站点找到它。创建一个新的虚拟机类型选择“Linux”版本选择“Ubuntu32-bit”。在分配硬件资源时可以参考以下配置内存1024 MB1GB基本足够如果你的主机内存充裕分配1536 MB会更流畅。硬盘创建一个新的虚拟硬盘类型用默认的VDI即可大小强烈建议分配25GB以上并选择“动态分配”。这确保了后续安装编译工具和内核源码后有充足空间。处理器1-2个CPU核心即可。安装Ubuntu系统的过程是图形化的按照提示进行即可。有几个关键点需要注意在分区时如果你不熟悉可以选择“清除整个磁盘并安装Ubuntu”让安装程序自动处理。务必记住你设置的用户名和密码后续在终端中会频繁使用。安装过程中系统会提示你安装一些第三方软件和更新为了保持环境纯净和避免不必要的兼容性问题建议先跳过这些更新等系统安装完毕后再根据需要处理。系统安装完成后第一件事是更新软件包列表并安装一些最基础的开发工具。打开终端CtrlAltT执行以下命令sudo apt-get update sudo apt-get upgrade -y sudo apt-get install build-essential libncurses5-dev gcc-multilib -y这里解释一下这几个包的作用build-essential包含了GCC编译器、make等构建软件的核心工具集。libncurses5-dev一个库的开发文件后续配置内核菜单时会用到。gcc-multilib允许我们编译32位程序因为Linux 0.11是32位系统。2.2 获取实验源码与配套工具环境就绪现在让我们请出主角——Linux 0.11的源代码。哈工大课程维护了一个非常优秀的GitHub仓库它不仅仅包含了原始的Linux 0.11源码还整合了实验所需的脚本、修改记录以及一些已经完成的实验代码参考。在终端中我们通过git命令来克隆这个仓库cd ~ git clone https://github.com/hoverwinter/HIT-OSLab.git克隆完成后你会得到一个名为HIT-OSLab的目录。进入这个目录看看里面有什么cd HIT-OSLab ls -la你会看到类似如下的结构部分文件Linux-0.11/这是最核心的Linux 0.11内核源代码目录。hit-oslab-linux-20110823/这是一个包含了编译工具链和模拟器的目录是实验运行的“发动机”。README.md仓库的说明文档务必花几分钟读一下。其他一些实验相关的脚本和文档。接下来我们需要解压工具链。工具链被压缩在hit-oslab-linux-20110823.tar.gz这个文件中。执行解压命令tar -zxvf hit-oslab-linux-20110823.tar.gz解压后会生成一个同名的目录。关键的一步来了我们需要将这个工具链的路径添加到系统的环境变量PATH中这样系统才能找到正确的编译器如as86,ld86等和模拟器bochs或qemu。编辑当前用户的家目录下的.bashrc文件nano ~/.bashrc在文件的末尾添加下面这行请根据你实际的解压路径调整如果严格按照上述步骤路径就是~/HIT-OSLab/hit-oslab-linux-20110823/export PATH~/HIT-OSLab/hit-oslab-linux-20110823/:$PATH保存并退出编辑器在nano中是CtrlX然后按Y确认再按回车。让配置立即生效source ~/.bashrc现在你可以测试一下工具是否就位。在终端输入which as86和which bochs如果它们能正确打印出路径位于你刚才添加的目录下那么恭喜你工具链配置成功了。3. 首次编译与运行让“古董”内核动起来环境搭建好了源码也有了是时候进行第一次编译看看这个三十年前的内核能否在我们的现代环境中成功构建并运行。这个过程本身就是一个完整的“Hello World”级别的内核实验。进入Linux 0.11的源码目录cd ~/HIT-OSLab/Linux-0.11在编译之前我建议先进行一次彻底的清理确保我们从干净的状态开始make clean然后执行编译命令。这里我们使用make all它会编译内核映像、文件系统等所有必要的部分make all如果一切顺利你会在终端看到大量的编译输出最后如果没有报错就说明编译成功了。此时在源码目录下会生成几个重要的文件其中最核心的是Image编译生成的内核映像文件。hdc-0.11.img一个包含了Minix文件系统的硬盘镜像可以把它理解为内核要运行的“硬盘”。接下来我们使用模拟器来运行这个内核。HIT-OSLab仓库默认提供了两种模拟器Bochs和QEMU。Bochs是一个纯模拟器速度较慢但调试功能强大QEMU则兼具模拟和虚拟化速度更快。对于初次运行我们可以先用Bochs因为它对图形界面的支持更简单。在Linux-0.11目录下直接运行make start-hd或者你也可以进入工具链目录直接运行Bochs的配置文件cd ~/HIT-OSLab/hit-oslab-linux-20110823/ ./run此时应该会弹出一个新的窗口这就是Bochs模拟器的界面。你会看到类似老式PC启动的画面屏幕上开始滚动Linux 0.11内核的启动信息。如果最终出现了登录提示符可能是Login:那么恭喜你你已经成功让一个历史性的操作系统内核在你的电脑上“复活”了注意第一次启动时Bochs可能会在屏幕上显示很多调试信息这是正常的。如果它停住了可以尝试在Bochs窗口按几次回车键。默认的登录用户名是root没有密码。在这个过程中你可能会遇到一些典型的“拦路虎”。下面是我总结的几个常见问题及解决方案问题一make all编译失败提示as86或ld86找不到。原因环境变量PATH没有正确设置或者工具链没有正确解压。解决回头检查2.2节中export PATH那一步确保路径完全正确并且执行了source ~/.bashrc。可以用echo $PATH命令查看当前路径。问题二Bochs窗口弹出后一片漆黑或者很快关闭。原因可能是虚拟机没有启用硬件虚拟化支持或者Bochs配置有问题。解决首先确保你主机BIOS中的Intel VT-x或AMD-V虚拟化技术已经启用。其次尝试使用QEMU来运行命令是make start-hd-qemu需要先安装qemusudo apt-get install qemu-system-i386。QEMU通常有更好的兼容性。问题三启动后内核panic提示VFS: Unable to mount root fs之类的错误。原因最常见的原因是硬盘镜像hdc-0.11.img损坏或者内核没有正确识别到它。解决回到HIT-OSLab目录重新解压或从仓库中重新下载这个硬盘镜像文件并复制到Linux-0.11目录下覆盖原文件。第一次成功编译和运行的喜悦是无与伦比的它证明你的整个环境链路是通的。接下来我们就可以安心地在这个基础上进行各种实验了。4. 实验代码复用与个性化修改策略哈工大的实验是循序渐进的从“操作系统的引导”到“系统调用”再到“进程轨迹跟踪”。GitHub仓库HIT-OSLab的一个巨大优势在于它的Linux-0.11目录下的代码已经为每个实验做好了“分支”管理。这意味着你可以轻松切换到某个实验完成后的代码状态进行参考也可以在一个干净的基础上开始自己的创作。理解仓库的代码结构是关键。在Linux-0.11目录下执行git log --oneline你可以看到一系列的提交记录每个重要的提交通常对应一个实验的完成。更直观的方法是使用git tag命令查看所有标签git tag你可能会看到类似lab1,lab2,lab3这样的标签。这些标签就像书签标记了每个实验结束时的代码状态。假设你想查看实验一引导实验完成后的代码是什么样子你可以创建一个新的分支并切换到那个标签点git checkout -b lab1-review lab1这条命令创建了一个名为lab1-review的新分支并将代码状态切换到了标签lab1所指向的提交。现在你可以自由浏览这个时间点的所有源代码看看为了完成实验一哪些文件被修改了修改了什么。使用git diff tag1 tag2可以比较两个实验之间的差异这对于理解实验的演进思路非常有帮助。提示在查看他人已完成的实验代码时目的是学习和理解解决方案而不是直接复制。尝试先自己思考实现遇到瓶颈时再参考这样的收获最大。当你准备开始自己的实验时务必确保你处在一个“干净”的起点上。通常主分支master就是最原始的Linux 0.11代码。你可以切换回主分支并基于它创建你自己的实验分支git checkout master git checkout -b my-lab1现在你就在my-lab1分支上工作了所有的修改都不会影响主分支和其他实验分支。这是一种非常专业且高效的工作方式。如何进行代码修改与测试内核代码修改后需要重新编译并运行来测试效果。基本流程是一个循环编辑源代码例如修改kernel/printk.c来添加日志。在Linux-0.11目录下执行make clean非必须但有时能避免奇怪问题。执行make all重新编译。执行make start-hd或make start-hd-qemu运行测试。观察运行结果如果不符合预期回到第1步。这个循环可能会重复很多次。为了提升效率我有几个小技巧使用终端多标签页一个标签页用于编辑代码用vim或nano一个标签页专门用于执行编译和运行命令。善用Bochs的调试功能对于引导实验或深入理解内核启动流程Bochs的内置调试器是神器。你可以使用make debug-hd命令启动带调试的Bochs然后设置断点、单步执行汇编指令。备份工作成果每完成一个重要的步骤或者实验记得在git中提交你的更改git add .然后git commit -m “完成了引导程序修改”。这不仅是备份也让你能清晰地回顾自己的开发历程。5. 从实验到理解超越步骤的思考完成环境搭建和实验步骤只是第一步如何从这些实验中榨取最大的知识价值才是区分普通完成者和真正理解者的关键。操作系统实验不是“连连看”照着步骤做完就结束了。每一个实验步骤背后都对应着操作系统原理中的一个核心概念。以实验一操作系统的引导为例。这个实验要求你将内核镜像从软盘加载到内存中。如果你只是按照指导书修改boot/bootsect.s和boot/setup.s中的几个数字然后编译运行看到结果那么你只完成了任务的10%。剩下的90%在于思考为什么是软盘历史上确实如此但今天我们可以思考引导介质BIOS/UEFI如何与操作系统约定好交接工作。bootsect.s把自己从0x07C0移动到0x9000为什么要移动不移动会怎样这涉及到内存布局和后续代码的空间安排。它调用BIOS中断0x13读取setup.s和内核BIOS中断是什么现代UEFI启动还这样吗这引导你去了解从实模式到保护模式过渡的历史与现状。再比如实验二系统调用。你添加了一个iam()和whoami()的系统调用。除了修改那几个关键的文件include/unistd.h,include/linux/sys.h,kernel/system_call.s,kernel/who.c你更应该画出一张清晰的流程图用户态程序调用whoami()。这个库函数如何通过int 0x80软中断陷入内核内核的system_call总入口如何根据系统调用号__NR_whoami跳转到sys_whoamisys_whoami在内核态如何安全地操作用户空间传递来的指针参数get_fs_byte结果如何返回给用户态把这个流程用自己的话解释清楚你才真正理解了用户态与内核态隔离、系统调用门机制以及参数传递的安全性这些核心概念。为了帮助你建立这种全局观我建议在每次实验后尝试回答下面几个问题并记录下来这个实验主要验证或演示了操作系统哪个些核心机制我修改了哪些文件每个修改点起到了什么作用可以列一个简表整个执行流程中数据和控制流是如何传递的能否画一个简单的示意图如果我要在Linux 0.11上再添加一个类似的简单系统调用我能否不参考任何资料只凭记忆和推理完成学习操作系统的乐趣就在于这种从模糊到清晰、从表象到本质的探索过程。当你通过实验亲眼看到自己写的几行代码如何让内核做出响应那种对计算机系统的掌控感和理解深度是单纯阅读教科书无法比拟的。环境搭建的琐碎、编译报错的烦躁在最终理解的那一刻都会变成值得的回味。