1. Linux内核启动流程概述
在Ubuntu 20.04系统上进行Linux内核移植时,理解内核启动流程是至关重要的基础工作。内核启动过程就像一场精心编排的交响乐,每个阶段都有其特定的职责和时序要求。从按下电源键到出现登录提示符,整个过程可以分为几个关键阶段:
首先是BIOS/UEFI固件阶段,这个阶段与具体硬件平台紧密相关。以x86架构为例,当电源接通后,CPU会从特定的内存地址开始执行,这个地址通常指向主板上的固件程序(BIOS或UEFI)。固件会完成硬件自检(POST)、初始化关键设备,然后按照预设的启动顺序查找可启动设备。
注意:现代服务器和工作站大多采用UEFI启动方式,它与传统BIOS在启动流程上有显著差异,特别是在分区表和文件系统支持方面。
接下来是bootloader阶段,在Ubuntu 20.04中默认使用的是GRUB2。GRUB2的主要职责是:
- 提供启动菜单让用户选择要启动的内核版本
- 加载选定的内核映像和initramfs到内存
- 设置必要的启动参数
- 将控制权转交给内核
内核接管后,会依次经历以下关键步骤:
- 解压和自解压(如果使用压缩内核)
- 架构特定的低级初始化
- 通用内核初始化
- 驱动初始化和外设检测
- 挂载根文件系统
- 启动用户空间的init进程
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 内核移植前的准备工作
2.1 硬件环境确认
在进行内核移植前,必须对目标硬件有充分了解。以常见的x86_64平台为例,需要确认以下硬件信息:
bash复制lscpu # 查看CPU架构和特性
lsblk # 查看存储设备布局
lspci -vvnn # 查看PCI设备详情
dmesg | grep -i memory # 查看内存信息
对于嵌入式设备,可能还需要:
- 确认SoC型号和特性
- 获取原理图和硬件手册
- 了解启动介质类型(NOR Flash, eMMC等)
- 确认外设接口和引脚分配
2.2 软件环境准备
Ubuntu 20.04作为开发环境,需要安装必要的工具链:
bash复制sudo apt update
sudo apt install build-essential libncurses-dev bison flex libssl-dev \
libelf-dev bc git dwarves zstd
关键工具版本要求:
- gcc: ≥ 7.5.0
- make: ≥ 4.2.1
- binutils: ≥ 2.34
- flex/bison: 最新稳定版
提示:建议使用Ubuntu官方源中的默认版本,避免因工具链版本不匹配导致构建问题。
2.3 内核源码获取
获取内核源码有两种主要方式:
- 从Ubuntu官方仓库获取已定制的内核源码:
bash复制apt source linux-image-$(uname -r)
- 从kernel.org获取主线内核源码:
bash复制wget https://cdn.kernel.org/pub/linux/kernel/v5.x/linux-5.15.123.tar.xz
tar xvf linux-5.15.123.tar.xz
对于嵌入式开发,可能需要从SoC厂商获取经过修改的内核源码树,这些源码通常包含针对特定硬件的补丁和驱动。
3. 内核配置与编译
3.1 配置内核选项
进入内核源码目录后,首先需要配置内核:
bash复制make menuconfig
关键配置区域:
- General setup → 设置本地版本字符串
- Processor type and features → 选择正确的CPU架构和特性
- Device Drivers → 启用必要的硬件驱动
- File systems → 启用目标系统所需的文件系统支持
- Kernel hacking → 调试选项(开发阶段建议启用)
对于Ubuntu 20.04桌面系统,可以从当前运行的系统复制配置:
bash复制cp /boot/config-$(uname -r) .config
make olddefconfig
3.2 编译内核映像
配置完成后,开始编译内核:
bash复制make -j$(nproc) bindeb-pkg # 生成Debian包
# 或者
make -j$(nproc) bzImage # 仅编译内核映像
make -j$(nproc) modules # 编译内核模块
编译产物说明:
- arch/x86/boot/bzImage:压缩的内核映像
- System.map:内核符号表
- .config:使用的配置
- 各模块的.ko文件
3.3 处理内核模块
内核模块需要单独安装到指定目录:
bash复制sudo make modules_install INSTALL_MOD_PATH=/target/rootfs
对于嵌入式系统,可能需要调整模块目录结构或压缩模块:
bash复制find /target/rootfs/lib/modules -name "*.ko" | xargs strip --strip-unneeded
4. 启动加载器配置
4.1 GRUB2配置详解
Ubuntu 20.04使用GRUB2作为默认启动加载器,其配置文件位于:
- /etc/default/grub:全局设置
- /etc/grub.d/:脚本目录
- /boot/grub/grub.cfg:生成的最终配置
修改GRUB配置后需要更新:
bash复制sudo update-grub
关键GRUB参数:
GRUB_CMDLINE_LINUX_DEFAULT:默认内核参数GRUB_TIMEOUT:菜单等待时间GRUB_DISABLE_RECOVERY:是否禁用恢复模式
4.2 内核启动参数解析
常见的Linux内核启动参数包括:
text复制root=UUID=xxxx # 指定根文件系统
ro/rw # 只读/读写挂载根
init=/sbin/init # 指定init程序
console=ttyS0,115200 # 指定控制台
loglevel=7 # 设置内核日志级别
对于调试目的,可以添加:
text复制earlyprintk=serial,ttyS0,115200
kgdboc=ttyS0,115200
5. 内核启动流程深度解析
5.1 从_start到start_kernel
x86架构的内核启动始于arch/x86/boot/header.S中的_start,主要阶段:
-
实模式初始化:
- 设置堆栈
- 检测内存布局
- 初始化视频模式
- 进入保护模式
-
解压缩阶段:
- 检查硬件兼容性
- 解压内核到指定内存位置
- 跳转到解压后的内核入口
-
内核初始化:
- 设置分页和内存管理
- 初始化中断描述符表(IDT)
- 检测CPU特性
- 调用start_kernel()
5.2 核心初始化流程
start_kernel()是Linux内核的主要初始化函数,位于init/main.c,其关键调用顺序:
c复制start_kernel()
→ setup_arch() // 架构相关初始化
→ setup_command_line() // 解析启动参数
→ trap_init() // 初始化异常处理
→ mm_init() // 内存管理初始化
→ sched_init() // 调度器初始化
→ init_IRQ() // 中断初始化
→ time_init() // 时钟初始化
→ console_init() // 控制台初始化
→ rest_init() // 创建init进程
5.3 用户空间初始化
rest_init()会创建两个关键进程:
- 内核线程init(PID 1)
- kthreadd(PID 2)负责管理其他内核线程
init进程会尝试执行用户空间的init程序,Ubuntu 20.04使用systemd作为init系统:
text复制kernel → /sbin/init → systemd → target units → getty/login
如果initramfs被使用,流程会稍有不同:
- 内核执行initramfs中的/init
- init脚本加载必要驱动
- 挂载真正的根文件系统
- 执行switch_root切换到新根
- 执行/sbin/init
6. 常见问题与调试技巧
6.1 启动卡住问题排查
当内核启动过程中卡住时,可以:
- 添加启动参数
loglevel=7 debug增加日志输出 - 检查卡住前最后显示的信息
- 尝试禁用图形输出
nomodeset - 逐个禁用可能有问题的高级功能(如ACPI、SMP等)
常见卡住点及解决方案:
- 卡在"ACPI"相关消息:尝试
acpi=off - 卡在"Waiting for root device":检查
root=参数是否正确 - 卡在"SMP"初始化:尝试
nosmp
6.2 内核崩溃分析
当遇到内核崩溃(Kernel Panic)时:
- 记录完整的错误信息
- 检查调用栈(stack trace)
- 确认崩溃时的CPU状态
- 检查内存信息
使用kdump获取崩溃转储:
- 配置kdump内核
bash复制sudo apt install kdump-tools
sudo dpkg-reconfigure kdump-tools
- 触发崩溃后分析/vmcore
6.3 性能优化建议
针对启动速度的优化:
- 精简内核配置,移除不需要的驱动和功能
- 使用并行初始化:
text复制init=/lib/systemd/systemd --parallel
- 预链接动态库:
bash复制sudo apt install prelink
sudo prelink -amR
- 使用initramfs-tools的"fastboot"选项
7. 高级主题:自定义初始化内存盘
7.1 initramfs原理与结构
initramfs是一个cpio格式的归档,包含启动早期需要的文件:
bash复制lsinitramfs /boot/initrd.img-$(uname -r)
典型目录结构:
- /bin:必要的工具(busybox)
- /lib:共享库和内核模块
- /scripts:挂载根文件系统的脚本
- /init:初始化脚本
7.2 构建自定义initramfs
手动构建initramfs的步骤:
- 创建基本目录结构:
bash复制mkdir -p initramfs/{bin,dev,etc,lib,proc,sys}
- 复制必要工具(使用busybox):
bash复制cp $(which busybox) initramfs/bin/
ln -s bin/busybox initramfs/init
- 创建初始化脚本:
bash复制cat > initramfs/init <<EOF
#!/bin/sh
mount -t proc proc /proc
mount -t sysfs sysfs /sys
exec /bin/sh
EOF
chmod +x initramfs/init
- 打包为cpio归档:
bash复制(cd initramfs && find . | cpio -H newc -o | gzip > ../custom-initramfs.cpio.gz)
7.3 集成到启动流程
将自定义initramfs与内核一起使用:
- GRUB配置中添加:
text复制initrd /boot/custom-initramfs.cpio.gz
- 或者直接整合到内核:
bash复制mkinitramfs -o custom-initrd
objcopy --add-section .initrd=custom-initrd \
--change-section-vma .initrd=0x3000000 \
vmlinux vmlinux.with-initrd
8. 实际移植案例:Ubuntu 20.04到ARM开发板
8.1 交叉编译环境搭建
针对ARM架构的交叉编译工具链安装:
bash复制sudo apt install gcc-arm-linux-gnueabihf \
binutils-arm-linux-gnueabihf \
libc6-dev-armhf-cross
验证工具链:
bash复制arm-linux-gnueabihf-gcc --version
8.2 内核配置调整
针对ARM的关键配置:
bash复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- menuconfig
必须配置:
- System Type → 选择正确的SoC系列
- Kernel Features → 调整内存布局
- Boot options → 设置默认命令行
- Device Drivers → 启用板级特定驱动
8.3 设备树处理
现代ARM Linux使用设备树描述硬件:
- 编译设备树:
bash复制make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- dtbs
- 设备树源文件位置:
text复制arch/arm/boot/dts/
- 典型修改内容:
- 内存大小和布局
- 时钟配置
- 外设地址和中断
- 引脚复用配置
8.4 部署与测试
将编译产物部署到开发板:
- 通过TFTP加载测试:
bash复制tftp 0x82000000 zImage
tftp 0x83000000 dtb-file.dtb
bootz 0x82000000 - 0x83000000
- 永久烧写到Flash:
bash复制flash_erase /dev/mtd0 0 0
nandwrite -p /dev/mtd0 zImage
- 验证启动:
bash复制dmesg | grep -i error
cat /proc/cmdline
ls /dev
