Linux 启动时内核参数的作用机制

12次阅读

内核参数由引导加载器(如GRUB或U-Boot)传给内核,或通过设备树/chosen/bootargs提供,最终统一存入command_line[];内核不解析配置文件,仅处理启动时传递的字符串

Linux 启动时内核参数的作用机制

内核参数是谁传给谁的?

内核参数不是内核自己“想出来”的,而是由引导加载器(如 GRUBU-Boot)在跳转到内核入口前,把一串空格分隔的字符串(比如 root=/dev/sda2 ro console=ttyS0,115200)放进内存特定位置,再通知内核去读。ARM64 上还可能来自设备树 /chosen/bootargs,但最终都合并进同一个命令行缓冲区 command_line[]

关键点在于:内核本身不解析配置文件,它只认这一条“启动时塞过来的字符串”。所以改参数,本质是改引导阶段的传递行为。

内核怎么“看懂”这些参数?

内核用三类注册机制分阶段处理参数:

  • early_param():极早期就跑,比如 console=loglevel=,确保日志能立刻输出——否则你连“参数没生效”都看不到
  • __setup():主初始化阶段调用,处理 root=init=acpi=off 这类核心配置,失败通常导致挂载失败或卡死
  • module_param():模块加载时才解析,比如某些驱动支持 xxx.debug=1,不加载模块时该参数直接被忽略

所有未被上述机制捕获的参数(如 TERM=vt100)会原样传给 init 进程当环境变量;而像 single 这种既不是内核关键字、也不是环境变量的,会被 init 自己解释为单用户模式。

为什么改了参数却没效果?

常见失效原因不是语法错,而是优先级和时机问题:

  • U-Boot 的 bootargs 会覆盖设备树里的 /chosen/bootargs;而内核编译时硬编码CONFIG_CMDLINE 只有在前两者都为空时才生效
  • CONFIG_CMDLINE_FORCE=y 会强制使用内核自带参数,哪怕 U-Boot 明确传了也无视——调试时容易误以为“我改了 GRUB 却没变”,其实是被内核自己锁死了
  • 某些参数(如 mem=maxcpus=)必须在内核解压后、内存管理初始化前生效,晚一秒就无效;而 selinux=0 这类则依赖 LSM 框架初始化顺序,太早或太晚都可能被忽略

验证是否真正生效,别只看 GRUB 菜单里显示的字符串,要进系统后检查 /proc/cmdline ——那才是内核实际收到的原始输入。

临时调试 vs 永久生效:操作差异在哪?

临时改(比如按 e 进 GRUB 编辑)只影响当前启动,适合快速验证硬件兼容性问题(如加 nomodeset 看是否显卡导致黑屏);永久改则必须落盘:

  • GRUB 系统:/etc/default/grubGRUB_CMDLINE_LINUX,再运行 grub2-mkconfig -o /boot/grub2/grub.cfg
  • U-Boot 系统:用 setenv bootargs "..." + saveenv,注意有些板子需写入 SPI Flash 才持久

最容易漏的是:改完 /etc/default/grub 忘了重生成配置,或者重生成时指错了输出路径(比如 EFI 分区挂载在 /boot/efi 却写到了 /boot/grub2),结果重启还是老参数。

真正难的不是记参数,而是理解“哪个组件在哪个时刻读它、读不到会怎样”。比如 rootwait 不是让内核等,而是让 initramfs 的挂载逻辑主动轮询设备就绪;少了它,NVMe 盘稍慢一点就直接 panic——这种隐含依赖,文档里往往不写,只能从 init/do_mounts.c 源码里翻。

text=ZqhQzanResources