Linux 启动传参
Linux 启动参数形成了完整链条:U-Boot 设置 bootargs 或 DTB→ 内核命令行→__setup()→ 各子系统应用,包括 console、rootfs、网络配置、调试等。优先级遵循 U-Boot > DTB > 内核默认,CONFIG_CMDLINE_FORCE 可覆盖一切。rdinit与init分别控制早期用户态与正式根文件系统用户态初始化,为嵌入式系统提供灵活的启动策略。
一、启动参数来源
Linux 内核的启动参数主要来源于两个渠道:Bootloader 和 设备树(Device Tree Blob, DTB)。
-
Bootloader(U-Boot 为例)
在 ARM/ARM64 平台上,U-Boot 在启动内核前完成三项工作:
-
设置内核入口地址(
zImage或Image)。 -
准备设备树(FDT)或 ATAGS 结构。
-
传递启动参数字符串,通常通过
bootargs环境变量。
例如,在 U-Boot 中设置 bootargs 并启动内核:
setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait" bootz ${kernel_addr} - ${fdt_addr}
U-Boot 会把该字符串传递给内核。老式 ARM 使用 ATAGS 结构,新式 ARM64 使用 FDT 中 /chosen 节点。
-
Device Tree Blob(DTB)
DTB 中 /chosen 节点也可以定义启动参数,例如:
/ { chosen { bootargs = "console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait"; stdout-path = "serial0:115200n8"; }; };
内核启动时,drivers/of/fdt.c 中的 early_init_dt_scan_chosen() 会解析 /chosen 节点,把 bootargs 拷贝到内核命令行。DTB 还可以定义 initrd 的起止地址、early console 配置等。
总结:U-Boot 和 DTB 最终传递给内核的都是空格分隔的参数字符串,格式相同,内核解析不区分来源。
二、启动参数优先级
Linux 内核接收启动参数时遵循以下优先级:
-
U-Boot bootargs:如果设置了 bootargs,则覆盖 DTB 中的 bootargs。
-
DTB
/chosen/bootargs:U-Boot 未传递参数时使用。 -
内核编译时 CONFIG_CMDLINE:为默认参数,当 U-Boot 和 DTB 都没有时使用。
-
CONFIG_CMDLINE_FORCE:强制使用内核自带参数,覆盖 U-Boot 和 DTB。
开发阶段常在 DTS 中提供最小可启动 bootargs,产品阶段通过 U-Boot 动态传递参数更灵活。
三、内核解析流程
-
内核命令行存储
内核入口 init/main.c 中全局数组保存启动参数:
static char __initdata command_line[COMMAND_LINE_SIZE];
-
DTB 参数解析
DTB /chosen 节点的解析在 drivers/of/fdt.c 的 early_init_dt_scan_chosen() 函数中完成,包括:
-
拷贝 bootargs 到内核命令行
-
读取 initrd 起止地址
-
配置 early console


-
参数解析机制
内核解析启动参数分为三类:
-
早期参数(early_param):在内核初始化早期处理,如
console=,保证日志输出可用。

-
普通参数(__setup):在后续初始化中解析,用于挂载根文件系统、启用模块功能等。

这里都是解析参数中心,可以定义自己关注的key


-
模块参数(module_param):供加载模块解析启动参数。

示例:console 参数解析

根文件系统参数 root= 的解析位于 init/do_mounts.c 的 root_dev_setup(),initramfs 的 init 解析在 init/initramfs.c 中完成。


四、启动参数格式
-
空格分隔:key=value 或单独 flag
-
示例:
console=ttyS0,115200 root=/dev/mmcblk0p2 rw rootwait earlyprintk
-
DTS 中为 FDT blob,字符串用双引号并以分号结束。
-
U-Boot 中为 shell 风格字符串。
-
内核解析时不区分来源。
五、常用经典参数
控制台与调试参数:
-
console=ttyS0,115200:指定内核日志输出到串口

-
earlycon:启用 early console -
loglevel=7:设置内核日志等级
根文件系统参数:

-
root=/dev/mmcblk0p2:指定根文件系统设备 -
rootfstype=ext4:指定根文件系统类型 -
rootwait:等待根设备准备就绪 -
init=/sbin/init:覆盖默认 init 程序 -
rdinit=/linuxrc或rdinit=/init:指定 initramfs 内的 init

Initramfs / Initrd 参数:
-
initrd=<start>,<size>:initrd 内存起止地址initrd=0x900000,9M -
rdinit=/init:initramfs 的 init -
noinitrd:禁止使用 initrd/initramfs

网络与 NFS 参数:
-
ip=dhcp:通过 DHCP 获取 IP -
nfsroot=<server>:/path:指定 NFS 根路径

内存与调试参数:
-
mem=512M:限制可用内存 -
panic=5:内核崩溃后自动重启时间
六、rdinit 与 init 对比
在启动流程中,init 与 rdinit 都用于指定用户态初始化程序,但存在区别:
init
-
指定内核挂载完成根文件系统后执行的初始化程序
-
常见值
/sbin/init或/lib/systemd/systemd -
解析位置:
init/main.c中init_task设置,并通过do_basic_setup()调用
rdinit
-
指定 initramfs / initrd 内部的初始化程序
-
内核在挂载 initramfs 后立即执行
rdinit指定程序 -
优先于根文件系统 init 执行
-
解析位置:
init/initramfs.c中initramfs_load()和populate_rootfs()
总结:rdinit 用于早期用户态(initramfs),在根文件系统可用之前执行,Linux 内核内置的一种 cpio 打包格式,会被解压到一个 ramfs 或 tmpfs 上,作为临时的 rootfs;init 用于根文件系统挂载完成后启动用户态初始化。
七、启动参数总结
Linux 启动参数形成了完整链条:U-Boot 设置 bootargs 或 DTB /chosen/bootargs → 内核命令行 command_line[] → parse_early_param() / __setup() → 各子系统应用,包括 console、rootfs、网络配置、调试等。优先级遵循 U-Boot > DTB > 内核默认,CONFIG_CMDLINE_FORCE 可覆盖一切。
rdinit 与 init 分别控制早期用户态与正式根文件系统用户态初始化,为嵌入式系统提供灵活的启动策略。
八、启动过程中切换文件系统
Linux 启动的核心点在 switch_root / pivot_root:
-
内核初始化时,挂载 initramfs → 作为 rootfs。
-
执行 initramfs 中的
/init脚本或二进制。 -
在
/init里面通常会做:
-
挂载真正的根文件系统(例如 eMMC 上的 ext4,UFS,NAND,NFS root 等)。
-
执行
switch_root /newroot /init,把 rootfs 从 initramfs 切换到真正的存储设备。 -
或者执行
pivot_root实现类似效果。
-
switch_root 的作用:
-
把新的文件系统挂到
/。 -
把原来的 initramfs 挂到某个临时目录(通常是
/old_root),再卸载释放。
源码入口:
-
init/do_mounts_initrd.c(旧 initrd 方案) -
init/do_mounts.c(rootfs 挂载) -
用户态执行
/init(busybox 里常见switch_root工具)。
如果系统 不具备持久化存储(例如FPGA原型验证),完全可以只依赖内存里的 initramfs 来跑。
此时就不会调用 switch_root,而是直接在 initramfs 的 /init 里面启动服务(例如 shell、应用程序)。
代价:
-
所有数据都在内存中,掉电即失。
-
内存占用不可释放,因为 ramfs 不支持 swap-out,initramfs 的文件会一直常驻内存。

更多推荐



所有评论(0)