深入理解zygote–2
1、welcome to Java World
这个Java世界的从入口在哪里?根据前面的分析,CallStaticVoidMethod最终将调用com.android.internal.os.ZygoteInit的main函数,下面就来就看看这个入口函数,代码如下所示:
Zygoteinit.java :
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
//注册zygote用的socket
registerZygoteSocket();
//预加载类和资源
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preloadClasses();
//cacheRegisterMaps();
preloadResources();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
if (SamplingProfilerIntegration.isEnabled()) {
SamplingProfiler sp = SamplingProfiler.getInstance();
sp.pause();
SamplingProfilerIntegration.writeZygoteSnapshot();
sp.shutDown();
}
// Do an initial gc to clean up after startup
//强制一次性垃圾收集
gc();
// If requested, start system server directly from Zygote
//我们传入的参数满足if分支
if (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals("true")) {
startSystemServer();//启动system_server进程
} else if (!argv[1].equals("false")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
//ZYGOTE_FORK_MODE被定义为false,所以满足else的条件
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode();//zygote调用这个函数
}
closeServerSocket();//关闭socket
} catch (MethodAndArgsCaller caller) {
caller.run();//很重要的caller run函数,以后分析
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
在ZygoteInit的main函数中,我们列举除了5大关键点:registerZygoteSocket(); preloadClasses(); startSystemServer(); runSelectLoopMode(); caller.run(),下面我们对其一一进行分析。
1.1、建立IPC通信服务端——-registerZygoteSocket
zygote 及系统中其他程序的通信没有使用binder,而是采用基于AF_UNIX类型的Socket。registerZygoteSocket的函数的使命正是建立这个socket。代码如下所示:
ZygoteInit.java :
private static void registerZygoteSocket(){}
private static void registerZygoteSocket() {
if (sServerSocket == null) {
int fileDesc;
try {
//从环境中获取socket的fd,这个环境变量由execv传入
String env = System.getenv(ANDROID_SOCKET_ENV);
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(
ANDROID_SOCKET_ENV + " unset or invalid", ex);
}
try {
//创建服务端socket,这个socket将listen并入accept Client。
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
registerZygoteSocket 很简单,就是创建一个服务端的socket。不过我们应该想到下面两个问题:
谁是客户端。
服务端是怎么处理客户端的消息。
1.2、预加载类和资源
现在我们要分析的就是preloadClass和preloadResources函数,先来看看preloadClasses.
ZygoteInit.java:
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
//预加载类的信息存储在PRELOADED_CLASSES变量中,他的值为“preloaded-classes”
InputStream is = ZygoteInit.class.getClassLoader().getResourceAsStream(
PRELOADED_CLASSES);
if (is == null) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
} else {
//做一些统计和准备工作
Log.i(TAG, "Preloading classes...");
long startTime = SystemClock.uptimeMillis();
// Drop root perms while running static initializers.
setEffectiveGroup(UNPRIVILEGED_GID);
setEffectiveUser(UNPRIVILEGED_UID);
// Alter the target heap utilization. With explicit GCs this
// is not likely to have any effect.
float defaultUtilization = runtime.getTargetHeapUtilization();
runtime.setTargetHeapUtilization(0.8f);
// Start with a clean slate.
runtime.gcSoftReferences();
runtime.runFinalizationSync();
Debug.startAllocCounting();
try {
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
//读取文件的第一行,忽略#开头的注释行
int count = 0;
String line;
String missingClasses = null;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
try {
//通过java反射来加载类,line中的存储是预加载的类名。
if (Config.LOGV) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line);
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (Config.LOGV) {
此处代码省略``````````
}
}
preloadClass看起来是如此简单,但是你知道他有多少个类需要预先加载吗?
用coolfind工具程序在framework中搜索名为“”“preloaded-classes的文件,最后会在framework/base目录下找到,他是一个文本文件,内容如下:
preloaded-classes :
# Classes which are preloaded by com.android.internal.os.ZygoteInit.
# Automatically generated by frameworks/base/tools/preload/WritePreloadedClassFile.java.
# MIN_LOAD_TIME_MICROS=1250 //超时控制。
android.R$styleable
android.accounts.AccountManager
android.accounts.AccountManager$4
android.accounts.AccountManager$6
android.accounts.AccountManager$AmsTask
android.accounts.AccountManager$BaseFutureTask
android.accounts.AccountManager$Future2Task
此处代码省略``````
这个preloaded-class 一共有1267行,试想,加载这么多的类得花多长时间:
说明 preload_class 文件由framework/base/tool/preload 工具生成,他需要判断每一个类加载的时间是否大于1250微秒,超过这个时间的类就会被写到preload-classes文件中,最后由zygote预加载。这方面的内容读者可以参考有关preload工具中的内容说明。
preload_class 函数执行的时间比较长,这是导致Android系统启动慢的原因之一,可对比进行一些优化,但是是基于对系统有比较深得了解之后才能够实现的。
preloadResources和preloadClass类似,他主要是加载framework-res.apk中的资源,这里就不再介绍它了。
1.3、启动system_server
我们要分析的是第三个关键点:startSystemServer。这个函数会创建java世界中系统Service所驻留的进程system_service,该进程是framework的核心,如果他死了,就会导致zygote自杀,先来看看这个核心进程是如何启动的。
zygoteInit.java :
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
/*
* First, set the capabilities if necessary
*/
if (parsedArgs.uid != 0) {
try {
setCapabilities(parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IOException ex) {
Log.e(TAG, "Error setting capabilities", ex);
}
}
closeServerSocket();
/*
* Pass the remaining arguments to SystemServer.
* "--nice-name=system_server com.android.server.SystemServer"
*/
RuntimeInit.zygoteInit(parsedArgs.remainingArgs);
/* should never reach here */
}
/**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
/* Hardcoded command line to start the system server */
//设置参数
String args[] = {
"--setuid=1000", //uid,gid等设置
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,3001,3002,3003",
"--capabilities=130104352,130104352",
"--runtime-init",
"--nice-name=system_server",//进程名,叫system_server
"com.android.server.SystemServer",//启动的类名
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
//把上面的字符串数组转换成Arguments对象。
parsedArgs = new ZygoteConnection.Arguments(args);
/*
* Enable debugging of the system process if *either* the command line flags
* indicate it should be debuggable or the ro.debuggable system property
* is set to "1"
*/
int debugFlags = parsedArgs.debugFlags;
if ("1".equals(SystemProperties.get("ro.debuggable")))
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
/* Request to fork the system server process */
//fork一个子进程,看来这个进程就是system_server进程
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
//下面的代码。如果pid为零,则表示处于子进程中,也就是处于system_server进程中
if (pid == 0) {
//system_server进程的工作
handleSystemServerProcess(parsedArgs);
}
return true;
}
这里出现了一个分水岭,即zygotye进行了一次无性繁殖,分裂出一个system_server
进程,(即代码中Zygote.forkSystemServer这句话),关于他的故事,我们会在后文中专门分析,这里先说zygote。
1.4、有求比应之等待请求 —–runSelectLoopMode
待zygote从startSystemServer 返回后,将进入第四个关键的函数;runSelectLoopMode.
前面的地一个关键点registerZygoteSocket中注册了一个用于IPC的socket,不过那是还没有地方用到他,他的用途将在这个runselectLoopMode中体现出来,请看下面的代码:
ZygoteInit.java :
private static void runSelectLoopMode() throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList();
ArrayList<ZygoteConnection> peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
//sSserversocket是我们先前在registerZygoteSocket中建立起来的Socket。
fds.add(sServerSocket.getFileDescriptor());
peers.add(null);
int loopCount = GC_LOOP_COUNT;
while (true) {
int index;
/*
* Call gc() before we block in select().
* It's work that has to be done anyway, and it's better
* to avoid making every child do it. It will also
* madvise() any free memory as a side-effect.
*
* Don't call it every time, because walking the entire
* heap is a lot of overhead to free a few hundred bytes.
*/
if (loopCount <= 0) {
gc();
loopCount = GC_LOOP_COUNT;
} else {
loopCount--;
}
try {
fdArray = fds.toArray(fdArray);
/*
selectReadable 内部调用select,使用多路复用I/O模型,当有客户端链接或有数据时,则selectReadable就会返回。
*/
index = selectReadable(fdArray);
} catch (IOException ex) {
throw new RuntimeException("Error in select()", ex);
}
if (index < 0) {
throw new RuntimeException("Error in select()");
} else if (index == 0) {
//有一个客户端链接上,请注意,客户端在Zygote的代表的是ZygoteConnection
ZygoteConnection newPeer = acceptCommandPeer();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
//客户端发送了一个请求,peer.get 返回的是ZygoteConnection。
//后续处理将交给ZygoteConnection的runOnce函数完成。
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
runSelectLoopMode 比较简单,就是:
处理客户连接和客户请求,其中客户在zygote中用zygoteConnection对象来表示。
客户的请求由zygoteconnection的runOnce来处理。
2、关于zygote的总结
zygote是在Android系统中创建Java世界的盘古,他创建了第一个java虚拟机,同时他又是女娲成功的繁殖了framework的核心system_server进程。作为java语言的受益者,我们理应回顾一下zygote创建java世界的步骤:
第一天:创建AppRuntime对象,并调用他的start,此后的活动Appruntime 来控制。
第二天:调用startvm创建java虚拟机,然后用startReg来注册JNI函数。
第三天: 通过JNI来调用com.android.internal.os.ZygoteInit类的main函数,从此进入了java世界,然而这只是刚刚开始什么都没有。
第四天:调用registerZygoteSocket,通过这个函数,他可以响应子孙后代的请求,同时zygote调用preloadClass和preloadResources,为Java世界填砖加瓦。
第五天:zygote觉得自己工作挺多,便通过调用startSystemServer分裂一个进程,system_server来为java世界服务。
第六天:zygote完成了java世界的处创工作,下一步该做的就是调用runselectloopmode后,便沉沉的睡去了。
以后zygote随时守候在我们的周围i,当接受到子孙后代的请求时,他会随时醒来,为他们工作。
文献参考:
整理抄录自 — 《深入理解Android卷1》,邓凡平著。