news 2026/10/17 9:07:09

Linux内核编译实验全攻略:从源码到启动的踩坑记录

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Linux内核编译实验全攻略:从源码到启动的踩坑记录

操作系统课程第一个实验就是 Linux 内核编译及运行,这个实验放在很多高校的第一周,看起来就是“下载源码、make menuconfig、make、重启”,实际操作起来却能把人折腾到怀疑人生。这篇文章把我自己做这个实验的全过程、踩过的坑、排查思路都记录下来,希望能给准备做这个实验或者正在做的小伙伴一个完整的参考路径,也算是对实验一的一次复盘。

1. 实验目的与环境准备

1.1 实验目的:弄懂内核编译的全链路

很多同学一开始会疑惑:操作系统课为什么要从编译内核开始?直接用发行版不香吗?答案在于,这个实验真正要训练的不是“会敲命令”,而是理解操作系统的构建过程。

所谓内核编译,就是把一份 Linux 内核源码,通过工具链生成可执行的二进制内核镜像和大量可加载模块。这个过程涉及源码树的组织方式、Kconfig 配置体系、Makefile 构建逻辑、模块机制,以及 grub 引导流程。可以说,一个实验把操作系统课程里“进程管理、内存管理、文件系统、设备驱动”这些大概念背后的工程实现串起来了。

实验中你会看到,内核不是一整块巨大的程序,而是一个由上千个目录、几万个文件组成的工程集合。它的构建系统会按照 .config 文件里的配置项决定哪些功能编译进内核(<*>)、哪些编译成模块(<M>)、哪些干脆不编译(< >)。这和你平时写一个小项目直接 make all 完全是两码事,也正因如此,它才能成为操作系统课第一个实验的理想起点。

1.2 实验环境搭建

我是用虚拟机完成实验的,方案如下:

  • 宿主机:普通 Windows 笔记本,开启硬件虚拟化
  • 虚拟机:VMware Workstation,分配 4 核 CPU、8 GB 内存
  • 虚拟磁盘:80 GB,动态分配
  • 系统镜像:Ubuntu 22.04 LTS

这里特别提醒一下磁盘问题。内核源码解压后大约 1.2 GB,但编译过程中会产生大量的 .o 目标文件和临时文件,整个 /usr/src/linux 目录编译完能膨胀到 13~15 GB。如果虚拟机初始只给了 20 GB 系统盘,那基本必挂。建议至少给 60 GB,我甚至见过有人因为磁盘不够把实验拖了一周才做完。

进入系统后,先把基础工具链装上:

sudo apt update sudo apt install build-essential libncurses-dev libssl-dev flex bison bc dwarves

这些包各司其职:

  • build-essential:包含 gcc、make、ld 等编译必备工具
  • libncurses-dev:提供 menuconfig 图形界面所依赖的 ncurses 库
  • flex / bison:内核源码构建时需要生成的词法、语法分析工具
  • libssl-dev:编译内核模块时需要的 OpenSSL 头文件
  • bc:内核配置阶段计算依赖时用到的计算器工具
  • dwarves:生成 BTF 信息,部分新内核默认启用这个依赖

还有一个比较容易忽略的点:编译工具版本要和内核版本匹配。Ubuntu 22.04 自带的 gcc 是 11.x,如果下载特别老的 Linux 内核(比如 4.x 时代的老版本),可能会遇到头文件不兼容、API 变更导致编译失败。所以我建议直接选 5.15.y 或者 6.1.y 这些 LTS 版本,社区文档多,遇到的问题网上也基本都有答案。

提示:做任何操作前,先用uname -r看一下当前系统内核版本,然后选择一个比它新的内核来编译,避免出现版本倒退或者覆盖当前可用内核的风险。

2. 内核源码获取与内核配置

2.1 获取内核源码:三种方式怎么选

实验的第一步是拿到内核源码。我实践下来主要有三种方式,各自的使用场景不一样。

第一种,从 kernel.org 下载稳定版 tar.xz 源码包。这是最推荐的实验方式:

wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.1.119.tar.xz tar -xf linux-6.1.119.tar.xz cd linux-6.1.119

优点很直接:一条命令拿到完整源码,版本固定,不会因为网络拉取中断导致源码树不完整。缺点是下载要花一点时间,取决于网速。

第二种,用 git 克隆官方的 stable 仓库:

git clone --depth 1 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux.git

加了--depth 1只拉取最新提交,能省不少流量。但要注意,浅克隆仓库里的版本号不像 tar 包那样固定在 6.1.119 这种 tag 上,实验时如果想对齐某个版本,还得手动 checkout 指定 tag,稍微麻烦一点。如果你后续想做内核开发、改代码提交,那 git 方式是必要的;单纯做实验,tar 包更省心。

第三种,用 apt 的源码管理机制从系统源里拉取,需要提前添加 deb-src 源。这种方式受发行版维护周期影响,拉到的可能不是最新版,而且配置起来比较繁琐,实验里我个人不推荐。

我实验用的是 Linux 6.1.119,这是 6.1 LTS 系列的一个版本。选它没有太多玄学,就是看重长期维护这个特点,出了问题也好查资料。

2.2 配置内核:menuconfig 的正确打开方式

拿到源码后,很多人第一反应就是直接 make。错,大错特错。编译前必须先配置内核,否则 make 会提示找不到 .config 文件,或者使用一个默认的不适合你环境的配置。

内核提供几档配置工具,我分别说下体验:

  • make config:命令行逐项问答式配置,几百个选项,全部答完大概需要一两个小时,非常折磨人。
  • make menuconfig:基于 ncurses 的图形化菜单,上下键选择、空格切换、回车进入子菜单,也是实验中最推荐的一种。
  • make defconfig:直接采用内核默认配置,省事但不对你当前系统做任何定制,适合快速验证流程,不适合做深入实验。
  • make olddefconfig:基于现有 .config 生成新配置,通常在升级内核配置时用。

实验环境里,建议直接 menuconfig。但在此之前,先执行一次彻底清理:

make mrproper

make mrproper比make clean更彻底,它会清掉源码树中所有之前生成的配置、编译产物、依赖文件,保证你从一个干净的状态开始。我第一次没执行这一步,结果后面反复出现奇怪的头文件依赖错误,后来养成习惯,每次重新编译前先 mrproper。

执行:

make menuconfig

进去之后,你会看到 General setup、Processor type and features、Power management、Device Drivers、File systems 等分类。重点理解这三个符号的含义:

  • <*>:编入内核,即直接整合到 vmlinuz 镜像里,开机常驻内存
  • <M>:编译成模块,以 .ko 文件形式存在,需要时通过 modprobe/insmod 加载
  • < >:不编译,完全不包含该功能

对于实验来说,你可以在 Device Drivers 下面找到块设备、网络设备、显卡驱动等选项,观察哪些是内建、哪些是模块。如果想明确看到效果,可以特意把一个驱动从<*>改成<M>,后面用 modprobe 手动加载,会非常有成就感。

配置完成后退出,会询问是否保存新配置。选 Yes,生成的配置文件叫 .config。可以查看验证:

grep CONFIG_BLK_DEV_SD .config

这条命令能确认常见 SCSI 磁盘驱动是否被编译。

2.3 配置阶段容易掉进的坑

我在实验时遇到三个问题,提前帮大家避开。

第一个坑:menuconfig 打不开,提示缺少 ncurses。这纯粹是依赖安装不完整导致的,执行sudo apt install libncurses-dev即可解决。

第二个坑:.config 文件被覆盖或丢失。这是新手的重灾区。如果你先用了 defconfig,再打开 menuconfig 改了配置,但最后退出时没保存,那么你之前所有修改全部作废。我建议在改动前先留一份备份:

cp .config .config.bak

第三个坑:新内核的 Kconfig 体系对配置依赖非常敏感,某些选项如果依赖其他被关闭的选项,会直接置灰无法勾选。这时候不要硬选,更不要手动往 .config 里强行加没被支持的 CONFIG_XX,否则编译时大概率出现找不到头文件或 undefined reference 的报错。

保险的做法是把当前系统内核的配置作为起点:

cp /boot/config-$(uname -r) .config

然后基于这份配置增量修改,这样能最大限度保留对现有硬件的支持,避免把系统“配成一个启动不了的形态”。实验做完后我发现,很多同学出问题都是因为从零开始配内核,结果漏掉了自己虚拟机的磁盘控制器驱动、网卡驱动,重启后直接失联。

3. 内核编译、安装与引导

3.1 编译过程与关键产物解读

配置完成后,进入最耗时的阶段:编译。命令很简单:

make -j4

这里的-j4表示 4 个并发编译任务。如果直接写-j$(nproc),系统自动用全部 CPU 线程数。但虚拟机上如果本身内存不大,并发开太高容易把内存吃满,出现 OOM(Out of Memory)。我的虚拟机分到 4 核,实测-j4比较稳,-j8虽然快一点,但内存占用会显著增加,还出现过一次编译崩溃。所以如果你不是专门为了拼速度,老老实实用 4 并发就好。

编译过程中,你会看到大量 C 源文件被编译成 .o,再被链接成各子系统的目标文件。重点关注这几个产物:

  • vmlinux:未压缩的内核镜像,带符号表,体积很大,通常位于源码根目录
  • arch/x86/boot/bzImage:压缩后的内核镜像,是引导加载器真正加载的文件
  • System.map:内核符号表,调试内核和模块时使用
  • .ko模块文件:分散在各级目录中,后续会被安装到 /lib/modules

当终端里出现这句话时,说明内核本体编译成功:

Kernel: arch/x86/boot/bzImage is ready

我第一次看到这行提示的时候还挺激动的,以为实验马上结束了,结果后面安装和引导还有一堆事。

关于编译时长,我的实测数据是:4 核虚拟机、8 GB 内存、默认配置基础上增加少量选项,编译完大约花了 35 分钟。如果配置里勾选了大量驱动、文件系统支持,或者把并发调低,耗时可能到 1 小时以上。建议实验安排时留足时间,别等到实验课快下课才开始编译。

3.2 模块安装与内核安装

编译完成后,先把模块安装到系统目录:

sudo make modules_install

这一步会把所有 .ko 文件拷贝到/lib/modules/$(uname -r)/下。注意这里的$(uname -r)是你在内核源码顶层 Makefile 里设置的版本号,不是当前运行系统的版本号。在执行前,建议在源码顶层目录看一下版本变量:

make kernelrelease

这个命令会打印出即将安装的内核版本,确保和你预期一致。

接下来安装内核镜像本身:

sudo make install

这一步会执行几个动作:把 bzImage 拷贝为 /boot/vmlinuz-版本号,生成 /boot/initrd.img-版本号,拷贝 System.map 到 /boot,并自动调用 update-grub 更新引导菜单。如果你这个时候/boot分区空间不足,会直接报错中断,具体解决方式后面单独讲。

安装完成后检查两个目录,确认文件真的到位了:

ls -lh /boot/vmlinuz-* ls -lh /boot/initrd.img-*

正常情况下,你应该能看到新版本号对应的 vmlinuz 和 initrd.img。

3.3 更新引导配置与重启验证

虽然 make install 大概率已经自动执行了 update-grub,但为保险起见,我习惯手动再执行一次:

sudo update-grub

这个命令会扫描 /boot 目录,把所有可用内核写入 grub 菜单。执行后重启:

sudo reboot

重启时,grub 菜单会列出多个内核版本,选择带新版本号的项进入。如果没看到选择菜单,按住 Shift 或按 Esc 进入 grub 菜单,具体按键取决于你的 grub 版本。

进入系统后,第一件事验证:

uname -r

显示的结果如果是你编译的新内核版本,说明你已经成功让系统运行在一个“自己编译出来的内核”上了。这一步是整个实验最核心的验证点。

还可以更进一步:

dmesg | grep "Linux version"

启动日志里会显示内核编译时间、使用的 gcc 版本、源码版本号,这些信息能辅助确认你启动的确实是刚刚编译的内核,而不仅仅是看起来版本号相同。

3.4 实测数据参考

我把个人实验的关键数据整理成一个表,方便后面同学对照:

项目参考值
虚拟机配置4 vCPU / 8 GB 内存 / 80 GB 动态磁盘
内核版本Linux 6.1.119 LTS
配置方式复制系统默认配置 + menuconfig 增量修改
编译命令make -j4
源码解压后体积约 1.2 GB
编译完成后源码目录体积约 13~15 GB
完整编译耗时约 35 分钟
生成的 bzImage 大小约 12~15 MB
/boot 新增占用约 60 MB

这些数值和具体配置选择、硬件性能有很大关系,只能作为参考方向,不必过分纠结具体数字。但磁盘空间和编译时间这两项,确实是最容易影响实验进度的变量。

4. 实验过程中遇到的坑与排查记录

这部分是整份实验报告里我觉得价值最高的地方。内核编译的报错五花八门,很多问题不亲手踩一遍,光看文档真的想象不到。

4.1 编译刚开始就报错:bison/flex 缺失

我自己第一次编译时,没有装 flex 和 bison。执行 make 后不久,终端直接报错:

/usr/bin/ld: scripts/mod/devicetable-offsets.s: No such file or directory yacc: command not found

表面看是缺 yacc,实际是 bison 没装。补齐依赖之后,还要注意一个细节:如果编译中途失败,不要直接再跑一遍 make,最好先make clean然后重新编译,因为中途产生的某些生成文件可能不完整,直接复跑会引发奇奇怪怪的链接错误。

4.2 /boot 分区空间不足

我用的虚拟机当时 /boot 分区只有 1 GB,装过两次内核之后,空间消耗殆尽。make install到一半直接报:

No space left on device df -h /boot

排查思路主要有三条:

  • 清理旧内核文件,比如 /boot 下之前版本的 vmlinuz、initrd.img、System.map
  • 删除 /boot 下不再使用的 abi-、config-文件
  • 扩容 /boot 分区,但在虚拟机里操作比较麻烦,不建议实验中途做

我最终选择了删除旧的 vmlinuz-5.15 和对应 initrd,然后重新执行:

sudo make install sudo update-grub

空间腾出来后安装顺利完成。这里给个建议:平时养成保持两个可用内核的习惯,别为了省空间把旧内核全删了,新内核一旦启动失败,你连退回的机会都没有。

4.3 启动卡在 initrd,进 BusyBox

这是整个实验里最折磨人的一个问题。编译和安装都很顺利,重启后却卡在 loading initial ramdisk,或者直接掉进 initramfs 的 BusyBox 界面,命令行前缀变成了(initramfs)。

这个问题的本质是:内核无法挂载根文件系统。大多数情况是因为根文件系统对应的驱动被配置成了模块(<M>),而不是内建(<*>)。系统启动早期,内核还没有加载模块的能力,如果磁盘控制器驱动、文件系统驱动不在内核镜像里,自然就找不到根分区。

排查手段是先在 initramfs 环境里看一下:

(initramfs) ls /lib/modules/$(uname -r)

如果发现 ext4 或 xfs 这类文件系统驱动确实缺失,回到 menuconfig,把CONFIG_EXT4_FS这类关键选项从<M>改成<*>:

make menuconfig # 进入 File systems -> <*> The Extended 4 (ext4) filesystem make -j4 sudo make modules_install sudo make install sudo update-grub

重新编译并安装后重启,问题通常能解决。这个坑特别容易出现在“从零配置内核”的同学身上,所以我前面才反复强调,最好基于系统自带 config 修改,不要自己从头配。

4.4 编译报错速查表

我一共整理了八类常见的编译安装问题,做成速查表:

报错现象可能原因解决措施
fatal error: libelf.h: No such file缺少 libelf 开发库sudo apt install libelf-dev
yacc: command not found缺少 bison安装 bison 后make clean重编
/bin/sh: 1: bison: not found缺少 bison安装 bison 并重新配置
No rule to make target 'debian/certs/...'源码目录被污染make mrproper后重新配置
Unable to find the ncurses libraries缺少 libncurses-dev安装 ncurses 开发库后重开 menuconfig
insufficient memory并发过高或内存不足减少-j并发数或增加虚拟机内存
No space left on device/boot 分区或根分区满清理旧内核文件或扩容
gcc: error: elf.h: No such file缺少 libelf 头文件安装 libelf-dev 并重新编译

这张表我实验时是贴在桌面上的,每遇到一个问题,就对应排查一遍,效率高很多。

4.5 实用的恢复技巧

实验里的保命技巧就一条:grub 菜单里保留一个旧内核作为保底。我图省事删过旧内核,结果新内核启动失败,那一刻真的欲哭无泪,最后只能拿 live CD 进去修。如果你正在做实验,无论如何都别急着删旧内核。等新内核连续重启两三次都稳定了,确认模块、驱动、网络都正常,再考虑清理工作。

另外,如果修改配置后想快速返回上一个可用状态,可以在 grub 菜单里选 Advanced options for Ubuntu,里面会列出所有已安装的内核版本,选旧版本进入即可。

5. 实验心得与进阶方向建议

5.1 给后面做实验的同学几条掏心窝的建议

首先,时间规划上,这个实验至少留两到三个晚上。第一晚理解源码结构和配置体系,第二晚集中编译安装,第三晚排查问题写报告。不要天真地以为“一节课就能搞定”,实际编译失败、重来、再失败是常态。

其次,实验报告不要只截图,要记录关键改动。我见过太多人实验做了,报告里却只写“用 menuconfig 配置”“make 编译”几句流水账。答辩时被问“你改了什么配置”“编译参数用的什么”“遇到过什么错”,直接哑口无言。我的做法是把 .config 里的关键修改项、make 命令的参数、每次报错的信息和解决过程都记录下来,这些才是实验报告真正的价值。

最后,不要害怕报错。内核编译的报错信息其实是很好的学习材料,它逼着你去查依赖关系、看源代码、理解构建系统。这一套流程走完,你再回去看操作系统教材里的“系统启动”“模块加载”这些章节,理解会完全不一样。

5.2 实验之后还能怎么玩

如果你对这个方向产生了兴趣,后续有几个很好的进阶点:

第一个,把某个驱动单独编译成模块,然后手动 insmod/modprobe 加载和卸载,观察 dmesg 里的驱动加载日志。这比单纯看教程理解“内核模块”更直观。

第二个,修改内核源码里的调度算法参数,比如 CFS 调度器的某些默认值,重新编译后跑一个 CPU 密集型程序,对比修改前后的运行表现。这个实验能让你对“操作系统调度”从抽象概念变成可触摸的工程实践。

第三个,尝试用 menuconfig 把内核裁剪到最小,做成一个精简内核。嵌入式开发里经常要这么干,把不需要的驱动、虚拟化支持、蓝牙、Wi-Fi 全裁掉,最终得到几 MB 大小的镜像。这个方向特别适合对嵌入式感兴趣的同学。

第四个,如果你愿意花点时间,可以顺着源码里的arch/x86/kernel、kernel/sched这些目录去读一部分源码,配合 System.map 和调试工具,看看系统调用到底是怎么从用户态走进内核态的。这一步走通之后,你对“操作系统”四个字的理解,会远超课本上的定义。

就我个人而言,这个实验最大的收获是第一次意识到:操作系统不是安装起来就完事的黑盒,它是有血有肉、可以被亲手修改和重建的工程系统。编译内核这件事,看起来只是课程的第一关,但它建立的“源码—配置—构建—部署—验证”的完整心智模型,后面读源码、写驱动、做系统优化都用得上。

如果你正在为这个实验头疼,别慌,按我记录的这些步骤一步步来,重点放在配置和排障上,最后一定能看到自己编译的内核成功启动。那个瞬间,你会觉得之前所有折腾都值了。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/17 9:06:44

网页内嵌Excel在线编辑方案:公式、多sheet与数据回传实战

简介&#xff1a;这份资源面向需要在网页端实现表格在线编辑的前端开发者与企业应用团队&#xff0c;围绕HTML页面集成Excel编辑能力这一常见需求&#xff0c;提供可运行的示例工程与配套代码。压缩包共41个文件&#xff0c;约1.44MB&#xff0c;以css样式表、js脚本、png图片资…

作者头像 李华
网站建设 2026/10/17 9:06:40

Superpowers协议:重构AI编程协作的标准化开发范式

1. 项目概述&#xff1a;Superpowers 不是超能力&#xff0c;而是开发者工具链的“认知增强层”你搜“superpowers”时&#xff0c;第一反应可能是漫威电影里的变种人&#xff0c;或者某个科幻游戏的技能树。但最近半年&#xff0c;在开发者社区、技术论坛和 GitHub Trending 榜…

作者头像 李华
网站建设 2026/10/17 9:06:37

SpringBoot+Vue房屋租赁管理系统:从数据库设计到部署全解

每年到了项目季&#xff0c;总有人拿着类似的标题来问我&#xff1a;基于SpringBootVue的房屋租赁管理系统&#xff0c;到底能不能跑起来&#xff0c;好不好改。说实话&#xff0c;这种标题背后不只是一套代码&#xff0c;而是很多人课程设计、毕业设计或者第一次接触全栈开发时…

作者头像 李华
网站建设 2026/10/17 9:05:54

纯CSS实现书本翻页效果:零JS高性能动效实战

1. 项目概述&#xff1a;为什么一个“翻页”动效值得花半天时间抠细节&#xff1f;“书本翻页效果 htmlcss”——这八个字在前端圈子里看似轻巧&#xff0c;实则藏着一套完整的视觉心理学、几何建模与浏览器渲染机制的微型战场。我第一次在某高校数字人文实验室看到这个效果时&…

作者头像 李华
网站建设 2026/10/17 9:04:15

Linux线程优先级设置详解:从调度策略到实时调优实践

写多线程程序的朋友应该都遇到过这种场景&#xff1a;代码里有三个线程在跑&#xff0c;一个负责音频播放&#xff0c;一个写日志&#xff0c;还有一个做后台统计。跑起来之后音频偶尔卡一下&#xff0c;日志线程倒是欢快得很。你直觉上知道要给音频线程提优先级&#xff0c;于…

作者头像 李华
网站建设 2026/10/17 9:04:04

视频编解码标准体系全解析:从H.26x到AV1的提案实战指南

视频编解码标准体系这个词&#xff0c;听起来像是一堆躺在官网上的技术文档&#xff0c;实际上它是所有编码器、播放器、芯片和云端转码服务共同默认的那条暗线。我在音视频标准化组织里泡了几年&#xff0c;从最初只顾着跑运动搜索算法&#xff0c;到后来真正参与提案、审阅文…

作者头像 李华