public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
boolean startSystemServer = false;
String socketName = "zygote";
String abiList = null;
for (int i = 1; i < argv.length; i++) {
if ("start-system-server".equals(argv[i])) {
startSystemServer = 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.");
}
registerZygoteSocket(socketName);
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preload();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,
SystemClock.uptimeMillis());
// Finish profiling the zygote initialization.
SamplingProfilerIntegration.writeZygoteSnapshot();
// Do an initial gc to clean up after startup
gc();
// Disable tracing so that forked processes do not inherit stale tracing tags from
// Zygote.
Trace.setTracingEnabled(false);
if (startSystemServer) {
startSystemServer(abiList, socketName);
}
Log.i(TAG, "Accepting command socket connections");
runSelectLoop(abiList);
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
我们一步步的来解析
找到文件路径 com\android\internal\os\SamplingProfilerIntegration.java文件,打开该文件可以看到文件注释说明。“集成Dalvik的采样规范”,这个地方引申出一个知识点Dalvik采样规范。那么什么是Dalvik采样规范了?
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.internal.os;
import android.content.pm.PackageInfo;
import android.os.Build;
import android.os.SystemProperties;
import android.util.Log;
import dalvik.system.profiler.BinaryHprofWriter;
import dalvik.system.profiler.SamplingProfiler;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.util.Date;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.atomic.AtomicBoolean;
import libcore.io.IoUtils;
/**
* Integrates the framework with Dalvik's sampling profiler.
*/
public class SamplingProfilerIntegration {
private static final String TAG = "SamplingProfilerIntegration";
public static final String SNAPSHOT_DIR = "/data/snapshots";
private static final boolean enabled;
private static final Executor snapshotWriter;
private static final int samplingProfilerMilliseconds;
private static final int samplingProfilerDepth;
/** Whether or not a snapshot is being persisted. */
private static final AtomicBoolean pending = new AtomicBoolean(false);
static {
samplingProfilerMilliseconds = SystemProperties.getInt("persist.sys.profiler_ms", 0);
samplingProfilerDepth = SystemProperties.getInt("persist.sys.profiler_depth", 4);
if (samplingProfilerMilliseconds > 0) {
File dir = new File(SNAPSHOT_DIR);
dir.mkdirs();
// the directory needs to be writable to anybody to allow file writing
dir.setWritable(true, false);
// the directory needs to be executable to anybody to allow file creation
dir.setExecutable(true, false);
if (dir.isDirectory()) {
snapshotWriter = Executors.newSingleThreadExecutor(new ThreadFactory() {
public Thread newThread(Runnable r) {
return new Thread(r, TAG);
}
});
enabled = true;
Log.i(TAG, "Profiling enabled. Sampling interval ms: "
+ samplingProfilerMilliseconds);
} else {
snapshotWriter = null;
enabled = true;
Log.w(TAG, "Profiling setup failed. Could not create " + SNAPSHOT_DIR);
}
} else {
snapshotWriter = null;
enabled = false;
Log.i(TAG, "Profiling disabled.");
}
}
private static SamplingProfiler samplingProfiler;
private static long startMillis;
/**
* Is profiling enabled?
*/
public static boolean isEnabled() {
return enabled;
}
/**
* Starts the profiler if profiling is enabled.
*/
public static void start() {
if (!enabled) {
return;
}
if (samplingProfiler != null) {
Log.e(TAG, "SamplingProfilerIntegration already started at " + new Date(startMillis));
return;
}
ThreadGroup group = Thread.currentThread().getThreadGroup();
SamplingProfiler.ThreadSet threadSet = SamplingProfiler.newThreadGroupThreadSet(group);
samplingProfiler = new SamplingProfiler(samplingProfilerDepth, threadSet);
samplingProfiler.start(samplingProfilerMilliseconds);
startMillis = System.currentTimeMillis();
}
/**
* Writes a snapshot if profiling is enabled.
*/
public static void writeSnapshot(final String processName, final PackageInfo packageInfo) {
if (!enabled) {
return;
}
if (samplingProfiler == null) {
Log.e(TAG, "SamplingProfilerIntegration is not started");
return;
}
/*
* If we're already writing a snapshot, don't bother enqueueing another
* request right now. This will reduce the number of individual
* snapshots and in turn the total amount of memory consumed (one big
* snapshot is smaller than N subset snapshots).
*/
if (pending.compareAndSet(false, true)) {
snapshotWriter.execute(new Runnable() {
public void run() {
try {
writeSnapshotFile(processName, packageInfo);
} finally {
pending.set(false);
}
}
});
}
}
/**
* Writes the zygote's snapshot to internal storage if profiling is enabled.
*/
public static void writeZygoteSnapshot() {
if (!enabled) {
return;
}
writeSnapshotFile("zygote", null);
samplingProfiler.shutdown();
samplingProfiler = null;
startMillis = 0;
}
/**
* pass in PackageInfo to retrieve various values for snapshot header
*/
private static void writeSnapshotFile(String processName, PackageInfo packageInfo) {
if (!enabled) {
return;
}
samplingProfiler.stop();
/*
* We use the global start time combined with the process name
* as a unique ID. We can't use a counter because processes
* restart. This could result in some overlap if we capture
* two snapshots in rapid succession.
*/
String name = processName.replaceAll(":", ".");
String path = SNAPSHOT_DIR + "/" + name + "-" + startMillis + ".snapshot";
long start = System.currentTimeMillis();
OutputStream outputStream = null;
try {
outputStream = new BufferedOutputStream(new FileOutputStream(path));
PrintStream out = new PrintStream(outputStream);
generateSnapshotHeader(name, packageInfo, out);
if (out.checkError()) {
throw new IOException();
}
BinaryHprofWriter.write(samplingProfiler.getHprofData(), outputStream);
} catch (IOException e) {
Log.e(TAG, "Error writing snapshot to " + path, e);
return;
} finally {
IoUtils.closeQuietly(outputStream);
}
// set file readable to the world so that SamplingProfilerService
// can put it to dropbox
new File(path).setReadable(true, false);
long elapsed = System.currentTimeMillis() - start;
Log.i(TAG, "Wrote snapshot " + path + " in " + elapsed + "ms.");
samplingProfiler.start(samplingProfilerMilliseconds);
}
/**
* generate header for snapshots, with the following format
* (like an HTTP header but without the \r):
*
* Version: \n
* Process: \n
* Package: \n
* Package-Version: \n
* Build: \n
* \n
*
*/
private static void generateSnapshotHeader(String processName, PackageInfo packageInfo,
PrintStream out) {
// profiler version
out.println("Version: 3");
out.println("Process: " + processName);
if (packageInfo != null) {
out.println("Package: " + packageInfo.packageName);
out.println("Package-Version: " + packageInfo.versionCode);
}
out.println("Build: " + Build.FINGERPRINT);
// single blank line means the end of snapshot header.
out.println();
}
}
通过阅读代码可以发现在静态代码块中,初始化了samplingProfilerMilliseconds,该变量表示的意思为采样的间隔时间,单位为毫秒。执行start方法后,创建SamplingProfiler对象,执行start方法,该对象所在文件dalvik.system.profiler.SamplingProfiler
开始采样。
采样过程开始后
循环读取 传递给main函数中的参数
如果参数中有start-system-server,则 startSystemServer = true;循环读取参数结束后,判断apilist=null,如果为空则抛出异常.
然后注册socket, registerZygoteSocket(socketName);注册的目的是为了和zygote进行命令通信
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
private static void registerZygoteSocket(String socketName) {
if (sServerSocket == 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 {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
该方法是首先创建一个文件描述符,然后再创建一个LocalServerSocket对象,android\net\LocalServerSocket.java文件
打开这个文件。看看这个文件的解释
/**
* Non-standard class for creating an inbound UNIX-domain socket
* in the Linux abstract namespace.
*/
这个不是一个标准类用于在linux 的抽象名字空间中创建入队的Unix-domain socket
/**
* Create a LocalServerSocket from a file descriptor that's already
* been created and bound. listen() will be called immediately on it.
* Used for cases where file descriptors are passed in via environment
* variables
*
* @param fd bound file descriptor
* @throws IOException
*/
public LocalServerSocket(FileDescriptor fd) throws IOException
{
impl = new LocalSocketImpl(fd);
impl.listen(LISTEN_BACKLOG);
localAddress = impl.getSockAddress();
}
创建localservcerSocket对象时,会调用此构造函数。通过查看解释,该构造函数适用于一个文件描述符已经被创建和入件
可以看到
LISTEN_BACKLOG=50,该socket server在50端口监控
/*
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.net;
import java.io.IOException;
import java.io.OutputStream;
import java.io.InputStream;
import java.io.FileDescriptor;
import java.net.SocketOptions;
import android.system.ErrnoException;
import android.system.Os;
import android.system.OsConstants;
/**
* Socket implementation used for android.net.LocalSocket and
* android.net.LocalServerSocket. Supports only AF_LOCAL sockets.
*/
class LocalSocketImpl
{
private SocketInputStream fis;
private SocketOutputStream fos;
private Object readMonitor = new Object();
private Object writeMonitor = new Object();
/** null if closed or not yet created */
private FileDescriptor fd;
/** whether fd is created internally */
private boolean mFdCreatedInternally;
// These fields are accessed by native code;
/** file descriptor array received during a previous read */
FileDescriptor[] inboundFileDescriptors;
/** file descriptor array that should be written during next write */
FileDescriptor[] outboundFileDescriptors;
/**
* An input stream for local sockets. Needed because we may
* need to read ancillary data.
*/
class SocketInputStream extends InputStream {
/** {@inheritDoc} */
@Override
public int available() throws IOException {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
return available_native(myFd);
}
/** {@inheritDoc} */
@Override
public void close() throws IOException {
LocalSocketImpl.this.close();
}
/** {@inheritDoc} */
@Override
public int read() throws IOException {
int ret;
synchronized (readMonitor) {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
ret = read_native(myFd);
return ret;
}
}
/** {@inheritDoc} */
@Override
public int read(byte[] b) throws IOException {
return read(b, 0, b.length);
}
/** {@inheritDoc} */
@Override
public int read(byte[] b, int off, int len) throws IOException {
synchronized (readMonitor) {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
if (off < 0 || len < 0 || (off + len) > b.length ) {
throw new ArrayIndexOutOfBoundsException();
}
int ret = readba_native(b, off, len, myFd);
return ret;
}
}
}
/**
* An output stream for local sockets. Needed because we may
* need to read ancillary data.
*/
class SocketOutputStream extends OutputStream {
/** {@inheritDoc} */
@Override
public void close() throws IOException {
LocalSocketImpl.this.close();
}
/** {@inheritDoc} */
@Override
public void write (byte[] b) throws IOException {
write(b, 0, b.length);
}
/** {@inheritDoc} */
@Override
public void write (byte[] b, int off, int len) throws IOException {
synchronized (writeMonitor) {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
if (off < 0 || len < 0 || (off + len) > b.length ) {
throw new ArrayIndexOutOfBoundsException();
}
writeba_native(b, off, len, myFd);
}
}
/** {@inheritDoc} */
@Override
public void write (int b) throws IOException {
synchronized (writeMonitor) {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
write_native(b, myFd);
}
}
/**
* Wait until the data in sending queue is emptied. A polling version
* for flush implementation.
* @throws IOException
* if an i/o error occurs.
*/
@Override
public void flush() throws IOException {
FileDescriptor myFd = fd;
if (myFd == null) throw new IOException("socket closed");
while(pending_native(myFd) > 0) {
try {
Thread.sleep(10);
} catch (InterruptedException ie) {
return;
}
}
}
}
private native int pending_native(FileDescriptor fd) throws IOException;
private native int available_native(FileDescriptor fd) throws IOException;
private native int read_native(FileDescriptor fd) throws IOException;
private native int readba_native(byte[] b, int off, int len,
FileDescriptor fd) throws IOException;
private native void writeba_native(byte[] b, int off, int len,
FileDescriptor fd) throws IOException;
private native void write_native(int b, FileDescriptor fd)
throws IOException;
private native void connectLocal(FileDescriptor fd, String name,
int namespace) throws IOException;
private native void bindLocal(FileDescriptor fd, String name, int namespace)
throws IOException;
private native void listen_native(FileDescriptor fd, int backlog)
throws IOException;
private native void shutdown(FileDescriptor fd, boolean shutdownInput);
private native Credentials getPeerCredentials_native(
FileDescriptor fd) throws IOException;
private native int getOption_native(FileDescriptor fd, int optID)
throws IOException;
private native void setOption_native(FileDescriptor fd, int optID,
int b, int value) throws IOException;
// private native LocalSocketAddress getSockName_native
// (FileDescriptor fd) throws IOException;
/**
* Accepts a connection on a server socket.
*
* @param fd file descriptor of server socket
* @param s socket implementation that will become the new socket
* @return file descriptor of new socket
*/
private native FileDescriptor accept
(FileDescriptor fd, LocalSocketImpl s) throws IOException;
/**
* Create a new instance.
*/
/*package*/ LocalSocketImpl()
{
}
/**
* Create a new instance from a file descriptor representing
* a bound socket. The state of the file descriptor is not checked here
* but the caller can verify socket state by calling listen().
*
* @param fd non-null; bound file descriptor
*/
/*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
{
this.fd = fd;
}
public String toString() {
return super.toString() + " fd:" + fd;
}
/**
* Creates a socket in the underlying OS.
*
* @param sockType either {@link LocalSocket#SOCKET_DGRAM}, {@link LocalSocket#SOCKET_STREAM}
* or {@link LocalSocket#SOCKET_SEQPACKET}
* @throws IOException
*/
public void create (int sockType) throws IOException {
// no error if socket already created
// need this for LocalServerSocket.accept()
if (fd == null) {
int osType;
switch (sockType) {
case LocalSocket.SOCKET_DGRAM:
osType = OsConstants.SOCK_DGRAM;
break;
case LocalSocket.SOCKET_STREAM:
osType = OsConstants.SOCK_STREAM;
break;
case LocalSocket.SOCKET_SEQPACKET:
osType = OsConstants.SOCK_SEQPACKET;
break;
default:
throw new IllegalStateException("unknown sockType");
}
try {
fd = Os.socket(OsConstants.AF_UNIX, osType, 0);
mFdCreatedInternally = true;
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
}
}
/**
* Closes the socket.
*
* @throws IOException
*/
public void close() throws IOException {
synchronized (LocalSocketImpl.this) {
if ((fd == null) || (mFdCreatedInternally == false)) {
fd = null;
return;
}
try {
Os.close(fd);
} catch (ErrnoException e) {
e.rethrowAsIOException();
}
fd = null;
}
}
/** note timeout presently ignored */
protected void connect(LocalSocketAddress address, int timeout)
throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
connectLocal(fd, address.getName(), address.getNamespace().getId());
}
/**
* Binds this socket to an endpoint name. May only be called on an instance
* that has not yet been bound.
*
* @param endpoint endpoint address
* @throws IOException
*/
public void bind(LocalSocketAddress endpoint) throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
bindLocal(fd, endpoint.getName(), endpoint.getNamespace().getId());
}
protected void listen(int backlog) throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
listen_native(fd, backlog);
}
/**
* Accepts a new connection to the socket. Blocks until a new
* connection arrives.
*
* @param s a socket that will be used to represent the new connection.
* @throws IOException
*/
protected void accept(LocalSocketImpl s) throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
s.fd = accept(fd, s);
s.mFdCreatedInternally = true;
}
/**
* Retrieves the input stream for this instance.
*
* @return input stream
* @throws IOException if socket has been closed or cannot be created.
*/
protected InputStream getInputStream() throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
synchronized (this) {
if (fis == null) {
fis = new SocketInputStream();
}
return fis;
}
}
/**
* Retrieves the output stream for this instance.
*
* @return output stream
* @throws IOException if socket has been closed or cannot be created.
*/
protected OutputStream getOutputStream() throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
synchronized (this) {
if (fos == null) {
fos = new SocketOutputStream();
}
return fos;
}
}
/**
* Returns the number of bytes available for reading without blocking.
*
* @return >= 0 count bytes available
* @throws IOException
*/
protected int available() throws IOException
{
return getInputStream().available();
}
/**
* Shuts down the input side of the socket.
*
* @throws IOException
*/
protected void shutdownInput() throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
shutdown(fd, true);
}
/**
* Shuts down the output side of the socket.
*
* @throws IOException
*/
protected void shutdownOutput() throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
shutdown(fd, false);
}
protected FileDescriptor getFileDescriptor()
{
return fd;
}
protected boolean supportsUrgentData()
{
return false;
}
protected void sendUrgentData(int data) throws IOException
{
throw new RuntimeException ("not impled");
}
public Object getOption(int optID) throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
if (optID == SocketOptions.SO_TIMEOUT) {
return 0;
}
int value = getOption_native(fd, optID);
switch (optID)
{
case SocketOptions.SO_RCVBUF:
case SocketOptions.SO_SNDBUF:
return value;
case SocketOptions.SO_REUSEADDR:
default:
return value;
}
}
public void setOption(int optID, Object value)
throws IOException {
/*
* Boolean.FALSE is used to disable some options, so it
* is important to distinguish between FALSE and unset.
* We define it here that -1 is unset, 0 is FALSE, and 1
* is TRUE.
*/
int boolValue = -1;
int intValue = 0;
if (fd == null) {
throw new IOException("socket not created");
}
if (value instanceof Integer) {
intValue = (Integer)value;
} else if (value instanceof Boolean) {
boolValue = ((Boolean) value)? 1 : 0;
} else {
throw new IOException("bad value: " + value);
}
setOption_native(fd, optID, boolValue, intValue);
}
/**
* Enqueues a set of file descriptors to send to the peer. The queue
* is one deep. The file descriptors will be sent with the next write
* of normal data, and will be delivered in a single ancillary message.
* See "man 7 unix" SCM_RIGHTS on a desktop Linux machine.
*
* @param fds non-null; file descriptors to send.
* @throws IOException
*/
public void setFileDescriptorsForSend(FileDescriptor[] fds) {
synchronized(writeMonitor) {
outboundFileDescriptors = fds;
}
}
/**
* Retrieves a set of file descriptors that a peer has sent through
* an ancillary message. This method retrieves the most recent set sent,
* and then returns null until a new set arrives.
* File descriptors may only be passed along with regular data, so this
* method can only return a non-null after a read operation.
*
* @return null or file descriptor array
* @throws IOException
*/
public FileDescriptor[] getAncillaryFileDescriptors() throws IOException {
synchronized(readMonitor) {
FileDescriptor[] result = inboundFileDescriptors;
inboundFileDescriptors = null;
return result;
}
}
/**
* Retrieves the credentials of this socket's peer. Only valid on
* connected sockets.
*
* @return non-null; peer credentials
* @throws IOException
*/
public Credentials getPeerCredentials() throws IOException
{
return getPeerCredentials_native(fd);
}
/**
* Retrieves the socket name from the OS.
*
* @return non-null; socket name
* @throws IOException on failure
*/
public LocalSocketAddress getSockAddress() throws IOException
{
return null;
//TODO implement this
//return getSockName_native(fd);
}
@Override
protected void finalize() throws IOException {
close();
}
}
android\net\LocalSocketImpl.java文件中查找构造函数
/**
* Create a new instance from a file descriptor representing
* a bound socket. The state of the file descriptor is not checked here
* but the caller can verify socket state by calling listen().
*
* @param fd non-null; bound file descriptor
*/
/*package*/ LocalSocketImpl(FileDescriptor fd) throws IOException
{
this.fd = fd;
}
该构造函数只是将文件描述符复制个fd
接着看该函数的listen函数
protected void listen(int backlog) throws IOException
{
if (fd == null) {
throw new IOException("socket not created");
}
listen_native(fd, backlog);
}
会调用底层的
listen_native()方法
/**
* Retrieves the socket name from the OS.
*
* @return non-null; socket name
* @throws IOException on failure
*/
public LocalSocketAddress getSockAddress() throws IOException
{
return null;
//TODO implement this
//return getSockName_native(fd);
}
调用获取address方法是,该函数直接返回null;
此时,注册zygote完成。接着研究
preload();
static void preload() {
Log.d(TAG, "begin preload");
preloadClasses();
preloadResources();
preloadOpenGL();
preloadSharedLibraries();
// Ask the WebViewFactory to do any initialization that must run in the zygote process,
// for memory sharing purposes.
WebViewFactory.prepareWebViewInZygote();
Log.d(TAG, "end preload");
}
先是预加载一些类,然后预加载一些资源。预加载opengl,预加载一些共享库
/**
* Performs Zygote process initialization. Loads and initializes
* commonly used classes.
*
* Most classes only cause a few hundred bytes to be allocated, but
* a few will allocate a dozen Kbytes (in one case, 500+K).
*/
private static void preloadClasses() {
final VMRuntime runtime = VMRuntime.getRuntime();
InputStream is;
try {
is = new FileInputStream(PRELOADED_CLASSES);
} catch (FileNotFoundException e) {
Log.e(TAG, "Couldn't find " + PRELOADED_CLASSES + ".");
return;
}
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.
System.gc();
runtime.runFinalizationSync();
Debug.startAllocCounting();
try {
BufferedReader br
= new BufferedReader(new InputStreamReader(is), 256);
int count = 0;
String line;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim();
if (line.startsWith("#") || line.equals("")) {
continue;
}
try {
if (false) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line);
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (false) {
Log.v(TAG,
" GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
count++;
} catch (ClassNotFoundException e) {
Log.w(TAG, "Class not found for preloading: " + line);
} catch (UnsatisfiedLinkError e) {
Log.w(TAG, "Problem preloading " + line + ": " + e);
} catch (Throwable t) {
Log.e(TAG, "Error preloading " + line + ".", t);
if (t instanceof Error) {
throw (Error) t;
}
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
}
throw new RuntimeException(t);
}
}
Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
IoUtils.closeQuietly(is);
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
// Fill in dex caches with classes, fields, and methods brought in by preloading.
runtime.preloadDexCaches();
Debug.stopAllocCounting();
// Bring back root. We'll need it later.
setEffectiveUser(ROOT_UID);
setEffectiveGroup(ROOT_GID);
}
}
/**
* The path of a file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "/system/etc/preloaded-classes";
这个是预加载类所在的目录
/system/etc
用adb shell cat /system/etc/preloaded-classes查看。该文件中需要载入的类有
sun.util.calendar.JulianCalendar
sun.util.calendar.LocalGregorianCalendar
sun.util.locale.BaseLocale
sun.util.locale.BaseLocale$Cache
sun.util.locale.BaseLocale$Key
sun.util.locale.Extension
sun.util.locale.InternalLocaleBuilder
sun.util.locale.InternalLocaleBuilder$CaseInsensitiveChar
sun.util.locale.LanguageTag
sun.util.locale.LocaleExtensions
sun.util.locale.LocaleObjectCache
sun.util.locale.LocaleObjectCache$CacheEntry
sun.util.locale.LocaleSyntaxException
sun.util.locale.LocaleUtils
sun.util.locale.ParseStatus
sun.util.locale.StringTokenIterator
sun.util.locale.UnicodeLocaleExtension
sun.util.logging.LoggingProxy
sun.util.logging.LoggingSupport
sun.util.logging.LoggingSupport$1
sun.util.logging.PlatformLogger
sun.util.logging.PlatformLogger$1
sun.util.logging.PlatformLogger$Level等等
android.database.sqlite.SQLiteClosable
android.database.sqlite.SQLiteConnection
android.database.sqlite.SQLiteConnection$Operation
android.database.sqlite.SQLiteConnection$OperationLog
android.database.sqlite.SQLiteConnection$PreparedStatement
android.database.sqlite.SQLiteConnection$PreparedStatementCache
android.database.sqlite.SQLiteConnectionPool
android.database.sqlite.SQLiteConnectionPool$AcquiredConnectionStatus
android.database.sqlite.SQLiteConnectionPool$ConnectionWaiter
android.database.sqlite.SQLiteCursor
android.database.sqlite.SQLiteCursorDriver
android.database.sqlite.SQLiteCustomFunction
android.database.sqlite.SQLiteDatabase
android.database.sqlite.SQLiteDatabase$1
android.database.sqlite.SQLiteDatabase$CursorFactory
android.database.sqlite.SQLiteDatabaseConfiguration
android.database.sqlite.SQLiteDatabaseCorruptException
android.database.sqlite.SQLiteDatabaseLockedException
android.database.sqlite.SQLiteDebug
android.database.sqlite.SQLiteDebug$PagerStats
android.database.sqlite.SQLiteDirectCursorDriver
android.database.sqlite.SQLiteException
android.database.sqlite.SQLiteGlobal
android.database.sqlite.SQLiteOpenHelper
android.database.sqlite.SQLiteProgram
android.database.sqlite.SQLiteQuery
android.database.sqlite.SQLiteQueryBuilder
android.database.sqlite.SQLiteSession
android.database.sqlite.SQLiteSession$Transaction
如何加载类的了?
先一行行的读取这个文件
如果有注释的就跳过去
然后调用 Class.forName(line);
预加载类执行完毕
接着看preloadResources()
/**
* Load in commonly used resources, so they can be shared across
* processes.
*
* These tend to be a few Kbytes, but are frequently in the 20-40K
* range, and occasionally even larger.
*/
private static void preloadResources() {
final VMRuntime runtime = VMRuntime.getRuntime();
Debug.startAllocCounting();
try {
System.gc();
runtime.runFinalizationSync();
mResources = Resources.
mResources.startPreloading(); if (PRELOAD_RESOURCES) { Log.i(TAG, "Preloading resources..."); long startTime = SystemClock.uptimeMillis(); TypedArray ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_drawables); int N = preloadDrawables(runtime, ar); ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); startTime = SystemClock.uptimeMillis(); ar = mResources.obtainTypedArray( com.android.internal.R.array.preloaded_color_state_lists); N = preloadColorStateLists(runtime, ar); ar.recycle(); Log.i(TAG, "...preloaded " + N + " resources in " + (SystemClock.uptimeMillis()-startTime) + "ms."); } mResources.finishPreloading(); } catch (RuntimeException e) { Log.w(TAG, "Failure preloading resources", e); } finally { Debug.stopAllocCounting(); } }
/**
* Return a global shared Resources object that provides access to only
* system resources (no application resources), and is not configured for
* the current screen (can not use dimension units, does not change based
* on orientation, etc). 返回一个全局功效的Resources资源文
*/
public static Resources getSystem() {
synchronized (sSync) {
Resources ret = mSystem;
if (ret == null) {
ret = new Resources();
mSystem = ret;
}
return ret;
}
}
/**
* Start preloading of resource data using this Resources object. Only
* for use by the zygote process for loading common system resources.
* {@hide}
*/
public final void startPreloading() {
synchronized (sSync) {
if (sPreloaded) {
throw new IllegalStateException("Resources already preloaded");
}
sPreloaded = true;
mPreloading = true;
sPreloadedDensity = DisplayMetrics.DENSITY_DEVICE;
mConfiguration.densityDpi = sPreloadedDensity;
updateConfiguration(null, null);
}
} 加载资源的方式 更新屏幕密度
/**
* @hide
*/
public void updateConfiguration(Configuration config,
DisplayMetrics metrics, CompatibilityInfo compat) {
synchronized (mAccessLock) {
if (false) {
Slog.i(TAG, "**** Updating config of " + this + ": old config is "
+ mConfiguration + " old compat is " + mCompatibilityInfo);
Slog.i(TAG, "**** Updating config of " + this + ": new config is "
+ config + " new compat is " + compat);
}
if (compat != null) {
mCompatibilityInfo = compat;
}
if (metrics != null) {
mMetrics.setTo(metrics);
}
// NOTE: We should re-arrange this code to create a Display
// with the CompatibilityInfo that is used everywhere we deal
// with the display in relation to this app, rather than
// doing the conversion here. This impl should be okay because
// we make sure to return a compatible display in the places
// where there are public APIs to retrieve the display... but
// it would be cleaner and more maintainble to just be
// consistently dealing with a compatible display everywhere in
// the framework.
mCompatibilityInfo.applyToDisplayMetrics(mMetrics);
int configChanges = calcConfigChanges(config);
if (mConfiguration.locale == null) {
mConfiguration.locale = Locale.getDefault();
mConfiguration.setLayoutDirection(mConfiguration.locale);
}
if (mConfiguration.densityDpi != Configuration.DENSITY_DPI_UNDEFINED) {
mMetrics.densityDpi = mConfiguration.densityDpi;
mMetrics.density = mConfiguration.densityDpi * DisplayMetrics.DENSITY_DEFAULT_SCALE;
}
mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;
String locale = null;
if (mConfiguration.locale != null) {
locale = adjustLanguageTag(mConfiguration.locale.toLanguageTag());
}
int width, height;
if (mMetrics.widthPixels >= mMetrics.heightPixels) {
width = mMetrics.widthPixels;
height = mMetrics.heightPixels;
} else {
//noinspection SuspiciousNameCombination
width = mMetrics.heightPixels;
//noinspection SuspiciousNameCombination
height = mMetrics.widthPixels;
}
int keyboardHidden = mConfiguration.keyboardHidden;
if (keyboardHidden == Configuration.KEYBOARDHIDDEN_NO
&& mConfiguration.hardKeyboardHidden
== Configuration.HARDKEYBOARDHIDDEN_YES) {
keyboardHidden = Configuration.KEYBOARDHIDDEN_SOFT;
}
mAssets.setConfiguration(mConfiguration.mcc, mConfiguration.mnc,
locale, mConfiguration.orientation,
mConfiguration.touchscreen,
mConfiguration.densityDpi, mConfiguration.keyboard,
keyboardHidden, mConfiguration.navigation, width, height,
mConfiguration.smallestScreenWidthDp,
mConfiguration.screenWidthDp, mConfiguration.screenHeightDp,
mConfiguration.screenLayout, mConfiguration.uiMode,
Build.VERSION.RESOURCES_SDK_INT);
if (DEBUG_CONFIG) {
Slog.i(TAG, "**** Updating config of " + this + ": final config is " + mConfiguration
+ " final compat is " + mCompatibilityInfo);
}
clearDrawableCachesLocked(mDrawableCache, configChanges);
clearDrawableCachesLocked(mColorDrawableCache, configChanges);
mAnimatorCache.onConfigurationChange(configChanges);
mStateListAnimatorCache.onConfigurationChange(configChanges);
mColorStateListCache.clear();
flushLayoutCache();
}
synchronized (sSync) {
if (mPluralRule != null) {
mPluralRule = NativePluralRules.forLocale(config.locale);
}
}
}
private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
int N = ar.length();
for (int i=0; i PRELOAD_GC_THRESHOLD) {
if (false) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
System.gc();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
if (false) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
if (mResources.getDrawable(id, null) == null) {
throw new IllegalArgumentException(
"Unable to find preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ")");
}
}
}
return N;
}阅读源代码可以知道,图片是有缓存在Resources对象中
private static void preloadOpenGL() {
if (!SystemProperties.getBoolean(PROPERTY_DISABLE_OPENGL_PRELOADING, false)) {
EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY);
}
}
接着来看preloadopenGl
android\opengl\EGL14.java
public static native EGLDisplay eglGetDisplay(
int display_id
);
private static void preloadSharedLibraries() {
Log.i(TAG, "Preloading shared libraries...");
System.loadLibrary("android");
System.loadLibrary("compiler_rt");
System.loadLibrary("jnigraphics");
}
预加载库
加载android compiler_rt jnigraphics等库
WebViewFactory
/**
* Perform any WebView loading preparations that must happen in the zygote.
* Currently, this means allocating address space to load the real JNI library later.
*/
public static void prepareWebViewInZygote() {
try {
System.loadLibrary("webviewchromium_loader");
long addressSpaceToReserve =
SystemProperties.getLong(CHROMIUM_WEBVIEW_VMSIZE_SIZE_PROPERTY,
CHROMIUM_WEBVIEW_DEFAULT_VMSIZE_BYTES);
sAddressSpaceReserved = nativeReserveAddressSpace(addressSpaceToReserve);
if (sAddressSpaceReserved) {
if (DEBUG) {
Log.v(LOGTAG, "address space reserved: " + addressSpaceToReserve + " bytes");
}
} else {
Log.e(LOGTAG, "reserving " + addressSpaceToReserve +
" bytes of address space failed");
}
} catch (Throwable t) {
// Log and discard errors at this stage as we must not crash the zygote.
Log.e(LOGTAG, "error preparing native loader", t);
}
}
private static native boolean nativeReserveAddressSpace(long addressSpaceToReserve);
到此,预加载执行完毕
然后开始执行 系统服务
/**
* Prepare the arguments and fork for the system server process.
*/
private static boolean startSystemServer(String abiList, String socketName)
throws MethodAndArgsCaller, RuntimeException {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_BLOCK_SUSPEND,
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_RESOURCE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG
);
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
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);
}
handleSystemServerProcess(parsedArgs);
}
return true;
}
该函数比较复杂
需要慢慢来分析
/**
* Gets the bit array representation of the provided list of POSIX capabilities.
*/
private static long posixCapabilitiesAsBits(int... capabilities) {
long result = 0;
for (int capability : capabilities) {
if ((capability < 0) || (capability > OsConstants.CAP_LAST_CAP)) {
throw new IllegalArgumentException(String.valueOf(capability));
}
result |= (1L << capability);
}
return result;
}
/**
* Handles argument parsing for args related to the zygote spawner.
*
* Current recognized args:
*
* - --setuid=uid of child process, defaults to 0
*
- --setgid=gid of child process, defaults to 0
*
- --setgroups=comma-separated list of supplimentary gid's
*
- --capabilities=a pair of comma-separated integer strings
* indicating Linux capabilities(2) set for child. The first string
* represents the
permitted
set, and the second the
* effective
set. Precede each with 0 or
* 0x for octal or hexidecimal value. If unspecified, both default to 0.
* This parameter is only applied if the uid of the new process will
* be non-0.
* - --rlimit=r,c,mtuple of values for setrlimit() call.
*
r
is the resource, c
and m
* are the settings for current and max value.
* - --classpath=colon-separated classpath indicates
* that the specified class (which must b first non-flag argument) should
* be loaded from jar files in the specified classpath. Incompatible with
* --runtime-init
*
- --runtime-init indicates that the remaining arg list should
* be handed off to com.android.internal.os.RuntimeInit, rather than
* processed directly
* Android runtime startup (eg, Binder initialization) is also eschewed.
*
- --nice-name=nice name to appear in ps
*
- If
--runtime-init
is present:
* [--]
* - If
--runtime-init
is absent:
* [--] [args...]
* - --instruction-set=instruction-set-string which instruction set to use/emulate.
*
*/
static class Arguments {
/** from --setuid */
int uid = 0;
boolean uidSpecified;
/** from --setgid */
int gid = 0;
boolean gidSpecified;
/** from --setgroups */
int[] gids;
/**
* From --enable-debugger, --enable-checkjni, --enable-assert,
* --enable-safemode, and --enable-jni-logging.
*/
int debugFlags;
/** From --mount-external */
int mountExternal = Zygote.MOUNT_EXTERNAL_NONE;
/** from --target-sdk-version. */
int targetSdkVersion;
boolean targetSdkVersionSpecified;
/** from --classpath */
String classpath;
/** from --runtime-init */
boolean runtimeInit;
/** from --nice-name */
String niceName;
/** from --capabilities */
boolean capabilitiesSpecified;
long permittedCapabilities;
long effectiveCapabilities;
/** from --seinfo */
boolean seInfoSpecified;
String seInfo;
/** from all --rlimit=r,c,m */
ArrayList rlimits;
/** from --invoke-with */
String invokeWith;
/**
* Any args after and including the first non-option arg
* (or after a '--')
*/
String remainingArgs[];
/**
* Whether the current arguments constitute an ABI list query.
*/
boolean abiListQuery;
/**
* The instruction set to use, or null when not important.
*/
String instructionSet;
/**
* The app data directory. May be null, e.g., for the system server. Note that this might
* not be reliable in the case of process-sharing apps.
*/
String appDataDir;
/**
* Constructs instance and parses args
* @param args zygote command-line args
* @throws IllegalArgumentException
*/
Arguments(String args[]) throws IllegalArgumentException {
parseArgs(args);
}
/**
* Parses the commandline arguments intended for the Zygote spawner
* (such as "--setuid=" and "--setgid=") and creates an array
* containing the remaining args.
*
* Per security review bug #1112214, duplicate args are disallowed in
* critical cases to make injection harder.
*/
private void parseArgs(String args[])
throws IllegalArgumentException {
int curArg = 0;
for ( /* curArg */ ; curArg < args.length; curArg++) {
String arg = args[curArg];
if (arg.equals("--")) {
curArg++;
break;
} else if (arg.startsWith("--setuid=")) {
if (uidSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
uidSpecified = true;
uid = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--setgid=")) {
if (gidSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
gidSpecified = true;
gid = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.startsWith("--target-sdk-version=")) {
if (targetSdkVersionSpecified) {
throw new IllegalArgumentException(
"Duplicate target-sdk-version specified");
}
targetSdkVersionSpecified = true;
targetSdkVersion = Integer.parseInt(
arg.substring(arg.indexOf('=') + 1));
} else if (arg.equals("--enable-debugger")) {
debugFlags |= Zygote.DEBUG_ENABLE_DEBUGGER;
} else if (arg.equals("--enable-safemode")) {
debugFlags |= Zygote.DEBUG_ENABLE_SAFEMODE;
} else if (arg.equals("--enable-checkjni")) {
debugFlags |= Zygote.DEBUG_ENABLE_CHECKJNI;
} else if (arg.equals("--enable-jni-logging")) {
debugFlags |= Zygote.DEBUG_ENABLE_JNI_LOGGING;
} else if (arg.equals("--enable-assert")) {
debugFlags |= Zygote.DEBUG_ENABLE_ASSERT;
} else if (arg.equals("--runtime-init")) {
runtimeInit = true;
} else if (arg.startsWith("--seinfo=")) {
if (seInfoSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
seInfoSpecified = true;
seInfo = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--capabilities=")) {
if (capabilitiesSpecified) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
capabilitiesSpecified = true;
String capString = arg.substring(arg.indexOf('=')+1);
String[] capStrings = capString.split(",", 2);
if (capStrings.length == 1) {
effectiveCapabilities = Long.decode(capStrings[0]);
permittedCapabilities = effectiveCapabilities;
} else {
permittedCapabilities = Long.decode(capStrings[0]);
effectiveCapabilities = Long.decode(capStrings[1]);
}
} else if (arg.startsWith("--rlimit=")) {
// Duplicate --rlimit arguments are specifically allowed.
String[] limitStrings
= arg.substring(arg.indexOf('=')+1).split(",");
if (limitStrings.length != 3) {
throw new IllegalArgumentException(
"--rlimit= should have 3 comma-delimited ints");
}
int[] rlimitTuple = new int[limitStrings.length];
for(int i=0; i < limitStrings.length; i++) {
rlimitTuple[i] = Integer.parseInt(limitStrings[i]);
}
if (rlimits == null) {
rlimits = new ArrayList();
}
rlimits.add(rlimitTuple);
} else if (arg.equals("-classpath")) {
if (classpath != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
try {
classpath = args[++curArg];
} catch (IndexOutOfBoundsException ex) {
throw new IllegalArgumentException(
"-classpath requires argument");
}
} else if (arg.startsWith("--setgroups=")) {
if (gids != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
String[] params
= arg.substring(arg.indexOf('=') + 1).split(",");
gids = new int[params.length];
for (int i = params.length - 1; i >= 0 ; i--) {
gids[i] = Integer.parseInt(params[i]);
}
} else if (arg.equals("--invoke-with")) {
if (invokeWith != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
try {
invokeWith = args[++curArg];
} catch (IndexOutOfBoundsException ex) {
throw new IllegalArgumentException(
"--invoke-with requires argument");
}
} else if (arg.startsWith("--nice-name=")) {
if (niceName != null) {
throw new IllegalArgumentException(
"Duplicate arg specified");
}
niceName = arg.substring(arg.indexOf('=') + 1);
} else if (arg.equals("--mount-external-multiuser")) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER;
} else if (arg.equals("--mount-external-multiuser-all")) {
mountExternal = Zygote.MOUNT_EXTERNAL_MULTIUSER_ALL;
} else if (arg.equals("--query-abi-list")) {
abiListQuery = true;
} else if (arg.startsWith("--instruction-set=")) {
instructionSet = arg.substring(arg.indexOf('=') + 1);
} else if (arg.startsWith("--app-data-dir=")) {
appDataDir = arg.substring(arg.indexOf('=') + 1);
} else {
break;
}
}
if (runtimeInit && classpath != null) {
throw new IllegalArgumentException(
"--runtime-init and -classpath are incompatible");
}
remainingArgs = new String[args.length - curArg];
System.arraycopy(args, curArg, remainingArgs, 0,
remainingArgs.length);
}
}
Zygote.java
/**
* Special method to start the system server process. In addition to the
* common actions performed in forkAndSpecialize, the pid of the child
* process is recorded such that the death of the child process will cause
* zygote to exit.
*
* @param uid the UNIX uid that the new process should setuid() to after
* fork()ing and and before spawning any threads.
* @param gid the UNIX gid that the new process should setgid() to after
* fork()ing and and before spawning any threads.
* @param gids null-ok; a list of UNIX gids that the new process should
* setgroups() to after fork and before spawning any threads.
* @param debugFlags bit flags that enable debugging features.
* @param rlimits null-ok an array of rlimit tuples, with the second
* dimension having a length of 3 and representing
* (resource, rlim_cur, rlim_max). These are set via the posix
* setrlimit(2) call.
* @param permittedCapabilities argument for setcap()
* @param effectiveCapabilities argument for setcap()
*
* @return 0 if this is the child, pid of the child
* if this is the parent, or -1 on error.
*/
public static int forkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
VM_HOOKS.preFork();
int pid = nativeForkSystemServer(
uid, gid, gids, debugFlags, rlimits, permittedCapabilities, effectiveCapabilities);
VM_HOOKS.postForkCommon();
return pid;
}
native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
private static void callPostForkChildHooks(int debugFlags, String instructionSet) {
long startTime = SystemClock.elapsedRealtime();
VM_HOOKS.postForkChild(debugFlags, instructionSet);
checkTime(startTime, "Zygote.callPostForkChildHooks");
}
native private static int nativeForkSystemServer(int uid, int gid, int[] gids, int debugFlags,
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
* Finish remaining work for the newly forked system server process.
*/
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
closeServerSocket();
// set umask to 0077 so new files and directories will default to owner-only permissions.
Os.umask(S_IRWXG | S_IRWXO);
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");
if (systemServerClasspath != null) {
performSystemServerDexOpt(systemServerClasspath);
}
if (parsedArgs.invokeWith != null) {
String[] args = parsedArgs.remainingArgs;
// If we have a non-null system server class path, we'll have to duplicate the
// existing arguments and append the classpath to it. ART will handle the classpath
// correctly when we exec a new process.
if (systemServerClasspath != null) {
String[] amendedArgs = new String[args.length + 2];
amendedArgs[0] = "-cp";
amendedArgs[1] = systemServerClasspath;
System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);
}
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
null, args);
} else {
ClassLoader cl = null;
if (systemServerClasspath != null) {
cl = new PathClassLoader(systemServerClasspath, ClassLoader.getSystemClassLoader());
Thread.currentThread().setContextClassLoader(cl);
}
/*
* Pass the remaining arguments to SystemServer.
*/
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);
}
/* should never reach here */
}
/**
* Performs dex-opt on the elements of {@code classPath}, if needed. We
* choose the instruction set of the current runtime.
*/
private static void performSystemServerDexOpt(String classPath) {
final String[] classPathElements = classPath.split(":");
final InstallerConnection installer = new InstallerConnection();
final String instructionSet = VMRuntime.getRuntime().vmInstructionSet();
try {
for (String classPathElement : classPathElements) {
final byte dexopt = DexFile.isDexOptNeededInternal(classPathElement, "*", instructionSet,
false /* defer */);
if (dexopt == DexFile.DEXOPT_NEEDED) {
installer.dexopt(classPathElement, Process.SYSTEM_UID, false, instructionSet);
} else if (dexopt == DexFile.PATCHOAT_NEEDED) {
installer.patchoat(classPathElement, Process.SYSTEM_UID, false, instructionSet);
}
}
} catch (IOException ioe) {
throw new RuntimeException("Error starting system_server", ioe);
} finally {
installer.disconnect();
}
}
Wrappter.java
/**
* Executes a runtime application with a wrapper command.
* This method never returns.
*
* @param invokeWith The wrapper command.
* @param niceName The nice name for the application, or null if none.
* @param targetSdkVersion The target SDK version for the app.
* @param pipeFd The pipe to which the application's pid should be written, or null if none.
* @param args Arguments for {@link RuntimeInit#main}.
*/
public static void execApplication(String invokeWith, String niceName,
int targetSdkVersion, FileDescriptor pipeFd, String[] args) {
StringBuilder command = new StringBuilder(invokeWith);
command.append(" /system/bin/app_process /system/bin --application");
if (niceName != null) {
command.append(" '--nice-name=").append(niceName).append("'");
}
command.append(" com.android.internal.os.WrapperInit ");
command.append(pipeFd != null ? pipeFd.getInt$() : 0);
command.append(' ');
command.append(targetSdkVersion);
Zygote.appendQuotedShellArgs(command, args);
Zygote.execShell(command.toString());
}
/**
* Appends quotes shell arguments to the specified string builder.
* The arguments are quoted using single-quotes, escaped if necessary,
* prefixed with a space, and appended to the command.
*
* @param command A string builder for the shell command being constructed.
* @param args An array of argument strings to be quoted and appended to the command.
* @see #execShell(String)
*/
public static void appendQuotedShellArgs(StringBuilder command, String[] args) {
for (String arg : args) {
command.append(" '").append(arg.replace("'", "'\\''")).append("'");
}
}
/**
* Executes "/system/bin/sh -c " using the exec() system call.
* This method throws a runtime exception if exec() failed, otherwise, this
* method never returns.
*
* @param command The shell command to execute.
*/
public static void execShell(String command) {
String[] args = { "/system/bin/sh", "-c", command };
try {
Os.execv(args[0], args);
} catch (ErrnoException e) {
throw new RuntimeException(e);
}
}
/**
* The main function called when started through the zygote process. This
* could be unified with main(), if the native code in nativeFinishInit()
* were rationalized with Zygote startup.
*
* Current recognized args:
*
* -
[--]
*
*
* @param targetSdkVersion target SDK version
* @param argv arg strings
*/
public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)
throws ZygoteInit.MethodAndArgsCaller {
if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");
redirectLogStreams();
commonInit();
nativeZygoteInit();
applicationInit(targetSdkVersion, argv, classLoader);
}
private static final void commonInit() {
if (DEBUG) Slog.d(TAG, "Entered RuntimeInit!");
/* set default handler; this applies to all threads in the VM */
Thread.setDefaultUncaughtExceptionHandler(new UncaughtHandler());
/*
* Install a TimezoneGetter subclass for ZoneInfo.db
*/
TimezoneGetter.setInstance(new TimezoneGetter() {
@Override
public String getId() {
return SystemProperties.get("persist.sys.timezone");
}
});
TimeZone.setDefault(null);
/*
* Sets handler for java.util.logging to use Android log facilities.
* The odd "new instance-and-then-throw-away" is a mirror of how
* the "java.util.logging.config.class" system property works. We
* can't use the system property here since the logger has almost
* certainly already been initialized.
*/
LogManager.getLogManager().reset();
new AndroidConfig();
/*
* Sets the default HTTP User-Agent used by HttpURLConnection.
*/
String userAgent = getDefaultUserAgent();
System.setProperty("http.agent", userAgent);
/*
* Wire socket tagging to traffic stats.
*/
NetworkManagementSocketTagger.install();
/*
* If we're running in an emulator launched with "-trace", put the
* VM into emulator trace profiling mode so that the user can hit
* F9/F10 at any time to capture traces. This has performance
* consequences, so it's not something you want to do always.
*/
String trace = SystemProperties.get("ro.kernel.android.tracing");
if (trace.equals("1")) {
Slog.i(TAG, "NOTE: emulator trace profiling enabled");
Debug.enableEmulatorTraceOutput();
}
initialized = true;
}
初始化完毕之后
开始执行ZygoneInit.java的runselectLoop方法/**
* Runs the zygote process's select loop. Accepts new connections as
* they happen, and reads commands from connections one spawn-request's
* worth at a time.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runSelectLoop(String abiList) throws MethodAndArgsCaller {
ArrayList fds = new ArrayList();
ArrayList peers = new ArrayList();
FileDescriptor[] fdArray = new FileDescriptor[4];
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);
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) {
ZygoteConnection newPeer = acceptCommandPeer(abiList);
peers.add(newPeer);
fds.add(newPeer.getFileDescriptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
/**
* Invokes select() on the provider array of file descriptors (selecting
* for readability only). Array elements of null are ignored.
*
* @param fds non-null; array of readable file descriptors
* @return index of descriptor that is now readable or -1 for empty array.
* @throws IOException if an error occurs
*/
static native int selectReadable(FileDescriptor[] fds) throws IOException;
/**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
private static ZygoteConnection acceptCommandPeer(String abiList) {
try {
return new ZygoteConnection(sServerSocket.accept(), abiList);
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
/**
* Reads one start command from the command socket. If successful,
* a child is forked and a {@link ZygoteInit.MethodAndArgsCaller}
* exception is thrown in that child while in the parent process,
* the method returns normally. On failure, the child is not
* spawned and messages are printed to the log and stderr. Returns
* a boolean status value indicating whether an end-of-file on the command
* socket has been encountered.
*
* @return false if command socket should continue to be read from, or
* true if an end-of-file has been encountered.
* @throws ZygoteInit.MethodAndArgsCaller trampoline to invoke main()
* method in child process
*/
boolean runOnce() throws ZygoteInit.MethodAndArgsCaller {
String args[];
Arguments parsedArgs = null;
FileDescriptor[] descriptors;
long startTime = SystemClock.elapsedRealtime();
try {
args = readArgumentList();
descriptors = mSocket.getAncillaryFileDescriptors();
} catch (IOException ex) {
Log.w(TAG, "IOException on command socket " + ex.getMessage());
closeSocket();
return true;
}
checkTime(startTime, "zygoteConnection.runOnce: readArgumentList");
if (args == null) {
// EOF reached.
closeSocket();
return true;
}
/** the stderr of the most recent request, if avail */
PrintStream newStderr = null;
if (descriptors != null && descriptors.length >= 3) {
newStderr = new PrintStream(
new FileOutputStream(descriptors[2]));
}
int pid = -1;
FileDescriptor childPipeFd = null;
FileDescriptor serverPipeFd = null;
try {
parsedArgs = new Arguments(args);
if (parsedArgs.abiListQuery) {
return handleAbiListQuery();
}
if (parsedArgs.permittedCapabilities != 0 || parsedArgs.effectiveCapabilities != 0) {
throw new ZygoteSecurityException("Client may not specify capabilities: " +
"permitted=0x" + Long.toHexString(parsedArgs.permittedCapabilities) +
", effective=0x" + Long.toHexString(parsedArgs.effectiveCapabilities));
}
applyUidSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyRlimitSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyInvokeWithSecurityPolicy(parsedArgs, peer, peerSecurityContext);
applyseInfoSecurityPolicy(parsedArgs, peer, peerSecurityContext);
checkTime(startTime, "zygoteConnection.runOnce: apply security policies");
applyDebuggerSystemProperty(parsedArgs);
applyInvokeWithSystemProperty(parsedArgs);
checkTime(startTime, "zygoteConnection.runOnce: apply security policies");
int[][] rlimits = null;
if (parsedArgs.rlimits != null) {
rlimits = parsedArgs.rlimits.toArray(intArray2d);
}
if (parsedArgs.runtimeInit && parsedArgs.invokeWith != null) {
FileDescriptor[] pipeFds = Os.pipe();
childPipeFd = pipeFds[1];
serverPipeFd = pipeFds[0];
ZygoteInit.setCloseOnExec(serverPipeFd, true);
}
/**
* In order to avoid leaking descriptors to the Zygote child,
* the native code must close the two Zygote socket descriptors
* in the child process before it switches from Zygote-root to
* the UID and privileges of the application being launched.
*
* In order to avoid "bad file descriptor" errors when the
* two LocalSocket objects are closed, the Posix file
* descriptors are released via a dup2() call which closes
* the socket and substitutes an open descriptor to /dev/null.
*/
int [] fdsToClose = { -1, -1 };
FileDescriptor fd = mSocket.getFileDescriptor();
if (fd != null) {
fdsToClose[0] = fd.getInt$();
}
fd = ZygoteInit.getServerSocketFileDescriptor();
if (fd != null) {
fdsToClose[1] = fd.getInt$();
}
fd = null;
checkTime(startTime, "zygoteConnection.runOnce: preForkAndSpecialize");
pid = Zygote.forkAndSpecialize(parsedArgs.uid, parsedArgs.gid, parsedArgs.gids,
parsedArgs.debugFlags, rlimits, parsedArgs.mountExternal, parsedArgs.seInfo,
parsedArgs.niceName, fdsToClose, parsedArgs.instructionSet,
parsedArgs.appDataDir);
checkTime(startTime, "zygoteConnection.runOnce: postForkAndSpecialize");
} catch (IOException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (ErrnoException ex) {
logAndPrintError(newStderr, "Exception creating pipe", ex);
} catch (IllegalArgumentException ex) {
logAndPrintError(newStderr, "Invalid zygote arguments", ex);
} catch (ZygoteSecurityException ex) {
logAndPrintError(newStderr,
"Zygote security policy prevents request: ", ex);
}
try {
if (pid == 0) {
// in child
IoUtils.closeQuietly(serverPipeFd);
serverPipeFd = null;
handleChildProc(parsedArgs, descriptors, childPipeFd, newStderr);
// should never get here, the child is expected to either
// throw ZygoteInit.MethodAndArgsCaller or exec().
return true;
} else {
// in parent...pid of < 0 means failure
IoUtils.closeQuietly(childPipeFd);
childPipeFd = null;
return handleParentProc(pid, descriptors, serverPipeFd, parsedArgs);
}
} finally {
IoUtils.closeQuietly(childPipeFd);
IoUtils.closeQuietly(serverPipeFd);
}
}
/**
* Handles post-fork setup of child proc, closing sockets as appropriate,
* reopen stdio as appropriate, and ultimately throwing MethodAndArgsCaller
* if successful or returning if failed.
*
* @param parsedArgs non-null; zygote args
* @param descriptors null-ok; new file descriptors for stdio if available.
* @param pipeFd null-ok; pipe for communication back to Zygote.
* @param newStderr null-ok; stream to use for stderr until stdio
* is reopened.
*
* @throws ZygoteInit.MethodAndArgsCaller on success to
* trampoline to code that invokes static main.
*/
private void handleChildProc(Arguments parsedArgs,
FileDescriptor[] descriptors, FileDescriptor pipeFd, PrintStream newStderr)
throws ZygoteInit.MethodAndArgsCaller {
/**
* By the time we get here, the native code has closed the two actual Zygote
* socket connections, and substituted /dev/null in their place. The LocalSocket
* objects still need to be closed properly.
*/
closeSocket();
ZygoteInit.closeServerSocket();
if (descriptors != null) {
try {
ZygoteInit.reopenStdio(descriptors[0],
descriptors[1], descriptors[2]);
for (FileDescriptor fd: descriptors) {
IoUtils.closeQuietly(fd);
}
newStderr = System.err;
} catch (IOException ex) {
Log.e(TAG, "Error reopening stdio", ex);
}
}
if (parsedArgs.niceName != null) {
Process.setArgV0(parsedArgs.niceName);
}
if (parsedArgs.runtimeInit) {
if (parsedArgs.invokeWith != null) {
WrapperInit.execApplication(parsedArgs.invokeWith,
parsedArgs.niceName, parsedArgs.targetSdkVersion,
pipeFd, parsedArgs.remainingArgs);
} else {
RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion,
parsedArgs.remainingArgs, null /* classLoader */);
}
} else {
String className;
try {
className = parsedArgs.remainingArgs[0];
} catch (ArrayIndexOutOfBoundsException ex) {
logAndPrintError(newStderr,
"Missing required class name argument", null);
return;
}
String[] mainArgs = new String[parsedArgs.remainingArgs.length - 1];
System.arraycopy(parsedArgs.remainingArgs, 1,
mainArgs, 0, mainArgs.length);
if (parsedArgs.invokeWith != null) {
WrapperInit.execStandalone(parsedArgs.invokeWith,
parsedArgs.classpath, className, mainArgs);
} else {
ClassLoader cloader;
if (parsedArgs.classpath != null) {
cloader = new PathClassLoader(parsedArgs.classpath,
ClassLoader.getSystemClassLoader());
} else {
cloader = ClassLoader.getSystemClassLoader();
}
try {
ZygoteInit.
/**
* Invokes a static "main(argv[]) method on class "className".
* Converts various failing exceptions into RuntimeExceptions, with
* the assumption that they will then cause the VM instance to exit.
*
* @param loader class loader to use
* @param className Fully-qualified class name
* @param argv Argument vector for main()
*/
static void invokeStaticMain(ClassLoader loader,
String className, String[] argv)
throws ZygoteInit.MethodAndArgsCaller {
Class> cl;
try {
cl = loader.loadClass(className);
} 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);
}
/*
* This throw gets caught in ZygoteInit.main(), which responds
* by invoking the exception's run() method. This arrangement
* clears up all the stack frames that were required in setting
* up the process.
*/
throw new ZygoteInit.MethodAndArgsCaller(m, argv);
}
/**
* Registers a server socket for zygote command connections
*
* @throws RuntimeException when open fails
*/
private static void registerZygoteSocket(String socketName) {
if (sServerSocket == 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 {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc));
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}