这一篇介绍Android系统用户空间的孵化者Zygote是如何启动的,分析的源码版本为Android11。
这个要回到initrc的脚本开始讲起。
在init第二阶段的初始化最后,只触发了early-init,init,charger或者late-init,那么是如何启动后续的一些daemon进程以及最重要的zygote呢?
在init.rc文件中,如果不是关机充电的启动方式,就会选择走late-init,在代码中可以看到这部分由会调用很多trigger的动作
包括zygote-start来启动zygote,以及trigger early-boot和trigger boot来启动系统服务。
# Mount filesystems and start core system services.
on late-init
trigger early-fs
# Mount fstab in init.{$device}.rc by mount_all command. Optional parameter
# '--early' can be specified to skip entries with 'latemount'.
# /system and /vendor must be mounted by the end of the fs stage,
# while /data is optional.
trigger fs
trigger post-fs
# Mount fstab in init.{$device}.rc by mount_all with '--late' parameter
# to only mount entries with 'latemount'. This is needed if '--early' is
# specified in the previous mount_all command on the fs stage.
# With /system mounted and properties form /system + /factory available,
# some services can be started.
trigger late-fs
# Now we can mount /data. File encryption requires keymaster to decrypt
# /data, which in turn can only be loaded when system properties are present.
trigger post-fs-data
# Load persist properties and override properties (if enabled) from /data.
trigger load_persist_props_action
# Should be before netd, but after apex, properties and logging is available.
trigger load_bpf_programs
# Now we can start zygote for devices with file based encryption
trigger zygote-start
# Remove a file to wake up anything waiting for firmware.
trigger firmware_mounts_complete
trigger early-boot
trigger boot
以adbd为例,它定义的是class core。在trigger boot时,会走到init.rc中的on boot,在on boot下又有class_start core的action,于是就会启动所有class core的服务。
# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /system/bin/adbd --root_seclabel=u:r:su:s0
class core
socket adbd seqpacket 660 system system
disabled
updatable
seclabel u:r:adbd:s0
而zygote是通过trigger zygote-start来触发on zygote-start来启动的
# It is recommended to put unnecessary data/ initialization from post-fs-data
# to start-zygote in device's init.rc to unblock zygote start.
on zygote-start && property:ro.crypto.state=unencrypted
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=unsupported
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
start netd
start zygote
start zygote_secondary
on zygote-start && property:ro.crypto.state=encrypted && property:ro.crypto.type=file
# A/B update verifier that marks a successful boot.
exec_start update_verifier_nonencrypted
start statsd
start netd
start zygote
start zygote_secondary
zygote的服务定义如下
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
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
task_profiles ProcessCapacityHigh MaxPerformance
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
定义了32位的zygote和64位的zygote,两者的区别:1、两者都用app_process,但是带了不同的后缀;2、64位app_process启动时多了一个—start-system-server的参数,可见是它启动了SystemServer,而32位的仅是—socket-name不同,用作了32位进程的孵化。
具体是如何实现的,来看app_process的代码。
int main(int argc, char* const argv[])
{
if (!LOG_NDEBUG) {
String8 argv_String;
for (int i = 0; i < argc; ++i) {
argv_String.append("\"");
argv_String.append(argv[i]);
argv_String.append("\" ");
}
ALOGV("app_process main with argv: %s", argv_String.string());
}
AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
// Everything up to '--' or first non '-' arg goes to the vm.
//
// The first argument after the VM args is the "parent dir", which
// is currently unused.
//
// After the parent dir, we expect one or more the following internal
// arguments :
//
// --zygote : Start in zygote mode
// --start-system-server : Start the system server.
// --application : Start in application (stand alone, non zygote) mode.
// --nice-name : The nice name for this process.
//
// For non zygote starts, these arguments will be followed by
// the main class name. All remaining arguments are passed to
// the main method of this class.
//
// For zygote starts, all remaining arguments are passed to the zygote.
// main function.
//
// Note that we must copy argument string values since we will rewrite the
// entire argument block when we apply the nice name to argv0.
//
// As an exception to the above rule, anything in "spaced commands"
// goes to the vm even though it has a space in it.
const char* spaced_commands[] = { "-cp", "-classpath" };
// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).
bool known_command = false;
int i;
//1.构造runtime参数
for (i = 0; i < argc; i++) {
if (known_command == true) {
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add known option '%s'", argv[i]);
known_command = false;
continue;
}
for (int j = 0;
j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));
++j) {
if (strcmp(argv[i], spaced_commands[j]) == 0) {
known_command = true;
ALOGV("app_process main found known command '%s'", argv[i]);
}
}
if (argv[i][0] != '-') {
break;
}
if (argv[i][1] == '-' && argv[i][2] == 0) {
++i; // Skip --.
break;
}
runtime.addOption(strdup(argv[i]));
// The static analyzer gets upset that we don't ever free the above
// string. Since the allocation is from main, leaking it doesn't seem
// problematic. NOLINTNEXTLINE
ALOGV("app_process main add option '%s'", argv[i]);
}
// Parse runtime arguments. Stop at first unrecognized option.
bool zygote = false;
bool startSystemServer = false;
bool application = false;
String8 niceName;
String8 className;
//2.构造zygote参数
++i; // Skip unused "parent dir" argument.
while (i < argc) {
const char* arg = argv[i++];
if (strcmp(arg, "--zygote") == 0) {
zygote = true;
niceName = ZYGOTE_NICE_NAME;
} else if (strcmp(arg, "--start-system-server") == 0) {
startSystemServer = true;
} else if (strcmp(arg, "--application") == 0) {
application = true;
} else if (strncmp(arg, "--nice-name=", 12) == 0) {
niceName.setTo(arg + 12);
} else if (strncmp(arg, "--", 2) != 0) {
className.setTo(arg);
break;
} else {
--i;
break;
}
}
Vector<String8> args;
if (!className.isEmpty()) {
// We're not in zygote mode, the only argument we need to pass
// to RuntimeInit is the application argument.
//
// The Remainder of args get passed to startup class main(). Make
// copies of them before we overwrite them with the process name.
args.add(application ? String8("application") : String8("tool"));
runtime.setClassNameAndArgs(className, argc - i, argv + i);
if (!LOG_NDEBUG) {
String8 restOfArgs;
char* const* argv_new = argv + i;
int argc_new = argc - i;
for (int k = 0; k < argc_new; ++k) {
restOfArgs.append("\"");
restOfArgs.append(argv_new[k]);
restOfArgs.append("\" ");
}
ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
}
} else {
// We're in zygote mode.
maybeCreateDalvikCache();
if (startSystemServer) {
args.add(String8("start-system-server"));
}
char prop[PROP_VALUE_MAX];
if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {
LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",
ABI_LIST_PROPERTY);
return 11;
}
String8 abiFlag("--abi-list=");
abiFlag.append(prop);
args.add(abiFlag);
// In zygote mode, pass all remaining arguments to the zygote
// main() method.
for (; i < argc; ++i) {
args.add(String8(argv[i]));
}
}
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
if (zygote) {//3.zygote模式,去反射调用到ZygoteInit,传入args参数和zygote参数
runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
} else if (className) {
runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
} else {
fprintf(stderr, "Error: no class name or --zygote supplied.\n");
app_usage();
LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
}
}
这个函数中做了以下几件事
1. 构造Runtime参数
2. 构造Zygote参数
3. 通过反射调用到ZygoteInit,也就是启动到Zygote中
@UnsupportedAppUsage
public static void main(String argv[]) {
ZygoteServer zygoteServer = null;
// Mark zygote start. This ensures that thread creation will throw
// an error.
ZygoteHooks.startZygoteNoThreadCreation();
// Zygote goes into its own process group.
try {
Os.setpgid(0, 0);
} catch (ErrnoException ex) {
throw new RuntimeException("Failed to setpgid(0,0)", ex);
}
Runnable caller;
try {
// Store now for StatsLogging later.
final long startTime = SystemClock.elapsedRealtime();
final boolean isRuntimeRestarted = "1".equals(
SystemProperties.get("sys.boot_completed"));
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.preForkInit();
boolean startSystemServer = false;
String zygoteSocketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
// 4.通过传进来的参数初始化一些启动变量
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = true;
} else if ("--enable-lazy-preload".equals(argv[i])) {
enableLazyPreload = true;
} else if (argv[i].startsWith(ABI_LIST_ARG)) {
abiList = argv[i].substring(ABI_LIST_ARG.length());
} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {
zygoteSocketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
// 主Zygote就是zygote,另一个就是zygote_secondary
final boolean isPrimaryZygote = zygoteSocketName.equals(Zygote.PRIMARY_SOCKET_NAME);
if (!isRuntimeRestarted) {
if (isPrimaryZygote) {
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__ZYGOTE_INIT_START,
startTime);
} else if (zygoteSocketName.equals(Zygote.SECONDARY_SOCKET_NAME)) {
FrameworkStatsLog.write(FrameworkStatsLog.BOOT_TIME_EVENT_ELAPSED_TIME_REPORTED,
BOOT_TIME_EVENT_ELAPSED_TIME__EVENT__SECONDARY_ZYGOTE_INIT_START,
startTime);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
// 正常不走lazy的模式
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
// 调用preload作为预加载的入口
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// 5.会调用到com_android_internal_os_Zygote.cpp中的com_android_internal_os_zygote_nativeInitNativeState
// 做一些socket的初始化动作
Zygote.initNativeState(isPrimaryZygote);
ZygoteHooks.stopZygoteNoThreadCreation();
zygoteServer = new ZygoteServer(isPrimaryZygote);
// 6.准备启动SystemServer了,具体代码分析见下一篇
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);
// {@code r == null} in the parent (zygote) process, and {@code r != null} in the
// child (system_server) process.
if (r != null) {
r.run();
return;
}
}
Log.i(TAG, "Accepting command socket connections");
// The select loop returns early in the child process after a fork and
// loops forever in the zygote.
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
if (zygoteServer != null) {
zygoteServer.closeServerSocket();
}
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
进入到ZygoteInit中之后就是做一些Zygote需要处理的任务了:
4. 通过传进来的参数初始化一些启动变量,这里有个很重要的参数是start-system-server来决定需不需要启动SystemServer
5. 会调用到com_android_internal_os_Zygote.cpp中的com_android_internal_os_zygote_nativeInitNativeState来做一些socket的初始化动作
6. 准备启动SystemServer了
在Zygote启动过程中有个资源加载的过程,通常会在开机时间性能优化中被关注到。来看下在Android11上这部分是怎么做的。
static void preload(TimingsTraceLog bootTimingsTraceLog) {
Log.d(TAG, "begin preload");
bootTimingsTraceLog.traceBegin("BeginPreload");
// 这是一个预留的钩子程序入口,详细介绍和用法示例参考下面ChatGPT的回答
beginPreload();
bootTimingsTraceLog.traceEnd(); // BeginPreload
bootTimingsTraceLog.traceBegin("PreloadClasses");
// 在非root特权用户下读取/system/etc/preloaded-classes这个文件,忽略#开头的注释和空行
// 将读取到的类名用Class.forName加载,异常则报错
preloadClasses();
bootTimingsTraceLog.traceEnd(); // PreloadClasses
// 加载哪些被大多数应用使用,但是没法放到boot classpath里的类,AOSP源码里主要是hidl相关的几个jar,考虑后面增加的优化的话可以放这里
bootTimingsTraceLog.traceBegin("CacheNonBootClasspathClassLoaders");
cacheNonBootClasspathClassLoaders();
bootTimingsTraceLog.traceEnd(); // CacheNonBootClasspathClassLoaders
bootTimingsTraceLog.traceBegin("PreloadResources");
preloadResources();
bootTimingsTraceLog.traceEnd(); // PreloadResources
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadAppProcessHALs");
// 会走到com_android_internal_os_ZygoteInit中的android_internal_os_ZygoteInit_nativePreloadAppProcessHALS
// 这里的定位是预加载那些被用的比较多的passthrough的hals进程
// 目前AOSP源码中只会去做GraphicBufferMapper中的preload
nativePreloadAppProcessHALs();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "PreloadGraphicsDriver");
maybePreloadGraphicsDriver();
Trace.traceEnd(Trace.TRACE_TAG_DALVIK);
// 预加载一些共享库,这里也可以做自定义
preloadSharedLibraries();
preloadTextResources();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
endPreload();
warmUpJcaProviders();
Log.d(TAG, "end preload");
sPreloadComplete = true;
}
其他都是常规的,我发现了这里有个隐藏的宝藏:ZygoteHooks。能参考的介绍资料很少,以下是ChatGPT对于ZygoteHooks的介绍
在Android系统中,ZygoteHooks是一个用于定制化应用程序启动过程的机制。它允许开发者在应用程序启动的不同阶段插入自定义的逻辑代码,以实现一些特定的功能或行为。
具体来说,ZygoteHooks提供了以下几个主要的功能:
- 应用程序预加载:ZygoteHooks可以在应用程序启动之前,预先加载一些资源或执行一些初始化操作,以提高应用程序的启动速度和性能。
- 应用程序监控和调试:ZygoteHooks可以用于监控应用程序的启动过程,以及在应用程序启动时插入调试代码,方便开发者进行调试和错误排查。
- 应用程序权限控制:ZygoteHooks可以用于在应用程序启动之前,检查和修改应用程序的权限设置,以增强系统的安全性和隐私保护。
- 应用程序行为定制:ZygoteHooks可以在应用程序启动过程中,修改或扩展应用程序的行为,以满足特定的业务需求或用户定制化需求。
需要注意的是,ZygoteHooks是一个高级的开发者工具,一般用于定制化或高级应用开发场景。对于一般的应用程序开发,通常不需要直接使用ZygoteHooks机制。
希望以上解释对你有所帮助!
很抱歉,我无法提供完整的示例代码,因为ZygoteHooks的使用是相对高级的开发技术,需要深入理解Android系统和应用程序启动流程。
不过,我可以给你一个简单的示例来说明如何使用ZygoteHooks。请注意,这只是一个示例,具体的实现可能因应用程序的需求而有所不同。
首先,你需要创建一个类来实现ZygoteHooks接口,并重写其中的方法。下面是一个简单的示例:
import dalvik.system.ZygoteHooks; public class CustomZygoteHooks implements ZygoteHooks { @Override public void preFork() { // 在应用程序启动之前执行的操作 // 可以进行资源预加载、权限检查等 } @Override public void postForkCommon() { // 在应用程序启动之后执行的操作 // 可以进行一些初始化操作、调试代码等 } @Override public void postForkChild() { // 在应用程序启动之后执行的操作,仅在子进程中执行 // 可以进行一些特定于子进程的初始化操作 } }
然后,在应用程序的入口处,通过调用ZygoteHooks.install()方法来安装自定义的ZygoteHooks实现。例如:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); // 安装自定义的ZygoteHooks实现 ZygoteHooks.install(new CustomZygoteHooks()); } }
这样,当应用程序启动时,Zygote进程会调用相应的ZygoteHooks方法,执行你自定义的操作。
需要注意的是,以上只是一个简单的示例,实际使用时需要根据具体需求进行适当的修改和扩展。
希望以上示例能够对你有所帮助!