本篇文章的内容中,我将要给大家介绍Android开发中最长见到的一个底层进程——Zygote进程,它是我们开发的所有APP进程的父进程。可以说,Android中所有的Java进程都是由Zygote进程fork出来的。
那面Zygote进程又是如何启动的呢?它的启动过程从init进程启动开始,到Zygote进程真正运行起来,并且进行相关初始化完成为止。
我们来详细分析下Zygote进程的启动过程。
说明:我们的源码是基于Android 8.1系统进行分析的。
init进程是Android系统启动的第一个进程。我们来看init进程是如何启动的?(源码位置:system/core/init/init.cpp)
Linux内核启动的是从start_kernel函数开始的,start_kernel是所有Linux平台进入系统内核初始化后的入口函数,它主要完成一系列内核初始化相关工作,并且调用第一个用户进程——init进程。init启动之后,也就代表系统已经顺利地启动了Linux内核。
我们来看init进程的入口函数——init.cpp的main()方法:
int main(int argc, char** argv) {
if (!strcmp(basename(argv[0]), "ueventd")) {//1、如果文件名是"ueventd",则执行守护进程ueventd的主函数ueventd_main()
return ueventd_main(argc, argv);
}
if (!strcmp(basename(argv[0]), "watchdogd")) {//2、如果文件名是"watchdogd",则执行看门狗守护进程的主函数
return watchdogd_main(argc, argv);
}
……
add_environment("PATH", _PATH_DEFPATH);//3、设置环境变量
bool is_first_stage = (getenv("INIT_SECOND_STAGE") == nullptr);
if (is_first_stage) {
// Clear the umask.
umask(0);
//4、创建一些基本的目录,包括/dev、/porc、/sysfc等。同时把一些文件系统,如tmpfs、devpt、proc、sysfs等mount到项目的目录。
mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
mkdir("/dev/pts", 0755);
mkdir("/dev/socket", 0755);
mount("devpts", "/dev/pts", "devpts", 0, NULL);
#define MAKE_STR(x) __STRING(x)
mount("proc", "/proc", "proc", 0, "hidepid=2,gid=" MAKE_STR(AID_READPROC));
// Don't expose the raw commandline to unprivileged processes.
chmod("/proc/cmdline", 0440);
gid_t groups[] = { AID_READPROC };
setgroups(arraysize(groups), groups);
mount("sysfs", "/sys", "sysfs", 0, NULL);
mount("selinuxfs", "/sys/fs/selinux", "selinuxfs", 0, NULL);
mknod("/dev/kmsg", S_IFCHR | 0600, makedev(1, 11));
mknod("/dev/random", S_IFCHR | 0666, makedev(1, 8));
mknod("/dev/urandom", S_IFCHR | 0666, makedev(1, 9));
//5、SELinux初始化逻辑
// Set up SELinux, loading the SELinux policy.
selinux_initialize(true);
// We're in the kernel domain, so re-exec init to transition to the init domain now
// that the SELinux policy has been loaded.
if (selinux_android_restorecon("/init", 0) == -1) {
PLOG(ERROR) << "restorecon failed";
security_failure();
}
……
}
……
close(open("/dev/.booting", O_WRONLY | O_CREAT | O_CLOEXEC, 0000));//6、在/dev目录下创建一个空文件".booting"表示初始化正在进行。初始化结束后,这个文件会被删除。
// Propagate the kernel variables to internal variables
// used by init as well as the current required properties.
export_kernel_boot_props();//7、主要是调用property_init()函数来初始化Android的属性系统。
……
epoll_fd = epoll_create1(EPOLL_CLOEXEC);//8、调用epoll_create1创建epoll句柄,如果创建失败,则退出。
if (epoll_fd == -1) {
PLOG(ERROR) << "epoll_create1 failed";
exit(1);
}
signal_handler_init();//9、调用signal_handler_init()函数,主要是装载进程信号处理器。当子进程被kill之后,会在父进程接受一个信号。防止称为僵尸进程的子进程占用程序表的空间。
……
//10、解析init.rc文件
std::string bootscript = GetProperty("ro.boot.init_rc", "");
if (bootscript.empty()) {
parser.ParseConfig("/init.rc");
parser.set_is_system_etc_init_loaded(
parser.ParseConfig("/system/etc/init"));
parser.set_is_vendor_etc_init_loaded(
parser.ParseConfig("/vendor/etc/init"));
parser.set_is_odm_etc_init_loaded(parser.ParseConfig("/odm/etc/init"));
} else {
parser.ParseConfig(bootscript);
parser.set_is_system_etc_init_loaded(true);
parser.set_is_vendor_etc_init_loaded(true);
parser.set_is_odm_etc_init_loaded(true);
}
……
}
执行过程如上,主要步骤我们通过注释可以清楚的了解到。
init进程启动过程中,会执行init.rc脚本文件(位置: /system/core/rootdir/init.rc)。它通过解析init.rc脚本来构建出系统的初始形态,解析init.rc会把一条条命令映射到内存中,然后依次启动。init.rc里面的顺序大致顺序如下:on early-init -> init -> late-init -> boot。
其中,init.rc会执行Zygote相关的启动脚本。所以说,Zygote进程是Linux系统的init进程通过解析配置脚本来进行启动的。脚本位置:/system/core/rootdir/init.zygote64.rc (64位)。
通常,子进程被fork出来后,会继续执行系统调用exec,exec将用一个新的可执行文件的内容替换当前进程的代码段、数据段、堆和栈段。Fork加exec 是Linux启动应用的标准做法,init进程也是这样来启动的各种服务的。
Zygote创建应用程序时却只使用了fork,没有调用exec。Zygote初始化时会创建创建虚拟机,同时把需要的系统类库和资源文件加载到内存里面。Zygote fork出子进程后,这个子进程也继承了能正常工作的虚拟机和各类系统资源,接下来子进程只需要装载APK文件的字节码文件就可以运行了。这样应用程序的启动时间就会大大缩短。
我们来看init.rc脚本是如何启动Zygote进程的:
import /init.${ro.zygote}.rc //这里通过ro.zygote属性来控制启动不同版本的Zyogte进程。例如,ro.zygote=zygote64_32(zygote32/zygote32_64/zygote64
)
……
on late-init
# Now we can start zygote for devices with file based encryption
trigger 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 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 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 netd
start zygote
start zygote_secondary
……
我们可以看到,Zygote进程是通过init.rc脚本,在init进程中启动的(以service的方式启动),详细过程后面会讲到。
另外,在init.rc中,我们可以看到anr文件的权限设置:
mkdir /data/anr 0775 system system
这里可以看到其实anr文件的目录其实对我们正常应用的APP是开放了rx权限(读、执行权限),但是非系统应用在Android 5.0之后就读取不到anr日志了,这个原因是因为在Android5.0之后,Android默认开启了SELinux权限控制。
ro.zygote变量
我们看到,脚本文件使用了另一个.rc文件,文件名称使用了ro.zygote变量,它的值有四种:
zygote32
zygote32_64
zygote64
zygote64_32
分别对应了4个.rc文件(在init.rc同目录下):
init.zygote32
init.zygote64
init.zygote32_64
init.zygote64_32
为什么会有4个Zygote脚本文件呢?
这其实代表了Andorid系统支持4种运行模式:
纯32位模式:属性ro.zygote的值为zygote32
混32位模式(即32位为主,64位为辅)模式:属性ro.zygote的值为zygote32_64
纯64位模式:属性ro.zygote的值为zygote64
混64位模式(即 64位为主,32位为辅)模式:属性ro.zygote值为zygote64_32
Zygote对应的配置脚本文件,描述了init该如何启动Zygote进程。
我们以64位CPU架构的脚本文件init.zygote64.rc作为示例:
service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server
class main
priority -20
user root
group root readproc
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
它使用的是Android init配置脚本的语法,我们重点关注第一行内容:
service zygote:它告诉init进程,我们现在要配置一个名为zygote的服务(这里的服务是init内部的概念)。
/system/bin/app_process64:指明zygote服务对应的二进制文件的路径。init进程会fork一个子进程来运行指定的程序。对应zygote而言,这个程序就是/system/bin/app_process。
-Xzygote /system/bin --zygote --start-system-server:传递给app_process的启动参数。
当init进程真的启动zygote服务的时候,就会走到service_start()函数(init.cpp)。
下面我们来看Zygote真正的启动代码。
Zygote进程的启动,涉及到了init.cpp、service.cpp、app_main.cpp以及脚本文件init.rc、init.zygote64.rc等文件。详细过程如图:
启动过程中的相关类及类的功能如下:
init.cpp
初始化相关配置。
通过handle_control_message()监听消息,执行Zygote服务。
调用service.cpp的Restart()方法。
service.cpp
调用Start()方法。
fork Zygote进程。
app_main.cpp
Zygote进程的入口函数main()
创建AppRuntime对象
调用AppRuntime对象的start方法
AndroidRuntime.cpp
AppRuntime的start方法实际指向AndroidRuntime的start方法
创建了一个JniInvocation的实例,并且调用它的成员函数init来初始化JNI环境
执行startVm,创建虚拟机及其对应的JNI接口
执行startReg,注册JNI函数
执行env.CallStaticVoidMethod调用到Java层,初始化Zygote进程
Zygoteinit.java
执行main方法
调用ZygoteServier的registerServerSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息
调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。
调用forkSystemServer()方法启动SystemServer进程
调动runSelectLoop方法来监听和处理启动应用的请求
我们来看重点来看下app_main.cpp的main函数:
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));//1、创建AppRuntime对象
// Process command line arguments
// ignore argv[0]
argc--;
argv++;
……
bool known_command = false;
//****************2、从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server
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(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;
//****************3、将上面的内容赋给相应的变量
++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;//niceName将被设置为app_process的进程名,32位机为“zygote”,64位机为“zygote64”。
} 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;
}
}
//*************4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.
Vector 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]));
}
}
//5、进程的名称修改
if (!niceName.isEmpty()) {
runtime.setArgv0(niceName.string(), true /* setProcName */);
}
//6、启动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.");
}
}
主要过程如下:
1、创建AppRuntime对象。
2、处理从init.rc传入的参数 为 -Xzygote /system/bin --zygote --start-system-server,传递给AppRuntime对象。
-Xzygote是传递给虚拟机的参数
/system/bin 是 parent dir(程序运行目录)
--zygote表示以zygote模式启动
3、将上面的内容赋给相应的变量
以-Xzygote /system/bin --zygote --start-system-server为例,结果如下:
变量 parentDir 等于/system/bin
变量 niceName 等于 zyoget
变量 startSystemServer 等于 true
变量 zygote 等于 true
4、准备启动ZygoteInit类或者RuntimeInit类,这里根据类名是否存在为空,来区别是非zyoget模式和zyoget模式.
5、如果niceName不为空,则将本进程的名称修改为参数** --nice-name** 指定的字符串。缺省的情况下,niceName 的值为"zygote"或者"zygote64"。其中set_process_name函数用来改变进程的mi9ngcheng。setArgv0函数是用来替换启动参数串中的"app_process"为参数。
6、启动Java类,如果启动参数带有 "--zygote"。则执行ZygoteInit。
分三种情况:
zygote 模式,即启动ZygoteInit
非zygote 模式,即启动RuntimeInit
以上两种均不是
我们再来分析下上述过程中的ZygoteInit的main()方法:
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();//1、创建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();
//***********2、解析参数
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.");
}
//3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息
zygoteServer.registerServerSocket(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());
preload(bootTimingsTraceLog);//4、调用preload()方法装载系统资源,包括系统预加载类、Framework资源和openGL的资源。这样当程序被fork处理后,应用的进程内已经包含了这些系统资源,大大节省了应用的启动时间。
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 process unmounts root storage spaces.
Zygote.nativeUnmountStorageOnInit();
// Set seccomp policy
Seccomp.setPolicy();
ZygoteHooks.stopZygoteNoThreadCreation();
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);//5、启动SystemServer进程
// {@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);//6、调动runSelectLoop方法来监听和处理启动应用的请求
} 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();
}
}
1、创建ZygoteServer对象。
2、解析参数。
3、调用registerZygoteSocket(socketName)方法注册Zygote的socket监听接口,用来启动应用程序的消息。
为zygote命令注册一个socket连接的服务端socket。init进程会根据这条选项来创建一个"AF_UNIX"socket,并把它的句柄放到环境变量"ANDROID_SOCKET_zygote"中。同理我们也可以这样得到句柄,得到句柄后,new了一个FileDescriptor对象,并通过调用setInt$()方法来设置其值。最后new了LocalServerSocket对象,来创建本地的服务socket,并将其值保存在全局变量sServerSocket中。
4、调用preload()方法预加载资源。
为了加快应用程序的启动,Android把系统公用的Java类和一部分Framework的资源保存在zygote中了,这样就可以保证zygote进程fork子进程的是共享的。
preloadClasses():预加载Java类
preloadResources():预加资源
preloadOpenGL():预加载OpenGL资源
preloadSharedLibraries():预计加载共享库
preloadTextResources():预加载文本资源
WebViewFactory.prepareWebViewInZygote():初始化WebView
5、启动SystemServer进程。
为fork准备参数parsedArgs
调用Zygote.forkSystemServer()方法来创建system_server
调用handleSystemServerProcess()方法执行system_server的剩余工作
6、调动runSelectLoop方法来监听和处理启动应用的请求。
ZygoteInit类的main()方法调用runSelectLoop()方法来监听和处理启动应用的请求。
执行zygote进程的循环。当来一个新的连接请求时,则建立接受并建立连接,并在连接中读取请求的命令。
至此,Init进程以及Zygote进程的启动过程就分析完成了。