目录
[2.1 内核版本命名规则](#2.1 内核版本命名规则)
[2.2 下载内核源码](#2.2 下载内核源码)
[2.3 安装编译依赖](#2.3 安装编译依赖)
[三、配置内核:make menuconfig](#三、配置内核:make menuconfig)
[3.1 三种配置界面](#3.1 三种配置界面)
[3.2 以当前配置为起点](#3.2 以当前配置为起点)
[3.3 进入配置菜单](#3.3 进入配置菜单)
[3.4 精简内核的核心思路](#3.4 精简内核的核心思路)
[3.5 配置速查](#3.5 配置速查)
[4.1 编译内核](#4.1 编译内核)
[4.2 安装模块](#4.2 安装模块)
[4.3 安装内核](#4.3 安装内核)
[5.1 确认新内核已添加到启动菜单](#5.1 确认新内核已添加到启动菜单)
[5.2 暴露启动菜单(Ubuntu默认隐藏)](#5.2 暴露启动菜单(Ubuntu默认隐藏))
[5.3 双内核启动策略(保底方案)](#5.3 双内核启动策略(保底方案))
[6.1 不要删除正在运行的内核](#6.1 不要删除正在运行的内核)
[6.2 启动失败的处理](#6.2 启动失败的处理)
[6.3 内存和磁盘要求](#6.3 内存和磁盘要求)
一、引言:为什么要自己编译内核?
对于95%的Linux用户来说,发行版提供的内核已经足够好。但在某些场景下,自己编译内核是有实际价值的:
-
精简与性能:通用内核为了支持从嵌入式设备到超级计算机的所有硬件,包含了数千个驱动模块。裁剪掉你不需要的部分,内核体积更小、启动更快、内存占用更低
-
学习与研究:编译内核是理解Linux底层机制的入口------你会接触到进程调度器、内存管理、文件系统驱动、网络协议栈等底层子系统
-
特定需求:启用发行版默认关闭的内核特性(如实时内核抢占、特定的安全模块),或者你的硬件需要最新的驱动支持
⚠️ 重要提醒 :本文的练习必须在虚拟机中进行。在物理机或生产服务器上编译内核,一旦配置出错,可能导致系统无法启动。先创建虚拟机快照,再动手操作。
二、内核源码获取与准备
2.1 内核版本命名规则
Linux内核的版本号经历了两次命名规则变化:
bash
uname -r # 查看当前内核版本
2.6时代(2003-2011) :2.6.x.y------2.6.32.45中,32是主线版本,45是bug修复号。
3.x/4.x/5.x时代(2011-2024):当2.6的bug修复号积累到一定程度(2.6.39),Linus决定简化版本号,跳到3.0。之后大版本号每隔2-3年升级一次。
6.x时代(2024至今) :版本号彻底变成"无特殊含义"的数字,纯粹是序号。6.x只是5.x之后的自然延续。
内核官网 kernel.org 提供以下几种版本类型:
| 版本类型 | 标识 | 说明 |
|---|---|---|
| 主线版(mainline) | 最新版本号 | Linus Torvalds亲自维护的开发主线 |
| 稳定版(stable) | 版本号 | 主线稳定后转入的维护分支 |
| 长期支持版(longterm) | 标注LTS | 支持长达6年,生产环境使用(也是我们的选择) |
建议 :选择最新的longterm(LTS)版本。LTS版持续接收安全更新和关键bug修复,比最新的主线版本更稳定。
2.2 下载内核源码
bash
# 通过wget下载最新LTS源码(以6.12.13为例,请到kernel.org确认最新LTS版本)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.13.tar.xz
# 解压
tar -xf linux-6.12.13.tar.xz
cd linux-6.12.13/
源码体积约1.5GB(解压后),确保至少有10GB可用空间(编译还会产生大量临时文件)。
2.3 安装编译依赖
bash
sudo apt update
sudo apt install -y \
build-essential \
flex bison \
libssl-dev \
libelf-dev \
libncurses-dev \
dwarves \
bc
各依赖的作用:
-
build-essential:gcc、make等编译工具链 -
flex、bison:词法分析和语法分析工具 -
libssl-dev:内核模块签名 -
libelf-dev:ELF格式支持(内核模块和vmlinux) -
libncurses-dev:make menuconfig的图形菜单界面 -
dwarves:生成BTF(BPF Type Format)调试信息,现代内核必需的依赖 -
bc:数学计算工具
三、配置内核:make menuconfig
3.1 三种配置界面
| 命令 | 界面类型 | 适用场景 |
|---|---|---|
make menuconfig |
终端菜单(ncurses) | 推荐,终端下直观易用 |
make config |
逐行问答 | 极端情况,体验较差 |
make xconfig |
Qt图形界面 | 桌面环境下使用 |
3.2 以当前配置为起点
bash
# 复制当前系统的内核配置作为起点(免去从零开始的繁琐)
cp /boot/config-$(uname -r) .config
# 更新配置以适配新内核版本(自动处理新增/废弃的选项)
make olddefconfig
make olddefconfig的作用:以.config为基础,对于新内核中增加的配置项自动选择默认值,对于已废弃的选项自动忽略。这样可以省去手动回答大量新增选项的麻烦。
3.3 进入配置菜单
bash
make menuconfig
界面操作:
-
方向键:移动光标
-
空格 :切换选项状态(
*=编译进内核,M=编译为可加载模块,空=不编译) -
?:查看选项帮助 -
/:搜索配置项 -
Tab/Enter:切换和进入菜单
-
Esc:返回上层或退出
3.4 精简内核的核心思路
通用内核约80%的驱动和模块是多余的。按以下思路精简:
确认你自己的硬件:
bash
# CPU型号
cat /proc/cpuinfo | grep "model name" | head -1
# 网卡型号
lspci | grep -i ethernet
# 或
lspci | grep -i net
# 磁盘控制器
lspci | grep -i sata
lspci | grep -i nvme
# USB控制器
lspci | grep -i usb
# 文件系统使用情况
df -Th
在menuconfig中按硬件筛选:
-
Processor type and features → 选择你的CPU系列(如Intel/AMD),关闭不相关的微码更新,关闭无关的CPU特性
-
Device Drivers → 这是最大的精简空间。
lspci列出的硬件才编译,其他的都可以关掉 -
File systems → 只选你实际使用的(通常是ext4和xfs),关掉reiserfs、jfs、几十种文件系统
-
Networking support → 网卡驱动只保留你的型号,其余全关
-
Kernel hacking → 生产环境全部关闭(调试选项会拖慢性能)
3.5 配置速查
如果不想深入每个菜单,在做完上述针对性精简后,还有一个原则:确保硬盘控制器驱动 编译进内核(*而非M),否则内核启动时无法加载根文件系统而直接panic。可以先用虚拟机快照备份,然后大胆尝试------实践中踩过的坑往往印象最深。
四、编译与安装
4.1 编译内核
bash
# -j$(nproc) 使用所有CPU核心并行编译
make -j$(nproc)
这个过程通常需要30分钟到2小时,取决于你的CPU性能和精简程度。
4.2 安装模块
bash
sudo make modules_install
这一步将编译好的内核模块安装到/lib/modules/内核版本/目录下。
4.3 安装内核
bash
sudo make install
这一步执行三件事:
-
将内核镜像(
bzImage)复制到/boot/ -
生成
initramfs(初始RAM文件系统) -
自动更新Grub引导菜单(添加新内核条目)
五、Grub引导配置
5.1 确认新内核已添加到启动菜单
bash
# 查看Grub配置文件中的内核条目
grep -E "^menuentry|submenu" /boot/grub/grub.cfg | grep -v recovery
你应该能看到新旧两个内核的条目。默认启动的是排在最前面的(通常是新安装的内核)。
5.2 暴露启动菜单(Ubuntu默认隐藏)
很多发行版默认隐藏Grub菜单,直接启动。修改配置让菜单显示出来,以便手动选择内核:
bash
sudo vim /etc/default/grub
修改以下几行:
ini
GRUB_TIMEOUT_STYLE=menu # 显示菜单(不是hidden)
GRUB_TIMEOUT=5 # 菜单等待5秒
# GRUB_HIDDEN_TIMEOUT=0 # 注释掉这一行
更新Grub使配置生效:
bash
sudo update-grub # Ubuntu/Debian
sudo grub2-mkconfig -o /boot/grub/grub.cfg # CentOS/RHEL
5.3 双内核启动策略(保底方案)
Grub自动帮我们保留了旧内核------新内核启动失败时,重启后在Grub菜单中选择旧内核即可正常进入系统。这就是内核编译的"安全气囊"。
六、安全须知与回滚
6.1 不要删除正在运行的内核
bash
# 千万不要在运行着新内核时删除旧内核!
# 如果新内核出现问题,旧内核是你的救生圈。确认新内核稳定运行一周后再考虑清理
6.2 启动失败的处理
如果重启后新内核启动失败:
-
看到Grub菜单时,选择"Advanced options for Ubuntu"
-
选择旧的、能正常工作的内核版本启动
-
进入系统后,可以将有问题的内核卸载:
bash
dpkg --list | grep linux-image # 找到有问题的内核包
sudo apt remove --purge linux-image-问题版本
6.3 内存和磁盘要求
-
内存:编译消耗约2-3GB,剩余内存在4GB以上
-
磁盘:预留10GB用于编译和安装
-
时间 :首次编译大约2小时(虚拟机上更慢),使用
-j$(nproc)充分利用多核
七、本篇小结
内核编译流程:
text
下载源码 → 安装依赖 → 复制当前配置 → make olddefconfig → make menuconfig
→ make -j$(nproc) → sudo make modules_install → sudo make install
→ 修改Grub显示菜单 → 重启验证
三条安全法则:
-
虚拟机+快照:这是最安全的实验环境,搞崩了秒回滚
-
保留旧内核:Grub自动保留,新内核启动失败时它就是救生圈
-
理解再动手:不要为了"精简数字"盲目关闭不了解的选项
动手练习
bash
# 1. 查看当前内核版本和配置文件
uname -r
ls -lh /boot/config-$(uname -r)
# 2. 查看当前内核的模块数量(感受通用内核的"臃肿")
ls /lib/modules/$(uname -r)/kernel/ | wc -l
# 3. 在虚拟机中走一遍完整流程(核心练习)
wget https://cdn.kernel.org/pub/linux/kernel/v6.x/linux-6.12.13.tar.xz
tar -xf linux-6.12.13.tar.xz
cd linux-6.12.13
cp /boot/config-$(uname -r) .config
make olddefconfig
make menuconfig # 探索一下菜单结构,不一定要改什么
# Ctrl+C 退出即可(如果不想真的编译)
八、下篇预告
内核编译让你看到了操作系统的"零件清单"。但你知道Linux是如何管理内存的吗?free -h看到的buff/cache到底是什么?为什么系统有时候会杀掉你的程序(OOM Killer)?
下一篇我们将深入Linux内存管理 的核心机制------物理内存与虚拟内存的区别、Buffer与Cache的不同角色、Swap分区的真正作用。理解这些,你才能真正看懂free、vmstat的输出,在内存问题排查时做到心中有数。
延伸思考 :你刚刚编译的内核配置中,有数千个选项被设置为m(编译为模块)。模块的优点是按需加载、节省初始内存,但被编译为模块加载需要额外时间。哪些驱动必须 编译进内核(*)?答案是:启动过程中访问根文件系统所必需的驱动------磁盘控制器驱动和根文件系统驱动。如果它们被编译为模块,内核就需要"从磁盘加载磁盘驱动模块",这形成了一个死循环。这也是initramfs存在的原因之一(第14篇已介绍过)。