文章目录
Zygote 概述
- Zygote 时android中非常重要的一个进程,它和 init 进程、SystemServer 进程同为支持Android最重要的进程。、
- Linux 的进程时通过系统调用 fork 产生的,fork 出的子进程除了内核中的一些核心数据结构和父进程不同外,其余的内存影像都是和父进程共享的。只有当子进程需要去修改这些共享内存时,操作系统才会为子进程分配一个新的页面,并将老的页面上的数据复制一份到新的页面,这就是所谓的写时拷贝(Copy On Write)
- 通常子进程被fork出来后,会继续执行系统调用 exec 。exec将用一个新的可执行文件的内容替换当前的代码块、数据段、堆和栈。fork + exec 是 Linux 启动应用标准做法,init 也是通过这样启动各种服务。
- Zygote 启动各种应用时却只调用了 fork 没有调用 exec。Android引用中执行的是 java 代码,Java代码不同才造成了应用的不同,而对于Java 运行环境是相同的。
- Zygote 初始化时会创建虚拟机,同时把需要的系统类库和资源文件加载到内存里。Zygote fork出进程后,这个子进程也继承了能正常工作的虚拟机和各种系统资源,接下来的子进程中需要加在 APK 中的字节码就可以运行了。这样运行时间会大大缩短。
- 下图时 init 和 Zygote 区别
Zygote 进程的初始化
- Zygote 进程在 init 进程中以 Service 的方式启动的,5.0 以前是直接放在 init.rc 的代码块中,现在是放在了单独的文件中,然后在 init.rc 通过 import 方式引入文件,如下:
import /init.${ro.zygote}.rc
从import中可以看出,init.rc 并不是直接引入了某个固定的文件而是根据ro.zygote的属性来引入不同的文件,这是因为从 5.0 开始android开始支持64位编译,Zygote 本身也会有 32位和64位的区别,因此通过 ro.zygote 来控制启动不同的版本的 Zygote 进程。属性 ro.zygote 可能有“zygote32”、“zygote32_64”、“zygote64”、“zygote64_32”。所以在 init.rc同级目录下可能有四个zygote.rc文件,我在下载 9.0源码就可以搜索出如下结果:
- 下面是 zygote32.rc 文件内容
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
onrestart write /sys/android_power/request_state wake
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
- 下面是 zygote32_64.rc 文件内容
service zygote /system/bin/app_process32 -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
onrestart write /sys/android_power/request_state wake
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
service zygote_secondary /system/bin/app_process64 -Xzygote /system/bin --zygote --socket-name=zygote_secondary
class main
priority -20
user root
group root readproc reserved_disk
socket zygote_secondary stream 660 root system
onrestart restart zygote
writepid /dev/cpuset/foreground/tasks
从上面两个文件中可以看出 zygote32_64.rc 有两个 Zygote 服务,zygote 和 zygote_secondary,这两个服务最大的区别是启动可执行文件不同,一个是app_process32,另外一个是 app_process64。zygote64 和 zygote64_32 也差不带多,只不过把可执行文件换过来了。从这里可以知道Android支持四种运行模式。
- 纯32位模式:属性为ro.zygote的值为zygote32
- 以32位为主,64位为辅:属性为ro.zygote的值为zygote32_64
- 纯64位模式:属性为ro.zygote的值为zygote64
- 以64位为主,32位为辅:属性为ro.zygote的值为zygote64_32
- 从上面 Zygote 文件可以看出 Zygote进程可执行文件是 app_process,app_process文件的源文件位于下图:
app_process 的 main 函数
-
main 函数主要功能是解析启动参数。虽然Zygote 进程是通过 app_process 可执行文件创建的,但是app_process 除了可以创建 Zygote 外,还可以创建普通进程。前面介绍 Zygote 进程创建的时候是没有调用 exec ,从执行角度来看是有优点的,但是在某些情况下,例如调试内存分配时,会带来很多麻烦。所以Android也提供了传统方式启动应用。
-
这两种启动方式是通过参数来区别的
启动格式为:app_process[虚拟机参数] 运行目录 参数 [java 类]
- 虚拟机参数:以 _ 开头启动虚拟机时传递给虚拟机
- 运行目录:程序运行目录,通常是 /system/bin
- 参数:以–开头。参数 --zygote 表示启动 zygote,参数 --application表示以普通进程方式执行Java代码。
- Java类:将要执行的Java类,必须有一个静态的 main 。使用 -Zygote 时不会执行这个类,而是固定的执行 ZygoteInit 类。
main函数启动流程
- 创建AppRuntime 对象并保存参数
AppRuntime是在app_process 中定义的类,继承了系统的 AndroidRuntime类。AndroidRuntime类主要作用是创建初始化虚拟机。
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++;
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;
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]));
ALOGV("app_process main add option '%s'", argv[i]);
}
- 解析启动参数
int i;
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]);
}
通常是从 init.rc 中传入的参数,“-Xzygote/system/bin --zygote --start-system-server”,解析后得到的结果将是
- 变量 parentDir 等于 /system/bin
- 变量 niceName 等于 zygote
- 变量 startSystemServer 等于 true
- 变量 zygote 等于 true
- 准备执行 ZygoteInit 或者 RuntimeInit 类的参数
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]));
}
}
- 将本进程的名称改为–nice-name 指定的字符串。缺省情况下niceName为zygote或者zygote64
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
- 启动 Java 类,如果参数带有 --zygote 执行 zygoteInit类,否则执行传进来的Java类。
if (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.");
}
app_process 除了能启动 Zygote进程还可以执行系统的某个Java类。Android手机常用工具 am 就是一个很好的例子。am 是一个通过发送 Intent 来启动应用程序的工具,但是 am 只是一个包含几行代码的脚本文件,实际上启动还是通过 app_process 来完成的。
am基本用法
- am start -n 包(package)名/包名.活动(activity)名称
启动虚拟机 AndroidRuntime类
- Android Runtime类是 Android底层很重要的一个类,它负责启动虚拟机以及Java线程。AndroidRuntime在一个进程中只有一个实例对象,保存在全局变量 gCurRuntime中。
- AndroidRuntime 构造函数如下(android9.0)
AndroidRuntime::AndroidRuntime(char* argBlockStart, const size_t argBlockLength) :
mExitWithoutCleanup(false),
mArgBlockStart(argBlockStart),
mArgBlockLength(argBlockLength)
{
// 初始化 skia 图形系统;skia是个Google的2D向量图形处理函数库,包含字型、座标转换,以及点阵图都有高效能且简洁的表现。
SkGraphics::Init();
// Pre-allocate enough space to hold a fair number of options.
// 预先分配空间来存放虚拟机的参数
mOptions.setCapacity(20);
// 只能初始化一次 gCurRuntime
assert(gCurRuntime == NULL); // one per process
gCurRuntime = this;
}
AndroidRuntime 的构造函数中,5.0以前会对 skia图形系统进行设置,把底层使用的图形格式设置成 RGB565,一种16位图形格式,16位的图像格式没有24位的色彩丰富,但是能节省内存空间。Android5.0以后去调了这个设置,只保留了 mOptions.setCapacity(20); 来设置虚拟机的参数。
最后 AndroidRuntime 指针被放入了 gCurRuntime 全局指针,但是前面的 main() 函数中AndroidRuntime 是作为一个局部变量 runtime 创建的。这是因为如果启动的 Zygote ,在 ZygoteInit 中会进入等待 Socket 事件循环中,这样布局变量并不会被销毁,如果只执行一个 Java 类,执行结束完就结束了,说 runtime是一个局部变量也没什么问题。
启动虚拟机
if (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.");
}
- 在main() 函数的结尾,调用了 AndroidRuntime的start函数来执行 Java类。Zygote在运行Java前,还需要初始化整个Java环境,下面看看 start() 函数执行流程。
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
// 打印Log,标志着Android系统已经启动 因为以后的进程都是从 Zygote fork出来的所以不会再执行 start 函数了。
// 如果Log反复出现下面Log ,则说明系统出错了如果id是zygote 则说明 zygote正在不断重启。
ALOGD(">>>>>> START %s uid %d <<<<<<\n",
className != NULL ? className : "(unknown)", getuid());
static const String8 startSystemServer("start-system-server");
/*
* 'startSystemServer == true' means runtime is obsolete and not run from
* init.rc anymore, so we print out the boot start event here.
*/
for (size_t i = 0; i < options.size(); ++i) {
if (options[i] == startSystemServer) {
/* track our progress through the boot sequence */
const int LOG_BOOT_PROGRESS_START = 3000;
LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
}
}
// 系统目录从环境变量 ANDROID_ROOT 中获取,如果没有获取到则默认目录位 /system ;
// !hasDir("/system") 如果没有/System 则直接 return 退出了
// 系统目录是在 init 进程中创建出来的
const char* rootDir = getenv("ANDROID_ROOT");
if (rootDir == NULL) {
rootDir = "/system";
if (!hasDir("/system")) {
LOG_FATAL("No root directory specified, and /android does not exist.");
return;
}
setenv("ANDROID_ROOT", rootDir, 1);
}
//const char* kernelHack = getenv("LD_ASSUME_KERNEL");
//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);
/* start the virtual machine */
// 启动虚拟机 5.0 以后启动的是 Art虚拟机
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
// 调用虚拟机 onVmCreated onVmCreated类实际上是一个虚函数,调用它实际上调用的是继承类 AndroidRuntime 中的重载函数。下面会贴出来
onVmCreated(env);
/*
* Register android functions. 注册系统 JNI 函数
* startReg 通过调用 register_jni_procs(gRegJNI, NELEM(gRegJNI), env) 函数将全局数组gRegJNI中的JNI本地函数在虚拟机中注册
*/
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}
/*
* We want to call main() with a String array with arguments in it.
* At present we have two arguments, the class name and an option string.
* Create an array to hold them.
*/
// 下面是准备 Java 类的 main 函数的参数
jclass stringClass;
jobjectArray strArray;
jstring classNameStr;
stringClass = env->FindClass("java/lang/String");
assert(stringClass != NULL);
// 先通过 NewObjectArray 创建两个元素的数组
strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
assert(strArray != NULL);
classNameStr = env->NewStringUTF(className);
assert(classNameStr != NULL);
// 通过 SetObjectArrayElement 为数组赋值 因为要到Java的世界所以会比较麻烦
env->SetObjectArrayElement(strArray, 0, classNameStr);
for (size_t i = 0; i < options.size(); ++i) {
jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
assert(optionsStr != NULL);
env->SetObjectArrayElement(strArray, i + 1, optionsStr);
}
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
/* keep going */
} else {
// 先通过 GetStaticMethodID 获取 Java main 函数的id
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
ALOGE("JavaVM unable to find main() in '%s'\n", className);
/* keep going */
} else {
// 然后通过 CallStaticVoidMethod 来调用 Java 层函数了,至此 Zygote初始化将转入 Java 函数了。
// 如果不是启动 Zygote 则执行 Java 函数的是 RuntimeInit
env->CallStaticVoidMethod(startClass, startMeth, strArray);
#if 0
if (env->ExceptionCheck())
threadExitUncaughtException(env);
#endif
}
}
free(slashClassName);
ALOGD("Shutting down VM\n");
if (mJavaVM->DetachCurrentThread() != JNI_OK)
ALOGW("Warning: unable to detach main thread\n");
if (mJavaVM->DestroyJavaVM() != 0)
ALOGW("Warning: VM did not shut down cleanly\n");
}
- onVmCreated
onVmCreated会在当前虚拟机环境中根据类名来查找类对象,这表明 app_process 将要调用的Java类对象必须是系统Java类,而不能是随意的应用java类,如果调用普通Java类对象则方式不同。
virtual void onVmCreated(JNIEnv* env)
{
if (mClassName.isEmpty()) {
return; // Zygote. Nothing to do here.
}
/*
* This is a little awkward because the JNI FindClass call uses the
* class loader associated with the native method we're executing in.
* If called in onStarted (from RuntimeInit.finishInit because we're
* launching "am", for example), FindClass would see that we're calling
* from a boot class' native method, and so wouldn't look for the class
* we're trying to look up in CLASSPATH. Unfortunately it needs to,
* because the "am" classes are not boot classes.
*
* The easiest fix is to call FindClass here, early on before we start
* executing boot class Java code and thereby deny ourselves access to
* non-boot classes.
*/
char* slashClassName = toSlashClassName(mClassName.string());
mClass = env->FindClass(slashClassName);
if (mClass == NULL) {
ALOGE("ERROR: could not find class '%s'\n", mClassName.string());
}
free(slashClassName);
mClass = reinterpret_cast<jclass>(env->NewGlobalRef(mClass));
}
初始化工作 ZygoteInit 类
- Zygote 的 main 方法做了大概一下几个事情
:解析调用参数
:注册 Zygote 的 socket 监听接口,用来接收启动应用程序监听
:调用 preload() 方法装载系统资源。这样fork出的应用可以包含这些资源了 加快了启动速度。
:启动 SystemServer 进程
:调用 runSelectLoo() 方法进入监听和接收消息的循环
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
// 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);
}
final Runnable caller;
try {
// Report Zygote start time to tron unless it is a runtime restart
if (!"1".equals(SystemProperties.get("sys.boot_completed"))) {
MetricsLogger.histogram(null, "boot_zygote_init",
(int) SystemClock.elapsedRealtime());
}
String bootTimeTag = Process.is64Bit() ? "Zygote64Timing" : "Zygote32Timing";
TimingsTraceLog bootTimingsTraceLog = new TimingsTraceLog(bootTimeTag,
Trace.TRACE_TAG_DALVIK);
bootTimingsTraceLog.traceBegin("ZygoteInit");
RuntimeInit.enableDdms();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
boolean enableLazyPreload = false;
// 解析调用的参数
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)) {
socketName = argv[i].substring(SOCKET_NAME_ARG.length());
} else {
throw new RuntimeException("Unknown command line argument: " + argv[i]);
}
}
if (abiList == null) {
throw new RuntimeException("No ABI list supplied.");
}
// 注册 Zygote 的 socket 监听接口,用来接收启动应用程序的消息
zygoteServer.registerServerSocketFromEnv(socketName);
// In some configurations, we avoid preloading resources and classes eagerly.
// In such cases, we will preload things prior to our first fork.
if (!enableLazyPreload) {
bootTimingsTraceLog.traceBegin("ZygotePreload");
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
// 装载系统资源,包括系统预加载类,Framework 资源和 openGL 的资源。
// 这样应用程序被 fork 出来后,应用程序内就已经包含了这些系统资源,大大节省了引用启动时间。
preload(bootTimingsTraceLog);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
bootTimingsTraceLog.traceEnd(); // ZygotePreload
} else {
Zygote.resetNicePriority();
}
// Do an initial gc to clean up after startup
bootTimingsTraceLog.traceBegin("PostZygoteInitGC");
gcAndFinalize();
bootTimingsTraceLog.traceEnd(); // PostZygoteInitGC
bootTimingsTraceLog.traceEnd(); // ZygoteInit
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false, 0);
Zygote.nativeSecurityInit();
// Zygote process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
// 准备 SystemServer 参数并且 fork 出 SystemServer 进程
Runnable r = forkSystemServer(abiList, socketName, 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.
// 通过调用 runSelectLoop 进入监听和接收消息的环节 里面有一个 while (true)
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
// We're in the child process and have exited the select loop. Proceed to execute the
// command.
if (caller != null) {
caller.run();
}
}
总结一下 Zygote 进程启动过程
- init 进程 fork 出 zygote 进程
- 启动虚拟机注册 jni 函数
- 预加载系统资源
- 启动 SystemServer
- 进入 Socket Loop 循环等待消息