内核启动参数基础:格式与传递方式
说实话,很多人一上来就研究那些复杂的启动参数,结果连最基本的格式都没搞明白。我见过不少同事,在 grub 配置文件里写参数时,少了个等号或者多了个空格,系统直接起不来。嗯,今天我们就从最基础的东西说起。
启动参数长什么样?
内核启动参数,说白了就是一组键值对。你想想看,内核启动时就像一个人刚睡醒,需要知道今天要干什么——从哪个硬盘启动?要不要安静模式?内存怎么分配?这些信息就靠启动参数传给它。
基本格式很简单:
参数名=参数值
参数名1=值1 参数名2=值2
参数名(不带值的布尔参数)
举个例子:
root=/dev/sda1 ro quiet splash
这行参数的意思是:根文件系统在 /dev/sda1,以只读方式挂载,启动时安静模式,显示开机画面。每个参数用空格隔开,就这么简单。
核心要点:参数之间用空格分隔,参数名和值之间用等号连接。布尔型参数(如 quiet)不需要等号,出现即生效。
参数的类型
我个人习惯把启动参数分成三类。这样记起来比较清楚:
| 类型 | 格式 | 示例 | 说明 |
|---|---|---|---|
| 键值对 | key=value | root=/dev/sda1 | 最常用,指定具体值 |
| 布尔开关 | key | quiet | 出现即启用,不出现即禁用 |
| 带参数的开关 | key=val1,val2 | console=tty0,ttyS0 | 逗号分隔多个值 |
我在项目中遇到过一种情况:有人把 root=/dev/sda1 写成了 root = /dev/sda1(等号两边加了空格)。结果内核把 root 当成布尔参数,把 /dev/sda1 当成另一个参数。系统当然起不来。这种坑,踩过一次就记住了。
参数怎么传给内核?
内核本身不直接跟用户交互。它启动的时候,需要一个「中间人」来告诉它这些参数。这个中间人就是 bootloader。
目前主流的 bootloader 就两个:
- GRUB2:Linux 世界的事实标准,几乎所有的发行版都在用
- U-Boot:嵌入式设备、开发板上的常客
它们的传递方式不太一样,但核心逻辑是一样的——把参数字符串放在内存的某个位置,然后告诉内核去哪里找。
GRUB2 的传递方式
GRUB2 的配置文件在 /boot/grub/grub.cfg。不过我不建议你直接改这个文件,因为它是由 update-grub 命令自动生成的。正确的做法是改 /etc/default/grub。
关键变量就一个:
GRUB_CMDLINE_LINUX_DEFAULT="quiet splash"
这个变量里的内容,会被 GRUB2 自动拼接到内核启动命令行上。你改完之后,运行 update-grub 让它生效。
小技巧:临时调试时,不用改配置文件。在 GRUB2 启动菜单界面,按 e 键编辑当前启动项,找到 linux 开头的那一行,直接在末尾加参数。按 Ctrl+X 或 F10 启动。这个修改只对本次启动有效,重启后恢复原样。
U-Boot 的传递方式
嵌入式开发中,U-Boot 更常见。它的方式更直接——通过环境变量:
setenv bootargs 'root=/dev/mmcblk0p2 ro console=ttyS0,115200'
bootm 0x80000000
这里 bootargs 就是 U-Boot 用来存放启动参数的环境变量。内核启动时,U-Boot 会把这个变量的内容传给内核。
我曾经调试一块 ARM 开发板,串口怎么都不输出信息。查了半天,发现是 console=ttyS0,115200 写成了 console=ttyS0,没指定波特率。内核用了默认的 9600,跟终端对不上。嗯,这种细节不注意,真的会浪费一整天。
参数传递的底层原理
你可能想问:bootloader 到底是怎么把参数传给内核的?
其实就两种方式:
- ATAG 列表(老方式,ARM 架构常见)
- 设备树(Device Tree)(新方式,现在的主流)
GRUB2 用的是 x86 特有的 setup_header 结构,内核在 arch/x86/boot/header.S 里定义了一个固定格式的内存区域,bootloader 把参数填进去,内核启动时直接读这块内存。
U-Boot 则更灵活,它把 bootargs 的内容放到设备树的 /chosen/bootargs 属性里,内核从设备树中读取。
注意:不同架构的传递方式有差异。x86 用 setup_header,ARM/ARM64 用设备树,RISC-V 也走设备树。写参数时,要确认你的平台用的是哪种方式。不过好消息是,参数本身的语法是统一的,不管底层怎么传,你写的字符串格式都一样。
一张图看懂整体流程
下面这张图展示了从你写下参数到内核真正用上它的完整路径:
常见误区
我总结几个新手最容易犯的错误:
- 等号两边加空格:
root = /dev/sda1是错的,必须写成root=/dev/sda1 - 参数名拼写错误:比如把
console写成consol,内核直接忽略 - 忘记更新 grub 配置:改了
/etc/default/grub但不跑update-grub,重启后没变化 - 参数顺序搞混:参数之间用空格,不是逗号也不是分号
曾经我踩过的坑:有一次我在 /etc/default/grub 里加了 GRUB_CMDLINE_LINUX_DEFAULT="quiet splash mem=4G",结果系统只认到 3.5G 内存。查了半天才发现,应该用 mem=4096M 而不是 mem=4G。内核解析 G 这个单位的方式跟我想的不一样。从那以后,我所有内存参数都老老实实写 M 为单位。
小结
启动参数的格式其实不复杂。记住三点:
- 参数名=值,布尔参数直接写名字
- 空格分隔,等号两边不要空格
- 通过 bootloader 传递,GRUB2 改
/etc/default/grub,U-Boot 改bootargs
掌握了这些,你就已经迈出了第一步。后面我们会深入具体的参数,看看每个参数到底能干什么。