編輯:關於Android編程
Zygote意為“受精卵”,即所有的Android進程都是由其“發育”而來的意思。本人試著把啃代碼學到的內容整理出來,希望會對自己以後有用。
Init進程是Linux系統上的第一個用戶進程,用戶為root,擁有系統中最高的權限。它會實現掛載文件系統,並啟動ServiceManager/Zygote兩大進程,正式這兩大進程構成了Android系統的基礎。
簡單來說,Init進程通過解析init.rc文件,然後執行自己的工作,其中,Zygote進程相關的配置如下(system/core/rootdir/init.rc):
service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server class main socket zygote stream 660 root system onrestart write /sys/android_power/request_state wake onrestart write /sys/power/state on onrestart restart media onrestart restart netd從上面,可以看到Zygote實際上執行的是/system/bin/app_process應用程序,參數中包含了--zygote和--start-system-server。 另外,
socket zygote stream 660 root system屬性的配置,說明Init進程在啟動zygote進程時,會為zygote進程創建一個stream型的domain socket,並保存到zygote系統變量中。(對這個過程涉及到init進程的實現,可以參考代碼system/core/init/init.c)
int main(int argc, const char* const argv[]) { // These are global variables in ProcessState.cpp mArgC = argc; mArgV = argv; mArgLen = 0; for (int i=0; i main函數首先分析參數,決定下一步要做什麼:while (i < argc) { const char* arg = argv[i++]; if (!parentDir) { parentDir = arg; } else if (strcmp(arg, "--zygote") == 0) { zygote = true; niceName = "zygote"; } else if (strcmp(arg, "--start-system-server") == 0) { startSystemServer = true; } else if (strcmp(arg, "--application") == 0) { application = true; } else if (strncmp(arg, "--nice-name=", 12) == 0) { niceName = arg + 12; } else { className = arg; break; } }上一步中,我們知道Init進程啟動Zygote進程的時候,包含了--zygote參數和--start-system-server參數,所以,現在zygote和startSystemServer都為true,所以接下來執行:if (zygote) { runtime.start("com.android.internal.os.ZygoteInit", startSystemServer ? "start-system-server" : ""); } else if (className) { ...... } else { ...... }這次的參數分別為“com.android.internal.os.ZygoteInit”和“start-system-server”。AndroidRunTime
runtime的類型為AppRunTime,AppRumTime的定義(frameworks/base/cmds/app_process/app_main.cpp):class AppRuntime : public AndroidRuntime { public: AppRuntime() : mParentDir(NULL) , mClassName(NULL) , mClass(NULL) , mArgC(0) , mArgV(NULL) { } #if 0 // this appears to be unused const char* getParentDir() const { return mParentDir; } #endif const char* getClassName() const { return mClassName; } virtual void onVmCreated(JNIEnv* env) { if (mClassName == NULL) { return; // Zygote. Nothing to do here. } /* * This is a little awkward because the JNI FindClass call uses the * class loader associated with the native method we're executing in. * If called in onStarted (from RuntimeInit.finishInit because we're * launching "am", for example), FindClass would see that we're calling * from a boot class' native method, and so wouldn't look for the class * we're trying to look up in CLASSPATH. Unfortunately it needs to, * because the "am" classes are not boot classes. * * The easiest fix is to call FindClass here, early on before we start * executing boot class Java code and thereby deny ourselves access to * non-boot classes. */ char* slashClassName = toSlashClassName(mClassName); mClass = env->FindClass(slashClassName); if (mClass == NULL) { ALOGE("ERROR: could not find class '%s'\n", mClassName); } free(slashClassName); mClass = reinterpret_cast可以看到,AppRuntime並沒有重載start函數,所以,目前會執行其父類AndroidRuntime的start函數(frameworks/base/core/jni/AndroidRunTime.cpp):(env->NewGlobalRef(mClass)); } virtual void onStarted() { sp proc = ProcessState::self(); ALOGV("App process: starting thread pool.\n"); proc->startThreadPool(); AndroidRuntime* ar = AndroidRuntime::getRuntime(); ar->callMain(mClassName, mClass, mArgC, mArgV); IPCThreadState::self()->stopProcess(); } virtual void onZygoteInit() { sp proc = ProcessState::self(); ALOGV("App process: starting thread pool.\n"); proc->startThreadPool(); } virtual void onExit(int code) { if (mClassName == NULL) { // if zygote IPCThreadState::self()->stopProcess(); } AndroidRuntime::onExit(code); } const char* mParentDir; const char* mClassName; jclass mClass; int mArgC; const char* const* mArgV; }; } /* * 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". * * Passes the main function two arguments, the class name and the specified * options string. */ void AndroidRuntime::start(const char* className, const char* options) { ALOGD("\n>>>>>> AndroidRuntime START %s <<<<<<\n", className != NULL ? className : "(unknown)"); blockSigpipe(); /* * 'startSystemServer == true' means runtime is obsolete and not run from * init.rc anymore, so we print out the boot start event here. */ if (strcmp(options, "start-system-server") == 0) { /* track our progress through the boot sequence */ const int LOG_BOOT_PROGRESS_START = 3000; LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START, ns2ms(systemTime(SYSTEM_TIME_MONOTONIC))); } const char* rootDir = getenv("ANDROID_ROOT"); if (rootDir == NULL) { rootDir = "/system"; if (!hasDir("/system")) { LOG_FATAL("No root directory specified, and /android does not exist."); return; } setenv("ANDROID_ROOT", rootDir, 1); } //const char* kernelHack = getenv("LD_ASSUME_KERNEL"); //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack); /* start the virtual machine */ JNIEnv* env; if (startVm(&mJavaVM, &env) != 0) { return; } onVmCreated(env); /* * Register android functions. */ if (startReg(env) < 0) { ALOGE("Unable to register all android natives\n"); return; } /* * We want to call main() with a String array with arguments in it. * At present we have two arguments, the class name and an option string. * Create an array to hold them. */ jclass stringClass; jobjectArray strArray; jstring classNameStr; jstring optionsStr; stringClass = env->FindClass("java/lang/String"); assert(stringClass != NULL); strArray = env->NewObjectArray(2, stringClass, NULL); assert(strArray != NULL); classNameStr = env->NewStringUTF(className); assert(classNameStr != NULL); env->SetObjectArrayElement(strArray, 0, classNameStr); optionsStr = env->NewStringUTF(options); env->SetObjectArrayElement(strArray, 1, optionsStr); /* * Start VM. This thread becomes the main thread of the VM, and will * not return until the VM exits. */ char* slashClassName = toSlashClassName(className); jclass startClass = env->FindClass(slashClassName); if (startClass == NULL) { ALOGE("JavaVM unable to locate class '%s'\n", slashClassName); /* keep going */ } else { jmethodID startMeth = env->GetStaticMethodID(startClass, "main", "([Ljava/lang/String;)V"); if (startMeth == NULL) { ALOGE("JavaVM unable to find main() in '%s'\n", className); /* keep going */ } else { env->CallStaticVoidMethod(startClass, startMeth, strArray); #if 0 if (env->ExceptionCheck()) threadExitUncaughtException(env); #endif } } free(slashClassName); ALOGD("Shutting down VM\n"); if (mJavaVM->DetachCurrentThread() != JNI_OK) ALOGW("Warning: unable to detach main thread\n"); if (mJavaVM->DestroyJavaVM() != 0) ALOGW("Warning: VM did not shut down cleanly\n"); }AndroidRumTime.start主要做了三項工作: startVM啟動Android虛擬機,並呼叫onVMCreate函數。前面,我們有看到AppRuntime有重載onVMCreated函數(不過,對於app_process作為Zygote進程啟動的情況,不做任何處理就返回)startReg注冊需要的Native函數,基本Android的每個模塊都有一些native實現需要和Java代碼關聯起來,事先注冊能夠提高性能,(另一種方案是第一次調用的時候查找實現函數並完成注冊)查找className指定的class的main函數,並以options為參數,調用main函數。這裡不對代碼做解釋,用java寫過反射調用的人應該基本能看懂。 runtime.start最後會調用ZygoteIni的.main函數,參數為--start-system-server。 因為已經啟動了java虛擬機,接下來可以執行Java代碼了,所以,我們將進入下一個世界。ZygoteInit
ZygoteInit從類名看,從現在開始是真正的“Zygote”(frameworks/base/core/java/com/android/internal/os/ZygoteInit.java):public static void main(String argv[]) { try { // Start profiling the zygote initialization. SamplingProfilerIntegration.start(); registerZygoteSocket(); 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(); // If requested, start system server directly from Zygote if (argv.length != 2) { throw new RuntimeException(argv[0] + USAGE_STRING); } if (argv[1].equals("start-system-server")) { startSystemServer(); } else if (!argv[1].equals("")) { throw new RuntimeException(argv[0] + USAGE_STRING); } Log.i(TAG, "Accepting command socket connections"); if (ZYGOTE_FORK_MODE) { runForkMode(); } else { runSelectLoopMode(); } closeServerSocket(); } catch (MethodAndArgsCaller caller) { caller.run(); } catch (RuntimeException ex) { Log.e(TAG, "Zygote died with exception", ex); closeServerSocket(); throw ex; } }從代碼看,main函數完成了四項工作: registerZygoteSocketpreloadstartSystemServer,因為之前AndroidRuntime是以“start-system-server”為參數調用main函數的,所以這裡會執行startSystemServerrunSelectLoopMode,ZYGOTE_FORK_MODE常量恆定為false,所以會執行runSelectLoopMode 再繼續分析前,先要做點鋪墊。Zygote進程的作用是為了fork出新的Android進程,那Zygote是如何得知它需要fork一個進程的呢? 先看一段“客戶端“的代碼(frameworks/base/core/java/android/os/Process.java):public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int mountExternal, int targetSdkVersion, String seInfo, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, mountExternal, targetSdkVersion, seInfo, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } } private static void openZygoteSocketIfNeeded() throws ZygoteStartFailedEx { int retryCount; if (sPreviousZygoteOpenFailed) { /* * If we've failed before, expect that we'll fail again and * don't pause for retries. */ retryCount = 0; } else { retryCount = 10; } /* * See bug #811181: Sometimes runtime can make it up before zygote. * Really, we'd like to do something better to avoid this condition, * but for now just wait a bit... */ for (int retry = 0 ; (sZygoteSocket == null) && (retry < (retryCount + 1)) ; retry++ ) { if (retry > 0) { try { Log.i("Zygote", "Zygote not up yet, sleeping..."); Thread.sleep(ZYGOTE_RETRY_MILLIS); } catch (InterruptedException ex) { // should never happen } } try { sZygoteSocket = new LocalSocket(); sZygoteSocket.connect(new LocalSocketAddress(ZYGOTE_SOCKET, LocalSocketAddress.Namespace.RESERVED)); sZygoteInputStream = new DataInputStream(sZygoteSocket.getInputStream()); sZygoteWriter = new BufferedWriter( new OutputStreamWriter( sZygoteSocket.getOutputStream()), 256); Log.i("Zygote", "Process: zygote socket opened"); sPreviousZygoteOpenFailed = false; break; } catch (IOException ex) { if (sZygoteSocket != null) { try { sZygoteSocket.close(); } catch (IOException ex2) { Log.e(LOG_TAG,"I/O exception on close after exception", ex2); } } sZygoteSocket = null; } } if (sZygoteSocket == null) { sPreviousZygoteOpenFailed = true; throw new ZygoteStartFailedEx("connect failed"); } } private static ProcessStartResult zygoteSendArgsAndGetResult(ArrayList這段代碼是framework層通知Zygote進程進行fork用的,代碼略長,也沒有必要仔細分析,只要看明白一點即可: framework層會通過Unix domain socket連接到Zygote進程,並發送字符串型的參數。args) throws ZygoteStartFailedEx { openZygoteSocketIfNeeded(); try { sZygoteWriter.write(Integer.toString(args.size())); sZygoteWriter.newLine(); int sz = args.size(); for (int i = 0; i < sz; i++) { String arg = args.get(i); if (arg.indexOf('\n') >= 0) { throw new ZygoteStartFailedEx( "embedded newlines not allowed"); } sZygoteWriter.write(arg); sZygoteWriter.newLine(); } sZygoteWriter.flush(); // Should there be a timeout on this? ProcessStartResult result = new ProcessStartResult(); result.pid = sZygoteInputStream.readInt(); if (result.pid < 0) { throw new ZygoteStartFailedEx("fork() failed"); } result.usingWrapper = sZygoteInputStream.readBoolean(); return result; } catch (IOException ex) { try { if (sZygoteSocket != null) { sZygoteSocket.close(); } } catch (IOException ex2) { // we're going to fail anyway Log.e(LOG_TAG,"I/O exception on routine close", ex2); } sZygoteSocket = null; throw new ZygoteStartFailedEx(ex); } } registerZygoteSocket
剛才有看到Process會通過domain socket連接到Zygote進程,那Zygote進程理所當然的要監聽這個socket端口了,這就是registerZygoteSocket函數要做的(或者說它做了一半):/** * Registers a server socket for zygote command connections * * @throws RuntimeException when open fails */ private static void registerZygoteSocket() { if (sServerSocket == null) { int fileDesc; try { String env = System.getenv(ANDROID_SOCKET_ENV); fileDesc = Integer.parseInt(env); } catch (RuntimeException ex) { throw new RuntimeException( ANDROID_SOCKET_ENV + " unset or invalid", ex); } try { sServerSocket = new LocalServerSocket( createFileDescriptor(fileDesc)); } catch (IOException ex) { throw new RuntimeException( "Error binding to local socket '" + fileDesc + "'", ex); } } }registerZygoteSocket函數中,先從private static final String ANDROID_SOCKET_ENV = "ANDROID_SOCKET_zygote";環境變量中,讀取一個int值作為文件描述父,創建文件描述符,為什麼呢? 如果還記得init.rc的配置到話,一切就可以解釋了:socket zygote stream 660 root systeminit進程在處理這一行屬性時,會先在dev/socket下創建一個名為zygote的設備文件,然後打開這個文件,並把文件描述符保存到環境變量中。環境變量名的規則為"ANDROID_SOCKET_"+第一參數(這裡為zygote)。所以,app_main就可以通過指定的環境變量來獲取這個文件描述符,並由此創建LocalServerSocket。preload
因為所有的Android進程都是從Zygote進程fork出來的,而子進程會繼承Zygote的資源。換言之,如果Zygote進程持有了資源,所有android進程就都有了。所以,Zygote會預加載一些系統資源,以加速子進程的啟動速度(子進程以寫時復制的機制進行資源共享,所以不必擔心內存占用問題。另外Android系統的啟動時間可能有大概40%的時間耗費在這個preload函數上,但是從整體上來說,這是利大於弊的)。
static void preload() { preloadClasses();//加載java類 preloadResources();//加載icon,字串等資源 }startSystemServer
Zygote進程啟動以後,會由Zygote進程fork出SystemServer,繼續啟動系統:/** * 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,1018,3001,3002,3003,3006,3007", "--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); 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) { handleSystemServerProcess(parsedArgs); } return true; }首先,確定SystemServer的啟動參數,然後,調用Zygote.forkSysetmServer函數,開始執行fork操作。這一塊和SystemServer的啟動過程比較相關,留待後面的文章分析。暫時只需要記住SystemServer是在這個位置啟動的就好,其他的可以暫時忽略。
runSelectLoopMode
前面,我們已經得到LocalServerLocket了,現在需要開始監聽LocalServerScoket了:/** * 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其中,selectReadable函數為native函數,實現了一個多連接的等待功能: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) {//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); } } } } static jint com_android_internal_os_ZygoteInit_selectReadable ( JNIEnv *env, jobject clazz, jobjectArray fds) { if (fds == NULL) { jniThrowNullPointerException(env, "fds == null"); return -1; } jsize length = env->GetArrayLength(fds); fd_set fdset;//fdset為連接集合 if (env->ExceptionOccurred() != NULL) { return -1; } FD_ZERO(&fdset);//清空fdset int nfds = 0; for (jsize i = 0; i < length; i++) {//把所有的連接保存到fdset中 jobject fdObj = env->GetObjectArrayElement(fds, i); if (env->ExceptionOccurred() != NULL) { return -1; } if (fdObj == NULL) { continue; } int fd = jniGetFDFromFileDescriptor(env, fdObj); if (env->ExceptionOccurred() != NULL) { return -1; } FD_SET(fd, &fdset);//保存fd到fdset中 if (fd >= nfds) { nfds = fd + 1; } } int err; do { err = select (nfds, &fdset, NULL, NULL, NULL);//fdset中任何一個連接有數據可讀,則返回該連接在fdset的序號,否則等待 } while (err < 0 && errno == EINTR); if (err < 0) { jniThrowIOException(env, errno); return -1; } for (jsize i = 0; i < length; i++) { jobject fdObj = env->GetObjectArrayElement(fds, i); if (env->ExceptionOccurred() != NULL) { return -1; } if (fdObj == NULL) { continue; } int fd = jniGetFDFromFileDescriptor(env, fdObj); if (env->ExceptionOccurred() != NULL) { return -1; } if (FD_ISSET(fd, &fdset)) { return (jint)i;//返回有數據可讀到連接的序號 } } return -1; }有數據可讀的連接的序號為0,則為監聽端口,應該accept這個連接:/** * 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); } }有數據可讀的連接的序號不為0,則說明有fork指令過來,執行ZygoteConnection.runOnce(),並視需要移除這個連接。這個過程和App進程的啟動相關,之後的文章會做進一步分析。總結
Zygote進程由Init進程啟動Zygote進程的實體為app_processZygote初始化Android虛擬機以後,開始執行Java類ZygoteInitZygoteInit會啟動SystemServerZygoteInit會監聽domain socket “dev/socket/zygote”,以執行framework層指定的fork操作
Android的traces.txt文件記錄了進程的堆棧情況,對於分析應用響應慢,以及ANR的原因很有幫助。traces.txt文件的位置位於/data/anr/。1.
一 、搭建Android Studio 安裝環境 二、安裝版本控制工具搭建好Android Studio後,開發過程中我們通常會用到版本控制工具來管理我們的代碼
Android Studio怎麼查看程序安全碼獲取SHA1值? Android Studio程序安全碼怎麼用?我們使用百度地圖SDK進行Android開發,
在默認情況下,在android下默認只打印info級別的日志信息,所以在默認情況只能打印ormlite中創建數據庫表的語句,而對於數據的插入和查詢等sql語句是不會打印出