kernel 构建 ubuntu deb(1)dpkg/grub/initrd
最近在给Ubuntu提供可安装kernel的deb包,先介绍一下用于管理deb软件包的核心工具,主要用于安装/卸载/查询/管理本地软件包
1.dpkg
基本语法:dpkg <选项> <包名>
安装:dpkg -i <x.deb> , 可能会出现依赖缺失导致的安装失败,可修复依赖后重新安装
卸载:dpkg -r x.deb 卸载 / -P 彻底卸载
查找信息:-l 查找装了那些包 / -s 可查看安装后的详细信息 / -I 可以显示control文件中对deb的描述/ -c 显示出这些包包含了哪些内容
2.update-grub
它会自动检测系统中已安装的内核和操作系统,并生成新的引导菜单(通常位于 /boot/grub/grub.cfg
)
在你安装内核的过程中,update-grub
主要起到两个作用:
- 识别新内核:安装
linux-kernel-<版本>
包后,update-grub
会检测到/boot/vmlinuz-<版本>
和对应的initrd.img-<版本>
,并将其添加到引导菜单。 - 移除旧内核:卸载内核包后,执行
update-grub
会从引导菜单中删除已卸载的内核条目,避免引导错误
3.grub.cfg
# 5 秒后自动启动默认项 set timeout=5 set default="questing_crd_lx" # ID,使用唯一标识 # 第一个启动项(原 Ubuntu 官方版本) menuentry "Ubuntu for X Elite CRD" --id questing_crd_ubuntu { set root=(hd0,gpt13) devicetree /lib/firmware/6.16.0-rc4-g52f18bdf8da1/device-tree/x1e80100-crd.dtb linux /boot/vmlinuz-6.16.0-rc4-g52f18bdf8da1 \ earlycon console=ttyMSM0,115200n8 \ root=/dev/nvme0n1p13 \ cma=128m rw clk_ignore_unused pd_ignore_unused \ efi=noruntime rootwait ignore_loglevel initrd /boot/initrd.img-6.16.0-rc4-g52f18bdf8da1 } # 第二个启动项(你的自定义版本) menuentry "LX for X Elite CRD" --id questing_crd_lx { set root=(hd0,gpt13) devicetree /lib/firmware/6.16.0-rc2-00098-g3c20b5578437/device-tree/x1e80100-crd.dtb linux /boot/vmlinuz-6.16.0-rc2-00098-g3c20b5578437 \ earlycon console=ttyMSM0,115200n8 \ root=/dev/nvme0n1p13 \ cma=128m rw clk_ignore_unused pd_ignore_unused \ efi=noruntime rootwait ignore_loglevel initrd /boot/initrd.img-6.16.0-rc2-00098-g3c20b5578437 }
1.set default是指定默认启动的菜单项
2.menuentry就是定义启动项的关键字,双引号就是启动配置的名字
3.set root=(hd0,gpt13)
定义该启动项的 “根设备”(即启动文件所在的分区)。
(hd0)表示第一块硬盘(GRUB 中硬盘从hd0开始编号,第二块为hd1等)。
gpt13表示该硬盘使用 GPT 分区表,且启动文件在第 13 个分区
4.devicetree是针对嵌入式系统(如 ARM 架构设备)的配置,指定设备树文件(.dtb)的路径。
5.linux /boot/vmlinuz-6.16.0-rc4-g52f18bdf8da1 earlycon console=ttyMSM0,115200n8 root=/dev/nvme0n1p13 cma=128m rw clk_ignore_unused pd_ignore_unused efi=noruntime rootwait ignore_loglevel
linux命令指定 Linux 内核文件的路径及内核启动参数,是启动项的核心配置。
/boot/vmlinuz-6.16.0-rc4-g52f18bdf8da1:内核镜像文件的路径,vmlinuz是压缩的内核文件,后面的字符串是内核版本号(6.16.0-rc4是主版本,g52f18bdf8da1是编译时的 Git commit 号,用于区分内核版本)。
后续参数是内核启动参数(关键参数解析):
earlycon:启用早期控制台输出(内核启动初期就输出日志,方便调试)。
console=ttyMSM0,115200n8:指定系统控制台设备和波特率。ttyMSM0是串口设备名(高通 MSM 芯片的串口),115200n8表示波特率 115200、无校验位、8 位数据位。
root=/dev/nvme0n1p13:指定系统根文件系统所在的分区。nvme0n1是第一块 NVMe 固态硬盘,p13是该硬盘的第 13 个分区(与前面set root的分区一致,确保内核能找到根文件系统)。
cma=128m:设置连续内存分配器(CMA)的大小为 128MB,用于内核分配大块连续内存(如显卡、摄像头等设备需要)。
rw:以 “读写模式” 挂载根文件系统(默认是只读,rw确保系统启动后能写入文件)。
clk_ignore_unused / pd_ignore_unused:忽略未使用的时钟(clk)和电源域(pd),常用于嵌入式设备减少不必要的功耗或避免未使用硬件的报错。
efi=noruntime:禁用 EFI 运行时服务(部分嵌入式设备可能不依赖 EFI,禁用可避免兼容性问题)。
rootwait:内核等待根文件系统就绪后再挂载(避免根分区还未初始化就尝试挂载导致失败)。
ignore_loglevel:忽略内核日志级别限制,输出所有级别的日志(方便调试,正常使用可去掉)。
4.vmlinuz和Image的区别
编译出来的内容是Image
打包的deb里面包含的是vmlinuz
比较一下
对比维度 | Image | vmlinuz |
压缩 | 未压缩 | 已压缩(带解压逻辑) |
适用平台 | 嵌入式系统(如 ARM 裸机) | 桌面 / 服务器(x86)、带 GRUB 的设备 |
启动流程 | 直接加载运行 | 引导程序加载 → 自动解压 → 运行 |
体积 | 大 | 小 |
5.initrd
5.1initrd出现的意义
先说一下linux启动的核心步骤:
- 引导程序(如 GRUB)加载内核(
vmlinuz
); - 内核启动后需要挂载根文件系统(
/
)才能继续运行用户态程序(如init
或systemd
)。
这里存在一个鸡生蛋,蛋生鸡的悖论:根文件系统依赖特定驱动(比如nvme硬盘驱动),而这些驱动就存储在根文件系统中,所以此时initrd出现了:
- initrd是一个预先打包了关键驱动、工具的临时文件系统,被内核直接加载到内存中。
- 内核先挂载
initrd
,利用其中的驱动(比如nvme硬件驱动)识别并挂载真正的根文件系统,最后切换到根文件系统继续启动。
5.2启动具体步骤
- 加载阶段:GRUB 引导程序将 vmlinuz(内核)和 initrd.img(临时文件系统)一起加载到内存中。
- 内核初始化:内核启动后,检测到 initrd 存在,会将其挂载为临时根文件系统(/),并执行其中的初始化脚本(通常是 /init)。
- 准备根文件系统:initrd 中的脚本(如 init)会:加载根文件系统所需的驱动(如 NVMe 硬盘驱动、加密模块);检测并激活 LVM、RAID 等复杂存储结构;解密加密的根分区(如 LUKS 加密);识别真正的根文件系统设备(如 /dev/nvme0n1p13)。
- 切换到根文件系统:完成准备后,initrd 会将真正的根文件系统挂载到 /sysroot,然后通过 pivot_root 或 switch_root 命令切换根目录(从 initrd 切换到实际的 /),最后执行根文件系统中的初始化程序(如 systemd)。
- 清理阶段:切换完成后,initrd 占用的内存会被释放,临时文件系统不再使用
5.3 为什么需要initrd
- 支持复杂存储设备:现代硬件(如 NVMe 硬盘、USB 3.0 存储、RAID 阵列)的驱动可能未编译进内核(仅作为模块),initrd 可预先加载这些模块,确保内核能识别根文件系统。
- 处理加密 / 特殊文件系统:若根文件系统是加密的(如 LUKS)或使用特殊格式(如 Btrfs、ZFS),initrd 中的工具(如 cryptsetup)可在挂载前完成解密或格式识别。
- 简化内核设计:内核无需内置所有可能的驱动(否则体积会过大),而是通过 initrd 动态加载所需驱动,保持内核精简。