您现在的位置是:首页 > 体坛时讯体坛时讯

内核的启动参数是谁拼出来的又是怎么传过去的?——bootargs全链路

查看 烟火之旅 的更多文章烟火之旅 2026-09-20 【体坛时讯】 1123人已围观

bootargs是Linux内核启动时的命令行参数,它告诉内核根文件系统在哪里、使用什么控制台、启用了哪些调试功能等。在U‑Boot引导过程中,bootargs通过环境变量配置,经FDT Fixup注入到设备树的chosen节点,最终被内核读取。这条链路虽然简单,但每一个环节都可能成为引导故障的根源。

问题场景

内核启动时提示Kernel panic - not syncing: VFS: Unable to mount root fs或控制台没有输出——这通常意味着bootargs没有正确传递到内核。bootargs是谁拼出来的?它在什么地方注入到设备树?内核又是从哪里读到的?本文追踪bootargs从U‑Boot到Linux内核的完整链路。

一、bootargs的完整生命周期

wKgZO2qtw56ARYqUAABmBdal4mg565.png

二、bootargs的配置来源

bootargs的值由多个来源拼接而成,优先级从高到低:

来源 优先级 设置方式 说明
环境变量bootargs 最高 setenv bootargs "..." 手动或启动脚本设置
CONFIG_BOOTARGS defconfig中定义 编译时的默认值
FDT中已有的bootargs DTB中chosen节点已有 被上述两者覆盖

在RK3576上,典型的环境变量设置如下:

=> printenv bootargsbootargs=console=ttyS2,1500000root=/dev/mmcblk0p5     rootfstype=ext4 rw init=/sbin/initearlyprintk

这是一个典型的嵌入式Linux bootargs,逐段解析:

参数 含义
console=ttyS2,1500000 控制台为串口2,波特率1500000
root=/dev/mmcblk0p5 根文件系统在eMMC第5分区
rootfstype=ext4 根文件系统类型为ext4
rw 以读写方式挂载根文件系统
init=/sbin/init init程序路径
earlyprintk 早期内核打印(调试用)

三、fdt_chosen注入机制

当U‑Boot执行到image_setup_libfdt()阶段(位于OS_PREP的boot_prep_linux()内部)时,fdt_chosen()函数将bootargs写入DTB:

// common/fdt_support.c — 实际代码intfdt_chosen(void*fdt){ intnodeoffset; interr; char*str; /* bootargs字符串 */ /* 1. 查找或创建 /chosen 节点 */  nodeoffset =fdt_find_or_add_subnode(fdt,0,"chosen"); if(nodeoffset < 0)        return nodeoffset;    /* 2. 通过板级回调获取bootargs */    str = board_fdt_chosen_bootargs(fdt);    if (str) {        err = fdt_setprop(fdt, nodeoffset, "bootargs",                          str, strlen(str) + 1);        if (err < 0) {            printf("WARNING: could not set bootargs %s.n",                   fdt_strerror(err));            return err;        }    }    /* 3. 配置stdout-path */    return fdt_fixup_stdout(fdt, nodeoffset);}

board_fdt_chosen_bootargs()的默认实现就是读取U‑Boot环境变量:

// common/fdt_support.c — 默认的board_fdt_chosen_bootargs__weak char *board_fdt_chosen_bootargs(void*fdt){ returnenv_get("bootargs");}

如果环境变量未设置,返回NULL,fdt_chosen()跳过bootargs写入(此时内核使用DTB中已有的bootargs或CONFIG_CMDLINE)。

注入完成后,DTB中的chosen节点内容如下:

/chosen {  bootargs ="console=ttyS2,1500000 root=/dev/mmcblk0p5        rootfstype=ext4 rw init=/sbin/init earlyprintk";  stdout-path = &uart2;};

四、内核如何读取bootargs

Linux内核在启动早期,通过early_init_dt_scan_chosen()函数从DTB的chosen节点中读取bootargs:

// kernel/drivers/of/fdt.c — Linux内核(实际代码简化)int__initearly_init_dt_scan_chosen(char*cmdline){ intl, node; constchar*p; constvoid*fdt = initial_boot_params; /* 查找/chosen节点 */  node = fdt_path_offset(fdt,"/chosen"); if(node < 0)        goto handle_cmdline;    /* 处理initrd信息 */    early_init_dt_check_for_initrd(node);    /*  读取bootargs属性 */    p = of_get_flat_dt_prop(node, "bootargs", &l);    if (p != NULL && l >0)    strscpy(cmdline, p, min(l, COMMAND_LINE_SIZE)); /* 也支持CONFIG_CMDLINE作为后备 */handle_cmdline: /* 如果bootargs为空,使用CONFIG_CMDLINE */  ...}

读取到的bootargs会存入boot_command_line全局变量,内核后续的early_param()解析器会逐个解析所有参数。

五、bootargs的动态生成

在实际开发中,bootargs经常需要根据运行时条件动态生成。RK3576的启动脚本中常见以下技巧:

# 方法1:基于分区选择root参数ifmmc dev0;then  setenv bootargs"console=ttyS2,1500000          root=/dev/mmcblk0p5          rootfstype=ext4 rw"else if mmc dev1;then  setenv bootargs"console=ttyS2,1500000          root=/dev/mmcblk1p5          rootfstype=ext4 rw"fi# 方法2:追加调试参数setenv bootargs${bootargs}initcall_debug# 方法3:使用CONFIG_BOOTARGS作为基础,追加运行时参数setenv bootargs"${CONFIG_BOOTARGS}quiet"

六、常见bootargs示例

七、RK3576上的典型bootargs

针对不同启动方式,RK3576的bootargs也有所不同:

eMMC启动:

console=ttyS2,1500000root=/dev/mmcblk0p5 rootfstype=ext4 rw

SD卡启动:

console=ttyS2,1500000root=/dev/mmcblk1p5 rootfstype=ext4 rw

NFS网络启动(开发调试):

console=ttyS2,1500000root=/dev/nfsrw nfsroot=192.168.1.100:/srv/nfs/rootfsip=dhcp

initramfs启动:

console=ttyS2,1500000earlyprintk

(根文件系统由initramfs提供,无需root =参数)

八、调试bootargs问题

# 查看当前bootargs=> printenv bootargs# 临时修改bootargs(仅本次启动有效)=> setenv bootargs"console=ttyS2,1500000 root=/dev/mmcblk0p5 debug"# 保存修改到环境变量存储=> saveenv# 检查注入后的DTB内容=> fdt addr${fdt_addr_r}=> fdt list /chosen

九、小结

•bootargs是Linux内核的命令行参数,决定了控制台、根文件系统等关键配置

•U‑Boot通过环境变量bootargs配置,经由fdt_chosen()注入到DTB的chosen节点

•内核在启动早期从DTB中读取bootargs,解析后执行对应的初始化

•bootargs有多种来源,环境变量优先级最高

•RK3576的典型bootargs包含console、root、rootfstype、rw等参数

•调试bootargs问题时应先检查printenv bootargs,再通过fdt list /chosen确认注入结果

审核编辑 黄宇

Tags: