目录
  1. 1. 一、init.rc 语言设计理念
    1. 1.1. 1.1 声明式 vs 过程式
    2. 1.2. 1.2 rc 文件的加载顺序
  2. 2. 二、五大语句类型
    1. 2.1. 2.1 语句类型一览
    2. 2.2. 2.2 Import 语句
  3. 3. 三、Action 语句详解
    1. 3.1. 3.1 Action 的基本语法
    2. 3.2. 3.2 触发器 (Trigger) 类型
      1. 3.2.1. 3.2.1 事件触发器 (Event Trigger)
      2. 3.2.2. 3.2.2 属性触发器 (Property Trigger)
      3. 3.2.3. 3.2.3 组合触发器 (Android 12+)
      4. 3.2.4. 3.2.4 所有内置事件触发器列表
  4. 4. 四、Command 语句详解
    1. 4.1. 4.1 Command 的解析与分发
    2. 4.2. 4.2 典型实现示例
    3. 4.3. 4.3 exec 命令与 exec_start 的区别
  5. 5. 五、Service 语句详解
    1. 5.1. 5.1 Service 语法
    2. 5.2. 5.2 Service 的所有 Option 详解
      1. 5.2.1. 5.2.1 进程身份类
      2. 5.2.2. 5.2.2 运行行为类
      3. 5.2.3. 5.2.3 重启策略类
      4. 5.2.4. 5.2.4 资源限制类
      5. 5.2.5. 5.2.5 环境变量类
      6. 5.2.6. 5.2.6 命名空间隔离类
      7. 5.2.7. 5.2.7 Lifecycle 管理
    3. 5.3. 5.3 Socket Option——服务间通信的关键
  6. 6. 六、init.rc 的解析流程深入
    1. 6.1. 6.1 Parser 类的设计
    2. 6.2. 6.2 Tokenize(分词)规则
  7. 7. 七、完整实战:编写自定义 rc 服务
    1. 7.1. 7.1 需求描述
    2. 7.2. 7.2 rc 文件
    3. 7.3. 7.3 Native Daemon 接收 Socket
    4. 7.4. 7.4 手动控制服务
  8. 8. 八、rc 文件的位置规范(Android 10+ Treble)
    1. 8.1. 8.1 rc 文件的标准路径
    2. 8.2. 8.2 最佳实践
  9. 9. 九、rc 语法扩展(Android 12+)
    1. 9.1. 9.1 Bootconfig 替代内核命令行
    2. 9.2. 9.2 条件触发器组合
    3. 9.3. 9.3 Per-Service 命名空间
  10. 10. 十、核心面试题
【吃透源码系列】之Android系统启动(二)rc文件语法

init.rc 是 Android init 进程的中央配置文件。它并不是一个可执行的脚本(如 bash),而是一套声明式的初始化语言,由 init 进程中的解析器在运行时加载并执行。本文将深入解析 init.rc 的完整语法规范,包括语句类型、触发器机制、Service 选项、Socket 管理,并结合实际源码(Android 11 / API 30)展示编写自定义 rc 服务的方法。

一、init.rc 语言设计理念

1.1 声明式 vs 过程式

传统 Linux init 系统(如 sysvinit)使用 shell 脚本进行初始化——本质上是过程式编程。Android 设计团队选择了一套自定义的声明式配置语言,原因包括:

  1. 确定性:声明式语言执行顺序明确,无循环依赖风险
  2. 解析效率:解析简单,不需要启动 shell 解释器
  3. 可扩展性:通过 import 机制支持模块化配置(每个 HAL 都可以带自己的 rc 文件)
  4. 安全性:语法受限,不会出现 shell 注入攻击
  5. 资源节省:不需要在每个 init 触发阶段启动 shell 环境

1.2 rc 文件的加载顺序

init 进程在第二阶段按以下顺序加载 rc 文件:

// system/core/init/init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);

// 1. 加载主 init.rc
parser.ParseConfig("/system/etc/init/hw/init.rc");

// 2. 加载所有 import 进来的 rc 文件
parser.set_is_system_etc_init_loaded(true);

// 3. 加载 system 分区 rc
parser.ParseConfig("/system/etc/init");

// 4. 加载 vendor 分区 rc(HAL 服务定义)
parser.ParseConfig("/vendor/etc/init");

// 5. 加载 odm 分区 rc
parser.ParseConfig("/odm/etc/init");

// 6. 加载 product 分区 rc
parser.ParseConfig("/product/etc/init");
}

二、五大语句类型

init.rc 语言包含五种顶层语句类型。在解析器层面,每一行都以一个关键字开头,由 SectionParser 机制分发到对应的解析器。

2.1 语句类型一览

类型 关键字 说明 SectionParser
Action on 定义由触发器激活的命令序列 ActionParser
Command (内置命令) Action 块内的具体操作 (Action 内部)
Service service 定义后台运行的守护进程 ServiceParser
Import import 引入其他 rc 文件 ImportParser
On on 与 Action 相同,关键字 onAction ActionParser

2.2 Import 语句

Import 语句是最简单的语句类型:

import /init.environ.rc
import /system/etc/init/hw/init.usb.rc
import /system/etc/init/hw/init.${ro.zygote}.rc
import /vendor/etc/init/hw/init.${ro.hardware}.rc
import /odm/etc/init/hw/init.${ro.hardware}.rc

Import 解析过程中支持三个特殊机制:

  1. 变量展开${ro.zygote} 会被替换为对应的系统属性值
  2. 路径通配import /vendor/etc/init/hw/*.rc 加载所有匹配文件
  3. 递归加载:被 import 的文件中的 import 语句也会被处理

ImportParser 实现:

// system/core/init/import_parser.cpp
Result<void> ImportParser::ParseSection(std::vector<std::string>&& args, int line) {
auto import_path = ExpandProps(args[1]); // 属性变量展开
auto paths = expand_glob(import_path); // glob 路径展开

for (const auto& path : paths) {
parser_->ParseConfig(path); // 递归解析
}
return {};
}

三、Action 语句详解

3.1 Action 的基本语法

on <trigger> [&& <trigger>]*
<command>
<command>
...

一个 Action 由一个或多个 trigger 条件以及一系列 command 组成。当 trigger 条件被触发时,该 Action 中的所有 command 被添加到执行队列的尾部(而不是立即执行)。

3.2 触发器 (Trigger) 类型

3.2.1 事件触发器 (Event Trigger)

最常用的触发器类型,由特定的系统事件名称标识。init 进程在启动过程中会按顺序触发这些事件:

# 系统启动事件的典型序列
on early-init
# 最早启动阶段

on init
# 基础初始化完成

on late-init
# 后期初始化
trigger early-fs
trigger fs
trigger post-fs
trigger late-fs
trigger post-fs-data
trigger load_persist_props_action
trigger zygote-start
trigger firmware_mounts_complete

on early-boot
# 早期启动

on boot
# 系统启动完成

事件触发的实现:

// system/core/init/action_manager.cpp
void ActionManager::QueueEventTrigger(const std::string& trigger) {
for (auto& action : actions_) {
if (action->CheckEventTrigger(trigger)) {
action->QueueAllCommands(current_executing_);
}
}
}

3.2.2 属性触发器 (Property Trigger)

当系统属性变为特定值时触发:

on property:sys.boot_completed=1
# 启动完成后的操作

on property:persist.sys.usb.config=*
# 任何 USB 配置改变时

on property:sys.sysctl.extra_free_kbytes=*
write /proc/sys/vm/extra_free_kbytes ${sys.sysctl.extra_free_kbytes}

属性触发器的内部实现:

// system/core/init/action.cpp
bool Action::CheckPropertyTrigger(const std::string& name,
const std::string& value) const {
for (const auto& [prop_name, prop_value] : property_triggers_) {
if (prop_name == name) {
// * 表示匹配任意值
if (prop_value == "*" || prop_value == value) {
return true;
}
}
}
return false;
}

3.2.3 组合触发器 (Android 12+)

Android 12 引入了 && 组合触发器语法:

on property:sys.boot_completed=1 && property:sys.user.0.ce_available=true
# 启动完成且用户 0 的 CE 存储就绪时触发

3.2.4 所有内置事件触发器列表

// system/core/init/README.md 文档中描述的触发器
// Builtin 触发器:
"early-init" // 最早触发,文件系统刚挂载
"init" // 基础初始化触发
"late-init" // 后期初始化触发
// 从 late-init 派生的触发器:
"early-fs" // 早期文件系统
"fs" // 文件系统
"post-fs" // 文件系统后
"late-fs" // 延迟文件系统
"post-fs-data" // /data 分区就绪后
// 启动阶段触发器:
"early-boot" // 早期启动
"boot" // 启动完成
// 其他:
"charger" // 充电模式
"factory-reset" // 恢复出厂设置
"userspace-reboot-resume" // 用户空间重启恢复

四、Command 语句详解

4.1 Command 的解析与分发

Command 定义在 Action 块内部,每行一个命令。init 进程通过 BuiltinFunctionMap 将命令名字符串映射到实际的函数指针:

// system/core/init/builtin_commands.cpp
BuiltinFunctionMap::BuiltinFunctionMap() {
// 文件系统命令
map_["chmod"] = std::make_unique<BuiltinFunctionMap::MapEntry>("chmod", 2, 2, {"path", "mode"}, do_chmod);
map_["chown"] = std::make_unique<BuiltinFunctionMap::MapEntry>("chown", 3, 3, {"path", "owner", "group"}, do_chown);
map_["mount"] = std::make_unique<BuiltinFunctionMap::MapEntry>("mount", 4, 5, {"type", "source", "target", "flags", "data"}, do_mount);
map_["mkdir"] = std::make_unique<BuiltinFunctionMap::MapEntry>("mkdir", 1, 4, {"path", "mode", "owner", "group"}, do_mkdir);
map_["symlink"] = std::make_unique<BuiltinFunctionMap::MapEntry>("symlink", 2, 2, {"target", "path"}, do_symlink);

// 服务控制命令
map_["class_start"] = std::make_unique<BuiltinFunctionMap::MapEntry>("class_start", 1, 1, {"class"}, do_class_start);
map_["class_stop"] = std::make_unique<BuiltinFunctionMap::MapEntry>("class_stop", 1, 1, {"class"}, do_class_stop);
map_["start"] = std::make_unique<BuiltinFunctionMap::MapEntry>("start", 1, 1, {"service"}, do_start);
map_["stop"] = std::make_unique<BuiltinFunctionMap::MapEntry>("stop", 1, 1, {"service"}, do_stop);
map_["restart"] = std::make_unique<BuiltinFunctionMap::MapEntry>("restart", 1, 1, {"service"}, do_restart);

// 系统属性命令
map_["setprop"] = std::make_unique<BuiltinFunctionMap::MapEntry>("setprop", 2, 2, {"key", "value"}, do_setprop);
map_["trigger"] = std::make_unique<BuiltinFunctionMap::MapEntry>("trigger", 1, 1, {"event"}, do_trigger);

// I/O 命令
map_["write"] = std::make_unique<BuiltinFunctionMap::MapEntry>("write", 2, 2, {"path", "value"}, do_write);
map_["exec"] = std::make_unique<BuiltinFunctionMap::MapEntry>("exec", 1, kMax, {"path", "args..."}, do_exec);
map_["exec_start"] = std::make_unique<BuiltinFunctionMap::MapEntry>("exec_start", 1, 1, {"service"}, do_exec_start);

// 系统控制命令
map_["sysclktz"] = std::make_unique<BuiltinFunctionMap::MapEntry>("sysclktz", 1, 1, {"timezone"}, do_sysclktz);
map_["domainname"] = std::make_unique<BuiltinFunctionMap::MapEntry>("domainname", 1, 1, {"name"}, do_domainname);
map_["hostname"] = std::make_unique<BuiltinFunctionMap::MapEntry>("hostname", 1, 1, {"name"}, do_hostname);

// 启用/禁用等
map_["enable"] = std::make_unique<BuiltinFunctionMap::MapEntry>("enable", 1, 1, {"service"}, do_enable);
map_["disable"] = std::make_unique<BuiltinFunctionMap::MapEntry>("disable", 1, 1, {"service"}, do_disable);
map_["copy"] = std::make_unique<BuiltinFunctionMap::MapEntry>("copy", 2, 2, {"src", "dst"}, do_copy);
map_["insmod"] = std::make_unique<BuiltinFunctionMap::MapEntry>("insmod", 1, kMax, {"module", "args"}, do_insmod);
map_["rm"] = std::make_unique<BuiltinFunctionMap::MapEntry>("rm", 1, 1, {"path"}, do_rm);
map_["rmdir"] = std::make_unique<BuiltinFunctionMap::MapEntry>("rmdir", 1, 1, {"path"}, do_rmdir);
map_["verity_update_state"] = ...;
map_["load_system_props"] = ...;
map_["load_persist_props"] = ...;
// ... 更多
}

4.2 典型实现示例

// system/core/init/builtin_commands.cpp
static Result<void> do_mkdir(const BuiltinArguments& args) {
mode_t mode = 0755;
if (args.size() >= 2) {
mode = strtoul(args[2].c_str(), 0, 8);
}
if (mkdir(args[1].c_str(), mode) != 0) {
return ErrnoError();
}
return {};
}

static Result<void> do_symlink(const BuiltinArguments& args) {
if (symlink(args[1].c_str(), args[2].c_str()) != 0) {
return ErrnoError();
}
return {};
}

static Result<void> do_write(const BuiltinArguments& args) {
if (!android::base::WriteStringToFile(args[2], args[1])) {
return Error() << "Unable to write to '" << args[1] << "'";
}
return {};
}

4.3 exec 命令与 exec_start 的区别

  • exec:在 init 进程中直接 fork+exec,阻塞 init 主循环直到命令执行完毕。只应在早期初始化阶段使用。
  • exec_start:本质上是启动一个 Service 对象并等待其完成。不阻塞 init 主循环,使用 epoll 异步等待。
# exec 的使用(阻塞 init)
on post-fs-data
exec - root system -- /system/bin/vdc volume mount /data

# exec_start 的使用(异步)
on post-fs-data
exec_start vold_mount_data

五、Service 语句详解

5.1 Service 语法

service <name> <pathname> [ <argument> ]*
<option>
<option>
...
  • name:服务名称,在 rc 文件中必须唯一
  • pathname:可执行文件的绝对路径
  • argument:传递给可执行文件的命令行参数

5.2 Service 的所有 Option 详解

5.2.1 进程身份类

user <username>     # 以指定用户身份运行(默认 root)
group <groupname> [groupname]* # 更改进程组
capabilities <capabilities> # 设置 Linux capabilities
seclabel <seclabel> # 设置 SELinux 上下文

5.2.2 运行行为类

oneshot             # 不自动重启(默认会自动重启)
disabled # 不会由 class_start 启动,只能手动 start
critical # 关键服务:4 分钟内 crash 4 次则重启到 recovery
class <name> # 归类,方便 class_start / class_stop 批量操作

Android 9+ 将 OOM 调整从 rc 文件 option 迁移到 init 框架统一管理:

oom_score_adjust <value>  # OOM 调整值 (-1000 到 1000)

5.2.3 重启策略类

onrestart <command>   # 服务重启时执行的命令
restart_period <seconds> # 服务重启最小间隔(Android 12+)
shutdown <action> # 关机时的行为(Android 12+)

5.2.4 资源限制类

rlimit <resource> <soft> <hard>    # 设置资源限制
ioprio <class> <priority> # 设置 I/O 优先级
priority <priority> # 设置进程优先级 (-20 到 19)

5.2.5 环境变量类

setenv <name> <value>              # 设置环境变量

5.2.6 命名空间隔离类

namespace <pid|mnt> [ <path> ]     # 进入指定命名空间
task_profiles <name> [name]* # 设置任务配置文件(cpuset 等)

5.2.7 Lifecycle 管理

interface <interface> <instance> <type>
# 声明此服务实现的 HIDL 接口(供 hwservicemanager 注册)
# 格式: android.hardware.IXXX IName default

reboot_on_failure <reboot_target>
# 服务启动失败后重启设备

5.3 Socket Option——服务间通信的关键

socket <name> <type> <perm> [ <user> [ <group> [ <context> ] ] ]

该 option 指示 init 在启动 service 之前创建一个 Unix Domain Socket,并将 socket fd 传递给子进程(通过环境变量 ANDROID_SOCKET_<name>)。

type 参数

  • stream:流式 socket (SOCK_STREAM)
  • dgram:数据报 socket (SOCK_DGRAM)
  • seqpacket:有序数据包 socket (SOCK_SEQPACKET)

示例——Zygote 的 socket 定义

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system

这创建了两个 socket:/dev/socket/zygote/dev/socket/usap_pool_primary。Zygote 进程可以通过以下方式获取 fd:

// frameworks/base/core/jni/fd_utils.cpp
int socket_fd = android_get_control_socket("zygote");

init 端创建 socket 的实现:

// system/core/init/service.cpp
Result<void> Service::OpenSocket() {
for (auto& [name, info] : sockets_) {
const std::string socket_path = "/dev/socket/" + name;
int fd = create_socket(socket_path, info.type, info.perm,
info.uid, info.gid, info.socketcon);
// 将 fd 注册到子进程环境变量
RegisterSocket(name, fd);
}
}

六、init.rc 的解析流程深入

6.1 Parser 类的设计

// system/core/init/parser.cpp
class Parser {
public:
bool ParseConfig(const std::string& path) {
// 1. 打开文件
auto contents = ReadFile(path);
// 2. 按行分割
auto lines = Split(contents, "\n");
// 3. 逐行解析
ParseData(path, &contents[0], contents.size());
}

void ParseData(const std::string& filename, const char* data, size_t len) {
// 逐行扫描
for (const auto& line : lines) {
auto args = Tokenize(line); // 分词:按空格分割
if (args.empty()) continue;

// 查找关键字的 SectionParser
auto section_parser = section_parsers_.find(args[0]);
if (section_parser != section_parsers_.end()) {
// 这是新 section 的开始(如 "on", "service", "import")
EndSection(); // 结束当前 section
current_section_parser_ = section_parser->second.get();
current_section_parser_->ParseSection(args, filename, line_num);
} else if (current_section_parser_) {
// 当前 section 的继续行
current_section_parser_->ParseLineSection(args, line_num);
}
}
EndSection(); // 文件结束,完成最后一个 section
}
};

6.2 Tokenize(分词)规则

// system/core/init/tokenizer.cpp
std::vector<std::string> Tokenize(const std::string& line) {
std::vector<std::string> tokens;
// 按空格和制表符分割
// 处理引号包裹的字符串
// 处理反斜杠转义
// 忽略 # 开头的注释
}

分词规则的要点:

  • 空格和 tab 是分隔符
  • 双引号内的内容作为单个 token(如 "hello world"
  • # 开头到行尾是注释
  • \ 在行末表示续行符

七、完整实战:编写自定义 rc 服务

7.1 需求描述

假设我们需要编写一个自定义的 native daemon 服务 my_monitord,它需要:

  • 以 system 用户身份运行
  • 在 boot 阶段自动启动
  • 创建自己的 socket 供其他进程通信
  • crash 后自动重启,但不触发 recovery
  • 启动前需要确保 /data/mymonitor/ 目录存在

7.2 rc 文件

# /system/etc/init/mymonitor.rc

# 1. 启动前准备工作(创建运行时目录)
on boot
mkdir /data/mymonitor/ 0700 system system
# 恢复 SELinux 上下文
restorecon_recursive /data/mymonitor/

# 2. 服务定义
service my_monitord /system/bin/my_monitord \
--monitor_dir=/data/mymonitor/ \
--log_level=info
# 身份设定
user system
group system
capabilities NET_BIND_SERVICE NET_ADMIN
# 运行行为
class main
# 通信 socket
socket my_monitord stream 0660 system system
# 重启策略
onrestart exec -- /system/bin/log -t my_monitord "my_monitord restarted!"
# 资源限制
oom_score_adjust -800
# 任务调度组
task_profiles ServiceCapacityLow
# SELinux
seclabel u:r:my_monitord:s0

7.3 Native Daemon 接收 Socket

// my_monitord.cpp
#include <cutils/sockets.h>
#include <sysutils/FrameworkListener.h>

int main(int argc, char* argv[]) {
// 获取 init 传递的 socket fd
const char* socket_name = "my_monitord";
int fd = android_get_control_socket(socket_name);

if (fd < 0) {
ALOGE("Failed to get socket '%s': %s", socket_name, strerror(errno));
return -1;
}

// 开始监听
if (listen(fd, 4) < 0) {
ALOGE("listen() failed: %s", strerror(errno));
return -1;
}

// 进入事件循环(epoll)
while (true) {
int client = accept(fd, NULL, NULL);
if (client >= 0) {
handle_client(client);
}
}
}

7.4 手动控制服务

启动后可以通过以下命令管理:

# 查看服务状态
getprop init.svc.my_monitord

# 启动 / 停止 / 重启
setprop ctl.start my_monitord
setprop ctl.stop my_monitord
setprop ctl.restart my_monitord

# 通过 socket 发送命令
echo "status" | nc -U /dev/socket/my_monitord

八、rc 文件的位置规范(Android 10+ Treble)

8.1 rc 文件的标准路径

路径 职责
/system/etc/init/hw/ 系统核心 rc(init.rc, init.usb.rc 等)
/system/etc/init/ Android 系统服务 rc
/vendor/etc/init/ SoC vendor HAL 服务 rc
/vendor/etc/init/hw/ SoC vendor 硬件相关 rc
/odm/etc/init/ ODM/OEM 定制 rc
/product/etc/init/ Product 层 rc
/system_ext/etc/init/ System Ext 层 rc

8.2 最佳实践

  1. 每个 HAL 应配一个 rc 文件:不应将所有 HAL 服务塞进一个文件
  2. 使用 class_start 而非硬编码启动顺序:让 init 框架管理启动并行度
  3. 通过属性触发器表达依赖on property:xxx.yyy=1 start my_service
  4. 使用 oneshot + disabled 控制一次性任务:不要让后台服务伪装成 oneshot
  5. socket 而非 /dev/ 节点:鼓励使用 socket 进行进程间通信

九、rc 语法扩展(Android 12+)

9.1 Bootconfig 替代内核命令行

Android 12 引入 bootconfig 机制替代传统的 /proc/cmdline

# 不再依赖内核命令行,改用 bootconfig 传递参数
androidboot.hardware = "sunfish"
androidboot.serialno = "ABC123"

9.2 条件触发器组合

# Android 12+ 支持多条件触发
on property:sys.boot_completed=1 && property:sys.user.0.ce_available=true
start data_init_services

9.3 Per-Service 命名空间

service my_service /system/bin/my_binary
namespace pid
task_profiles ServiceCapacityLow HighPerformance

十、核心面试题

Q1:oneshotdisabled 的配合使用场景是什么?如果只设 oneshot 会怎样?

答:oneshot 表示服务退出后不自动重启,disabled 表示服务不会随 class_start 一起启动。两者的典型配合场景是需要手动触发的一次性任务(如固件更新、一次性文件系统转换)。如果只设 oneshot 而不设 disabled,该服务会在 class_start <its class> 时启动一次,如果失败了不会重试,如果成功了退出后也不会重启——这通常用于有明确结束条件的工作负载而不是常驻守护进程。

Q2:init.rc 中的 exec 命令与 exec_start 命令选择标准是什么?

答:exec 会阻塞 init 进程的主事件循环,这意味着在 exec 返回之前,init 不能处理 SIGCHLD 信号、不能响应 property 变化、不能接受属性服务 socket 消息。因此 exec 仅应在 early-init 等极早期阶段使用,此时还没有关键服务在运行。exec_start 是异步的,它启动一个 Service 并等待其完成,但不阻塞 init 主循环。所有在 post-fs-data 及之后的阶段都应该使用 exec_start 来避免阻塞 Property Service、SIGCHLD 处理等关键 loop。

Q3:Service socket option 创建的 socket 在 init 重启时会怎样?init 如何向子进程传递 socket fd?

答:Socket 由 init 通过 socket() + bind() + listen() 创建,fd 在 Service 启动前通过环境变量 ANDROID_SOCKET_<name> 传递给 fork 出的子进程。如果服务 crash 并重启,init 不会重建 socket——socket 在 init 进程的生命周期内持久存在。因此,如果服务 fork 出的子进程关闭了 socket fd,重启后新进程会获得新的 fd(指向同一个底层 socket)。如果 init 自身 crash(极其罕见),所有 socket 会丢失,但 init crash 导致内核 panic 重启整个设备。

Q4:同一个 device 可能同时有 /system/etc/init//vendor/etc/init/ 中的同名 rc 文件,如果它们定义了同名的 Service,会发生什么?

答:ServiceParser 在解析到同名 Service 时会报错并忽略后续定义。具体而言,ServiceList::AddService() 会检查是否有重名,如果有重名,第二个 Service 定义将被丢弃并输出错误日志。这确保了服务名称在整个系统中的唯一性。开发者需要在构建时通过命名规范避免冲突(如使用 vendor.<name> 命名 vendor 服务)。

Q5:critical 标记的服务 crash 4 次/4 分钟触发 recovery 的机制是否在用户 build 和 eng build 上有区别?

答:机制本身在所有构建类型上相同——都是 service.cppReap() 方法内的 SVC_CRITICAL 检查。但在 user build(ro.debuggable=0)上,LOG(FATAL) 触发的设备重启比 userdebug/eng build 更快、更激进。在 eng build 上,开发者有更多时间观察日志。实际产品化时,vendor 通常避免标记过多服务为 critical,以防止因单个非致命服务的不稳定导致设备反复重启。

AOSP 核心路径参考:

  • system/core/init/README.md — init.rc 官方语言规范文档
  • system/core/init/parser.cpp — Parser 主逻辑
  • system/core/init/action_parser.cpp — Action 解析器
  • system/core/init/service_parser.cpp — Service 解析器
  • system/core/init/import_parser.cpp — Import 解析器
  • system/core/init/keyword_map.h — 关键字映射模板
  • system/core/init/builtin_commands.cpp — 所有内置命令实现
  • system/core/init/builtin_commands.h — BuiltinFunctionMap 定义
  • system/core/init/service.cpp — Service 类完整实现
  • system/core/init/action_manager.cpp — ActionManager 事件触发和命令队列
  • system/core/init/tokenizer.cpp — rc 文件行分词器
  • system/core/rootdir/init.rc — 系统主 rc 文件
  • system/core/rootdir/init.zygote64.rc — Zygote Service 定义范例
打赏
  • 微信
  • 支付宝

评论