1. 内核启动流程全景:从BIOS/UEFI到init进程的完整链路
大家好,我是你们的调试老手。今天咱们聊聊内核启动的全景图。
说实话,我见过太多工程师一遇到启动卡住就慌了神。要么对着黑屏发呆,要么在dmesg里乱翻。其实啊,你只要把启动链路理清楚,问题就解决了一半。
我个人习惯把启动分成四个宏观阶段。每个阶段都有它自己的脾气。你想想看,从按下电源键到出现登录提示符,这中间发生了多少事?
1.1 第一阶段:固件层(BIOS/UEFI)
按下电源键,CPU先跑的是固件代码。这时候内核还没影呢。
- BIOS(传统方式):先做POST自检,然后按启动顺序找MBR。我记得有次调试,客户说机器启动到一半就黑屏。查了半天,结果是BIOS里启动顺序配错了,它压根没去找硬盘上的内核。
- UEFI(现代方式):更灵活,支持GPT分区,能直接加载EFI应用。我建议新项目都用UEFI,省心。
关键点:这个阶段卡住,99%是硬件问题或固件配置错误。别急着怀疑内核。
1.2 第二阶段:Bootloader(引导加载器)
固件把控制权交给bootloader。常见的有GRUB2、U-Boot(嵌入式)。
Bootloader的任务很简单:加载内核镜像到内存,然后跳过去执行。但这里有个坑——它得知道内核放哪了,根文件系统在哪。
我曾经遇到一个案例:GRUB配置里root参数写错了,内核启动到一半就panic,提示找不到根设备。说白了,就是bootloader没把正确的参数传给内核。
调试技巧:在GRUB菜单按'e'编辑启动参数,加个init=/bin/bash试试。如果系统能进单用户模式,那问题多半在init进程或根文件系统上。
1.3 第三阶段:内核初始化(核心阶段)
这是最复杂、也最容易出问题的阶段。内核从入口函数start_kernel()开始,一路初始化各种子系统。
我把它拆成几个子步骤:
- 早期架构初始化:设置页表、中断向量、控制寄存器。嗯,这里要是出问题,你连个打印都看不到。
- 控制台初始化:printk开始工作。我个人习惯在这里加一些自定义打印,方便定位。
- 内存管理初始化:建立伙伴系统、slab分配器。我记得有次调试,内核在
mm_init()里卡住了,原因是内存描述符被破坏了。 - 调度器初始化:创建init进程(PID 1)。
- 设备驱动初始化:枚举PCI设备、加载驱动。这是panic高发区。
注意:如果内核在do_initcalls()阶段卡住,多半是某个驱动模块的init函数出了问题。我曾经遇到一个网卡驱动,它在申请DMA缓冲区时死循环了——因为硬件没就绪,它一直在轮询。
为什么会在这个阶段panic?说白了,内核发现某个关键资源拿不到,或者数据结构被破坏,它觉得没法继续了,就主动触发panic。比如BUG_ON()、panic()调用。
1.4 第四阶段:init进程启动
内核初始化完成后,会启动用户空间的第一个进程——init。传统上是/sbin/init,现代系统可能是systemd。
这个阶段卡住,通常是因为:
- 根文件系统挂载失败(比如fstab写错了)
- init程序本身损坏或不存在
- 依赖的服务启动超时
我遇到过最离谱的一次:客户说系统启动到一半就重启。查了三天,发现是init脚本里有个rm -rf /的bug——嗯,你没看错,它把根目录删了。
1.5 宏观阶段总结
为了让你更直观地理解,我画了一张流程图:
1.6 实战:如何快速定位卡住阶段
遇到启动卡住,我的第一反应是:先看它卡在哪个阶段。
| 现象 | 可能阶段 | 排查方法 |
|---|---|---|
| 黑屏,无任何输出 | BIOS/UEFI 或 Bootloader | 检查硬件、启动顺序、GRUB配置 |
| 有内核版本打印,然后卡住 | 内核初始化早期 | 加earlyprintk参数,看最后一条打印 |
| 有驱动加载信息,然后panic | 设备驱动初始化 | 查看panic栈回溯,定位哪个驱动 |
出现VFS: Cannot open root device |
根文件系统挂载 | 检查内核参数root=和initramfs |
出现Kernel panic - not syncing: Attempted to kill init! |
init进程启动 | 检查init程序是否存在、权限是否正确 |
我的小技巧:在编译内核时,打开CONFIG_DEBUG_KERNEL和CONFIG_EARLY_PRINTK。这样即使早期阶段出问题,也能看到一些输出。我曾经靠这个救回一块变砖的开发板。
好了,这一章就到这里。记住一句话:启动卡住不可怕,可怕的是你不知道卡在哪。把宏观链路刻在脑子里,排查起来就有方向了。
公众号:蓝海资料掘金营,微信deep3321