在linux kernel或android中解析cmdline参数

本文深入探讨Linux内核及Android系统如何解析启动参数。通过分析kernel_init函数所在文件main.c,揭示init=与rdinit=参数如何被处理。同时,介绍了Android如何读取/proc/cmdline并解析androidboot.selinux=permissive等参数。

快速链接:
.
👉👉👉 个人博客笔记导读目录(全部) 👈👈👈

Kernel command line: earlycon androidboot.selinux=permissive uart_dma keep_dbgclk_on clk_ignore_unused initrd=0xd0000000,38711808 rw crash_page=0x8f040000 initrd=/recoveryrc boot_reason=0x2000 ota_status=0x1001

1、在linux kernel中解析cmdline参数

(以init,rdinit为例)
vim kernel/linux/init/main.c (即kernel_init函数所在的文件)

如代码所示__setup是一个宏,如果cmdline中有这个参数,则会执行对应的后面的函数。
下面这两个函数,会将cmdline中解析到的“init=”、“rdinit=”后面的字串写入到execute_command和ramdisk_execute_command全局变量中.
然后在kernel的代码中,就可以使用这两个变量了.

static int __init init_setup(char *str)
{
	unsigned int i;

	execute_command = str;
	/*
	 * In case LILO is going to boot us with default command line,
	 * it prepends "auto" before the whole cmdline which makes
	 * the shell think it should execute a script with such name.
	 * So we ignore all arguments entered _before_ init=... [MJ]
	 */
	for (i = 1; i < MAX_INIT_ARGS; i++)
		argv_init[i] = NULL;
	return 1;
}
__setup("init=", init_setup);

static int __init rdinit_setup(char *str)
{
	unsigned int i;

	ramdisk_execute_command = str;
	/* See "auto" comment in init_setup */
	for (i = 1; i < MAX_INIT_ARGS; i++)
		argv_init[i] = NULL;
	return 1;
}
__setup("rdinit=", rdinit_setup);

2、在android中解析cmdline参数
(以androidboot.selinux=permissive为例)

如下列代码所示,android的应用程序会读取"/proc/cmdline"中的字符串,然后解析相应的字段,报错到变量中。

vim system/core/init/selinux.cpp
v

oid import_kernel_cmdline(bool in_qemu,
                           const std::function<void(const std::string&, const std::string&, bool)>& fn) {
    std::string cmdline;
    android::base::ReadFileToString("/proc/cmdline", &cmdline);

    for (const auto& entry : android::base::Split(android::base::Trim(cmdline), " ")) {
        std::vector<std::string> pieces = android::base::Split(entry, "=");
        if (pieces.size() == 2) {
            fn(pieces[0], pieces[1], in_qemu);
        }
    }
}

vim system/core/init/util.cpp

EnforcingStatus StatusFromCmdline() {
    EnforcingStatus status = SELINUX_ENFORCING;

    import_kernel_cmdline(false,
                          [&](const std::string& key, const std::string& value, bool in_qemu) {
                              if (key == "androidboot.selinux" && value == "permissive") {
                                  status = SELINUX_PERMISSIVE;
                              }
                          });

    return status;
}

在这里插入图片描述
《ARMv8/ARMv9架构学习系列课程》全系列,共计52节课,超16h的视频课程

<think>我们正在讨论Android系统中的`import_kernel_cmdline`函数。根据引用[2]中的代码片段,该函数位于`system/core/init/selinux.cpp`文件中,其作用是从内核命令行(通过`/proc/cmdline`)读取参数,并将这些参数解析为键值对,然后通过回调函数进行处理。函数定义: ```cppvoidimport_kernel_cmdline(bool in_qemu, conststd::function<void(const std::string&,const std::string&,bool)>& fn) ```函数流程:1.读取`/proc/cmdline`文件内容到字符串`cmdline`中。 2.使用`android::base::Trim`去除首尾空白,然后使用`android::base::Split`按空格分割成多个条目(每个条目是一个内核参数)。3.遍历每个条目,再按等号`=`分割成键和值(最多分割一次,所以每个条目按等号分割后最多两部分)。 4.如果分割后恰好是两部分(即键和值),则调用回调函数`fn`,传入键、值以及`in_qemu`标志。注意:函数有两个参数: -`in_qemu`:表示是否在QEMU(模拟器)环境中运行。 -`fn`:一个回调函数,用于处理每个解析出的键值对。 根据引用[1]中的代码片段,`import_kernel_cmdline`被调用了两次: ```cppstatic voidprocess_kernel_cmdline(){// Thefirst passdoes thecommon stuff, andfinds ifwe arein qemu.//The secondpass isonly necessaryfor qemu toexport allkernel params// asproperties.import_kernel_cmdline(false, import_kernel_nv);if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv); }``` 第一次调用时,`in_qemu`参数为`false`,第二次调用仅在检测到QEMU(即`qemu[0]`不为空)时进行,且`in_qemu`为`true`。回调函数`import_kernel_nv`(未在片段中给出完整定义)的作用可能是将内核参数设置为系统属性。在Android中,内核命令行参数通常会被设置为系统属性,以便在系统中使用。因此,`import_kernel_cmdline`函数的主要功能是解析内核命令行参数,并将其转换为系统属性。特别地,在QEMU环境中,可能需要额外的处理(例如,导出所有内核参数作为属性)。总结:- **功能**:解析内核命令行参数(`/proc/cmdline`),提取键值对,并通过回调函数处理这些键值对(通常是将其设置为系统属性)。- **用途**:在Android启动过程中(由init进程执行),将内核命令行参数导入到系统属性中,以便后续进程可以访问这些参数。 相关问题: 1.内核命令行参数Android启动过程中是如何被传递的?2.在非QEMU环境下,为什么需要两次解析内核命令行参数? 3.回调函数`import_kernel_nv`的具体实现是怎样的?</think>### Android `import_kernel_cmdline` 函数详解 `import_kernel_cmdline` 是 Android 启动过程中关键的内核命令行参数解析函数,主要功能如下: #### 核心功能 1. **读取内核参数** 通过访问 `/proc/cmdline` 文件获取 Linux 内核启动参数: ```cpp android::base::ReadFileToString("/proc/cmdline", &cmdline); ``` 示例参数格式:`console=ttyS0,115200 init=/init debug` 2. **参数解析处理** 将命令行分割为键值对: ```cpp for (const auto& entry : Split(Trim(cmdline), " ")) { std::vector<std::string> pieces = Split(entry, "="); if (pieces.size() == 2) { fn(pieces[0], pieces[1], in_qemu); // 调用回调处理 } } ``` 支持两种参数格式: - `key=value`(如 `androidboot.serialno=ABCDEF`) - 无值参数(如 `debug`) #### 特殊处理逻辑 1. **QEMU 环境检测** 函数通过 `in_qemu` 参数区分物理设备和模拟器环境: ```cpp import_kernel_cmdline(false, import_kernel_nv); // 首次调用 if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv); // QEMU环境二次处理 ``` 在 QEMU 模拟器中会执行两次解析,确保所有参数正确导出为系统属性[^1]。 2. **参数转换机制** 通过回调函数(如 `import_kernel_nv`)将内核参数转换为 Android 系统属性: - 例如 `ro.boot.serialno` 属性对应内核参数 `androidboot.serialno` - 实现内核空间到用户空间的参数传递 #### 典型应用场景 1. **设备初始化配置** 解析 `androidboot.*` 系列参数初始化硬件配置(如串口、内存分配) 2. **安全启动控制** 处理 SELinux 相关参数(如 `androidboot.selinux=enforcing`) 3. **调试模式启用** 识别 `debug`/`androidboot.debug` 等参数启用调试日志 4. **系统属性注入** 将内核参数转换为 `ro.boot.*` 只读属性,供 Framework 层使用 ```mermaid graph TD A[/proc/cmdline] --> B[读取原始参数] B --> C{是否为QEMU环境} C -->|是| D[二次解析] C -->|否| E[单次解析] D --> F[分割键值对] E --> F F --> G[回调处理] G --> H[设置系统属性] ``` #### 设计特点 - **模块化处理**:通过回调函数解耦解析逻辑与应用逻辑 - **环境感知**:针对模拟器环境特殊处理 - **健壮性**:`Trim()` 和 `Split()` 确保格式容错 - **轻量高效**:直接操作字符串避免额外开销 该函数在 Android 启动早期(init 进程阶段)执行,为后续系统初始化提供关键配置参数[^2]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Arm精选

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值