5. Zygote
init.rc
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-
server
2011 6 12 5
6. Zygote
frameworks/base/cmds/app_process/app_main.cpp
int main(int argc, const char* const argv[])
{
--- init
// Next arg is startup classname or "--zygote"
if (i < argc) { Zygote
arg = argv[i++];
if (0 == strcmp("--zygote", arg)) {
bool startSystemServer = (i < argc) ?
strcmp(argv[i], "--start-system-server") == 0 : false;
setArgv0(argv0, "zygote");
set_process_name("zygote");
runtime.start("com.android.internal.os.ZygoteInit",
startSystemServer);
---
2011 6 12 6
7. Zygote
frameworks/base/core/jni/AndroidRuntime.cpp
/*
* Start the Android runtime. This involves starting the virtual machine
* and calling the "static void main(String[] args)" method in the class
* named by "className".
*/
void AndroidRuntime::start(const char* className, const bool startSystemServer)
{
---
/* start the virtual machine */
VM
if (startVm(&mJavaVM, &env) != 0)
---
/*
* Start VM. This thread becomes the main thread of the VM, and will
* not return until the VM exits.
*/
--- “com.android.internal.os.ZygoteInit”
startClass = env->FindClass(slashClassName);
---
startMeth = env->GetStaticMethodID(startClass, "main",
"([Ljava/lang/String;)V");
---
env->CallStaticVoidMethod(startClass, startMeth, strArray);
---
}
JNI Java main
2011 6 12 7
8. Zygote
AndroidRuntime.cpp JNI
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
public static void main(String argv[]) { if (argv[1].equals("true")) {
try { startSystemServer();
VMRuntime.getRuntime().setMinimumHeapSize(5 * 1024 * 1024); } else if (!argv[1].equals("false")) {
throw new RuntimeException(argv[0] + USAGE_STRING);
// Start profiling the zygote initialization. }
SamplingProfilerIntegration.start();
Log.i(TAG, "Accepting command socket connections");
registerZygoteSocket();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START, if (ZYGOTE_FORK_MODE) {
SystemClock.uptimeMillis()); runForkMode();
preloadClasses(); PreLoad } else { Fork?
preloadResources(); runSelectLoopMode();
EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END, }
SystemClock.uptimeMillis());
closeServerSocket();
// Finish profiling the zygote initialization. } catch (MethodAndArgsCaller caller) {
SamplingProfilerIntegration.writeZygoteSnapshot(); caller.run();
} catch (RuntimeException ex) {
// Do an initial gc to clean up after startup Log.e(TAG, "Zygote died with exception", ex);
gc(); closeServerSocket();
throw ex;
// If requested, start system server directly from Zygote }
if (argv.length != 2) { }
throw new RuntimeException(argv[0] + USAGE_STRING);
}
2011 6 12 8
9. dvz
dalvik/dvz
int main (int argc, const char **argv) {
---
err = zygote_run_wait(argc - 1, argv + 1, post_run_func);
system/core/libcutils/zygote.c
int zygote_run_wait(int argc, const char **argv, void (*post_run_func)(int)) /dec/socket/zygote
{
fd = socket_local_client(ZYGOTE_SOCKET,
ANDROID_SOCKET_NAMESPACE_RESERVED, AF_LOCAL);
---
// The command socket is passed to the peer as close-on-exec
// and will close when the peer dies zygote
newargv[0] = "--peer-wait";
memcpy(newargv + 1, argv, argc * sizeof(*argv));
pid = send_request(fd, 1, argc + 1, newargv);
---
}
2011 6 12 9
10. system/core/libcutils/zygote.c
static int send_request(int fd, int sendStdio, int argc, const char **argv)
{
---
struct msghdr msg;
---
ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
sendmsg
---
// replace any newlines with spaces and send the args 1.stdio
For (i = 0; i < argc; i++) {
--- 2.argv
toprint = argv[i];
--- 3.PID
ivs[0].iov_base = (char *)toprint;
ivs[0].iov_len = strlen(toprint); ( fork PID
ivs[1].iov_base = (char *)newline_string; // ”¥n”
)
ivs[1].iov_len = 1;
msg.msg_iovlen = 2;
do {
ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
} while (ret < 0 && errno == EINTR);
---
// Read the pid, as a 4-byte network-order integer
ivs[0].iov_base = &pid;
---
ret = recvmsg(fd, &msg, MSG_NOSIGNAL | MSG_WAITALL);
2011 6 12 10
11. VM
frameworks/base/core/jni/AndroidRuntime.cpp
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{
---
if (executionMode == kEMIntPortable) { JIT
opt.optionString = "-Xint:portable";
mOptions.add(opt);
} else if (executionMode == kEMIntFast) {
opt.optionString = "-Xint:fast";
mOptions.add(opt);
#if defined(WITH_JIT)
} else if (executionMode == kEMJitCompiler) {
opt.optionString = "-Xint:jit";
mOptions.add(opt);
#endif
---
/*
* Initialize the VM.
*
* The JavaVM* is essentially per-process, and the JNIEnv* is per-thread.
* If this call succeeds, the VM is ready, and we can start issuing
* JNI calls.
*/
if (JNI_CreateJavaVM(pJavaVM, pEnv, &initArgs) < 0) {
VM
---
}
2011 6 12 11
12. VM
dalvik/vm/Jni.c
jint JNI_CreateJavaVM(JavaVM** p_vm, JNIEnv** p_env, void* vm_args) VM
{
/*
* Set up structures for JNIEnv and VM.
*/
pVM = (JavaVMExt*) malloc(sizeof(JavaVMExt));
memset(pVM, 0, sizeof(JavaVMExt));
pVM->funcTable = &gInvokeInterface;
pVM->envList = pEnv;
--- VM
/* set this up before initializing VM, so it can create some JNIEnvs */
gDvm.vmList = (JavaVM*) pVM; ※
/*
* Create an env for main thread. We need to have something set up
* here because some of the class initialization we do when starting
* up the VM will call into native code.
*/ JNI
pEnv = (JNIEnvExt*) dvmCreateJNIEnv(NULL);
---
/* initialize VM */
gDvm.initializing = true;
if (dvmStartup(argc, argv, args->ignoreUnrecognized, (JNIEnv*)pEnv) != 0) { --- }
---
*p_env = (JNIEnv*) pEnv;
*p_vm = (JavaVM*) pVM;
---
}
2011 6 12 12
14. VM
struct JavaVMExt;
typedef struct JNIEnvExt {
const struct JNINativeInterface* funcTable; /* must be first */
const struct JNINativeInterface* baseFuncTable;
/* pointer to the VM we are a part of */
struct JavaVMExt* vm;
u4 envThreadId;
Thread* self;
/* if nonzero, we are in a "critical" JNI call */
int critical;
/* keep a copy of this here for speed */
bool forceDataCopy;
struct JNIEnvExt* prev;
struct JNIEnvExt* next;
} JNIEnvExt;
typedef struct JavaVMExt {
const struct JNIInvokeInterface* funcTable; /* must be first */
const struct JNIInvokeInterface* baseFuncTable;
/* if multiple VMs are desired, add doubly-linked list stuff here */
/* per-VM feature flags */
bool useChecked;
bool warnError;
bool forceDataCopy;
/* head of list of JNIEnvs associated with this VM */
JNIEnvExt* envList;
pthread_mutex_t envListLock;
} JavaVMExt;
2011 6 12 14
15. dex 1
dalvik/vm/JarFile.c
/* classes.dex
* Open a Jar file. It's okay if it's just a Zip archive without all of
* the Jar trimmings, but we do insist on finding "classes.dex" inside .odex
* or an appropriately-named ".odex" file alongside.
*
* If "isBootstrap" is not set, the optimizer/verifier regards this DEX as
* being part of a different class loader.
*/
int dvmJarFileOpen(const char* fileName, const char* odexOutputName,
JarFile** ppJarFile, bool isBootstrap)
{
---
/* First, look for a ".odex" alongside the jar file. It will
* have the same name/path except for the extension. .odex
*/
fd = openAlternateSuffix(fileName, "odex", O_RDONLY, &cachedName); .odex
---
/*
* Pre-created .odex absent or stale. Look inside the jar for a
* "classes.dex".
*/ classes.dex
entry = dexZipFindEntry(&archive, kDexInJarName);
2011 6 12 15
16. dex 2
dalvik/vm/JarFile.c
/* classes.dex
* We've found the one we want. See if there's an up-to-date copy
* in the cache.
*
* On return, "fd" will be seeked just past the "opt" header. ( fd opt
*
* If a stale .odex file is present and classes.dex exists in )
* the archive, this will *not* return an fd pointing to the ( odex
* .odex file; the fd will point into dalvik-cache like any
classes.dex fd odex
* other jar.
*/ jar )
if (odexOutputName == NULL) {
cachedName = dexOptGenerateCacheFileName(fileName,
kDexInJarName);
---
/*
* If fd points to a new file (because there was no cached version,
* or the cached version was stale), generate the optimized DEX.
* The file descriptor returned is still locked, and is positioned
* just past the optimization header.
*/
if (newFile) {
--- DEX
result = dvmOptimizeDexFile(fd, dexOffset,
dexGetZipEntryUncompLen(&archive, entry),
( odex?)
fileName,
dexGetZipEntryModTime(&archive, entry),
dexGetZipEntryCrc32(&archive, entry),
odex
isBootstrap);
---
2011 6 12 16
17. dex 3
dalvik/vm/JarFile.c
---
/*
* Map the cached version. This immediately rewinds the fd, so it
* doesn't have to be seeked anywhere in particular. mmap
*/
if (dvmDexFileOpenFromFd(fd, &pDvmDex) != 0) {
seek
---
*ppJarFile = (JarFile*) calloc(1, sizeof(JarFile));
(*ppJarFile)->archive = archive;
(*ppJarFile)->cacheFileName = cachedName;
(*ppJarFile)->pDvmDex = pDvmDex;
---
( ) dalvik/libdex/SysUtil.c
}
fd
dalvik/DvmDex.c
dvmDexFileOpenFromFd() (writable read-only) pDvmDex mmap
{
mmap
prot=PROT_READ | PROT_WRITE
SHA-1 flags=MAP_FILE | MAP_PRIVATE (copy-on-write)
mprotect
} PROT_READ
2011 6 12 17
18. dex
dalvik/vm/analysis/DexPrepare.c
/* fd
* Given a descriptor for a file with DEX data in it, produce an
* optimized version.
*
* The file pointed to by "fd" is expected to be a locked shared resource
OK
* (or private); we make no efforts to enforce multi-process correctness
* here.
*
* "fileName" is only used for debug output. "modWhen" and "crc" are stored
* in the dependency set.
* bootstrap
* The "isBootstrap" flag determines how the optimizer and verifier handle
* package-scope access checks. When optimizing, we only load the bootstrap ※
* class DEX files and the target DEX, so the flag determines whether the
* target DEX classes are given a (synthetic) non-NULL classLoader pointer.
* This only really matters if the target DEX contains classes that claim to
* be in the same package as bootstrap classes. dex
*
* The optimizer will need to load every class in the target DEX file.
* This is generally undesirable, so we start a subprocess to do the
* work and wait for it to complete.
*
* Returns "true" on success. All data will have been written to "fd".
※
*/
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap) fd
2011 6 12 18
19. dex
dalvik/vm/analysis/DexPrepare.c
bool dvmOptimizeDexFile(int fd, off_t dexOffset, long dexLength,
const char* fileName, u4 modWhen, u4 crc, bool isBootstrap)
{
---
pid = fork();
if (pid == 0) {
static const int kUseValgrind = 0;
static const char* kDexOptBin = "/bin/dexopt"; ※fork
static const char* kValgrinder = "/usr/bin/valgrind";
---
strcpy(execFile, androidRoot);
dexopt
strcat(execFile, kDexOptBin); execv()
---
if (kUseValgrind) dexopt
execv(kValgrinder, argv);
else
execv(execFile, argv);
---
} else { ※fork
---
/*
* Wait for the optimization process to finish. We go into VMWAIT
* mode here so GC suspension won't have to wait for us.
*/ VM VMWAIT
oldStatus = dvmChangeStatus(NULL, THREAD_VMWAIT);
※GC
---
}
}
2011 6 12 19
20. 1( )
dalvik/
vm/
mterp/
gen-mterp.py
config-xxx xxx
config-yyy yyy
config-portstd
rebuild.sh
xxx/ xxx xxx
yyy/ yyy yyy
c/ C
out/
OP
out
2011 6 12 20
21. dalvik/vm/mterp/out/InterpC-portstd.c
( C )
/* File: portable/entry.c */
* Main interpreter loop.
bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState)
{
#define FETCH(_offset) (pc[(_offset)])
---
#define INST_INST(_inst) ((_inst) & 0xff)
/* copy state in */
# define HANDLE_OPCODE(_op) case _op:
curMethod = interpState->method;
# define ADJUST_PC(_offset) do {
pc = interpState->pc;
pc += _offset;
fp = interpState->fp;
EXPORT_EXTRA_PC();
---
} while (false)
methodClassDex = curMethod->clazz->pDvmDex;
# define FINISH(_offset) { ADJUST_PC(_offset); break; }
---
while (1) {
---
/* fetch the next 16 bits from the instruction stream */
inst = FETCH(0);
switch (INST_INST(inst)) { PC( ) (instruction)
---
/* File: c/OP_NOP.c */
HANDLE_OPCODE(OP_NOP) OP
FINISH(1);
OP_END OP case
---
PC
---
} JNI
2011 6 12 21