当前位置: 首页 > web >正文

Linux 启动传参

一、启动参数来源

Linux 内核的启动参数主要来源于两个渠道:Bootloader设备树(Device Tree Blob, DTB)

  1. Bootloader(U-Boot 为例)

在 ARM/ARM64 平台上,U-Boot 在启动内核前完成三项工作:

  • 设置内核入口地址(zImageImage)。

  • 准备设备树(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 节点。

  1. 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 动态传递参数更灵活。

三、内核解析流程

  1. 内核命令行存储

内核入口 init/main.c 中全局数组保存启动参数:

static char __initdata command_line[COMMAND_LINE_SIZE];

  1. DTB 参数解析

DTB /chosen 节点的解析在 drivers/of/fdt.cearly_init_dt_scan_chosen() 函数中完成,包括:

  • 拷贝 bootargs 到内核命令行

  • 读取 initrd 起止地址

  • 配置 early console

  1. 参数解析机制

内核解析启动参数分为三类:

  • 早期参数(early_param):在内核初始化早期处理,如 console=,保证日志输出可用。

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

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

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

示例:console 参数解析

根文件系统参数 root= 的解析位于 init/do_mounts.croot_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=/linuxrcrdinit=/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 对比

在启动流程中,initrdinit 都用于指定用户态初始化程序,但存在区别:

init

  • 指定内核挂载完成根文件系统后执行的初始化程序

  • 常见值 /sbin/init/lib/systemd/systemd

  • 解析位置:init/main.cinit_task 设置,并通过 do_basic_setup() 调用

rdinit

  • 指定 initramfs / initrd 内部的初始化程序

  • 内核在挂载 initramfs 后立即执行 rdinit 指定程序

  • 优先于根文件系统 init 执行

  • 解析位置:init/initramfs.cinitramfs_load()populate_rootfs()

总结rdinit 用于早期用户态(initramfs),在根文件系统可用之前执行,Linux 内核内置的一种 cpio 打包格式,会被解压到一个 ramfs 或 tmpfs 上,作为临时的 rootfsinit 用于根文件系统挂载完成后启动用户态初始化。

七、启动参数总结

Linux 启动参数形成了完整链条:U-Boot 设置 bootargs 或 DTB /chosen/bootargs → 内核命令行 command_line[]parse_early_param() / __setup() → 各子系统应用,包括 console、rootfs、网络配置、调试等。优先级遵循 U-Boot > DTB > 内核默认,CONFIG_CMDLINE_FORCE 可覆盖一切。

rdinitinit 分别控制早期用户态与正式根文件系统用户态初始化,为嵌入式系统提供灵活的启动策略。

八、启动过程中切换文件系统

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 的文件会一直常驻内存。

http://www.xdnf.cn/news/19563.html

相关文章:

  • 使用AdaLoRA 自适应权重矩阵微调大模型介绍篇
  • Docker一小时快速上手(附报错解决方式)
  • 【MLLM】具有长期记忆的多模态智能体框架M3-Agent
  • 《信息学奥林匹克辞典》中的一个谬误
  • Java异常处理完全指南:从入门到精通
  • 安装proteus,并实现stm32仿真
  • 如何解决pip安装报错ModuleNotFoundError: No module named ‘pydantic’问题
  • 从 ETL 到 ELT 再到 EAI:AI 如何重塑数据处理
  • 小迪安全v2023学习笔记(七十五讲)—— 验证码安全插件识别攻击利用宏命令
  • 设计模式在Java中的应用:从单例模式到工厂模式的全面解析!
  • 计算机网络总览
  • 使用 GLSL 实现真实自然的纹理混合技术详解
  • 【Java实战⑨】Java集合框架实战:List集合深度剖析
  • 【STM32】外部中断(下)
  • 829作业
  • 告别强化学习?GEPA:用“反思性提示词进化”实现超越的新范式
  • SpringMVC的执行流程
  • 阿里云-应用实时监控服务 ARMS
  • 想学怎么写网站怎么办?初学者专用! (HTML+CSS+JS)
  • 微知-Mellanox OFED编译的一些细节?无法编译怎么办?如何添加自定义编译选项?
  • selenium 元素操作
  • mysql5.7.44安装遇到登录权限问题
  • NM:微生物组数据分析的规划与描述
  • 数字世界的两面性:从乘积组合到最大公约数的算法之旅
  • MCP(Model Context Protocol,模型上下文协议)介绍
  • 计算机毕设选题:基于Python+Django实现电商评论情感分析系统
  • 如何利用AI IDE快速构建一个简易留言板系统
  • 基于SpringBoot + Vue 的宠物领养管理系统
  • Decoder 解码器
  • JPEG XS概述