系列第 2 篇|启动链第一环:把控制权交给 Linux 内核本篇重点建立概念,不讲移植手册
上一篇把整条启动链走了一遍。本篇只聚焦第一环:Bootloader(引导加载程序)——负责把控制权从机器固件交到 Linux 内核手里的那段程序。
本系列后面的 PC demo,实际用到的是 UEFI + GRUB;U-Boot 和 Little Kernel 作为嵌入式对照理解即可。
如果你写过 C:Bootloader 很像「自己实现一个简易加载器」——把另一个程序的二进制读进内存,设置好参数,然后 call 过去;区别是它运行得更早,几乎没有操作系统可依赖。
不管产品文档里写的是 GRUB 还是 U-Boot,核心职责通常都包括以下五步。理解这五步,比背产品名更重要。
做最小硬件初始化让内存、时钟、存储控制器等处在「至少能读到内核文件」的状态。PC 上很多事固件已经做了一部分;嵌入式上 U-Boot 往往要做更多。
从介质加载内核映像介质可能是硬盘、SSD、U 盘、eMMC、SD 卡,甚至网络。Bootloader 要访问这些介质上的分区或文件系统,把内核文件读出来。
准备硬件描述信息内核需要知道「这台机器有什么设备」。在 PC(x86)上,很多信息通过 ACPI 提供;在大量嵌入式板上,则使用设备树 DTB(Device Tree Blob)。
设置启动参数(cmdline)例如告诉内核:根文件系统在哪个分区(root=),日志打印到哪个控制台(console=)。
跳转到内核入口至此 Bootloader 的历史使命结束,CPU 开始执行内核代码。
.efi 程序(常是 GRUB) | ||
bootcmd、bootargs)描述如何加载与传参 | ||
本系列 demo 要在 Windows PC 上做双系统,所以你会反复碰到 UEFI 启动项和 GRUB 菜单;它们是上下游关系,不是二选一。
交接时,Bootloader 通常会准备好这些东西:
root= | |
console= | |
init= | /sbin/init 等默认位置 |
PC 上常见样子(示意):
root=UUID=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxxro quiet意思大概是:根分区用这个 UUID 标识;只读方式先挂上(ro);少打印一些信息(quiet)。
嵌入式串口调试时常见样子(示意):
console=ttyS0,115200root=/dev/mmcblk0p2 rw意思大概是:日志打到串口 ttyS0,波特率 115200;根文件系统在 eMMC / SD 的第 2 分区;可读写挂载(rw)。
你以后排错时,先读懂 cmdline,往往比一上来就翻内核源码有效得多。
下面例子只为建立形状印象,不必在此刻背命令。
GRUB 菜单项在配置里的样子
menuentry 'My Linux' { linux /boot/vmlinuz root=UUID=... ro initrd /boot/initrd.img}用文字翻译:
menuentry | |
linux ... | |
initrd ... |
U-Boot 环境变量的样子
setenv bootargs \ console=ttyS0,115200 \ root=/dev/mmcblk0p2 rwsetenv bootcmd \ 'fatload mmc 0:1 0x80080000 Image; \ booti 0x80080000 - 0x83000000'boot用文字翻译:
bootargs | |
bootcmd | |
boot |
请用自己的话确认能回答这三句:
root=、console=,你应该能说出它们在指挥内核做什么。下一篇我们继续往后看:内核接过控制权之后,如何一步步走到挂载 rootfs、启动 init。