您的位置:首页 > 编程语言

深入理解zygote——2(代码源于GooGle)

2017-05-16 15:24 453 查看

深入理解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 Zy
e4de
goteInit.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》,邓凡平著。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  zygote
相关文章推荐