Read The Fucking Source Code. ​​—— Linus​​​

站在’巨人’的肩膀上开始自己的旅途。​​​—— 佚名​​​

愉快的周末,从打开💻开始,到骑行归来结束。​​​—— 佚名​

Android 系统启动 <zygote 进程> 笔记【2】_Android

文章系列

​注:​​​ 本系列文章源码基于 ​​Android 11-r21 master 分支​

  • Android 系统启动 <init 进程> 笔记【1】
  • Android 系统启动 <zygote 进程> 笔记【2】
  • Android 系统启动 ​​<System server>​​ 笔记【3】

相关文件

  • /system/core/init/init.cpp
  • /system/etc/init/hw/init.rc (源码工程没找到,是从手机上获取)
  • /system/etc/init/hw/init.zygote32.rc (手机上获取)
  • /system/etc/init/hw/init.zygote64_32.rc (手机上获取)
  • /system/core/init/action.cpp
  • /system/core/init/service.cpp
  • /system/core/init/service_list.cpp
  • frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
  • frameworks/base/core/java/com/android/internal/os/ZygoteServer.java
  • frameworks/base/core/java/com/android/internal/os/Zygote.java
  • frameworks/base/core/java/com/android/internal/os/WrapperInit.java

解析 hw/init.rc

init.rc 解析

带着上文留下的疑惑继续看源码,之前提到执行到第二阶段时候进入了 loop 循环,似乎不知去向何处?第二阶段创建 init 进程中有一个重要的函数那就是 ​​LoadBootScripts(actionManager,serviceList)​​​ 加载启动脚本,相当重要,与 ​​init.rc​​ 文件存在千丝万缕的关系,那么就要看看这里究竟在执行什么。

//init.cpp
static void LoadBootScripts(ActionManager& action_manager, ServiceList& service_list) {
Parser parser = CreateParser(action_manager, service_list);
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
//解析 init.rc,启动的关键文件
parser.ParseConfig("/system/etc/init/hw/init.rc");
if (!parser.ParseConfig("/system/etc/init")) {
late_import_paths.emplace_back("/system/etc/init");
}

parser.ParseConfig("/system_ext/etc/init");
if (!parser.ParseConfig("/vendor/etc/init")) {
//vendor 厂商相关的初始化配置
late_import_paths.emplace_back("/vendor/etc/init");
}
if (!parser.ParseConfig("/odm/etc/init")) {
late_import_paths.emplace_back("/odm/etc/init");
}
if (!parser.ParseConfig("/product/etc/init")) {
late_import_paths.emplace_back("/product/etc/init");
}
} else {
parser.ParseConfig(bootscript);
}
}
//init.cpp
void SecondStageMain(){

//数据解析获得,开始构建 action 队列
ActionManager& am = ActionManager::GetInstance();
am.QueueBuiltinAction(SetupCgroupsAction, "SetupCgroups");
.... etc

//触发启动
am.QueueEventTrigger("init");

//若处于充电模式将延迟初始化
std::string bootmode = GetProperty("ro.bootmode", "");
if (bootmode == "charger") {
am.QueueEventTrigger("charger");
} else {
am.QueueEventTrigger("late-init");
}

//init 进程进入无限轮训
while(true){
//开始通过 command 命令执行 inir.rc 脚本各项服务以及初始化
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {
am.ExecuteOneCommand();
}
if (!(prop_waiter_state.MightBeWaiting() || Service::is_exec_service_running())) {

// If there's more work to do, wake up again immediately.
if (am.HasMoreCommands())
epoll_timeout = 0ms;
}
}
}

在 Android 11 上,init.rc 文件在​​/system/etc/init/hw/init.rc​

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FSCwp9hL-1658835973999)

这是在小米手机找的,看看内容,rc 文件被视为 Android 初始化语言,也有一定的语法,

总结下 Android 初始化语言

  • action on 后携带一组命令
  • trigger 触发器,确定何时执行命令
  • service 当 init 退出时启动或重启
  • options 进一步控制命令执行的方式和时间
  • 命令:on 每一行代表一条命令
  • import 导入额外的 rc 文件需要解析

下面简略看看 rc 文件:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gKFSboqt-1658835973999)

//  /system/etc/init/hw/init.rc

# 小米系统,肯定也是有厂商自己的解析文件需要执行属于自己的进程
# import 指明导入其他配置文件需要解析
# MIUI ADD:
import /init.miui.rc

# 还记得 SecondStageMain actionManage 吗
# am.QueueEventTrigger("early-init");
on early-init
# 一个守护进程,负责处理 uevent 消息
start ueventd
# apex 服务于系统模块安装
exec_start apexd-bootstrap

# 触发所有 action
# am.QueueEventTrigger("init");
on init
# 创建 stdio 标准输入输出链接
symlink /proc/self/fd/0 /dev/stdin
# 给 sdcard 更改权限
chmod 0770 /config/sdcardfs

# 启动服务
# 系统服务,越来越接近应用层了
start servicemanager
# hw——hardware,硬件服务
start hwservicemanager
#供应商服务
start vndservicemanager
# init action 就执行到这,中间省略很多命令,这里只是抽取几个,点到为止

# 挂载文件系统以及核心服务
# am.QueueEventTrigger("late-init");
on late-init
# 触发 fs:Vold 控制和管理外部存储的进程
trigger early-fs

# 重点来了⚠️⚠️⚠️
# import /system/etc/init/hw/init.${ro.zygote}.rc
# zygote 进来了,常说的 Android 应用层的鼻祖
trigger zygote-start

trigger early-boot
trigger boot

on boot
# 启动 HAL 硬件抽象类服务
class_start hal
# 启动核心类服务
class_start core

解析 zygote.rc

看上面截图,现在该执行​​init.zygote32.rc、init.zygote64_32.rc​​,继续往下看。

//  /system/etc/init/hw/init.zygote32.rc
// zygote32 : 只有一个 32,那就是纯纯的为 32 位准备的

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
writepid /dev/cpuset/foreground/tasks
//  /system/etc/init/hw/init.zygote64_32.rc
// zygote64_32 : 前部分 64 指主要模式,后部分 32 指辅助模式;同样的也会有 zygote32_64.rc、zygote32.rc、zygote64.rc etc.

# service 是 Android 初始化话语言的一部分,指 init 启动或退出时重新启动服务
# 显然,这里的服务名称就是‘家喻户晓’的 zygote 进程
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
class main
priority -20 ### 进程优先级 -20 ,值越小优先级越高,取值范围 [-20,19]
user root ### 由 root 用户执行
group root readproc reserved_disk
socket zygote stream 660 root system
socket usap_pool_primary stream 660 root system
onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
onrestart write /sys/power/state on
onrestart restart audioserver
onrestart restart cameraserver
onrestart restart media
onrestart restart netd
onrestart restart wificond
task_profiles ProcessCapacityHigh MaxPerformance

# zygote_secondary ?????
# 你在看前面提到的‘主模式’和‘辅模式’,恰好 zygote 是 app_process64,zygote_secondary 是 app_process32,
# 刚刚好对应上文件名 init.zygote64_32.rc 【主模式是64,辅模式是32】
service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preload
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
socket usap_pool_secondary stream 660 root system
onrestart restart zygote
task_profiles ProcessCapacityHigh MaxPerformance

创建 zygote Process

在第二阶段 SecondStageMain 初始化时候解析了 inir.rc,回到 main.cpp 看到 函数映射表 GetBuiltinFunctionMap 作为参数传进入 SubcontextMain 第四部分开始执行,接着看看执行流程。

//main.cpp
return SubcontextMain(argc, argv, &function_map);

//subcontext.cpp
auto subcontext_process = SubcontextProcess(function_map, context, init_fd);

//subcontext.cpp
SubcontextProcess(const BuiltinFunctionMap* function_map, std::string context, int init_fd)
: function_map_(function_map), context_(std::move(context)), init_fd_(init_fd){};
//通过构造函数,直接将函数映射表赋值给成员 function_map_
const BuiltinFunctionMap* function_map_;

//在 SubcontextMain 中开始主循环
subcontext_process.MainLoop();

//主循环中准备执行命令
RunCommand(subcontext_command.execute_command(), &reply);

//映射表 function_map_ 被使用
//根据参数(命令)查找对应的内置函数
auto map_result = function_map_->Find(args);
//找到了命令准备执行
result = RunBuiltinFunction(map_result->function, args, context_);

//构造参数,直接调用
//回想一下,映射表中是否有着一个 item
//{"class_start", {1, 1, {false, do_class_start}}}
//do_class_start:内置函数被声明在 builtins.cpp 中,下面看看其实现
auto builtin_arguments = BuiltinArguments(context);
return function(builtin_arguments);
//builtins.cpp
static Result<void> do_class_start(const BuiltinArguments& args) {
if (android::base::GetBoolProperty("persist.init.dont_start_class." + args[1], false))
return {};
//服务启动
/*
1、ServiceList::GetInstance() 到底是什么东西啊?service 列表又是什么?
还记得第二阶段初始化 SecondStageMain 中这段代码吗
ServiceList& sm = ServiceList::GetInstance();
LoadBootScripts(am, sm); //这正是在解析 init.rc 文件,其中就包含 hw/init.rc

2、可以认为 service 就是通过解析 init.rc 中的 service 获得的,此文件正好也导入 import hw/init.rc,其中包含 zygote 相关,
进而继续解析 init.zygote.rc,zygote.rc 文件内容也会被解析到

ServiceList.GetInstance 就是 std::vector<std::unique_ptr<Service>> services_;
service->classenames() 就是 std::set<std::string> classnames_;

3、创建 Service 的构造函数:
Service::Service(const std::string& name, unsigned flags, uid_t uid, gid_t gid,
const std::vector<gid_t>& supp_gids, int namespace_flags,
const std::string& seclabel, Subcontext* subcontext_for_restart_commands,
const std::vector<std::string>& args, bool from_apex)
: name_(name),
classnames_({"default"}),
... etc
){}

*/

for (const auto& service : ServiceList::GetInstance()) {
//参数的来源
/*
1、反复查阅资料得知 args 就是 rc 文件中每个 service 的参数
args[1] 自然是第二个参数
看 hw/zygote.rc service 执行 zygote 命令前部分
...
service zygote
class main
...

2、因此 args[1] 其实就是 main
同样,我们看 hw/init.usb.rc 也有一个 service
...
service adbd
class core
...
adb 的使用与 adbd 可有很大的关系,adbd 是一个远程服务进程

3、所以这里的意思是:
根据参数名称去服务列表中查找是否存在,如果服务存在那么开始执行
服务一般是以进程的形式存在,且很有可能是守护进程
*/
if (service->classnames().count(args[1])) {
if (auto result = service->StartIfNotDisabled(); !result.ok()) {
LOG(ERROR) << "Could not start service '" << service->name()
<< "' as part of class '" << args[1] << "': " << result.error();
}
}
}
return {};
}

//service.cpp
Result<void> Service::StartIfNotDisabled() {
if (!(flags_ & SVC_DISABLED)) {
return Start();
} else {
flags_ |= SVC_DISABLED_START;
}
return {};
}

//service.cpp
Result<void> Service::Start() {

pid_t pid = -1;
if (namespaces_.flags) {
pid = clone(nullptr, nullptr, namespaces_.flags | SIGCHLD, nullptr);
} else {
//就这?进程就被 fork 出来了???
pid = fork();
}

// pid 0 是 idle 进程,肯定不能
if (pid == 0) {
umask(077);
RunService(override_mount_namespace, descriptors, std::move(pipefd));
_exit(127);
}

//创建进程组
errno = -createProcessGroup(proc_attr_.uid, pid_, use_memcg);
}

到此,通过查找服务列表创建了一堆进程,现在我们主要关注 ​​zygote​​进程的创建,这时候间从 cpp 进入 Java

初始化 zygote

预加载

//ZygoteInit.java
public class ZygoteInit {
/*
* 初始化主要做:
* 1、完成预初始化
* 2、创建 zygote 服务
* 3、创建系统服务
*/
public static void main(String[] argv) {
//【1】完成预初始化
/*
1、调用 ZygoteHooks.onBeginPreload(); ZygoteHooks 从 Dalvik 包引入,在 framework 下没有找到的源码应该是在别处了,预想是对 Dalvik 的初始化;预加载结束时也会调用 ZygoteHooks.onEndPreload();
2、VMRuntime 为 Dalvik 预加载路径下的类 /system/etc/preloaded-classes、profilebootclasspath
3、创建并缓存非启动类路径下的类加载器 /system/framework/android.hidl.base-V1.0-java.jar、/system/framework/android.hidl.manager-V1.0-java.jar (HIDL 接口定义语言 —— https://source.android.google.cn/devices/architecture/hidl?hl=zh-cn)
4、加载资源,加载前先更新配置(比如当前设备分辨率、屏幕尺寸、语言),
根据分辨率加载 drawable、颜色资源
5、通过 native 加载为应用进程准备的 HAL 硬件抽象列表
6、如果开启了 ro.zygote.disable_gl_preload,也通过 native 执行图形 GL 预加载
7、通过 System.loadLibrary 加载共享库 android.lib、compiler_rt.lib、jnigraphics.lib
8、准备 Hyphenator 环境,缓存字体
9、加载 webviewchromium_loader.lib,准备 webview
10、通过 AndroidKeyStoreProvider 安装 keystore 内容提供者
*/
preload(bootTimingsTraceLog);

//初始化 GC,并执行一次清理
ZygoteHooks.gcAndFinalize();
//到这里 zygote 已经是【初始化完毕】
Zygote.initNativeState(isPrimaryZygote)

//【2】创建 zygote 服务
ZygoteServer zygoteServer = null;
zygoteServer = new ZygoteServer(isPrimaryZygote);

//【3】创建系统服务
if (startSystemServer) {
//fork,可见每一个系统服务都是独立的进程;ABI —— Application binary interface【参考链接】
//在 Android 项目中对应的就是 ndk filter,如 arm64、x86 .etc
//为支持不同平台,ndk filter 是能够配置多个的,所以是一个列表形式存在
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
if (r != null) {
//创建之后马上运行
return;
}
}

// zygote 服务进入自己的世界轮训
caller = zygoteServer.runSelectLoop(abiList);
if(caller != null){
caller.run();
}
}
}

创建 zygote Server

服务主要还是通过 socket 实现,等待来自 Linux、unix 守护进程 (socket) 的消息,同时也负责子进程的创建。

//ZygoteServer.java
class ZygoteServer {

//列举几个重要的成员
//用于监听 socket 连接
private LocalServerSocket mZygoteSocket;
//为 USAP 非专用应用进程池 服务
private final LocalServerSocket mUsapPoolSocket;

ZygoteServer(boolean isPrimaryZygote) {
//通过 native 调用获取
mUsapPoolEventFD = Zygote.getUsapPoolEventFD();

//主 zygote
if (isPrimaryZygote) {
//完成的 socket 名称需要和 ANDROID_SOCKET_ + socketname 拼接,
//然后拿完整的名称去系统环境变量中查找获取文件描述符 fd —— file describe,实际是一个整型数值【参考链接】
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.PRIMARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_PRIMARY_SOCKET_NAME);
} else { //辅 zygote
mZygoteSocket = Zygote.createManagedSocketFromInitSocket(Zygote.SECONDARY_SOCKET_NAME);
mUsapPoolSocket =
Zygote.createManagedSocketFromInitSocket(
Zygote.USAP_POOL_SECONDARY_SOCKET_NAME);
}

//获取 非专用应用进程池 配置,还是通过系统配置 SystemPropertice 获取
/*
mUsapPoolSizeMax —— usap_pool_size_max
mUsapPoolSizeMin —— usap_pool_size_min
mUsapPoolRefillThreshold —— usap_refill_threshold
*/
fetchUsapPoolPolicyProps();
}
}



//最重要的还是进入 poll 轮训【关于高并发 IO 多路复用,参考链接】
Runnable runSelectLoop(String abiList) {

while(true){
//每一次轮训且超过一分钟都更新 USAP 配置
fetchUsapPoolPolicyPropsWithMinInterval

//系统调用 poll 处理文件描述符 fd
//Os.poll 返回值0:表示处理超时或非阻塞状态没有可处理的文件描述符
pollReturnValue = Os.poll(pollFDs, pollTimeoutMs);

... etc

//还有一个需要关注的就是返回值,类型是 Runnable
//这是在特殊情况下发生重置 USAP,command 的内容是:
/*
fetchUsapPoolPolicyPropsIfUnfetched();
ZygoteHooks.preFork();
ZygoteHooks.postForkCommon();
*/
final Runnable command = fillUsapPool(sessionSocketRawFDs, isPriorityRefill);
if (command != null) {
return command;
}
}

创建 System Server

//ZygoteInit.java

/*
abiList —— ndl filter
socketname —— zygote 进程名称
zygoteServer —— 自然是 zygote 的主要服务
*/
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {


//启动参数
String[] args = {
"--setuid=1000", //linux 中不同 uid 可以代表拥有不同的权限
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
+ "1024,1032,1065,3001,3002,3003,3005,3006,3007,3009,3010,3011,3012",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
"--target-sdk-versinotallow=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
"com.android.server.SystemServer",
};


//省略参数构造过程
ZygoteArguments parsedArgs;

//创建服务进程,还是调用 native 方法
/*
int pid = nativeForkSystemServer(
uid, gid, gids, runtimeFlags, rlimits,
permittedCapabilities, effectiveCapabilities);
*/
pid = Zygote.forkSystemServer(
parsedArgs.mUid, parsedArgs.mGid,
parsedArgs.mGids,
parsedArgs.mRuntimeFlags,
null,
parsedArgs.mPermittedCapabilities,
parsedArgs.mEffectiveCapabilities);

/*
pid=0 则是子进程被创建
pid=-1 则表示出错
pid (非0值)创建父进程
*/
if (pid == 0) {
//???还会有第二个 zygote 进程,这是什么操作???
//看看官方描述:We determine this by comparing the device ABI list with this zygotes list.
// If this zygote supports all ABIs this device supports, there won't be another zygote.
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}

zygoteServer.closeServerSocket();
//继续把参数分发给系统服务进程,这里做的事情比较多了
/*
1、获取系统服务类路径 systemServerClassPath,首先还是从系统环境中读取Os.getenv("SYSTEMSERVERCLASSPATH");当进程执行时 ART 将会处理此路径
2、负责 zygote 的 native 初始化和 application 的执行
3、这里无论先走那个分支,后面都会走到同一个方法调用:return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);

if (parsedArgs.mInvokeWith != null) {
WrapperInit.execApplication(parsedArgs.mInvokeWith,
parsedArgs.mNiceName, parsedArgs.mTargetSdkVersion,
VMRuntime.getCurrentInstructionSet(), null, args);
} else {
ClassLoader cl = getOrCreateSystemServerClassLoader();
return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
parsedArgs.mDisabledCompatChanges,
parsedArgs.mRemainingArgs, cl);
}
*/
return handleSystemServerProcess(parsedArgs);
}
}

//RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {

//设置运行目标版本
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
//通过启动类名找到此类,由类加载器加载并调用其 main 方法
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
//RuntimeInit.java
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {

//常规方法,只是执行 classloader
Class<?> cl = Class.forName(className, true, classLoader);
Method m = cl.getMethod("main", new Class[] { String[].class });

//因为当前是在 zygote 进程创建 SystemServer,在此流程中本次执行我们认为参数 className="com.android.internal.os.SystemServer"
return new MethodAndArgsCaller(m, argv);
}

到这里 SystemServer 已经创建完成,接下来是通过 ​​MethodAndArgsCaller​​​ 方法执行其中的 ​​main​​​ 方法,源码路径是​​/frameworks/base/services/java/com/android/server/SystemServer.java​​。

那么本节笔记到此,我们下周再见😊。

附加

参考链接

  • Androi.bp:bp 文件,替换 .mk 的配置文,由 https://github.com/palantir/blueprint 框架解析
  • Android.mk:mk 文件,Android 程序编译
  • lmkd:low memory killer deamon 低内存终止守护进程
  • Apex:Android pony express 解决较低级别系统模块的安装流程 https://source.android.google.cn/devices/tech/ota/apex?hl=zh-cn
  • syspro 文件:系统共享信息的属性配置文件,通常作为系统 API 实现 https://source.android.google.cn/devices/architecture/sysprops-apis?hl=zh-cn#:~:text=一个,Sysprop%20说明文件包含一条属性消息,用来描述一组属性%E3%80%82
  • ABI:与 CPU 指令集相关 https://developer.android.google.cn/ndk/guides/abis?hl=zh-cn
  • Linux IO 多路复用:select、poll、epoll https://cloud.tencent.com/developer/article/1005481
  • MTE:memory tagging extension ​​ 内存标签扩展 ​
  • 已加标记指针:https://source.android.google.cn/devices/tech/debug/tagged-pointers?hl=zh-cn