Android 9 应用进程创建流程大揭秘
前言
我们知道zygote是Android世界的孵化大师,当Android终端在启动过程中创建了zygote进程之后,历史的重任就赋予在zygote身上了。zygote的重任主要就是孵化,而zygote进程对于孵化有两种不同的处理逻辑:
-
第一种就是zygote感觉自身担子太重了,所以必须得培养一个得力干将,从而zygote主动孵化了system_server进程来处理一些重要的事务,这个就是我们在章节 Android 9 系统启动之SystemServer大揭秘中重点介绍的。
-
第二种情况就是zygote主动孵化完system_server进程之后,进入runSelectLoop循环中被动接受等待客户端的请求(通常是AMS服务),进而孵化进程,这个是最常见的。譬如我们的App进程的创建就是属于这个的套路,这个也是我们这个篇章需要重点来说的。
在本篇章我将会带领大伙一起分析分析,Android的进程是如何一步步创建的!本篇是以msm8953 Android P为示意,其中涉及到的源码路径如下:
frameworks/base/core/java/android/app
public static final ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
return zygoteProcess.start(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, zygoteArgs);
}
好吗,这个方法参数有点多啊,我们挑几个我们需要重点关注的说明一下:
- processClass :这是新建进程初始化要加载的类,这个类加载到进程之后,就会把这个类的静态成员方法main作为进程的入口,它的值是"android.app.ActivityThread"
- niceName:将要新建进程的别名,通常我们可以在终端下通过ps查看,而对于Android应用进程来说通常如果没有列外则是包名
- abi:英文的注释如下non-null the ABI this app should be started with,按照咱们中国人的话来说就是abi不能为空,然后通过abi决定由那个zygote进程启动,因为通常64位终端运行二个zygote进程,如下所示:
E800:/ # ps | grep zygote
root 754 1 2172496 83288 poll_sched 7f8775ca10 S zygote64
root 755 1 1599324 71032 poll_sched 00f6e306d4 S zygote
- appDataDir :Android app安装以后的data数据目录
- zygoteArgs:这个是备用的参数提供给zygote进程启动adnroid app进程
好吗,我们需要重点关注的参数分析完成了。这里有一点需要打过注意的是Android的Activity的启动并不是一定会调用到这里的,该处的逻辑只有在应用冷启动的时候才会调用此处的,这也是为什么说Android的冷启动比热启动耗时要久一些的原因,因为冷启动牵涉到进程的创建。
3.2 zygoteProcess.start
其代码路径为frameworks/base/core/java/com/android/internal/os/zygoteProcess.java,其关键代码如下:
public final Process.ProcessStartResult start(final String processClass,
final String niceName,
int uid, int gid, int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
String[] zygoteArgs) {
try {
return startViaZygote(processClass, niceName, uid, gid, gids,
runtimeFlags, mountExternal, targetSdkVersion, seInfo,
abi, instructionSet, appDataDir, invokeWith, false ,
zygoteArgs);
} catch (ZygoteStartFailedEx ex) {
//进程创建发生异常
Log.e(LOG_TAG,
"Starting VM process through Zygote failed");
throw new RuntimeException(
"Starting VM process through Zygote failed", ex);
}
}
该方法优点过于简单啊,基本啥事没有干就是将前面传递过来的参数重新传递了一下,但是这里有一个参数需要重点关注一下就是传递为false的startChildZygote 这个参数。
3.3 zygoteProcess.startViaZygote
private Process.ProcessStartResult startViaZygote(final String processClass,
final String niceName,
final int uid, final int gid,
final int[] gids,
int runtimeFlags, int mountExternal,
int targetSdkVersion,
String seInfo,
String abi,
String instructionSet,
String appDataDir,
String invokeWith,
boolean startChildZygote,
String[] extraArgs)
throws ZygoteStartFailedEx {
ArrayList<String> argsForZygote = new ArrayList<String>();
// --runtime-args, --setuid=, --setgid=,
// and --setgroups= must go first
argsForZygote.add("--runtime-args");
argsForZygote.add("--setuid=" + uid);
argsForZygote.add("--setgid=" + gid);
argsForZygote.add("--runtime-flags=" + runtimeFlags);
if (mountExternal == Zygote.MOUNT_EXTERNAL_DEFAULT) {
argsForZygote.add("--mount-external-default");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_READ) {
argsForZygote.add("--mount-external-read");
} else if (mountExternal == Zygote.MOUNT_EXTERNAL_WRITE) {
argsForZygote.add("--mount-external-write");
}
argsForZygote.add("--target-sdk-version=" + targetSdkVersion);
// --setgroups is a comma-separated list
if (gids != null && gids.length > 0) {
StringBuilder sb = new StringBuilder();
sb.append("--setgroups=");
int sz = gids.length;
for (int i = 0; i < sz; i++) {
if (i != 0) {
sb.append(',');
}
sb.append(gids[i]);
}
argsForZygote.add(sb.toString());
}
if (niceName != null) {
argsForZygote.add("--nice-name=" + niceName);
}
if (seInfo != null) {
argsForZygote.add("--seinfo=" + seInfo);
}
if (instructionSet != null) {
argsForZygote.add("--instruction-set=" + instructionSet);
}
if (appDataDir != null) {
argsForZygote.add("--app-data-dir=" + appDataDir);
}
if (invokeWith != null) {
argsForZygote.add("--invoke-with");
argsForZygote.add(invokeWith);
}
if (startChildZygote) {
argsForZygote.add("--start-child-zygote");
}
argsForZygote.add(processClass);
if (extraArgs != null) {
for (String arg : extraArgs) {
argsForZygote.add(arg);
}
}
synchronized(mLock) {
return zygoteSendArgsAndGetResult(openZygoteSocketIfNeeded(abi), argsForZygote);
}
}
该方法平谈无奇,没有什么难点的知识点主要就是将传递参数封装到argsForZygote列表中,该数组主要保存了uid,gid,nice-name等信息。然后调用zygoteSendArgsAndGetResult请求Zygotefork进程。但是这里有个参数需要注意一下就是startChildZygote,按照英文的意思是Start a sub-zygote. This creates a new zygote process that has its state cloned from this zygote process,这个参数后续会有看到使用,如果不主要这个参数可能就要进入错误的参数分析了,这里该值为false。
3.4 zygoteProcess.openZygoteSocketIfNeeded
该方法主要通过传递进来的abi判断当前是要和zygote进程还是和zygote64进程通信,其主要的处理逻辑如下:
-
先判断是否已经和主zygote进程建立了连接,如果已经建立了连接不做任何处理
-
如果传递进来的abi和已经建立的primaryZygoteState匹配上了,那么就返回该primaryZygoteState
-
接下来判断是否和次zygote进程建立了连接,如果已经建立了连接不做任何处理
-
如果传递进来的abi和已经建立的secondaryZygoteState 匹配上了,那么就返回该secondaryZygoteState
private ZygoteState openZygoteSocketIfNeeded(String abi) throws ZygoteStartFailedEx {
Preconditions.checkState(Thread.holdsLock(mLock), "ZygoteProcess lock not held");
if (primaryZygoteState == null || primaryZygoteState.isClosed()) {
try {
//向主zygote发起connect()连接操作
primaryZygoteState = ZygoteState.connect(mSocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to primary zygote", ioe);
}
maybeSetApiBlacklistExemptions(primaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(primaryZygoteState);
}
if (primaryZygoteState.matches(abi)) {
return primaryZygoteState;
}
// The primary zygote didn't match. Try the secondary.
if (secondaryZygoteState == null || secondaryZygoteState.isClosed()) {
try {
//退而求其次,如果主zygote没有匹配成功的话,向次zygote发起connect()操作
secondaryZygoteState = ZygoteState.connect(mSecondarySocket);
} catch (IOException ioe) {
throw new ZygoteStartFailedEx("Error connecting to secondary zygote", ioe);
}
maybeSetApiBlacklistExemptions(secondaryZygoteState, false);
maybeSetHiddenApiAccessLogSampleRate(secondaryZygoteState);
}
if (secondaryZygoteState.matches(abi)) {
return secondaryZygoteState;
}
throw new ZygoteStartFailedEx("Unsupported zygote ABI: " + abi);
}
3.5 zygoteProcess.zygoteSendArgsAndGetResult
private static Process.ProcessStartResult zygoteSendArgsAndGetResult(
ZygoteState zygoteState, ArrayList<String> args)
throws ZygoteStartFailedEx {
try {
//判断相关参数值是否合法
int sz = args.size();
for (int i = 0; i < sz; i++) {
if (args.get(i).indexOf('\n') >= 0) {
throw new ZygoteStartFailedEx("embedded newlines not allowed");
}
}
//注意这里的zygoteState就是在3.4章节里面保存的和zygote进行socket通信的
final BufferedWriter writer = zygoteState.writer;
final DataInputStream inputStream = zygoteState.inputStream;
//将要发送给zygote进程的数据通过BufferedWriter 发送出去
writer.write(Integer.toString(args.size()));
writer.newLine();
for (int i = 0; i < sz; i++) {
String arg = args.get(i);
writer.write(arg);
writer.newLine();
}
writer.flush();
// Should there be a timeout on this?
Process.ProcessStartResult result = new Process.ProcessStartResult();
//等待socket服务端(即zygote)返回新创建的进程pid,这里是没有超时机制的,意思是zygote进程端没有返回的话会一直等待在此
result.pid = inputStream.readInt();
result.usingWrapper = inputStream.readBoolean();
//判断进程是否创建成功
if (result.pid < 0) {
throw new ZygoteStartFailedEx("fork() failed");
}
return result;
} catch (IOException ex) {
zygoteState.close();
throw new ZygoteStartFailedEx(ex);
}
}
这个方法主要是通过前面在3.4章节中和Zygote进程建立连接的socket通道向Zygote进程发送创建进程需要的相关信息参数列表,然后此方法进入阻塞等待的状态,等待远端Zygote进程的socket服务端将发送回来新创建的进程pid才继续往下执行。在获取到返回的pid之后,会判断pid是否有效即判断进程是否创建成功,如果创建失败则抛出异常!
这里需要主要一个问题就是,如果Zygote进程fork进程超时,System这段迟迟不能get到返回结果,会引起什么后果呢?这个是非常严重!
通过我们前面篇章的介绍我们知道zygote进程和system_server进程是休戚与共的。试想一下,当 AMS 需要创建进程时, 会通过 Socket 与 zygote 进程通信, 当 zygote 接收到请求后会 fork 出一个子进程, 并将其 pid 返回给 AMS。需要注意的是, 在收到 pid 之前, AMS 会一直持锁等待,而且这里持有的是AMS大锁, 所以就会 block 其他重要线程, 导致系统卡死,甚至进而Android重启zygote进程,用户反馈内容也会主要围绕 “系统卡死” “按键没有反应” ,“重启”等等。
那如何解决这个问题?google其实有注意到,准备加一个超时机制,但是一直没有加上,但是这种情况也是治标不治本,解决这种问题还是要具体分析,是内存碎片严重导致fork进程申请page失败,还是其他原因,需要根据Log具体对待。本人在实际的工作中暂时还没有遇到过这种情况,各位可以留意一下在实际中有么有遇到过这种情况。
3.5 system_server进程接受发起端请求并向zygote进程发送请求小结
Android应用进程的创建相关流程在system_server进程中的工作到此要暂时告一段落了,system_system进程通过zygoteSendArgsAndGetResult已经和zygote进程之间通过socket发送了进程创建的请求和相关参数,这时候就要轮到Zygote进程登场了,此时Zygote进程会被唤醒响应客户端的请求(即system_server进程),接下来我们就要重点介绍的是Zygote创建进程的流程了。
四. zygote进程接受请求并创建进程
梦里寻他千百度那人却在灯火阑珊处,我们终于来到了Zygote进程的地盘了。通过我们前面的篇章 Android P Zygote进程启动源码分析指南我们知道zygote进程是由init进程启动的,并且在zygote进程调用ZygoteInit.main()方法完成相关的初始化工作和创建完system_server进程之后会通过runSelectLoop()方法进入无限循环等待客户端的请求,而此时此刻生意已经上门来了,我们的system_server发来了请求我们要处理了。
4.1 ZygoteInit.main
还是让我们炒炒冷饭,从该方法说起虽然前面的篇章已经有说过了,该代码定义在frameworks/base/core/java/com/android/internal/os//ZygoteInit.java中,代码如下:
public static void main(String argv[]) {
//构造ZygoteServer对象
ZygoteServer zygoteServer = new ZygoteServer();
......
final Runnable caller;
try {
......
boolean startSystemServer = false;
//创建zygote通信服务端
zygoteServer.registerServerSocketFromEnv(socketName);
if (startSystemServer) {
Runnable r = forkSystemServer(abiList, socketName, zygoteServer);
if (r != null) {
r.run();
return;
}
}
//此处zygote进程开始进入runSelectLoop无限死循环,等待客户端的请求
caller = zygoteServer.runSelectLoop(abiList);
} catch (Throwable ex) {
Log.e(TAG, "System zygote died with exception", ex);
throw ex;
} finally {
zygoteServer.closeServerSocket();
}
if (caller != null) {//此处只有zygote创建的子进程才会运行,不包括system_server进程
caller.run();
}
}
在Android O及其之前版本runSelectLoop()方法会抛出异常MethodAndArgsCaller,从而进入caller.run()方法。但是Android P及之后都不是通过抛异常的方法来运行caller.run了而是直接子进程返回执行,这个是各位需要关注的点,还有一点就是不要迷糊着说不是说zygote进程在runSelectLoop中已经无限循环了吗,怎么又跑到后续执行caller.run了,此时执行caller.run不是zygote进程了而是创建的子进程了,各位一点要记住这点。
4.2 zygoteServer.registerServerSocketFromEnv
代码定义在frameworks/base/core/java/com/android/internal/os/zygoteServer.java中,我们知道Zygote进程需要通过socket通道接受客户端进程的请求,而建立socket server的逻辑就在registerServerSocketFromEnv中,其核心代码如下:
void registerServerSocketFromEnv(String socketName) {
if (mServerSocket == null) {
int fileDesc;
final String fullSocketName = ANDROID_SOCKET_PREFIX + socketName;
try {
//从环境变量获取socket端的服务名
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);//设置文件描述符
//LocalServerSocket,本地socket这个是谷歌改良版本的socket通信方式不同于传统的socket
mServerSocket = new LocalServerSocket(fd);
mCloseSocketFd = true;
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
这里有一点需要注意的是这里的LocalServerSocket和通常的socket是有区别的,LocalSocket比Java本身的socket效率要高,没有经过协议栈,是Android自己实现的类似共享内存一样的东东,在传输大量数据的时候就需要用到,比如Rild电话,,在创建应用进程和zygote通信,在应用安装过程中和installd通信等等就不一一枚举了。具体参见博客 Android Framework层LocalSocket实现通信。
4.3 zygoteServer.runSelectLoop
该代码定义在frameworks/base/core/java/com/android/internal/os/ZygoteServer.java,进入runSelectLoop处理逻辑之后,Zygote进程已经迫不及待的在等待客户端进程的请求,这不system_server进程的AMS服务发过来了请求,我们看看它究竟是怎么处理的。
Runnable runSelectLoop(String abiList) {
ArrayList<FileDescriptor> fds = new ArrayList<FileDescriptor>();
ArrayList<ZygoteConnection> peers = new ArrayList<ZygoteConnection>();
//sServerSocket是socket通信中的服务端,即zygote进程。保存到fds[0]
fds.add(mServerSocket.getFileDescriptor());
peers.add(null);
while (true) {
//每次循环,都重新创建需要监听的pollFds
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) {
//采用I/O多路复用机制,当接收到客户端发出连接请求 或者数据处理请求到来,则往下执行;
// 否则进入continue,跳出本次循环。
if ((pollFds[i].revents & POLLIN) == 0) {
continue;
}
if (i == 0) {
//即fds[0],代表的是sServerSocket因为它最先加入,则意味着有客户端连接请求;
// 则创建ZygoteConnection对象,并添加到fds。
ZygoteConnection newPeer = acceptCommandPeer(abiList);
//加入到peers和fds,下一次也开始监听
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
try {
//i>0,则代表通过socket接收来自对端的数据,并执行相应操作
ZygoteConnection connection = peers.get(i);
final Runnable command = connection.processOneCommand(this);
if (mIsForkChild) {
// We're in the child. We should always have a command to run at this
// stage if processOneCommand hasn't called "exec".
if (command == null) {
throw new IllegalStateException("command == null");
}
return command;
} else {
// We're in the server - we should never have any commands to run.
if (command != null) {
throw new IllegalStateException("command != null");
}
// We don't know whether the remote side of the socket was closed or
// not until we attempt to read from it from processOneCommand. This shows up as
// a regular POLLIN event in our regular processing loop.
if (connection.isClosedByPeer()) {
connection.closeSocket();
peers.remove(i);
fds.remove(i);//处理完则从fds中移除该文件描述符
}
}
} catch (Exception e) {
if (!mIsForkChild) {
// We're in the server so any exception here is one that has taken place
// pre-fork while processing commands or reading / writing from the
// control socket. Make a loud noise about any such exceptions so that
// we know exactly what failed and why.
Slog.e(TAG, "Exception executing zygote command: ", e);
// Make sure the socket is closed so that the other end knows immediately
// that something has gone wrong and doesn't time out waiting for a
// response.
ZygoteConnection conn = peers.remove(i);
conn.closeSocket();
fds.remove(i);
} else {
// We're in the child so any exception caught here has happened post
// fork and before we execute ActivityThread.main (or any other main()
// method). Log the details of the exception and bring down the process.
Log.e(TAG, "Caught post-fork exception in child process.", e);
throw e;
}
} finally {
// Reset the child flag, in the event that the child process is a child-
// zygote. The flag will not be consulted this loop pass after the Runnable
// is returned.
mIsForkChild = false;
}
}
}
}
}
从上面的代码可以看出,Zygote采用高效的I/O多路复用机制,保证在没有客户端连接请求或数据处理时休眠,否则响应客户端的请求。而接下来的代码就分两条分支进行了,其逻辑分别如下:
-
在最开始的时候fds中仅有server socket,因此当有数据到来时,将执行i等于0的分支。此时,显然是需要创建新的通信连接,因此acceptCommandPeer将被调用。让我们接着分析看看它究竟干了些啥!
-
当socket通信通道建立连接之后,就可以跟客户端通信,进入processOneCommand()方法来接收并处理客户端数据,并执行进程创建工作。
4.4 zygoteServer.acceptCommandPeer
让我们接着分析这段代码,如下:
private ZygoteConnection acceptCommandPeer(String abiList) {
try {
// socket编程中,accept()调用主要用在基于连接的套接字类型,比如SOCK_STREAM和SOCK_SEQPACKET
// 它提取出所监听套接字的等待连接队列中第一个连接请求,创建一个新的套接字,并返回指向该套接字的文件描述符
// 新建立的套接字不在监听状态,原来所监听的套接字的状态也不受accept()调用的影响,这个就是套接字编程的基础了
return createNewConnection(mServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
protected ZygoteConnection createNewConnection(LocalSocket socket, String abiList)
throws IOException {
return new ZygoteConnection(socket, abiList);
}
ZygoteConnection(LocalSocket socket, String abiList) throws IOException {
mSocket = socket;
this.abiList = abiList;
mSocketOutStream
= new DataOutputStream(socket.getOutputStream());
mSocketReader = new BufferedReader(
new InputStreamReader(socket.getInputStream()), 256);
mSocket.setSoTimeout(CONNECTION_TIMEOUT_MILLIS);
try {
peer = mSocket.getPeerCredentials();
} catch (IOException ex) {
Log.e(TAG, "Cannot read peer credentials", ex);
throw ex;
}
isEof = false;
}
通过上面的代码我们可以看到,acceptCommandPeer主要是基础的socket套接字编程,调用了server socket的accpet函数等待客户端的连接,在后面客户端就能调用write()写数据,Zygote进程能调用read()读数据。当有新的连接建立时,zygote进程将会创建出一个新的socket与其通信,并将该socket加入到fds中,所以一旦和客户端进程的通信连接建立后,fds中将会有多个socket至少会有两个。
这里我们可以看到createNewConnection创建了ZygoteConnection对象,而在该对象中封装了mServerSocket的输入流mSocketReader与输出流mSocketOutStream,这个与Clinet端的ZygoteState中封装的zygoteInputStream和zygoteWriter是对应起来的。
当poll监听到这一组sockets上有数据到来时,就会从阻塞中恢复。于是,我们需要判断到底是哪个socket收到了数据
4.5 ZygoteConnection.processOneCommand
该代码定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中,主要解析socket客户端即AMS传递过来的参数,然后调用forkAndSpecialize创建App进程。
Runnable processOneCommand(ZygoteServer zygoteServer) {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
try {
//读取socket客户端发送过来的参数列表
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
...
throw new IllegalStateException("IOException on command socket", ex);
}
...
//将socket客户端传递过来的参数,解析成Arguments对象格式
parsedArgs = new Arguments(args);
...
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.runtimeFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, fdsToIgnore, parsedArgs.startChildZygote,
parsedArgs.instructionSet, parsedArgs.appDataDir);
try {
if (pid == 0) {
// in child
//子进程执行
zygoteServer.setForkChild();
zygoteServer.closeServerSocket();
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
return handleChildProc(parsedArgs, descriptors, childPipeFd,
parsedArgs.startChildZygote);
} else {
// In the parent. A pid < 0 indicates a failure and will be handled in
// handleParentProc.
//父进程执行
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
handleParentProc(pid, descriptors, serverPipeFd);
return null;
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
4.6 Zygote.forkAndSpecialize
重点,该章节以后的篇章很多都是参见gityuan大神的理解Android进程创建流程来写的,这里为什么是参考他的,因为他的确实是太经典了,实在是绕不过他。各位见谅!
该代码定义在frameworks/base/core/java/com/android/internal/os/Zygote.java中,我们再前面的篇章也有和打过交道了,譬如system_server进程的创建篇章中。
private static final ZygoteHooks VM_HOOKS = new ZygoteHooks();
public static int forkAndSpecialize(int uid, int gid, int[] gids, int runtimeFlags,
int[][] rlimits, int mountExternal, String seInfo, String niceName, int[] fdsToClose,
int[] fdsToIgnore, boolean startChildZygote, String instructionSet, String appDataDir) {
VM_HOOKS.preFork();//
//重置当前线程优先级
resetNicePriority();
//画重点,调用nativeForkAndSpecialize创建子进程,见章节4.9
int pid = nativeForkAndSpecialize(
uid, gid, gids, runtimeFlags, rlimits, mountExternal, seInfo, niceName, fdsToClose,
fdsToIgnore, startChildZygote, instructionSet, appDataDir);
//对于子进程,开启trace跟踪
if (pid == 0) {
Trace.setTracingEnabled(true, runtimeFlags);
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "PostFork");
}
//将章节4.10
VM_HOOKS.postForkCommon();
return pid;
}
这里的VM_HOOKS是Zygote类的静态成员变量,无需多说。
4.6.1 浅说Zygote进程
在前面的篇章 Android 9 Zygote进程启动源码分析指南中我们知道在Android 64位终端中通常有两个Zygote进程,这里我们以zygote64为例来说明:
从图中可知Zygote64进程有4个Daemon子线程分别是ReferenceQueueDaemon,FinalizerDaemon,FinalizerWatchdogDaemon,HeapTaskDaemon。图中线程名显示的并不完整是由于底层的进程结构体task_struct是由长度为16的char型数组保存,超过15个字符便会截断。可能大伙要问这4个线程都是什么干什么的呢,这4个线程主要是ART的GC的守护线程,下面分别对其进行简单的介绍一番,因为GC要深入太难了,有兴趣的可以看看罗升阳的ART GC回收相关的比较详细ART运行时垃圾收集(GC)过程分析
- ReferenceQueueDaemon:引用队列守护进程。我们知道,在创建引用
对象的时候,可以关联一个队列。当被引用对象所引用的对象被GC回收的时候,被引用对象就会被加入
到其创建时关联的队列去。这个加入队列的操作就是由ReferenceQueueDaemon守护线程来完成的。这样
应用程序就可以知道那些被引用对象引用的对象已经被回收了。 - FinalizerDaemon:析构守护线程。对于重写了成员函数finalize的对象,
它们被GC决定回收时,并没有马上被回收,而是被放入到一个队列中,等待FinalizerDaemon守护线程去调
用它们的成员函数finalize,然后再被回收。 - FinalizerWatchdogDaemon:析构监护守护线程。用来监控FinalizerDaemon
线程的执行。一旦检测了那些重写了成员函数finalize的对象在执行成员函数finalize时超出一定的时候,
那么就会退出VM。 - HeapTrimmerDaemon:堆裁剪守护线程。用来执行裁剪堆的操作,也就是用来
将那些空闲的堆内存归还给系统。
可能会有小伙伴说zygote64进程不是还有system_server,com.android.phone等子线程,怎么会只有4个呢?那是因为这些并不是Zygote64的子线程,而是Zygote64的子进程。在图中用红色圈起来的是进程的 VSIZE,virtual size),代表的是进程虚拟地址空间大小。线程与进程的最为本质的区别便是是否共享内存空间,图中VSIZE和Zygote64进程相同的才是Zygote64的子线程,否则就是Zygote64的子进程。
4.7 ZygoteHooks.preFork
该代码定义在libcore/dalvik/src/main/java/dalvik/system/ZygoteHooks.java中,代码如下:
public void preFork() {
Daemons.stop();//停止4个Daemons子线程
waitUntilAllThreadsStopped();//等待所有子线程结束
token = nativePreFork();//完成GC堆的初始化工作
}
4.7.1 Daemons.stop
该代码定义在libcore/libart/src/main/java/java/lang/Daemons.java中,其逻辑如下所示,其中4个线程我们在4.6.1章节中有过简单的介绍了。
public static void stop() {
HeapTaskDaemon.INSTANCE.stop();//停止Java堆整理线程
ReferenceQueueDaemon.INSTANCE.stop();//停止引用队列线程
FinalizerDaemon.INSTANCE.stop();//停止析构守护线程
FinalizerWatchdogDaemon.INSTANCE.stop();//停止析构监护守护线程
}
此处守护线程stop的方式是先调用目标线程interrrupt()方法,然后再调用目标线程join()方法,等待线程执行完成。
4.7.2 ZygoteHooks.waitUntilAllThreadsStopped
private static void waitUntilAllThreadsStopped() {
File tasks = new File("/proc/self/task");
// All Java daemons are stopped already. We're just waiting for their OS counterparts to
// finish as well. This shouldn't take much time so spinning is ok here.
//好吗,这个有点专业了不是很明白,从注释来解释就是当/proc中线程数大于1,就出让CPU直到只有一个线程,才退出循环
while (tasks.list().length > 1) {
Thread.yield();
}
}
4.7.2 ZygoteHooks.nativePreFork
一看这方法命名就知道是本地方法,还记得我在前面篇章介绍的怎么查找Android系统对应的jni文件方法吗,可以知道对应的文件名为dalvik_system_ZygoteHooks.cc路径为art/runtime/native/dalvik_system_ZygoteHooks.cc。最终我们定位到了ZygoteHooks_nativePreFork函数,逻辑如下所示:
static jlong ZygoteHooks_nativePreFork(JNIEnv* env, jclass) {
Runtime* runtime = Runtime::Current();
CHECK(runtime->IsZygote()) << "runtime instance not started with -Xzygote";
runtime->PreZygoteFork();//堆的初始化工作,不是我的领域撤了
if (Trace::GetMethodTracingMode() != TracingMode::kTracingInactive) {
// Tracing active, pause it.
Trace::Pause();
}
// Grab thread before fork potentially makes Thread::pthread_key_self_ unusable.
//将线程转换为long型并保存到token,该过程是非安全的
return reinterpret_cast<jlong>(ThreadForEnv(env));
}
让我们继续跟进PreZygoteFork,该代码位于art/runtime/runtime.cc中,其逻辑如下所示:
void Runtime::PreZygoteFork() {
//好吗,这块牵涉到非常专业的知识了堆的初始化工作,这里应该牵涉到art虚拟机了,有兴趣的可以看看邓凡平大师或者罗升阳的博客或者书籍有比较深的介绍
heap_->PreZygoteFork();
}
VM_HOOKS.preFork的工作我们到这里就告一段落了,其主要功能便是停止Zygote64的4个Daemon子线程的运行,等待并确保Zygote64是单线程(用于提升fork效率),并等待这些线程的停止,初始化gc堆的工作, 并将线程转换为long型并保存到token中。
4.8 Zygote.nativeForkAndSpecialize
这又是一个本地方法,根据我们前面介绍的查找对应的JNI文件的方法,最终找到了frameworks/base/core/jni/com_android_internal_os_Zygote.cpp并通过JNI调用了com_android_internal_os_Zygote_nativeForkAndSpecialize函数,其代码逻辑如下:
static jint com_android_internal_os_Zygote_nativeForkAndSpecialize(
JNIEnv* env, jclass, jint uid, jint gid, jintArray gids,
jint runtime_flags, jobjectArray rlimits,
jint mount_external, jstring se_info, jstring se_name,
jintArray fdsToClose, jintArray fdsToIgnore, jboolean is_child_zygote,
jstring instructionSet, jstring appDataDir) {
jlong capabilities = 0;
// Grant CAP_WAKE_ALARM to the Bluetooth process.
// Additionally, allow bluetooth to open packet sockets so it can start the DHCP client.
// Grant CAP_SYS_NICE to allow Bluetooth to set RT priority for
// audio-related threads.
// TODO: consider making such functionality an RPC to netd.
if (multiuser_get_app_id(uid) == AID_BLUETOOTH) {
capabilities |= (1LL << CAP_WAKE_ALARM);
capabilities |= (1LL << CAP_NET_RAW);
capabilities |= (1LL << CAP_NET_BIND_SERVICE);
capabilities |= (1LL << CAP_SYS_NICE);
}
// Grant CAP_BLOCK_SUSPEND to processes that belong to GID "wakelock"
bool gid_wakelock_found = false;
if (gid == AID_WAKELOCK) {
gid_wakelock_found = true;
} else if (gids != NULL) {
jsize gids_num = env->GetArrayLength(gids);
ScopedIntArrayRO ar(env, gids);
if (ar.get() == NULL) {
RuntimeAbort(env, __LINE__, "Bad gids array");
}
for (int i = 0; i < gids_num; i++) {
if (ar[i] == AID_WAKELOCK) {
gid_wakelock_found = true;
break;
}
}
}
if (gid_wakelock_found) {
capabilities |= (1LL << CAP_BLOCK_SUSPEND);
}
// If forking a child zygote process, that zygote will need to be able to change
// the UID and GID of processes it forks, as well as drop those capabilities.
if (is_child_zygote) {
capabilities |= (1LL << CAP_SETUID);
capabilities |= (1LL << CAP_SETGID);
capabilities |= (1LL << CAP_SETPCAP);
}
// Containers run without some capabilities, so drop any caps that are not
// available.
//通过前面及此处的规则计算capabilities 的值
capabilities &= GetEffectiveCapabilityMask(env);
return ForkAndSpecializeCommon(env, uid, gid, gids, runtime_flags,
rlimits, capabilities, capabilities, mount_external, se_info,
se_name, false, fdsToClose, fdsToIgnore, is_child_zygote == JNI_TRUE,
instructionSet, appDataDir);
}
4.9 ForkAndSpecializeCommon
对于这个函数我想大伙肯定不陌生了,在前面篇章 Android 9 Zygote进程启动源码分析指南我们已经有见过其踪影了,但是这里还是得分析分析。源码如下:
static pid_t ForkAndSpecializeCommon(JNIEnv* env, uid_t uid, gid_t gid, jintArray javaGids,
jint runtime_flags, jobjectArray javaRlimits,
jlong permittedCapabilities, jlong effectiveCapabilities,
jint mount_external,
jstring java_se_info, jstring java_se_name,
bool is_system_server, jintArray fdsToClose,
jintArray fdsToIgnore, bool is_child_zygote,
jstring instructionSet, jstring dataDir) {
//设置子进程的signal信号处理函数
SetSignalHandlers();
sigset_t sigchld;
sigemptyset(&sigchld);
sigaddset(&sigchld, SIGCHLD);
......
pid_t pid = fork();
if (pid == 0) {
//开始初始化
PreApplicationInit();
//关闭并清除文件描述符
if (!DetachDescriptors(env, fdsToClose, &error_msg)) {
fail_fn(error_msg);
}
......
if (!is_system_server && getuid() == 0) {
//对于非system_server子进程,则创建进程组
int rc = createProcessGroup(uid, getpid());
if (rc != 0) {
if (rc == -EROFS) {
ALOGW("createProcessGroup failed, kernel missing CONFIG_CGROUP_CPUACCT?");
} else {
ALOGE("createProcessGroup(%d, %d) failed: %s", uid, pid, strerror(-rc));
}
}
}
std::string error_msg;
//设置进程组
if (!SetGids(env, javaGids, &error_msg)) {
fail_fn(error_msg);
}
//设置资源limits
if (!SetRLimits(env, javaRlimits, &error_msg)) {
fail_fn(error_msg);
}
SetUpSeccompFilter(uid);
rc = setresuid(uid, uid, uid);
if (rc == -1) {
fail_fn(CREATE_ERROR("setresuid(%d) failed: %s", uid, strerror(errno)));
}
if (NeedsNoRandomizeWorkaround()) {
// Work around ARM kernel ASLR lossage (http://b/5817320).
int old_personality = personality(0xffffffff);
int new_personality = personality(old_personality | ADDR_NO_RANDOMIZE);
if (new_personality == -1) {
ALOGW("personality(%d) failed: %s", new_personality, strerror(errno));
}
}
if (!SetCapabilities(permittedCapabilities, effectiveCapabilities, permittedCapabilities,
&error_msg)) {
fail_fn(error_msg);
}
//设置调度策略
if (!SetSchedulerPolicy(&error_msg)) {
fail_fn(error_msg);
}
//selinux安全上下文
rc = selinux_android_setcontext(uid, is_system_server, se_info_c_str, se_name_c_str);
if (rc == -1) {
fail_fn(CREATE_ERROR("selinux_android_setcontext(%d, %d, \"%s\", \"%s\") failed", uid,
is_system_server, se_info_c_str, se_name_c_str));
//在Zygote子进程中,设置信号SIGCHLD的处理器恢复为默认行为
UnsetChldSignalHandler();
//等价于调用zygote.callPostForkChildHooks,这个后面章节会讲到
env->CallStaticVoidMethod(gZygoteClass, gCallPostForkChildHooks, runtime_flags,
is_system_server, is_child_zygote, instructionSet);
......
} else if (pid > 0) {
//进入父进程,即Zygote进程
}
return pid;
}
4.9.1 fork原理简介
在这里fork函数采用的是copy-on-write即写时拷贝技术简称COW技术,zygote在这里使用了copy-on-write技术可以提高应用运行速度,因为该种方式对运行在内存中的进程实现了最大程度的复用,并通过库共享有效降低了内存的使用量。也就是说当新的App通过fork()创建的的时候不进行内存的复制,这是因为复制内存的开销是很大的,此时子进程只需要共享父进程的内存空间即可,因为这个时候他们没有差异。而当子进程需要需要修改共享内存信息时,此时才开始将内存信息复制到自己的内存空间中,并进行修改。
并且同时fork技术是linux创建进程的标准方法,调用一次,返回两次,其返回值有三种类型,其分别的定义如下:
- 父进程中,fork返回新创建的子进程的pid,此时的pid大于0
- 子进程中,fork返回0
- 当fork出现异常甚至错误时,fork返回负数(造成这种的原因可能是当前创建 的进程数超过系统允许的上限或者系统内存不存时导致的)
fork()的主要工作是寻找空闲的进程号pid,然后从父进程拷贝进程信息,例如数据段和代码段,fork()后子进程要执行的代码等。 Zygote进程是所有Android进程的母体,包括system_server和各个App进程。zygote利用fork()方法生成新进程,对于新进程A复用Zygote进程本身的资源,再加上新进程A相关的资源,构成新的应用进程A。其中下图中Zygote进程的libc、vm、preloaded classes、preloaded resources是如何生成的,可查看另一个文章 Android 9 Zygote进程启动源码分析指南中有比较详细的介绍,见下图:
前面说了一大堆的copy-on-write的作用,下面我们对其过程和原理总结一番:
-
copy-on-write过程:当父子进程任一方修改内存数据时(这是on-write时机),才发生缺页中断,从而分配新的物理内存(这是copy操作)
-
copy-on-write原理:写时拷贝是指子进程与父进程的页表都所指向同一个块物理内存,fork过程只拷贝父进程的页表,并标记这些页表是只读的。父子进程共用同一份物理内存,如果父子进程任一方想要修改这块物理内存,那么会触发缺页异常(page fault),Linux收到该中断便会创建新的物理内存,并将两个物理内存标记设置为可写状态,从而父子进程都有各自独立的物理内
4.9.2 fork函数分析
该函数定义在bionic/libc/bionic/fork.cpp中,其逻辑代码如下所示:
__BIONIC_WEAK_FOR_NATIVE_BRIDGE
int fork() {
__bionic_atfork_run_prepare();
//fork期间,获取父进程pid,并使其缓存值无效
pthread_internal_t* self = __get_thread();
//调用clone函数
int result = clone(nullptr,
nullptr,
(CLONE_CHILD_SETTID | CLONE_CHILD_CLEARTID | SIGCHLD),
nullptr,
nullptr,
nullptr,
&(self->tid));
if (result == 0) {
// Update the cached pid, since clone() will not set it directly (as
// self->tid is updated by the kernel).
self->set_cached_pid(gettid());
__bionic_atfork_run_child();//fork完成执行子进程回调方法
} else {
__bionic_atfork_run_parent();//fork完成执行父进程回调方法
}
return result;
}
这个有点太深入了,还是直接借鉴gityuan得总结,在执行clone前后都有对应的回调方法,如下:
- __bionic_atfork_run_prepare: fork完成前,父进程回调方法
- __bionic_atfork_run_child: fork完成后,子进程回调方法
- __bionic_atfork_run_paren: fork完成后,父进程回调方法
以上3个方法的实现都位于bionic/libc/bionic/pthread_atfork.cpp中。如果有需要,可以扩展该回调方法,添加相关的业务需求。
4.9.3 Zygote.callPostForkChildHooks
好吗说实话,这个是我放置了好久然后来重新分析的,我都不记得我分析到哪里了,好吗重新来一遍,这个是JNI通过CallStaticVoidMethod调用而来的,然后调用到Zygote.java中,代码如下:
private static void callPostForkChildHooks(int runtimeFlags, boolean isSystemServer,
boolean isZygote, String instructionSet) {
VM_HOOKS.postForkChild(runtimeFlags, isSystemServer, isZygote, instructionSet);
}
VM_HOOKS我们已经介绍过了,不做过多的分析,接着分析postForkChild,其逻辑如下:
//定义在ZygoteHooks.java
public void postForkChild(int runtimeFlags, boolean isSystemServer, boolean isZygote,
String instructionSet) {
//见章节4.9.4
nativePostForkChild(token, runtimeFlags, isSystemServer, isZygote, instructionSet);
Math.setRandomSeedInternal(System.currentTimeMillis());
}
在这里,设置了新进程Random随机数种子为当前系统时间,也就是在进程创建的那一刻就决定了未来随机数的情况,也就是伪随机。
4.9.4 nativePostForkChild
显而易见毋庸置疑的这又是一个JNI的调用,最终调用到了art/runtime/native/dalvik_system_ZygoteHooks.cc的ZygoteHooks_nativePostForkChild函数,其逻辑如下:
static void ZygoteHooks_nativePostForkChild(JNIEnv* env,
jclass,
jlong token,
jint runtime_flags,
jboolean is_system_server,
jboolean is_zygote,
jstring instruction_set) {
//此处token是由[小节8.3]创建的,记录着当前线程
Thread* thread = reinterpret_cast<Thread*>(token);
//设置新进程的主线程id
thread->InitAfterFork();
......
if (instruction_set != nullptr && !is_system_server) {
ScopedUtfChars isa_string(env, instruction_set);
InstructionSet isa = GetInstructionSetFromString(isa_string.c_str());
Runtime::NativeBridgeAction action = Runtime::NativeBridgeAction::kUnload;
if (isa != InstructionSet::kNone && isa != kRuntimeISA) {
action = Runtime::NativeBridgeAction::kInitialize;
}
//见章节4.9.5
Runtime::Current()->InitNonZygoteOrPostFork(
env, is_system_server, action, isa_string.c_str());
} else {
Runtime::Current()->InitNonZygoteOrPostFork(
env,
is_system_server,
Runtime::NativeBridgeAction::kUnload,
nullptr,
profile_system_server);
}
}
4.9.5 Runtime::InitNonZygoteOrPostFork
该文件的源码路径为art/runtime/runtime.cc,其业务逻辑如下:
void Runtime::InitNonZygoteOrPostFork(
JNIEnv* env,
bool is_system_server,
NativeBridgeAction action,
const char* isa,
bool profile_system_server) {
is_zygote_ = false;
if (is_native_bridge_loaded_) {
switch (action) {
case NativeBridgeAction::kUnload:
UnloadNativeBridge(); //卸载用于跨平台的桥连库
is_native_bridge_loaded_ = false;
break;
case NativeBridgeAction::kInitialize:
InitializeNativeBridge(env, isa);//初始化用于跨平台的桥连库
break;
}
}
//创建Java堆处理的线程池
heap_->CreateThreadPool();
//重置gc性能数据,以保证进程在创建之前的GCs不会计算到当前app上。
heap_->ResetGcPerformanceInfo();
//不会运行此处
if (is_system_server) {
jit_options_->SetUseJitCompilation(false);
jit_options_->SetSaveProfilingInfo(profile_system_server);
if (profile_system_server) {
jit_options_->SetWaitForJitNotificationsToSaveProfile(false);
VLOG(profiler) << "Enabling system server profiles";
}
}
if (!safe_mode_ &&
(jit_options_->UseJitCompilation() || jit_options_->GetSaveProfilingInfo()) &&
jit_ == nullptr) {
//当flag被设置,并且还没有创建JIT时,则创建JIT
CreateJit();
}
//创建信号处理函数
StartSignalCatcher();
ScopedObjectAccess soa(Thread::Current());
//启动JDWP线程,当命令debuger的flags指定"suspend=y"时,则暂停runtime
GetRuntimeCallbacks()->StartDebugger();
}
关于信号处理过程,其代码位于art/runtime/signal_catcher.cc文件中,这个对于native crash的捕获非常有用。
4.10 VM_HOOKS.postForkCommon
public void postForkCommon() {
Daemons.startPostZygoteFork();
}
public static void startPostZygoteFork() {
ReferenceQueueDaemon.INSTANCE.startPostZygoteFork();
FinalizerDaemon.INSTANCE.startPostZygoteFork();
FinalizerWatchdogDaemon.INSTANCE.startPostZygoteFork();
HeapTaskDaemon.INSTANCE.startPostZygoteFork();
}
方法的具体位置就不必过多介绍了,其业务逻辑主要是在fork新进程后,启动Zygote的4个Daemon线程,java堆整理,引用队列,以及析构线程。
4.11 Zygote.forkAndSpecialize小结
好吗,我也被搞得云里雾里了,套路太多了,下面我们还是将该方法主要功能总结一下:
- preFork: 停止Zygote的4个Daemon子线程的运行,初始化gc堆;
- nativeForkAndSpecialize:调用fork()创建新进程,设置新进程的主线程id,重置gc性能数据,设置信号处理函数等功能。
- postForkCommon:启动4个Deamon子线程。
该方法调用关系链如下:
Zygote.forkAndSpecialize
ZygoteHooks.preFork()
Daemons.stop
ZygoteHooks.nativePreFork
dalvik_system_ZygoteHooks.ZygoteHooks_nativePreFork
Runtime.PreZygoteFork
heap_->PreZygoteFork()
Zygote.nativeForkAndSpecialize
com_android_internal_os_Zygote.com_android_internal_os_Zygote_nativeForkAndSpecialize
com_android_internal_os_Zygote.ForkAndSpecializeCommon
fork()
Zygote.callPostForkChildHooks
ZygoteHooks.postForkChild
dalvik_system_ZygoteHooks.nativePostForkChild
Runtime::InitNonZygoteOrPostFork
ZygoteHooks.postForkCommon
Daemons.start
来个三连杀,上时序图,如下:
好吗行文至此,我们的App进程已经完成了创建的所有工作了,难道就要结束了吗,不这只是一个开始,真的只是一个开始。在接下来我们要开始新创建的App进程的工作。在前面ZygoteConnection.processOneCommand方法中,Zygote进程执行完forkAndSpecialize()后,新创建的App进程便进入handleChildProc()方法,下面的操作运行在App进程。
五. 开创新进程时代
我已经等待了五百年了,终于轮到我上场了。在前面的第四章节中,我们在processOneCommand方法中调用forkAndSpecialize创建完新进程之后,在返回值为pid=0的子进程中继续开始执行handleChildProc()方法。从此属于我的时代来临了。
5.1 handleChildProc
该源码定义在frameworks/base/core/java/com/android/internal/os/ZygoteConnection.java中,其源码如下:
private Runnable handleChildProc(Arguments parsedArgs, FileDescriptor[] descriptors,
FileDescriptor pipeFd, boolean isZygote) {
//关闭Zygote的socket两端的连接
closeSocket();
if (descriptors != null) {
try {
//重定向
Os.dup2(descriptors[0], STDIN_FILENO);
Os.dup2(descriptors[1], STDOUT_FILENO);
Os.dup2(descriptors[2], STDERR_FILENO);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
} catch (ErrnoException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
//设置进程名,通常是包名
Process.setArgV0(parsedArgs.niceName);
}
// End of the postFork event.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
if (parsedArgs.invokeWith != null) {
//据说这是用于检测进程内存泄露或溢出时场景而设计,这个不是我说的gityuan说的
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
VMRuntime.getCurrentInstructionSet(),
pipeFd, parsedArgs.remainingArgs);
// 正常的代码流程是不可能运行到这里的
throw new IllegalStateException("WrapperInit.execApplication unexpectedly returned");
} else {
if (!isZygote) {
//@param isZygote whether this new child process is itself a new Zygote
//我们应该走的是这个分析,即此处执行目标类的main方法
return ZygoteInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs,
null );
} else {
return ZygoteInit.childZygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null );
}
}
}
5.2 ZygoteInit.zygoteInit
该源码定义在frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中,代码如下:
public static final Runnable zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader) {
//调试相关
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
//调试相关
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
//重定向log输出
RuntimeInit.redirectLogStreams();
RuntimeInit.commonInit();//通用的初始化,见5.2.1
ZygoteInit.nativeZygoteInit();//zygote的初始化,见5.2.2
return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);//应用初始化,见5.2.3
}
5.2.1 RuntimeInit.commonInit
该源码定义在frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中,代码如下:
protected static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
//设置默认的未捕获异常的处理方式,当然用户也可以自行定义
LoggingHandler loggingHandler = new LoggingHandler();
Thread.setUncaughtExceptionPreHandler(loggingHandler);
Thread.setDefaultUncaughtExceptionHandler(new KillApplicationHandler(loggingHandler));
//设置时区,中国时区为"Asia/Shanghai"
TimezoneGetter.setInstance(new TimezoneGetter() {
@Override
public String getId() {
return SystemProperties.get("persist.sys.timezone");
}
});
TimeZone.setDefault(null);
//重置Log配置
LogManager.getLogManager().reset();
new AndroidConfig();
// 设置默认的HTTP User-agent格式,用于 HttpURLConnection。
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);
NetworkManagementSocketTagger.install();
// 设置socket的tag,用于网络流量统计
String trace = SystemProperties.get("ro.kernel.android.tracing");
if (trace.equals("1")) {
Slog.i(TAG, "NOTE: emulator trace profiling enabled");
Debug.enableEmulatorTraceOutput();
}
initialized = true;
}
好吗,是不是有种似曾相识的感觉,是的这部分代码在system_server进程的创建中有分析过。
5.2.2 ZygoteInit.nativeZygoteInit()
聪明人一看就知道这又是一个JNI的调用,最终会调用到frameworks/base/core/jni/AndroidRuntime.cpp文件里面而不是frameworks/base/core/jni/com_android_internal_os_ZygoteInit.cpp,这个各位一定要注意,注意,其代码如下:
static void com_android_internal_os_ZygoteInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{
//此处的gCurRuntime为AppRuntime,frameworks/base/cmds/app_process/App_main.cpp定义的
gCurRuntime->onZygoteInit();
}
这里至于为什么最终调用的是AppRuntime,大伙可以参见前面讲解system_server的博客 Android 9 系统启动之SystemServer大揭秘中有非常详细的介绍!
5.2.2 AppRuntime.onZygoteInit
源码的路径在frameworks/base/cmds/app_process/App_main.cpp中,主要功能开启Binder线程,这也是为什么App应用是天生支持Binder的。
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
- ProcessState::self():主要工作是调用open()打开/dev/binder驱动设备,再利用mmap()映射内核的地址空间,将Binder驱动的fd赋值ProcessState对象中的变量mDriverFD,用于交互操作。startThreadPool()是创建一个新的binder线程,不断进行talkWithDriver().
- startThreadPool(): 启动Binder线程池, 详见Android Binder原理深入分析开篇之Android Binder原理深入分析之进程的Binder线程池工作过程。
5.2.2 RuntimeInit.applicationInit
该源码的路径为frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中,代码如下:
protected static Runnable applicationInit(int targetSdkVersion, String[] argv,
ClassLoader classLoader) {
//true代表应用程序退出时不调用AppRuntime.onExit(),否则会在退出前调用
nativeSetExitWithoutCleanup(true);
//设置虚拟机的内存利用率参数值为0.75
VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);
VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);
final Arguments args = new Arguments(argv);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
//这个和Android 8之前的代码逻辑都有区别,主要找到startClass的static方法 main(),这个class是ActivityThread
return findStaticMain(args.startClass, args.startArgs, classLoader);
}
此处的args.startClass为”android.app.ActivityThread”,这个类对应应用开发的同事来说是最熟悉的陌生人了。
5.2 RuntimeInit.findStaticMain
该源码路径依然在RuntimeInit.java中,其代码逻辑如下:
protected static Runnable findStaticMain(String className, String[] argv,
ClassLoader classLoader) {
Class<?> cl;
try {
cl = Class.forName(className, true, classLoader);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(
"Missing class when invoking static main " + className,
ex);
}
Method m;
try {
m = cl.getMethod("main", new Class[] { String[].class });
} catch (NoSuchMethodException ex) {
throw new RuntimeException(
"Missing static main on " + className, ex);
} catch (SecurityException ex) {
throw new RuntimeException(
"Problem getting static main on " + className, ex);
}
int modifiers = m.getModifiers();
if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {
throw new RuntimeException(
"Main method is not public and static on " + className);
}
// //直接返回MethodAndArgsCaller,直接回到ZygoteInit.main()方法中,此时有一点需要注意的是这个时候运行的App进程中而不是在zygote进程中,不然你会陷入在runSelectLoop无法自拔出来了。
return new MethodAndArgsCaller(m, argv);
}
findStaticMain方法中直接创建MethodAndArgsCaller caller对象,该方法的参数m是指main()方法, argv是指ActivityThread. 这里直接return回去(重点,此时是运行在App进程中),即回到了ZygoteConnection.processOneCommand然后返回到ZygoteServer的runSelectLoop中然后返回到ZygoteInit.main中,下一步进入caller.run()方法,也就是MethodAndArgsCaller.run()。好吗是不是有点糊涂,还是直接看下调用链吗,逻辑如下:
//注意,这个时候是在App进程,否则你会一直在思考不是说runSelectLoop是个死循环吗,为啥出来了啊
return MethodAndArgsCaller
return RuntimeInit.findStaticMain
return ZygoteConnection.handleChildProc
return ZygoteConnection.processOneCommand
return ZygoteServer.runSelectLoop
ZygoteInit.main
caller.run
5.3 MethodAndArgsCaller
static class MethodAndArgsCaller implements Runnable {
private final Method mMethod;
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
//根据传递过来的参数,此处反射调用ActivityThread.main()方法
mMethod.invoke(null, new Object[] { mArgs });
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
} catch (InvocationTargetException ex) {
Throwable cause = ex.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
} else if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException(ex);
}
}
}
这个没有什么好说的了,最终App进程会回到ZygoteInit.main中调用此方法,从而进入到ActivityThread类的main()方法中。
5.4 ActivityThread.main
该方法的源码路径是frameworks/base/core/java/android/app/ActivityThread.java中,其主要逻辑如下:
public static void main(String[] args) {
...
Environment.initForCurrentUser();
...
Process.setArgV0("<pre-initialized>");
//创建主线程looper
Looper.prepareMainLooper();
ActivityThread thread = new ActivityThread();
//attach到系统进程
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
//主线程进入循环状态
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
总结
一路走过来都不容易啊,坚持看到此处的更加不容易啊。我为你点赞。
Process.start()方法是阻塞操作,等待直到进程创建完成并返回相应的新进程pid,才完成该方法。
当App第一次启动时或者启动远程Service,即AndroidManifest.xml文件中定义了process:remote属性时,都需要创建进程。比如当用户点击桌面的某个App图标,桌面本身是一个app(即Launcher App),那么Launcher所在进程便是这次创建新进程的发起进程,该通过binder发送消息给system_server进程,该进程承载着整个java framework的核心服务。system_server进程从Process.start开始,执行创建进程,流程图(以进程的视角)如下:
上图中,system_server进程通过socket IPC通道向zygote进程通信,zygote在fork出新进程后由于fork调用一次,返回两次,即在zygote进程中调用一次,在zygote进程和子进程中各返回一次,从而能进入子进程来执行代码。该调用流程图的过程:
- system_server进程(章节3.1-3.5):通过Process.start()方法发起创建新进程请求,会先收集各种新进程uid、gid、nice-name等相关的参数,然后通过socket通道发送给zygote进程;
- zygote进程(章节4.1-4.11):接收到system_server进程发送过来的参数后封装成Arguments对象,图中绿色框forkAndSpecialize()方法是进程创建过程中最为核心的一个环节(详见章节4.9),其具体工作是依次执行下面的3个方法:
(1).preFork():先停止Zygote的4个Daemon子线程(java堆内存整理线程、对线下引用队列线程、析构线程以及监控线程)的运行以及初始化gc堆;
(2).nativeForkAndSpecialize():调用linux的fork()出新进程,创建Java堆处理的线程池,重置gc性能数据,设置进程的信号处理函数,启动JDWP线程;
(3).postForkCommon():在启动之前被暂停的4个Daemon子线程。 - App process进程(5.1~5.4):进入handleChildProc()方法,设置进程名,打开binder驱动,启动新的binder线程;然后设置art虚拟机参数,再反射调用目标类的main()方法,即Activity.main()方法。
再之后的流程,如果是startActivity则将要进入Activity的onCreate/onStart/onResume等生命周期;如果是startService则将要进入Service的onCreate等生命周期。
system_server进程等待zygote返回进程创建完成(ZygoteConnection.handleParentProc), 一旦Zygote.forkAndSpecialize()方法执行完成, 那么分道扬镳, zygote告知system_server进程进程已创建, 而子进程继续执行后续的handleChildProc操作.
在章节5.2.2 AppRuntime.onZygoteInit的调用过程中会涉及到BInder线程的创建,这个过程中有startThreadPool()创建Binder线程池。也就是说每个进程无论是否包含任何activity等组件,一定至少会包含一个Binder线程。
写在最后
各位由于种种原因,这个是Android 9系列的最后一篇博客了,各位小伙伴们可能看不到我继续跟新关于Android 9的相关博客了。