Zygote (3) ZygoteInit 클래스의 기능
by Sihyeon Kim
최종 수정일 : 2019-04-03
ZygoteInit 클래스의 기능
(1) 새로운 Android 애플리케이션의 실행 요청을 받기 위한 소켓 바인딩
- registerZygoteSocket()
- UDS(Unix Domain Socket)을 사용하여 애플리케이션 실행을 위한 요청을 수신한다.
- init 프로세스가 init.rc 파일에서 app_process를 실행할 때 /dev/zygote로 등록한 socket을 사용한다.
(2) Android Application Framework에서 사용할 클래스들과 리소스의 로딩
- preloadClasses()
- preloadResources()
- 애플리케이션 프레임워크에 포함된 클래스와 플랫폼 자원(이미지, XML 정보, 문자열 등)을 미리 메모리에 로딩한다.
- 로딩된 클래스와 자원은 새로 생성되는 프로세스에서 다시 로딩하는 것이 아니라 그대로 사용되므로 애플리케이션이 빠르게 생성될 수 있다.
(3) SystemServer 시작
- startSystemServer()
- app_process를 통해 zygote를 실행할 때 인자로 전달한 –start-system-server 옵션에 의해 startSystemServer() 메소드가 호출되고, 시스템 서버를 실행한다.
- 시스템 서버는 주요한 네이티브 서비스들을 실행한다.
(SurfaceFlinger, AudioFlinger, MediaPlayerService, CameraService를 실행, com.android.server.SystemServer 클래스의 init2() 메소드 호출)
(4) 새로운 Android 애플리케이션 실행 요청에 대한 처리
- runSelectLoopMode()
- UDS를 모니터링하고 있다가 새로운 애플리케이션의 생성 요청을 받으면 이를 처리하는 루프로 진입한다.
ZygoteInit.java 코드
// 생략
/**
* Startup class for the zygote process.
*
* Pre-initializes some classes, and then waits for commands on a UNIX domain
* socket. Based on these commands, forks of child processes that inherit
* the initial state of the VM.
*
* Please see {@link ZygoteConnection.Arguments} for documentation on the
* client protocol.
*
* @hide
*/
public class ZygoteInit {
private static final String TAG = "Zygote";
private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";
private static final int LOG_BOOT_PROGRESS_PRELOAD_START = 3020;
private static final int LOG_BOOT_PROGRESS_PRELOAD_END = 3030;
/** when preloading, GC after allocating this many bytes */
private static final int PRELOAD_GC_THRESHOLD = 50000;
/** throw on missing preload, only if this looks like a developer */
private static final boolean THROW_ON_MISSING_PRELOAD =
"1".equals(SystemProperties.get("persist.service.adb.enable"));
public static final String USAGE_STRING =
" <\"true\"|\"false\" for startSystemServer>";
private static LocalServerSocket sServerSocket;
/**
* Used to pre-load resources. We hold a global reference on it so it
* never gets destroyed.
*/
private static Resources mResources;
/**
* The number of times that the main Zygote loop
* should run before calling gc() again.
*/
static final int GC_LOOP_COUNT = 10;
/**
* If true, zygote forks for each peer. If false, a select loop is used
* inside a single process. The latter is preferred.
*/
private static final boolean ZYGOTE_FORK_MODE = false;
/**
* The name of a resource file that contains classes to preload.
*/
private static final String PRELOADED_CLASSES = "preloaded-classes";
/** Controls whether we should preload resources during zygote init. */
private static final boolean PRELOAD_RESOURCES = true;
/**
* List of methods we "warm up" in the register map cache. These were
* chosen because they appeared on the stack in GCs in multiple
* applications.
*
* This is in a VM-ready format, to minimize string processing. If a
* class is not already loaded, or a method is not found, the entry
* will be skipped.
*
* This doesn't really merit a separately-generated input file at this
* time. The list is fairly short, and the consequences of failure
* are minor.
*/
private static final String[] REGISTER_MAP_METHODS = {
// (currently not doing any)
//"Landroid/app/Activity;.setContentView:(I)V",
};
/**
* 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() { // (1)
if (sServerSocket == null) {
int fileDesc;
try {
String env = System.getenv(ANDROID_SOCKET_ENV); // (1)-1
fileDesc = Integer.parseInt(env);
} catch (RuntimeException ex) {
throw new RuntimeException(
ANDROID_SOCKET_ENV + " unset or invalid", ex);
}
try {
sServerSocket = new LocalServerSocket(
createFileDescriptor(fileDesc)); // (1)-2
} catch (IOException ex) {
throw new RuntimeException(
"Error binding to local socket '" + fileDesc + "'", ex);
}
}
}
/**
* Waits for and accepts a single command connection. Throws
* RuntimeException on failure.
*/
private static ZygoteConnection acceptCommandPeer() {
try {
return new ZygoteConnection(sServerSocket.accept());
} catch (IOException ex) {
throw new RuntimeException(
"IOException during accept()", ex);
}
}
/**
* Close and clean up zygote sockets. Called on shutdown and on the
* child's exit path.
*/
static void closeServerSocket() {
try {
if (sServerSocket != null) {
sServerSocket.close();
}
} catch (IOException ex) {
Log.e(TAG, "Zygote: error closing sockets", ex);
}
sServerSocket = null;
}
private static final int UNPRIVILEGED_UID = 9999;
private static final int UNPRIVILEGED_GID = 9999;
private static final int ROOT_UID = 0;
private static final int ROOT_GID = 0;
/**
* Sets effective user ID.
*/
private static void setEffectiveUser(int uid) {
int errno = setreuid(ROOT_UID, uid);
if (errno != 0) {
Log.e(TAG, "setreuid() failed. errno: " + errno);
}
}
/**
* Sets effective group ID.
*/
private static void setEffectiveGroup(int gid) {
int errno = setregid(ROOT_GID, gid);
if (errno != 0) {
Log.e(TAG, "setregid() failed. errno: " + errno);
}
}
/**
* 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 = ZygoteInit.class.getClassLoader().getResourceAsStream( // (2)-1
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); // (2)-2
int count = 0;
String line;
String missingClasses = null;
while ((line = br.readLine()) != null) {
// Skip comments and blank lines.
line = line.trim(); // (2)-3
if (line.startsWith("#") || line.equals("")) {
continue;
}
try {
if (Config.LOGV) {
Log.v(TAG, "Preloading " + line + "...");
}
Class.forName(line); // (2)-4
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (Config.LOGV) {
Log.v(TAG,
" GC at " + Debug.getGlobalAllocSize());
}
runtime.gcSoftReferences();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
count++;
} catch (ClassNotFoundException e) {
Log.e(TAG, "Class not found for preloading: " + line);
if (missingClasses == null) {
missingClasses = line;
} else {
missingClasses += " " + line;
}
} 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);
}
}
if (THROW_ON_MISSING_PRELOAD &&
missingClasses != null) {
throw new IllegalStateException(
"Missing class(es) for preloading, update preloaded-classes ["
+ missingClasses + "]");
}
Log.i(TAG, "...preloaded " + count + " classes in "
+ (SystemClock.uptimeMillis()-startTime) + "ms.");
} catch (IOException e) {
Log.e(TAG, "Error reading " + PRELOADED_CLASSES + ".", e);
} finally {
// Restore default.
runtime.setTargetHeapUtilization(defaultUtilization);
Debug.stopAllocCounting();
// Bring back root. We'll need it later.
setEffectiveUser(ROOT_UID);
setEffectiveGroup(ROOT_GID);
}
}
}
/**
* Pre-caches register maps for methods that are commonly used.
*/
private static void cacheRegisterMaps() {
String failed = null;
int failure;
long startTime = System.nanoTime();
failure = 0;
for (int i = 0; i < REGISTER_MAP_METHODS.length; i++) {
String str = REGISTER_MAP_METHODS[i];
if (!Debug.cacheRegisterMap(str)) {
if (failed == null)
failed = str;
failure++;
}
}
long delta = System.nanoTime() - startTime;
if (failure == REGISTER_MAP_METHODS.length) {
if (REGISTER_MAP_METHODS.length > 0) {
Log.i(TAG,
"Register map caching failed (precise GC not enabled?)");
}
return;
}
Log.i(TAG, "Register map cache: found " +
(REGISTER_MAP_METHODS.length - failure) + " of " +
REGISTER_MAP_METHODS.length + " methods in " +
(delta / 1000000L) + "ms");
if (failure > 0) {
Log.i(TAG, " First failure: " + failed);
}
}
/**
* 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 {
runtime.gcSoftReferences();
runtime.runFinalizationSync();
mResources = Resources.getSystem();
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);
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);
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();
}
}
private static int preloadColorStateLists(VMRuntime runtime, TypedArray ar) {
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (Config.LOGV) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
runtime.gcSoftReferences();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
if (Config.LOGV) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
mResources.getColorStateList(id);
}
}
return N;
}
private static int preloadDrawables(VMRuntime runtime, TypedArray ar) {
int N = ar.length();
for (int i=0; i<N; i++) {
if (Debug.getGlobalAllocSize() > PRELOAD_GC_THRESHOLD) {
if (Config.LOGV) {
Log.v(TAG, " GC at " + Debug.getGlobalAllocSize());
}
runtime.gcSoftReferences();
runtime.runFinalizationSync();
Debug.resetGlobalAllocSize();
}
int id = ar.getResourceId(i, 0);
if (Config.LOGV) {
Log.v(TAG, "Preloading resource #" + Integer.toHexString(id));
}
if (id != 0) {
Drawable dr = mResources.getDrawable(id);
if ((dr.getChangingConfigurations()&~ActivityInfo.CONFIG_FONT_SCALE) != 0) {
Log.w(TAG, "Preloaded drawable resource #0x"
+ Integer.toHexString(id)
+ " (" + ar.getString(i) + ") that varies with configuration!!");
}
}
}
return N;
}
/**
* Runs several special GCs to try to clean up a few generations of
* softly- and final-reachable objects, along with any other garbage.
* This is only useful just before a fork().
*/
/*package*/ static void gc() {
final VMRuntime runtime = VMRuntime.getRuntime();
/* runFinalizationSync() lets finalizers be called in Zygote,
* which doesn't have a HeapWorker thread.
*/
runtime.gcSoftReferences();
runtime.runFinalizationSync();
runtime.gcSoftReferences();
runtime.runFinalizationSync();
runtime.gcSoftReferences();
runtime.runFinalizationSync();
}
/**
* Finish remaining work for the newly forked system server process.
*/
private static void handleSystemServerProcess(
ZygoteConnection.Arguments parsedArgs)
throws ZygoteInit.MethodAndArgsCaller {
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",
"--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",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
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 */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids, debugFlags, null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
}
return true;
}
public static void main(String argv[]) {
try {
// Start profiling the zygote initialization.
SamplingProfilerIntegration.start();
registerZygoteSocket(); // (1)
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,
SystemClock.uptimeMillis());
preloadClasses(); // (2)
//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 (argv.length != 2) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
if (argv[1].equals("true")) {
startSystemServer(); // (3)
} else if (!argv[1].equals("false")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
}
Log.i(TAG, "Accepting command socket connections");
if (ZYGOTE_FORK_MODE) {
runForkMode();
} else {
runSelectLoopMode(); // (4)
}
closeServerSocket();
} catch (MethodAndArgsCaller caller) {
caller.run();
} catch (RuntimeException ex) {
Log.e(TAG, "Zygote died with exception", ex);
closeServerSocket();
throw ex;
}
}
/**
* Runs the zygote in accept-and-fork mode. In this mode, each peer
* gets its own zygote spawner process. This code is retained for
* reference only.
*
* @throws MethodAndArgsCaller in a child process when a main() should
* be executed.
*/
private static void runForkMode() throws MethodAndArgsCaller {
while (true) {
ZygoteConnection peer = acceptCommandPeer();
int pid;
pid = Zygote.fork();
if (pid == 0) {
// The child process should handle the peer requests
// The child does not accept any more connections
try {
sServerSocket.close();
} catch (IOException ex) {
Log.e(TAG, "Zygote Child: error closing sockets", ex);
} finally {
sServerSocket = null;
}
peer.run();
break;
} else if (pid > 0) {
peer.closeSocket();
} else {
throw new RuntimeException("Error invoking fork()");
}
}
}
/**
* 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 runSelectLoopMode() throws MethodAndArgsCaller {
ArrayList<FileDescriptor> fds = new ArrayList();
ArrayList<ZygoteConnection> 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();
peers.add(newPeer);
fds.add(newPeer.getFileDesciptor());
} else {
boolean done;
done = peers.get(index).runOnce();
if (done) {
peers.remove(index);
fds.remove(index);
}
}
}
}
/**
* The Linux syscall "setreuid()"
* @param ruid real uid
* @param euid effective uid
* @return 0 on success, non-zero errno on fail
*/
static native int setreuid(int ruid, int euid);
/**
* The Linux syscall "setregid()"
* @param rgid real gid
* @param egid effective gid
* @return 0 on success, non-zero errno on fail
*/
static native int setregid(int rgid, int egid);
/**
* Invokes the linux syscall "setpgid"
*
* @param pid pid to change
* @param pgid new process group of pid
* @return 0 on success or non-zero errno on fail
*/
static native int setpgid(int pid, int pgid);
/**
* Invokes the linux syscall "getpgid"
*
* @param pid pid to query
* @return pgid of pid in question
* @throws IOException on error
*/
static native int getpgid(int pid) throws IOException;
/**
* Invokes the syscall dup2() to copy the specified descriptors into
* stdin, stdout, and stderr. The existing stdio descriptors will be
* closed and errors during close will be ignored. The specified
* descriptors will also remain open at their original descriptor numbers,
* so the caller may want to close the original descriptors.
*
* @param in new stdin
* @param out new stdout
* @param err new stderr
* @throws IOException
*/
static native void reopenStdio(FileDescriptor in,
FileDescriptor out, FileDescriptor err) throws IOException;
/**
* Calls close() on a file descriptor
*
* @param fd descriptor to close
* @throws IOException
*/
static native void closeDescriptor(FileDescriptor fd)
throws IOException;
/**
* Toggles the close-on-exec flag for the specified file descriptor.
*
* @param fd non-null; file descriptor
* @param flag desired close-on-exec flag state
* @throws IOException
*/
static native void setCloseOnExec(FileDescriptor fd, boolean flag)
throws IOException;
/**
* Retrieves the permitted capability set from another process.
*
* @param pid >=0 process ID or 0 for this process
* @throws IOException on error
*/
static native long capgetPermitted(int pid)
throws IOException;
/**
* Sets the permitted and effective capability sets of this process.
*
* @param permittedCapabilities permitted set
* @param effectiveCapabilities effective set
* @throws IOException on error
*/
static native void setCapabilities(
long permittedCapabilities,
long effectiveCapabilities) throws IOException;
/**
* 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;
/**
* Creates a file descriptor from an int fd.
*
* @param fd integer OS file descriptor
* @return non-null; FileDescriptor instance
* @throws IOException if fd is invalid
*/
static native FileDescriptor createFileDescriptor(int fd)
throws IOException;
/**
* Class not instantiable.
*/
private ZygoteInit() {
}
/**
* Helper exception class which holds a method and arguments and
* can call them. This is used as part of a trampoline to get rid of
* the initial process setup stack frames.
*/
public static class MethodAndArgsCaller extends Exception
implements Runnable {
/** method to call */
private final Method mMethod;
/** argument array */
private final String[] mArgs;
public MethodAndArgsCaller(Method method, String[] args) {
mMethod = method;
mArgs = args;
}
public void run() {
try {
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);
}
}
}
}
(1) /dev/socket/zygote 소켓 바인딩
registerZygoteSocket()
- ZygoteInit 클래스의 main method가 가장 먼저 호출하는 함수이다.
- 소켓은 부팅과정에서 init 프로세서에 의해 생성된다. init.rc 파일에 소켓 생성에 관련된 부분이 기술되어 있다.
(1)-1 System.getenv() 메소드 호출
init 프로세스가 ANDROID_SOCKET_zygote 환경변수로 등록한 소켓의 파일 디스크립터를 가져온다.
애플리케이션 프레임워크에서 소켓 파일 디스크립터 값을 이용해 LocalServerSocket 클래스의 인스턴스를 생성하고 /dev/socket/zygote와 바인딩한다.
생성된 LocalServerSocket 인스턴스는 이후 ZygoteInit 객체로 요청되는 새로운 안드로이드 프로세스 생성 메시지를 수신하는데 쓴다.
(2) 애플리케이션 프레임워크에 속한 클래스와 플랫폼 자원의 로딩
preloadClasses()
preloadResources()
// android froyo
// frameworks/base/preloadded-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
// 생략
dalvik.system.VMRuntime
dalvik.system.VMStack
dalvik.system.Zygote
java.beans.PropertyChangeEvent
java.beans.PropertyChangeListener
java.beans.PropertyChangeSupport
// 생략
(3) 시스템 서버 실행
startSystemServer()
(4) 새로운 안드로이드 애플리케이션 실행
runSelectLoopMode()
Subscribe via RSS