
Init加载RC文件
在Android系统启动流程中,Init进程是用户空间的第一个进程(PID=1),而RC文件(初始化脚本文件)则是Init进程的"操作手册"。
理解Init线程加载RC文件的过程,需结合源码逻辑才能掌握其核心机制。
本文基于Android 12+源码 (路径:system/core/init/),从概念、流程到关键机制,补充源码片段与注释,帮你建立"代码-功能"的对应认知。
Init进程与RC文件的角色定位
1. Init进程:用户空间的"启动管家"
Init进程由Linux内核通过/init可执行文件启动,其核心逻辑在init.cpp的main()函数中实现,主要职责通过源码可直观体现:
cpp
// system/core/init/main.cpp
int main(int argc, char** argv) {
// 1. 初始化日志系统(klog+main_log)
log_init();
// 2. 挂载核心文件系统(/sys、/dev、/proc等)
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
// 3. 初始化RC文件解析器
Parser parser;
// 4. 加载并解析RC文件(核心步骤)
parser.ParseConfig("/init.rc");
// 5. 执行RC文件中定义的动作与服务
ActionManager& am = ActionManager::GetInstance();
am.QueueEventTrigger("early-init");
// 6. 进入事件循环,处理服务监控与事件响应
return am.EventLoop();
}
2. RC文件:Init进程的"配置说明书"
RC文件通过特定语法定义"动作(Action)"和"服务(Service)",常见文件及作用如下表,其加载顺序由源码中ParseConfig()的调用逻辑决定。
| 文件名 | 路径 | 核心作用 | 加载触发逻辑(源码) |
|---|---|---|---|
init.rc |
/ |
系统主脚本 | parser.ParseConfig("/init.rc")(main函数直接调用) |
init.{ro.hardware}.rc |
/ |
硬件适配脚本 | 读取ro.hardware属性后拼接路径调用ParseConfig() |
init.vendor.rc |
/vendor/etc/init/ |
厂商定制脚本 | init.rc中通过import /vendor/etc/init/*.rc触发 |
完整流程
Init对RC文件的处理分为解析阶段 和执行阶段,每个阶段的核心逻辑均对应明确的源码实现,以下按步骤拆解。
阶段1:启动初期------初始化解析环境
Init进程启动后,先完成日志、文件系统、解析器的初始化,为RC文件解析做准备,关键源码如下:
cpp
// system/core/init/main.cpp(初始化关键步骤)
void InitializeEnvironment() {
// 1. 初始化日志:开启内核日志(klog)和Init进程日志(main_log)
log_init();
// 2. 挂载必要文件系统:/sys(设备信息)、/dev(设备节点)、/proc(进程信息)
if (mount("sysfs", "/sys", "sysfs", 0, NULL) == -1) {
PLOG(ERROR) << "mount sysfs failed";
}
// 3. 初始化SELinux(安全策略),后续RC文件执行需权限校验
selinux_init_all();
}
// 初始化解析器:加载RC文件语法规则(识别service、on等关键字)
Parser::Parser() {
// 注册"service"关键字的解析函数:ParseService
AddSectionParser("service", std::make_unique<ServiceParser>());
// 注册"on"关键字的解析函数:ParseAction
AddSectionParser("on", std::make_unique<ActionParser>());
// 注册"import"关键字的处理函数:ParseImport
AddLineParser(std::make_unique<ImportParser>());
}
源码注释:
log_init():日志输出到/dev/kmsg(内核日志)和logcat,后续解析错误可通过logcat -s init查看;Parser类:通过"关键字-解析函数"映射,确保不同RC指令被正确处理(如service对应ServiceParser)。
阶段2:解析阶段------加载并解析RC文件
解析器初始化后,Init按"主文件→硬件文件→厂商文件"的顺序加载RC文件,核心源码在parser.cpp中:
2.1 加载主文件与import指令
cpp
// system/core/init/parser.cpp
bool Parser::ParseConfig(const std::string& path) {
// 1. 打开RC文件,若不存在直接返回错误(如厂商文件可选)
std::unique_ptr<File> file = OpenFile(path);
if (!file) {
PLOG(ERROR) << "Could not open " << path;
return false;
}
// 2. 逐行解析文件内容
std::string line;
while (file->ReadLine(&line)) {
// 跳过注释行(#开头)和空行
if (line.empty() || line[0] == '#') continue;
// 3. 处理import指令:加载其他RC文件(如厂商脚本)
if (ParseImport(line, path)) continue;
// 4. 处理section指令(service/on)或普通指令(setprop/chmod)
ParseLine(line, path, file->LineNumber());
}
return true;
}
// 处理import指令:递归加载指定路径的RC文件
bool Parser::ParseImport(const std::string& line, const std::string& path) {
std::vector<std::string> args = Split(line, ' ');
if (args[0] != "import") return false;
// 拼接import路径(如import /vendor/etc/init/*.rc)
std::string import_path = ExpandArgs(args[1], path);
// 加载该路径下所有RC文件
for (const auto& file : Glob(import_path)) {
ParseConfig(file);
}
return true;
}
2.2 解析Service与Action指令
当解析到service或on指令时,会调用对应的解析函数,将配置信息存入全局管理类(ServiceList/ActionManager):
cpp
// 解析service指令:如"service zygote /system/bin/app_process64 ..."
// system/core/init/service_parser.cpp
bool ServiceParser::ParseSection(const std::vector<std::string>& args, const std::string& filename, int line) {
// 1. 提取service核心参数:服务名(args[1])、可执行路径(args[2])
std::string name = args[1];
std::string path = args[2];
// 2. 创建Service对象,设置启动参数(args[3...])
Service* service = new Service(name, path, args.slice(3));
// 3. 解析service属性(如oneshot/respawn)
for (size_t i = 3; i < args.size(); ++i) {
if (args[i] == "oneshot") {
service->flags |= SVC_ONESHOT; // 仅启动一次
} else if (args[i] == "respawn") {
service->flags |= SVC_RESPAWN; // 崩溃后自动重启
}
}
// 4. 将service存入全局ServiceList(供后续启动与监控)
ServiceList::GetInstance().AddService(service);
return true;
}
// 解析on指令:如"on early-init mkdir /data 0755 root root"
// system/core/init/action_parser.cpp
bool ActionParser::ParseSection(const std::vector<std::string>& args, const std::string& filename, int line) {
// 1. 提取触发条件(如"early-init")
std::string trigger = args[1];
// 2. 创建Action对象,绑定触发条件
std::unique_ptr<Action> action = std::make_unique<Action>(trigger);
// 3. 后续行的命令(如mkdir)加入Action的命令队列
action->AddCommand(std::make_unique<Command>(...));
// 4. 将Action存入全局ActionManager(供后续触发执行)
ActionManager::GetInstance().AddAction(std::move(action));
return true;
}
源码注释:
ServiceList:全局单例,存储所有服务,后续通过StartService()启动服务;ActionManager:全局单例,存储所有动作,通过QueueEventTrigger()触发指定动作(如"early-init")。
阶段3:执行阶段------按"触发条件"执行动作与服务
解析完成后,Init通过ActionManager触发不同阶段的动作,核心源码在action_manager.cpp中:
cpp
// system/core/init/action_manager.cpp
void ActionManager::QueueEventTrigger(const std::string& trigger) {
// 1. 查找所有触发条件为trigger的Action(如"early-init"对应的所有动作)
auto actions = actions_by_trigger_[trigger];
// 2. 将Action加入执行队列
for (auto& action : actions) {
if (!action->HasExecuted()) {
action_queue_.push_back(action);
}
}
// 3. 执行队列中的Action(同步执行)
ExecuteAllActions();
}
// 执行Action中的所有命令(如mkdir、start service)
void ActionManager::ExecuteAllActions() {
while (!action_queue_.empty()) {
auto action = action_queue_.front();
action_queue_.pop_front();
// 执行Action中的每个Command
for (const auto& command : action->Commands()) {
// 命令执行逻辑:如"start zygote"对应启动zygote服务
command->Execute();
}
// 标记Action已执行,避免重复触发
action->SetExecuted(true);
}
}
关键阶段触发逻辑(对应源码调用):
early-init阶段 :main()函数中调用am.QueueEventTrigger("early-init"),执行目录创建、权限设置;init阶段 :early-init执行完成后,通过queue_event_trigger("init")触发,启动ueventd、servicemanager;late-init阶段 :init阶段完成且/data挂载后,触发late-init,启动zygote、surfaceflinger。
阶段4:监控阶段------服务重启与RC重载
系统启动后,Init通过事件循环监控服务状态,核心源码在main()函数的EventLoop()中:
cpp
// system/core/init/main.cpp
int ActionManager::EventLoop() {
while (true) {
// 1. 监控子进程(服务)状态:若服务崩溃(exit code非0),检查是否需重启
CheckChildProcesses();
// 2. 处理外部指令(如initctl reload,触发RC文件重载)
HandleSignal();
// 3. 等待事件(如服务退出信号、外部指令),避免空循环占用CPU
epoll_wait(epoll_fd_, events, EPOLL_MAX_EVENTS, -1);
}
}
// 检查服务状态,重启配置了respawn的服务
void CheckChildProcesses() {
pid_t pid = waitpid(-1, &status, WNOHANG);
if (pid <= 0) return;
// 查找PID对应的服务
Service* service = ServiceList::GetInstance().FindServiceByPid(pid);
if (service && (service->flags & SVC_RESPAWN)) {
// 重启服务(如zygote崩溃后自动重启)
service->Start();
}
}
源码层面的可靠性设计
Init通过以下源码逻辑解决"配置冲突""服务依赖"等问题,确保RC文件加载的灵活性与稳定性。
1. 配置优先级:后加载文件覆盖前序文件
源码中,ServiceList::AddService()会先删除同名服务,再添加新服务,实现"后加载覆盖":
cpp
// system/core/init/service_list.cpp
void ServiceList::AddService(Service* service) {
// 1. 若已存在同名服务,先删除(确保后加载的服务生效)
auto it = services_.find(service->name());
if (it != services_.end()) {
delete it->second;
services_.erase(it);
}
// 2. 添加新服务
services_[service->name()] = service;
}
2. 服务依赖:after/require关键字的实现
解析service时,会将依赖信息存入service->after_list(),启动服务前检查依赖:
cpp
// system/core/init/service.cpp
bool Service::Start() {
// 1. 检查after依赖:确保所有"after"的服务已启动
for (const auto& after_name : after_list_) {
Service* after_service = ServiceList::GetInstance().FindService(after_name);
if (!after_service || !after_service->IsRunning()) {
LOG(ERROR) << "Service " << name() << " depends on " << after_name << " which is not running";
return false;
}
}
// 2. 启动服务(fork子进程执行服务可执行文件)
pid_t pid = fork();
if (pid == 0) {
execv(path_.c_str(), args_.data()); // 执行服务二进制文件
}
return true;
}
总结
Init加载RC文件的过程,本质是"源码调用链驱动配置解析与执行"的过程,核心对应关系如下:
main()→ 初始化环境 → 调用ParseConfig()加载RC文件;ParseConfig()→ 逐行解析 → 调用ServiceParser/ActionParser存储配置;QueueEventTrigger()→ 触发阶段动作 → 调用ExecuteAllActions()执行命令;EventLoop()→ 监控服务状态 → 调用CheckChildProcesses()重启服务。
掌握这一调用链,即可通过日志定位RC文件加载问题(如服务未启动可查Service::Start()的错误日志),也能通过修改RC文件或源码实现定制化需求(如新增开机服务)。
