目录
- 前言
- 一、init进程-->SecondStageMain
- 二、process_kernel_dt
-
- [2.1 is_android_dt_value_expected](#2.1 is_android_dt_value_expected)
-
- [2.1.1 read_android_dt_file](#2.1.1 read_android_dt_file)
- [2.1.2 get_android_dt_dir](#2.1.2 get_android_dt_dir)
- [2.1.3 init_android_dt_dir](#2.1.3 init_android_dt_dir)
- [2.1.4 import_kernel_cmdline](#2.1.4 import_kernel_cmdline)
- [2.2 property_set()设置属性](#2.2 property_set()设置属性)
- 三、process_kernel_cmdline
-
- [3.1 import_kernel_cmdline(false, import_kernel_nv);](#3.1 import_kernel_cmdline(false, import_kernel_nv);)
- [3.2 import_kernel_cmdline(true, import_kernel_nv);](#3.2 import_kernel_cmdline(true, import_kernel_nv);)
- 四、export_kernel_boot_props
- 五、总结
前言
在kermel cmdline
中我们经常会设置androidboot.xxx
比如androidboot.selinux=disabled
,
但是代码中并不能搜到对应的内容,其实是init进程
做了统一转换。
一、init进程-->SecondStageMain
在init
进程的SecondStageMain
阶段,执行了process_kernel_dt
和process_kernel_cmdline
c
system\core\init\main.cpp
c
int main(int argc, char** argv) {
......
if (!strcmp(argv[1], "second_stage")) {
return SecondStageMain(argc, argv);
}
}
......
return FirstStageMain(argc, argv);
}
c
int SecondStageMain(int argc, char** argv) {
......
property_init();
// If arguments are passed both on the command line and in DT,
// properties set in DT always have priority over the command-line ones.
process_kernel_dt();
process_kernel_cmdline();
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();
......
翻译一下:
如果参数既在命令行中传递,又在DT中传递,则DT中设置的属性始终优先于命令行中的属性。
将内核命令行传播到由
init进程
使用的内部变量以及当前所需的属性中。
二、process_kernel_dt
c
static void process_kernel_dt() {
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
continue;
}
std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
property_set("ro.boot."s + dp->d_name, dt_file);
}
}
2.1 is_android_dt_value_expected
c
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
从方法的名字上也可以推测出来,检查
设备树(Device Tree)
中的某个属性(这里是 "compatible
")的值是否符合预期(这里是 "android,firmware
")。如果不是,直接return。
c
bool is_android_dt_value_expected(const std::string& sub_path, const std::string& expected_content) {
std::string dt_content;
if (read_android_dt_file(sub_path, &dt_content)) {
if (dt_content == expected_content) {
return true;
}
}
return false;
}
"从设备树的
compatible
路径读取内容,并判断内容是否与android,firmware
完全相等。"如果相等,返回
true
;否则返回false
。
2.1.1 read_android_dt_file
c
// Reads the content of device tree file under the platform's Android DT directory.
// Returns true if the read is success, false otherwise.
bool read_android_dt_file(const std::string& sub_path, std::string* dt_content) {
const std::string file_name = get_android_dt_dir() + sub_path;
if (android::base::ReadFileToString(file_name, dt_content)) {
if (!dt_content->empty()) {
dt_content->pop_back(); // Trims the trailing '\0' out.
return true;
}
}
return false;
}
先来翻译一下:
读取平台Android DT目录下的设备树文件内容。如果读取成功,则返回
true
,否则返回false
。
- 1.构建完整文件路径file_name;
- 2.调用ReadFileToString()读取文件;
- 3.如果读取成功,且内容不为空:
删除内容末尾的\0字符;
返回true;
否则,返回false。
2.1.2 get_android_dt_dir
c
// FIXME: The same logic is duplicated in system/core/fs_mgr/
const std::string& get_android_dt_dir() {
// Set once and saves time for subsequent calls to this function
static const std::string kAndroidDtDir = init_android_dt_dir();
return kAndroidDtDir;
}
直接调用init_android_dt_dir
初始化设备树目录,然后返回这个目录
2.1.3 init_android_dt_dir
c
const std::string kDefaultAndroidDtDir("/proc/device-tree/firmware/android/");
static std::string init_android_dt_dir() {
// Use the standard procfs-based path by default
std::string android_dt_dir = kDefaultAndroidDtDir;
// The platform may specify a custom Android DT path in kernel cmdline
import_kernel_cmdline(false,
[&](const std::string& key, const std::string& value, bool in_qemu) {
if (key == "androidboot.android_dt_dir") {
android_dt_dir = value;
}
});
LOG(INFO) << "Using Android DT directory " << android_dt_dir;
return android_dt_dir;
}
- 初始化Android设备树(Device Tree)目录路径
- 默认路径为:
/proc/device-tree/firmware/android/
。 - 如果内核启动参数(cmdline)中指定了
androidboot.android_dt_dir
,则会覆盖默认路径,
这里使用了lambda
表达式
2.1.4 import_kernel_cmdline
c
void 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);
}
}
}
- 从
/proc/cmdline
读取内核启动参数字符串。 - 解析出所有参数(空格分隔的字符串,例如:param1=val1 param2=val2 ...)。
- 对每个参数,调用提供的回调函数fn,传入参数名、值和标志位
in_qemu
。
看一下我本地的路径以及其对应的值
c
Android:/proc/device-tree/firmware/android # ls
compatible hardware mode name serialno
Android:/proc/device-tree/firmware/android # cat compatible
android,firmware
2.2 property_set()设置属性
c
static void process_kernel_dt() {
if (!is_android_dt_value_expected("compatible", "android,firmware")) {
return;
}
std::unique_ptr<DIR, int (*)(DIR*)> dir(opendir(get_android_dt_dir().c_str()), closedir);
if (!dir) return;
std::string dt_file;
struct dirent *dp;
while ((dp = readdir(dir.get())) != NULL) {
if (dp->d_type != DT_REG || !strcmp(dp->d_name, "compatible") || !strcmp(dp->d_name, "name")) {
continue;
}
std::string file_name = get_android_dt_dir() + dp->d_name;
android::base::ReadFileToString(file_name, &dt_file);
std::replace(dt_file.begin(), dt_file.end(), ',', '.');
property_set("ro.boot."s + dp->d_name, dt_file);
}
}
然后这个目录下以每个文件名作为属性(除了compatible 和name
),文件里面的内容作为属性值。这里的话就是ro.boot.hareware
、 ro.boot.mode
、 ro.boot.serialno
这三个属性值
c
Android:/proc/device-tree/firmware/android # ls
compatible hardware mode name serialno
Android:/proc/device-tree/firmware/android #
三、process_kernel_cmdline
c
static char qemu[32];
static void process_kernel_cmdline() {
// The first pass does the common stuff, and finds if we are in qemu.
// The second pass is only necessary for qemu to export all kernel params
// as properties.
import_kernel_cmdline(false, import_kernel_nv);
if (qemu[0]) import_kernel_cmdline(true, import_kernel_nv);
}
void 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);
}
}
}
static void import_kernel_nv(char *name, bool for_emulator)
{
char *value = strchr(name, '=');
int name_len = strlen(name);
if (value == 0) return;
*value++ = 0;
if (name_len == 0) return;
if (for_emulator) {
/* in the emulator, export any kernel option with the
* ro.kernel. prefix */
char buff[PROP_NAME_MAX];
int len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
if (len < (int)sizeof(buff))
property_set( buff, value );
return;
}
if (!strcmp(name,"qemu")) {
strlcpy(qemu, value, sizeof(qemu));
} else if (!strncmp(name, "androidboot.", 12) && name_len > 12) {
const char *boot_prop_name = name + 12;
char prop[PROP_NAME_MAX];
int cnt;
cnt = snprintf(prop, sizeof(prop), "ro.boot.%s", boot_prop_name);
if (cnt < PROP_NAME_MAX)
property_set(prop, value);
}
}
- 调用
import_kernel_cmdline
函数,上面已经讲过,就是读取proc/cmdline
中的内容。这个函数有两个参数,第一个采纳数标识当前Android是否是模拟器。第二个参数是一个函数指针;主要指的是通过调用import_kernel_nv
函数来设置系统属性。
3.1 import_kernel_cmdline(false, import_kernel_nv);
import_kernel_cmdline
第一次执行时,传入import_kernel_nv
的形参为for_emulator
为0,
因此将匹配name是否为qemu。
- 如果是,将其值保存在
qemu全局静态缓冲区
中。
对于android模拟器,存在/proc/cmdline中存在"qemu=1"字段
。
如果for_emulator
为1,则将生成ro.kernel.{name}={value}
属性写入Android属性系统中。 - 如果不是,则将
/proc/cmdline
中以androidboot.
为开头的键值对,以ro.boot
替换,添加到系统属性中。
此时回到process_kernel_cmdline
函数,继续执行
3.2 import_kernel_cmdline(true, import_kernel_nv);
当系统为模拟器时,qemu[0]
其值为"1",第二次执行import_kernel_cmdline
函数,将再次调用import_kernel_nv
函数,生成ro.kernel.xxx
属性。
四、export_kernel_boot_props
c
static void export_kernel_boot_props() {
constexpr const char* UNSET = "";
struct {
const char *src_prop;
const char *dst_prop;
const char *default_value;
} prop_map[] = {
{ "ro.boot.serialno", "ro.serialno", UNSET, },
{ "ro.boot.mode", "ro.bootmode", "unknown", },
{ "ro.boot.baseband", "ro.baseband", "unknown", },
{ "ro.boot.bootloader", "ro.bootloader", "unknown", },
{ "ro.boot.hardware", "ro.hardware", "unknown", },
{ "ro.boot.revision", "ro.revision", "0", },
};
for (const auto& prop : prop_map) {
std::string value = GetProperty(prop.src_prop, prop.default_value);
if (value != UNSET)
property_set(prop.dst_prop, value);
}
}
- 定义一份"映射表"prop_map,列出需要读取和设置的属性
- 循环处理每个映射:
从源属性获取值(如果未定义则使用默认值)
如果这个值不是空(UNSET空定义),就设置到目标属性。
其实就是将已有的ro.boot.xxx
属性键值对设置到了ro.xxx
属性键值对中。
五、总结
-
process_kernel_dt()
作用
:判断设备树中的compatible属性是否为"android,firmware",确保硬件符合预期。
遍历设备树目录中的文件(排除特殊文件如
compatible
和name
)。读取每个文件的内容,将其中的逗号,替换为点.,并设置为系统属性(ro.boot.*)。
主要目的
:从设备树采集硬件描述信息(比如硬件版本、型号等),以供之后系统使用。
-
process_kernel_cmdline()
作用
:读取内核启动参数(存放在/proc/cmdline)。
解析参数,将符合特定条件的键值对(比如androidboot.android_dt_dir)传递给回调(如init_android_dt_dir()),用于动态配置系统参数(比如设备树路径)。
主要目的
:根据内核参数,动态调整系统配置(如设备树路径、硬件信息等)。
-
export_kernel_boot_props()
作用
:从系统属性(如ro.boot.serialno等)读取硬件和启动参数信息。
将这些信息导出到常规属性(如ro.serialno、ro.bootmode),方便系统和应用访问。
主要目的
:将硬件和启动信息显式存入标准化的系统属性,保证系统和应用可以一致地访问硬件配置信息。