zygote启动流程

时序图

init.rc中配置

在init进程创建时,会读取 init.zygote.rc 中的配置文件

init.zygote.rc

这个是在rc文件中的配置,下面我们会一次介绍各个命令的作用,这些命令具体都会在源码分析中看到

1
2
3
4
5
6
7
8
9
10
11
12
13
14
service zygote /system/bin/app_process -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

service

service指令告诉系统将zygote程序加入到系统服务中,基本语法如下

service service_name 可执行程序的路径 可执行程序自身所需的参数列表

这里我们的服务是 zygote 他的程序路径在 /system/bin/app_process ,后面那一堆,就是程序执行的参数,在 app_process 的main函数中会从argc和argv中取出

-Xzygote

作为虚拟机启动时所需要的参数,在AndroidRuntime.cpp中的 startVm() 函数中调用 JNI_CreateJavaVM 使用到

/system/bin

代表虚拟机程序所在目录,因为 app_process 可以不和虚拟机在一个目录,所以 app_process 需要知道虚拟机所在的目录

–zygote

指明以 ZygoteInit 类作为入口,否则需要指定需要执行的类名

–start-system-server

仅在有 –zygote 参数时可用,告知 ZygoteInit 启动完毕后孵化出的第一个进程是 SystemServer

其他命令

后续的命令是和socket相关,名称、类型、端口号、重启后的操作等等

app_process

该文件在 frameworks/base/cmds/app_process 中,找到 main 函数

1
2
3
4
5
6
7
8
9
10
11
//前面的操作都是对参数进行一些赋值等等,就是之前的命令中配置的参数
......
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.");
}

这个 runtime 是 AppRuntime

AppRuntime.start

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{

......
JniInvocation jni_invocation;
jni_invocation.Init(NULL);
JNIEnv* env;
//创建虚拟机相关信息
if (startVm(&mJavaVM, &env, zygote) != 0) {
return;
}
onVmCreated(env);

//JNI方法的注册
if (startReg(env) < 0) {
ALOGE("Unable to register all android natives\n");
return;
}

......

char* slashClassName = toSlashClassName(className != NULL ? className : "");
jclass startClass = env->FindClass(slashClassName);
if (startClass == NULL) {
......
} else {
jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
if (startMeth == NULL) {
......
} else {
env->CallStaticVoidMethod(startClass, startMeth, strArray);
}
}
}

对虚拟机和JNI方法的一些注册后,通过 CallStaticVoidMethod 来调用传过来的类名的main函数,我们传递过来的类名是 com.android.internal.os.ZygoteInit

ZygoteInit.main

该文件在 frameworks/base/core/java/com/android/internal/os 中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public static void main(String argv[]) {
ZygoteServer zygoteServer = new ZygoteServer();
......
final Runnable caller;
try {
......

zygoteServer.registerServerSocket(socketName);
preload();
......
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
......
} finally {
zygoteServer.closeServerSocket();
}
if (caller != null) {
caller.run();
}
}

registerServerSocket : 用于注册socket
preload : 预加载类和资源
forkSystemServer : 启动system_server
runSelectLoop : socket的处理

registerServerSocket

注册一个socket,来接收启动新进程一些命令操作,建立socket通道,zygote作为通信的服务端,用于响应客户端请求

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
void registerServerSocket(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
String env = System.getenv(fullSocketName);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(fullSocketName + " unset or invalid", ex);
}

try {
FileDescriptor fd = new FileDescriptor();
fd.setInt$(fileDesc);
mServerSocket = new LocalServerSocket(fd);
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}

fileDesc 是一个文件描述符

最终会构造一个 LocalServerSocket ,创建socket的本地服务端

关于socket

在Linux系统中,所有的资源都可以看做是文件,socket也是,所以传递了一个文件描述符来构建socket

:::tip
注意
socket也是IPC的一种方式

socket中有两种方式进行通信
:::

阻塞式

使用listen()监听某个端口,调用read()读取数据,没有数据时,会一直等待

非阻塞式

使用select()将需要检测的文件描述符作为 select() 函数的参数,当文件描述符上出现新的数据后,自动触发一个中断,然后在中断处理函数中再去读取指定文件描述符的数据

LocalServerSocke 用的是第二种 非阻塞式,读取的文件为 /dev/socket/zygote

LocalServerSocke 对应的客户端

前面的代码讲解的是关于socket服务端的,有服务端那就有客户端

它对应的客户端是进程 SystemServer 中创建出来的

preload

为了能够让 zygote 孵化进程进行的资源加载,预加载通用类、drawable和color资源、openGL以及共享库以及WebView,用于提高app启动效率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static void preload() {
//预加载位于/system/etc/preloaded-classes文件中的类
preloadClasses();

//预加载资源,包含drawable和color资源
preloadResources();

//预加载OpenGL
preloadOpenGL();

//通过System.loadLibrary()方法,
//预加载"android","compiler_rt","jnigraphics"这3个共享库
preloadSharedLibraries();

//预加载 文本连接符资源
preloadTextResources();

//仅用于zygote进程,用于内存共享的进程
WebViewFactory.prepareWebViewInZygote();
}

forkSystemServer

这个小节介绍SystemService的启动过程

fork

fork是Linux中的一个系统调用,作用是复制当前进程,除了进程ID不同,两个进程信息完全一致

新进程被创建后,两个进程将共享已经分配的内存空间,直到其中一个进程需要向内存写入数据的时候,系统才会复制一份目标地址空间,并将写的数据写入到新的地址中,这就是 copy-on-write 机制,”仅当写的时候才复制”,可以节省共享物理内存,调用一次,返回两次,返回值有3种类型

  • 父进程中,fork返回新创建的子进程的pid
  • 子进程中,fork返回0
  • 当出现错误时,fork返回负数(当进程数超过上限或者系统内存不足时会出错)

fork()的主要工作是寻找空闲的进程号pid,然后从父进程拷贝进程信息,例如数据段和代码段,fork()后子进程要执行的代码等。 Zygote进程是所有Android进程的母体,包括system_server和各个App进程。zygote利用fork()方法生成新进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A

copy-on-write原理

写时拷贝是指子进程与父进程的页表都所指向同一个块物理内存,fork过程只拷贝父进程的页表,并标记这些页表是只读的。父子进程共用同一份物理内存,如果父子进程任一方想要修改这块物理内存,那么会触发缺页异常(page fault),Linux收到该中断便会创建新的物理内存,并将两个物理内存标记设置为可写状态,从而父子进程都有各自独立的物理内存

为什么要fork

因为两个进程存在大量的共享程序,如果使用fork,可以节省大量的共享内存

SystemServer的启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
private static Runnable forkSystemServer(String abiList, String socketName,
ZygoteServer zygoteServer) {
......
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
"--capabilities=" + capabilities + "," + capabilities,
"--nice-name=system_server",
"--runtime-args",
//注意这里的类名,它会去装载这个类
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;

int pid;

try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

//主要的fork操作,会调用到native层的代码进行 fork
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}

/* For child process 这里代表新进程*/
if (pid == 0) {
if (hasSecondZygote(abiList)) {
waitForSecondaryZygote(socketName);
}

zygoteServer.closeServerSocket();
return handleSystemServerProcess(parsedArgs);
}

return null;
}

在 pid == 0 的节点中,关闭socket服务端(注意这里关闭的是从父进程复制过来的子进程里的socket,因为子进程不需要这个socket),
handleSystemServerProcess 函数的作用是调用 SystemServer 的main函数以及一些配置工作(调用新进程指定的class的main函数)

一旦 SystemServer 的配置工作完成后,就会从 SystemServer 的 main 函数开始运行

到此,调用main后,新进程就完全脱离了 zygote 进程的孵化过程了,成为一个真正的应用进程

runSelectLoop

socket创建后就是进入读操作了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//mServerSocket 是socket通信中的服务端,即zygote进程。保存到fds[0]
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);

while (true) {
StructPollfd[] pollFds = new StructPollfd[fds.size()];
for (int i = 0; i < pollFds.length; ++i) {
pollFds[i] = new StructPollfd();
pollFds[i].fd = fds.get(i);
pollFds[i].events = (short) POLLIN;
}
try {
//处理轮询状态,当pollFds有事件到来则往下执行,否则阻塞在这里
Os.poll(pollFds, -1);
} catch (ErrnoException ex) {
throw new RuntimeException("poll failed", ex);
}
for (int i = pollFds.length - 1; i >= 0; --i) {
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
// 第一个就是刚才的操作 fds.add(mServerSocket.getFileDescriptor());
if (i == 0) {
//接收客户端发送过来的connect()操作,Zygote作为服务端执行accept()操作。 再后面客户端调用write()写数据,Zygote进程调用read()读数据
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
......
} catch (Exception e) {
......
}
}
}
}
}

在读取数据的操作中的 processOneCommand 里,最为核心的方法就是 Zygote.forkAndSpecialize,看名字就应该知道他的作用就是fork进程,runSelectLoop 函数随时待命,当接收到请求创建新进程请求时立即唤醒并执行相应工作

总结

zygote的main函数的流程可以简化为下

1
2
3
4
5
6
7
8
9
10
11
12
public static void main(String argv[]) {
try {
caller = zygoteServer.runSelectLoop(abiList);
....
} catch (RuntimeException ex) {
closeServerSocket();
throw ex;
}
if (caller != null) {
caller.run();
}
}

::: warning
注意,之前在分析acceptCommandPeer部分的时候,父进程是直接返回null的,新进程是返回一个runnable,因此,这部分的代码在不同进程中的执行是不同的.对于父进程也就是Zygote进程,他是一直循环运行的,对于新进程,会完成启动操作后停止循环并调用目标类的main函数
:::

  1. 系统启动时init进程会创建Zygote进程,Zygote进程负责后续Android应用程序框架层的其它进程的创建和启动工作。

  2. Zygote进程会首先创建一个SystemServer进程,SystemServer进程负责启动系统的关键服务,如包管理服务PackageManagerService和应用程序组件管理服务ActivityManagerService。

  3. 当我们需要启动一个Android应用程序时,ActivityManagerService会通过Socket进程间通信机制,通知Zygote进程为这个应用程序创建一个新的进程。

参考博客 Android进程创建

参考书籍 Android内核剖析

引出知识点

  • rc文件语法
  • Linux fork 原理
  • socket IPC通信