Android开机流程分析 -- Zygote

news/2024/11/8 7:34:40/

一、第一个Dalvik虚拟机Zygote

    为什么将Zygote叫做受精卵呢?是因为在Android系统中,所有的应用程序进程以及系统服务进程SystemServer都是由Zygote进程孕育(fork)出来的。

    Android应用程序是由java语言编写的,运行于各自独立的Dalvik虚拟机中。那么Android的处理机制是什么呢?Android首先创建一个Zygote虚拟机,然后通过它孵化出其他的虚拟机进程,进而共享虚拟机内存和框架层资源,这样大幅度提高了应用程序启动和运行速度。

1、Zygote配置

   zygote是在init.rc中定义的守护进程服务(Daemon Service),在Android5.0中,Zygote的启动发生了一些变化,以前直接放在init.rc中的代码块放到了单独的文件中,在init.rc中通过import的方式引入文件,如下:

import /init.${ro.zygote}.rc

   从上面的语句可以看出,init.rc并不是直接引入某个固定的文件,而是根据属性“ro.zygote”的内容来引入不同的文件。这是因为Android从5.0开始,Android开始支持64位的编译,Zygote本身也会有32位和64位版本的区别,因此,这里通过ro.zygote属性来控制启动不同版本的Zygote进程。该文件位于system/core/init/下,目录下有Init.zygote64.rc,Init.zygote32.rc,Init.zygote32_64.rc,Init.zygot64_32.rc,至于调用哪个是由硬件决定的,这里以Init.zygote64_32.rc为例,代码如下:

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygote
       class main             #class是一个Option,指定zygote服务的类型是main
    socket zygote stream 660 root system         #socket是一个Option,创建一个Socket名为dev/socket/zygote,Socket类型是stream,权限为660
    onrestart write /sys/android_power/request_state wake   #onrestart是一个Option,设置zygote重启时需要执行的Command
    onrestart write /sys/power/state on
    onrestart restart media
    onrestart restart netd

service zygote_secondary /system/bin/app_process32 -Xzygote /system/bin--zygote --socket-name=zygote_secondary
    class main
    socket zygote_secondary stream 660 root system
    onrestart restart zygote

   从代码中我们可以看出,系统定义了两个Zygote服务:zygote和zygote_secondary。这两个服务最大的区别是启动的可执行文件不同,一个是app_process64,另一个是app_process32。由关键字service告诉init进程创建一个名为“zygote”的进程,这个zygote进程要执行的程序是/system/bin/app_process64(可执行文件),给这个zygote进程传递了5个参数,分别是-Xzygote,/system/bin,--zygote,--start-system-server,--socket-name=zygote;另外zygote服务需要开启一个Socket,并且zygote重启时,需要执行下面的命令:

    修改/sys/android_power/request_state;修改/sys/power/state;重启media;重启netd。

2、zygote的执行

我们先看下Zygote进程的初始化过程,如下:



从上面的分析中可以看出zygote要执行的程序是/system/bin/app_process,它的源码位于frameworks/base/cmds/app_process/App_main.cpp文件中,入口函数是main。

main函数的主要功能是解析启动参数。    

在源码中提供了app_process的启动方式,参数如下:

        app_process [java-options(虚拟机参数)] cmd-dir(运行目录) start-calss-name(java类) [options(参数)]

[java-options]:指定启动dalvik虚拟机时传递给虚拟机的参数,以“-”开头;

cmd-dir:要运行的进程所在的目录,通常是/system/bin;

start-class-name:指定要加载到虚拟机的类,而后调用其main方法;使用参数“--zygote”时会直接执行ZygoteInit类。

[options]:指定传递给类的参数;以符号“--”开头。参数“--zygote”表示要启动zygote进程。参数“--application”表示要以普通进程的方式执行java代码。

    我们看下App_main中main函数的代码,AppRuntime是在app_process中定义的类,继承了系统的AndroidRuntime类。AndroidRuntime类的主要作用是创建和初始化虚拟机。

int main(int argc, char* const argv[])
{if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0) < 0) {// Older kernels don't understand PR_SET_NO_NEW_PRIVS and return// EINVAL. Don't die on such kernels.if (errno != EINVAL) {LOG_ALWAYS_FATAL("PR_SET_NO_NEW_PRIVS failed: %s", strerror(errno));return 12;}}AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;   /* 因为argv[0]不是参数,所以忽略argv[0],并将argc减1*/// Everything up to '--' or first non '-' arg goes to the vm.//// The first argument after the VM args is the "parent dir", which// is currently unused.//// After the parent dir, we expect one or more the following internal// arguments ://// --zygote : Start in zygote mode// --start-system-server : Start the system server.// --application : Start in application (stand alone, non zygote) mode.// --nice-name : The nice name for this process.//// For non zygote starts, these arguments will be followed by// the main class name. All remaining arguments are passed to// the main method of this class.//// For zygote starts, all remaining arguments are passed to the zygote.// main function.//// Note that we must copy argument string values since we will rewrite the// entire argument block when we apply the nice name to argv0.int i;for (i = 0; i < argc; i++) {if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;++i;  // Skip unused "parent dir" argument.while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) { /* 如果参数中指定了--zygote,则启动zygote,niceName设置为“zygote” */zygote = true;niceName = ZYGOTE_NICE_NAME;} else if (strcmp(arg, "--start-system-server") == 0) {startSystemServer = true;   /* 是否启动System Server */} else if (strcmp(arg, "--application") == 0) {application = true; /* 是否启动application */} else if (strncmp(arg, "--nice-name=", 12) == 0) {niceName.setTo(arg + 12);   /* 是否指定了nice name */} else if (strncmp(arg, "--", 2) != 0) {className.setTo(arg);break;} else {--i;break;}}Vector<String8> args;if (!className.isEmpty()) {// We're not in zygote mode, the only argument we need to pass// to RuntimeInit is the application argument.//// The Remainder of args get passed to startup class main(). Make// copies of them before we overwrite them with the process name.args.add(application ? String8("application") : String8("tool"));//非zygote模式runtime.setClassNameAndArgs(className, argc - i, argv + i);} else {// We're in zygote mode.maybeCreateDalvikCache();if (startSystemServer) {args.add(String8("start-system-server"));}char prop[PROP_VALUE_MAX];if (property_get(ABI_LIST_PROPERTY, prop, NULL) == 0) {LOG_ALWAYS_FATAL("app_process: Unable to determine ABI list from property %s.",ABI_LIST_PROPERTY);return 11;}String8 abiFlag("--abi-list=");abiFlag.append(prop);args.add(abiFlag);// In zygote mode, pass all remaining arguments to the zygote// main() method.for (; i < argc; ++i) {args.add(String8(argv[i]));}}if (!niceName.isEmpty()) {runtime.setArgv0(niceName.string());set_process_name(niceName.string());/* 根据nicename调用系统函数prctl,open,write修改进程名,由于启动过程中指定了--zygote参数,所以这里的进程名是zygote */}/* if-else用于匹配两种启动类型,启动zygote或者是启动application */if (zygote) {   /* 如果参数中指定了--zygote,则启动zygote */runtime.start("com.android.internal.os.ZygoteInit", args);} else if (className) {   /* 启动应用程序 */runtime.start("com.android.internal.os.RuntimeInit", args);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");return 10;}
}

    从App_main的main函数中可以看出调用AppRuntime的start方法启动ZygoteInit。

二、ZygoteInit启动过程

    AppRuntime的实现代码位于frameworks\base\cmds\app_process\App_main.cpp中,它是AndroidRuntime的派生类,其start方法便是继承自AndroidRuntime。

    AndroidRuntime负责开启Android运行时环境,代码位于framewroks\base\core\jni\AndroidRuntime.cpp,定位到start方法,代码如下:

/** 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.*/ 
/* 根据上面的分析,init.rc中指定了参数--zygote,那么传入到这个函数的参数分别是:classname=“com.android.internal.os.ZygoteInit;options=start-system-server” */
void AndroidRuntime::start(const char* className, const Vector<String8>& options)
{ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());static const String8 startSystemServer("start-system-server");/** 'startSystemServer == true' means runtime is obsolete and not run from* init.rc anymore, so we print out the boot start event here.*/for (size_t i = 0; i < options.size(); ++i) {if (options[i] == startSystemServer) {/* 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 开启虚拟机*/JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (<span style="color:#3333ff;">startVm</span>(&mJavaVM, &env) != 0) {return;}/* 开启虚拟机后执行的工作,这里是空函数体,什么都没做。运行时实际调用的是AppRuntime的onVmCreated函数,保存了类的全局引用。*/onVmCreated(env);/** Register android functions.注册Android JNI函数*/if (<span style="color:#3333ff;">startReg</span>(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.调用指定类的main函数,并传入参数classname和options。在这之前需要将参数classname和options通过JNI函数转化为JAVA可识别的参数类型。*/jclass stringClass;jobjectArray strArray;jstring classNameStr;   /* 用于转换classname为java可识别类型的字符串 */stringClass = env->FindClass("java/lang/String");assert(stringClass != NULL);strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);/* 调用JNI函数生成一个对象数组,数组大小为options.size()+1(这里为2,只有一个参数start-system-server),存放字符串类型的元素 */assert(strArray != NULL);classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr); /* 将转化后的classname存入数组 */for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);/* 将转化后的options存入数组 */}/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*//* 将classname转化为slash格式的字符串,以符合JNI的命名规则。这里将com.android.internal.os.ZygoteInit转化为com/android/internal/os/ZygoteInit,用于通过JNI函数findClass找到这个JAVA类。 */char* slashClassName = toSlashClassName(className);jclass startClass = env->FindClass(slashClassName);if (startClass == NULL) {ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);/* keep going */} else {/* 得到指定类ZygoteInit的main方法的方法ID */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 {/* 通过调用JNI函数CallStaticVoidMethod调用JAVA类ZygoteInit的main方法,并传入参数com.android.internal.os.ZygoteInit和参数true */env-><span style="color:#3333ff;">CallStaticVoidMethod</span>(startClass, startMeth, strArray);#if 0if (env->ExceptionCheck())threadExitUncaughtException(env);
#endif}}free(slashClassName);/* 将转化后的classname存入数组 *//* detach当前线程,释放JNI资源并关闭虚拟机 */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");
}

   AndroidRuntime的start方法开启了Android运行时,start方法主要做了三部分工作:

  • 创建Dalvik虚拟机:startVm;
  • 注册JNI方法:startReg;
  • 开启java世界:JNIEnv.CallStaticVoidMethod。

1、创建Dalvik虚拟机

    首先分析start工作的第一步,Dalvik虚拟机是调用startVM函数创建的,该函数位于AndroidRuntime.cpp中。代码如下:

/** Start the Dalvik Virtual Machine.** Various arguments, most determined by system properties, are passed in.* The "mOptions" vector is updated.** CAUTION: when adding options in here, be careful not to put the* char buffer inside a nested scope.  Adding the buffer to the* options using mOptions.add() does not copy the buffer, so if the* buffer goes out of scope the option may be overwritten.  It's best* to put the buffer at the top of the function so that it is more* unlikely that someone will surround it in a scope at a later time* and thus introduce a bug.** Returns 0 on success.*/
int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv)
{int result = -1;JavaVMInitArgs initArgs;char propBuf[PROPERTY_VALUE_MAX];/* 虚拟机的配置参数 */char stackTraceFileBuf[sizeof("-Xstacktracefile:")-1 + PROPERTY_VALUE_MAX];char dexoptFlagsBuf[PROPERTY_VALUE_MAX];char enableAssertBuf[sizeof("-ea:")-1 + PROPERTY_VALUE_MAX];char jniOptsBuf[sizeof("-Xjniopts:")-1 + PROPERTY_VALUE_MAX];char heapstartsizeOptsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];char heapsizeOptsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];char heapgrowthlimitOptsBuf[sizeof("-XX:HeapGrowthLimit=")-1 + PROPERTY_VALUE_MAX];char heapminfreeOptsBuf[sizeof("-XX:HeapMinFree=")-1 + PROPERTY_VALUE_MAX];char heapmaxfreeOptsBuf[sizeof("-XX:HeapMaxFree=")-1 + PROPERTY_VALUE_MAX];char gctypeOptsBuf[sizeof("-Xgc:")-1 + PROPERTY_VALUE_MAX];char backgroundgcOptsBuf[sizeof("-XX:BackgroundGC=")-1 + PROPERTY_VALUE_MAX];char heaptargetutilizationOptsBuf[sizeof("-XX:HeapTargetUtilization=")-1 + PROPERTY_VALUE_MAX];char jitcodecachesizeOptsBuf[sizeof("-Xjitcodecachesize:")-1 + PROPERTY_VALUE_MAX];char dalvikVmLibBuf[PROPERTY_VALUE_MAX];char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];char dex2oatXmxImageFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];char dex2oatXmsFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];char dex2oatXmxFlagsBuf[sizeof("-Xmx")-1 + PROPERTY_VALUE_MAX];char dex2oatCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];char dex2oatImageCompilerFilterBuf[sizeof("--compiler-filter=")-1 + PROPERTY_VALUE_MAX];char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];char extraOptsBuf[PROPERTY_VALUE_MAX];char voldDecryptBuf[PROPERTY_VALUE_MAX];enum {kEMDefault,kEMIntPortable,kEMIntFast,kEMJitCompiler,} executionMode = kEMDefault;char profilePeriod[sizeof("-Xprofile-period:")-1 + PROPERTY_VALUE_MAX];char profileDuration[sizeof("-Xprofile-duration:")-1 + PROPERTY_VALUE_MAX];char profileInterval[sizeof("-Xprofile-interval:")-1 + PROPERTY_VALUE_MAX];char profileBackoff[sizeof("-Xprofile-backoff:")-1 + PROPERTY_VALUE_MAX];char profileTopKThreshold[sizeof("-Xprofile-top-k-threshold:")-1 + PROPERTY_VALUE_MAX];char profileTopKChangeThreshold[sizeof("-Xprofile-top-k-change-threshold:")-1 +PROPERTY_VALUE_MAX];char profileType[sizeof("-Xprofile-type:")-1 + PROPERTY_VALUE_MAX];char profileMaxStackDepth[sizeof("-Xprofile-max-stack-depth:")-1 + PROPERTY_VALUE_MAX];char langOption[sizeof("-Duser.language=") + 3];char regionOption[sizeof("-Duser.region=") + 3];char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];char jitOpBuf[sizeof("-Xjitop:")-1 + PROPERTY_VALUE_MAX];char jitMethodBuf[sizeof("-Xjitmethod:")-1 + PROPERTY_VALUE_MAX];char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];/* 通过属性系统获得dalvik.vm.checkjni的配置参数,checkjni是开发阶段一个非常重要的属性* 通过它可以对JNI调用参数进行自动检查,还可以定位一些JNI产生的异常,比如JNI内存泄露。* 如果checkjni设置为true,JNI调用过程就可以由dalvikvm记录并显示在logcat上。* JNI检查会大幅度降低性能,只能在eng或userDebug版本上进行配置。 */bool checkJni = false;property_get("dalvik.vm.checkjni", propBuf, "");if (strcmp(propBuf, "true") == 0) {checkJni = true;} else if (strcmp(propBuf, "false") != 0) {/* property is neither true nor false; fall back on kernel parameter */property_get("ro.kernel.android.checkjni", propBuf, "");if (propBuf[0] == '1') {checkJni = true;}}ALOGD("CheckJNI is %s\n", checkJni ? "ON" : "OFF");if (checkJni) {/* extended JNI checking 选项“-Xcheck:jni”,用来启动JNI方法检查。*/addOption("-Xcheck:jni");/* set a cap on JNI global references,设定JNI全局引用的个数不能超过2000 */addOption("-Xjnigreflimit:2000");/* with -Xcheck:jni, this provides a JNI function call trace *///addOption("-verbose:jni");}/* 指定虚拟机的执行模式,Dalvik虚拟机支持三种运行模式,分别为Portable、Fast、Jit */property_get("dalvik.vm.execution-mode", propBuf, "");if (strcmp(propBuf, "int:portable") == 0) {executionMode = kEMIntPortable;/* Dalvik虚拟机以可移植的方式进行编译,即编译出来的虚拟机可以在任何平台上运行 */} else if (strcmp(propBuf, "int:fast") == 0) {executionMode = kEMIntFast;/* Fast是针对当前平台对Dalvik虚拟机进行编译,这样编译出来的Dalvik虚拟机可以进行特殊的优化,从而使得它能更快的运行程序 */} else if (strcmp(propBuf, "int:jit") == 0) {executionMode = kEMJitCompiler;/* Jit不是解释执行代码,二是将代码动态编译成本地语言后再执行 */}/* -Xstacktracefile:用来指定调用堆栈输出文件。* 当虚拟机收到SIGQUIT(Ctrl-\或者kill -3)信号时,会将所有线程的堆栈信息写入指定文件。* 可以通过dalvik.vm.stack-trace-file系统属性来指定调用堆栈输出文件。* 这样可以保留异常发生时的现场,方便调试定位出错原因。*/parseRuntimeOption("dalvik.vm.stack-trace-file", stackTraceFileBuf, "-Xstacktracefile:");/* 对优化过的DEX文件进行检测的配置参数,关闭后会提升性能,但如果文件损坏会导致虚拟机崩溃。 */property_get("dalvik.vm.check-dex-sum", propBuf, "");if (strcmp(propBuf, "true") == 0) {/* perform additional DEX checksum tests */addOption("-Xcheckdexsum");}property_get("log.redirect-stdio", propBuf, "");if (strcmp(propBuf, "true") == 0) {/* convert stdout/stderr to log messages */addOption("-Xlog-stdio");}/* 设置是否支持java的断言表达式 */strcpy(enableAssertBuf, "-ea:");property_get("dalvik.vm.enableassertions", enableAssertBuf+sizeof("-ea:")-1, "");if (enableAssertBuf[sizeof("-ea:")-1] != '\0') {/* accept "all" to mean "all classes and packages" */if (strcmp(enableAssertBuf+sizeof("-ea:")-1, "all") == 0)enableAssertBuf[3] = '\0'; // truncate to "-ea"ALOGI("Assertions enabled: '%s'\n", enableAssertBuf);addOption(enableAssertBuf);} else {ALOGV("Assertions disabled\n");}strcpy(jniOptsBuf, "-Xjniopts:");if (parseRuntimeOption("dalvik.vm.jniopts", jniOptsBuf, "-Xjniopts:")) {ALOGI("JNI options: '%s'\n", jniOptsBuf);}/* route exit() to our handler */addOption("exit", (void*) runtime_exit);/* route fprintf() to our handler */addOption("vfprintf", (void*) runtime_vfprintf);/* register the framework-specific "is sensitive thread" hook */addOption("sensitiveThread", (void*) runtime_isSensitiveThread);/* enable verbose; standard options are { jni, gc, class } *///addOption("-verbose:jni");addOption("-verbose:gc");//addOption("-verbose:class");/** The default starting and maximum size of the heap.  Larger* values should be specified in a product property override.* 设置虚拟机heap的大小,包括启动值(heapstartsize)和最大值(heapsize)*/parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");// Increase the main thread's interpreter stack size for bug 6315322.addOption("-XX:mainThreadStackSize=24K");// Set the max jit code cache size.  Note: size of 0 will disable the JIT.parseRuntimeOption("dalvik.vm.jit.codecachesize",jitcodecachesizeOptsBuf,"-Xjitcodecachesize:");parseRuntimeOption("dalvik.vm.heapgrowthlimit", heapgrowthlimitOptsBuf, "-XX:HeapGrowthLimit=");parseRuntimeOption("dalvik.vm.heapminfree", heapminfreeOptsBuf, "-XX:HeapMinFree=");parseRuntimeOption("dalvik.vm.heapmaxfree", heapmaxfreeOptsBuf, "-XX:HeapMaxFree=");parseRuntimeOption("dalvik.vm.heaptargetutilization",heaptargetutilizationOptsBuf,"-XX:HeapTargetUtilization=");property_get("ro.config.low_ram", propBuf, "");if (strcmp(propBuf, "true") == 0) {addOption("-XX:LowMemoryMode");}parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC=");/** Enable or disable dexopt features, such as bytecode verification and* calculation of register maps for precise GC.*/property_get("dalvik.vm.dexopt-flags", dexoptFlagsBuf, "");if (dexoptFlagsBuf[0] != '\0') {const char* opc;const char* val;opc = strstr(dexoptFlagsBuf, "v=");     /* verification */if (opc != NULL) {switch (*(opc+2)) {case 'n':   val = "-Xverify:none";      break;case 'r':   val = "-Xverify:remote";    break;case 'a':   val = "-Xverify:all";       break;default:    val = NULL;                 break;}if (val != NULL) {addOption(val);}}opc = strstr(dexoptFlagsBuf, "o=");     /* optimization */if (opc != NULL) {switch (*(opc+2)) {case 'n':   val = "-Xdexopt:none";      break;case 'v':   val = "-Xdexopt:verified";  break;case 'a':   val = "-Xdexopt:all";       break;case 'f':   val = "-Xdexopt:full";      break;default:    val = NULL;                 break;}if (val != NULL) {addOption(val);}}opc = strstr(dexoptFlagsBuf, "m=y");    /* register map */if (opc != NULL) {addOption("-Xgenregmap");/* turn on precise GC while we're at it */addOption("-Xgc:precise");}}/* enable debugging; set suspend=y to pause during VM init *//* use android ADB transport */addOption("-agentlib:jdwp=transport=dt_android_adb,suspend=n,server=y");parseRuntimeOption("dalvik.vm.lockprof.threshold",lockProfThresholdBuf,"-Xlockprofthreshold:");/* Force interpreter-only mode for selected opcodes. Eg "1-0a,3c,f1-ff" */parseRuntimeOption("dalvik.vm.jit.op", jitOpBuf, "-Xjitop:");/* Force interpreter-only mode for selected methods */parseRuntimeOption("dalvik.vm.jit.method", jitMethodBuf, "-Xjitmethod:");if (executionMode == kEMIntPortable) {addOption("-Xint:portable");} else if (executionMode == kEMIntFast) {addOption("-Xint:fast");} else if (executionMode == kEMJitCompiler) {addOption("-Xint:jit");}// libart tolerates libdvm flags, but not vice versa, so only pass some options if libart.property_get("persist.sys.dalvik.vm.lib.2", dalvikVmLibBuf, "libart.so");bool libart = (strncmp(dalvikVmLibBuf, "libart", 6) == 0);if (libart) {// If we booting without the real /data, don't spend time compiling.property_get("vold.decrypt", voldDecryptBuf, "");bool skip_compilation = ((strcmp(voldDecryptBuf, "trigger_restart_min_framework") == 0) ||(strcmp(voldDecryptBuf, "1") == 0));// Extra options for boot.art/boot.oat image generation.parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,"-Xms", "-Ximage-compiler-option");parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,"-Xmx", "-Ximage-compiler-option");if (skip_compilation) {addOption("-Ximage-compiler-option");addOption("--compiler-filter=verify-none");} else {parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,"--compiler-filter=", "-Ximage-compiler-option");}// Make sure there is a preloaded-classes file.if (!hasFile("/system/etc/preloaded-classes")) {ALOGE("Missing preloaded-classes file, /system/etc/preloaded-classes not found: %s\n",strerror(errno));goto bail;}addOption("-Ximage-compiler-option");addOption("--image-classes=/system/etc/preloaded-classes");// If there is a compiled-classes file, push it.if (hasFile("/system/etc/compiled-classes")) {addOption("-Ximage-compiler-option");addOption("--compiled-classes=/system/etc/compiled-classes");}property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");// Extra options for DexClassLoader.parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xms", dex2oatXmsFlagsBuf,"-Xms", "-Xcompiler-option");parseCompilerRuntimeOption("dalvik.vm.dex2oat-Xmx", dex2oatXmxFlagsBuf,"-Xmx", "-Xcompiler-option");if (skip_compilation) {addOption("-Xcompiler-option");addOption("--compiler-filter=verify-none");} else {parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,"--compiler-filter=", "-Xcompiler-option");}property_get("dalvik.vm.dex2oat-flags", dex2oatFlagsBuf, "");parseExtraOpts(dex2oatFlagsBuf, "-Xcompiler-option");}/* extra options; parse this late so it overrides others */property_get("dalvik.vm.extra-opts", extraOptsBuf, "");parseExtraOpts(extraOptsBuf, NULL);/* Set the properties for locale */{strcpy(langOption, "-Duser.language=");strcpy(regionOption, "-Duser.region=");readLocale(langOption, regionOption);addOption(langOption);addOption(regionOption);}/** Set profiler options*/if (libart) {// Whether or not the profiler should be enabled.property_get("dalvik.vm.profiler", propBuf, "0");if (propBuf[0] == '1') {addOption("-Xenable-profiler");}// Whether the profile should start upon app startup or be delayed by some random offset// (in seconds) that is bound between 0 and a fixed value.property_get("dalvik.vm.profile.start-immed", propBuf, "0");if (propBuf[0] == '1') {addOption("-Xprofile-start-immediately");}// Number of seconds during profile runs.parseRuntimeOption("dalvik.vm.profile.period-secs", profilePeriod, "-Xprofile-period:");// Length of each profile run (seconds).parseRuntimeOption("dalvik.vm.profile.duration-secs",profileDuration,"-Xprofile-duration:");// Polling interval during profile run (microseconds).parseRuntimeOption("dalvik.vm.profile.interval-us", profileInterval, "-Xprofile-interval:");// Coefficient for period backoff.  The the period is multiplied// by this value after each profile run.parseRuntimeOption("dalvik.vm.profile.backoff-coeff", profileBackoff, "-Xprofile-backoff:");// Top K% of samples that are considered relevant when// deciding if the app should be recompiled.parseRuntimeOption("dalvik.vm.profile.top-k-thr",profileTopKThreshold,"-Xprofile-top-k-threshold:");// The threshold after which a change in the structure of the// top K% profiled samples becomes significant and triggers// recompilation. A change in profile is considered// significant if X% (top-k-change-threshold) of the top K%// (top-k-threshold property) samples has changed.parseRuntimeOption("dalvik.vm.profile.top-k-ch-thr",profileTopKChangeThreshold,"-Xprofile-top-k-change-threshold:");// Type of profile data.parseRuntimeOption("dalvik.vm.profiler.type", profileType, "-Xprofile-type:");// Depth of bounded stack dataparseRuntimeOption("dalvik.vm.profile.stack-depth",profileMaxStackDepth,"-Xprofile-max-stack-depth:");// Native bridge library. "0" means that native bridge is disabled.property_get("ro.dalvik.vm.native.bridge", propBuf, "");if (propBuf[0] == '\0') {ALOGW("ro.dalvik.vm.native.bridge is not expected to be empty");} else if (strcmp(propBuf, "0") != 0) {snprintf(nativeBridgeLibrary, sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX,"-XX:NativeBridge=%s", propBuf);addOption(nativeBridgeLibrary);}}initArgs.version = JNI_VERSION_1_4;initArgs.options = mOptions.editArray();initArgs.nOptions = mOptions.size();initArgs.ignoreUnrecognized = JNI_FALSE;/** Initialize the VM.创建java虚拟机对象。* 执行成功后,虚拟机便准备就绪,这时候java世界就可以分发JNI调用了。* 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) {ALOGE("JNI_CreateJavaVM failed\n");goto bail;}result = 0;bail:return result;
}
    根据代码可以看出startVM主要做了两部分工作:一是通过属性系统获取大量虚拟机配置信息,以此设置虚拟机参数;二是调用JNI_CreateJavaVM函数创建虚拟机实例。

    虚拟机参数很多,可以通过adb命令查看:adb shell dalvikvm。

    JNI_CreateJavaVM不再向下跟踪,可以参考:点击打开链接

2、注册JNI方法

    上面我们分析了是如何创建Dalvik虚拟机的,下面我们接着分析start方法的第二步重要工作:注册Android核心类的JNI函数。这些函数是java世界调用native方法的基础,是连接java世界和C/C++世界的桥梁。 

<span style="font-size:12px;">/** Register android native functions with the VM.*/
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{/** This hook causes all future threads created in this process to be* attached to the JavaVM.  (This needs to go away in favor of JNI* Attach calls.)*/androidSetCreateThreadFunc((android_create_thread_fn) javaCreateThreadEtc);ALOGV("--- registering native functions ---\n");/** Every "register" function calls one or more things that return* a local reference (e.g. FindClass).  Because we haven't really* started the VM yet, they're all getting stored in the base frame* and never released.  Use Push/Pop to manage the storage.*/env->PushLocalFrame(200);if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);//createJavaThread("fubar", quickTest, (void*) "hello");return 0;
}</span>

 

在startReg函数中首先调用了androidSetCerateThreadFunc函数,这个函数用来设置一个线程创建钩子javaCreateThreadEtc。这个线程创建钩子是用来干什么的呢?是用来初始化一个Native线程的JNI环境的,也就是说当我们在C++代码中创建一个Native线程的时候,会调用javaCreateThreadEtc函数来初始化该Native线程的JNI环境。

    调用register_jni_procs函数来注册Android核心类的JNI方法。在注册JNI方法的过程中,需要在Native代码中引用到一些JAVA对象,这些Java对象引用需要记录在当前线程的一个Native堆栈中。但是此时Dalvik虚拟机还没有真正运行起来,也就是当前线程的Native堆栈还没有准备就绪。那么在这种情况下,Android是如何处理的呢?在注册JNI方法之前,调用JNIEnv对象的PushLocalFrame函数在当前线程的Native堆栈中压入一个帧(Frame),这个帧是一个本地帧,只是用来保存java对象在Native代码中的本地引用;并且在注册JNI方法之后,调用JNIEnv对象的PopLocalFrame函数将帧弹出堆栈。在本地函数的入口处调用PushLocalFrame,在函数返回前调用PopLocalFrame,这样在两个函数之间创建的局部引用都会有效管理和释放,也就高效管理了局部引用的生命周期。

    startReg函数的关键是调用register_jni_procs函数注册JNI方法。

注意:在Android应用框架层有很多地方调用Native方法,这些方法的实现都借助于本地方法。这里便是注册Native方法的JNI实现方法,Android是采用注册方式实现Native方法和JNI方法的关联。register_jni_procs的实现如下:

static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{for (size_t i = 0; i < count; i++) {if (array[i].mProc(env) < 0) {
#ifndef NDEBUGALOGD("----------!!! %s failed to load\n", array[i].mName);
#endifreturn -1;}}return 0;
}
   从前面的调用过程可以知道 ,参数array指向的是全局变量gRegJNI所描述的一个JNI方法注册函数表,其中,每一个表项都用一个RegJNIRec对象来描述,而每一个RegJNIRec对象都有一个成员变量mProc,指向一个JNI方法注册函数。通过依次调用这些注册函数,就可以将Android核心类的JNI方法注册到前面的所创建的Dalvik虚拟机中去。

    那么我们看一下RegJNIRec到底是什么呢,代码如下:

#ifdef NDEBUG#define REG_JNI(name)      { name } /* 定义REG_JNI宏,宏以name为参数*/struct RegJNIRec { /* 定义一个结构体RegJNIRec,结构体的元素是一个函数指针,参数类型是(JNIEnv*) */int (*mProc)(JNIEnv*);};
#else#define REG_JNI(name)      { name, #name }struct RegJNIRec {int (*mProc)(JNIEnv*);const char* mName;};
#endif
    从代码中可以看出RegJNIRec是一个结构体,结构体的元素是一个函数指针。我们看一下gRegJNI描述的JNI方法注册函数表都注册了哪些Android核心类的JNI方法,代码如下:

static const RegJNIRec gRegJNI[] = {REG_JNI(register_com_android_internal_os_RuntimeInit),REG_JNI(register_android_os_SystemClock),REG_JNI(register_android_util_EventLog),REG_JNI(register_android_util_Log),REG_JNI(register_android_util_FloatMath),REG_JNI(register_android_content_AssetManager),REG_JNI(register_android_content_StringBlock),REG_JNI(register_android_content_XmlBlock),REG_JNI(register_android_emoji_EmojiFactory),REG_JNI(register_android_text_AndroidCharacter),REG_JNI(register_android_text_StaticLayout),REG_JNI(register_android_text_AndroidBidi),REG_JNI(register_android_view_InputDevice),REG_JNI(register_android_view_KeyCharacterMap),REG_JNI(register_android_os_Process),REG_JNI(register_android_os_SystemProperties),REG_JNI(register_android_os_Binder),REG_JNI(register_android_os_Parcel),REG_JNI(register_android_nio_utils),REG_JNI(register_android_graphics_Graphics),REG_JNI(register_android_view_DisplayEventReceiver),REG_JNI(register_android_view_RenderNode),REG_JNI(register_android_view_RenderNodeAnimator),REG_JNI(register_android_view_GraphicBuffer),REG_JNI(register_android_view_GLES20Canvas),REG_JNI(register_android_view_HardwareLayer),REG_JNI(register_android_view_ThreadedRenderer),REG_JNI(register_android_view_Surface),REG_JNI(register_android_view_SurfaceControl),REG_JNI(register_android_view_SurfaceSession),REG_JNI(register_android_view_TextureView),REG_JNI(register_com_android_internal_view_animation_NativeInterpolatorFactoryHelper),REG_JNI(register_com_google_android_gles_jni_EGLImpl),REG_JNI(register_com_google_android_gles_jni_GLImpl),REG_JNI(register_android_opengl_jni_EGL14),REG_JNI(register_android_opengl_jni_EGLExt),REG_JNI(register_android_opengl_jni_GLES10),REG_JNI(register_android_opengl_jni_GLES10Ext),REG_JNI(register_android_opengl_jni_GLES11),REG_JNI(register_android_opengl_jni_GLES11Ext),REG_JNI(register_android_opengl_jni_GLES20),REG_JNI(register_android_opengl_jni_GLES30),REG_JNI(register_android_opengl_jni_GLES31),REG_JNI(register_android_opengl_jni_GLES31Ext),REG_JNI(register_android_graphics_Bitmap),REG_JNI(register_android_graphics_BitmapFactory),REG_JNI(register_android_graphics_BitmapRegionDecoder),REG_JNI(register_android_graphics_Camera),REG_JNI(register_android_graphics_CreateJavaOutputStreamAdaptor),REG_JNI(register_android_graphics_Canvas),REG_JNI(register_android_graphics_CanvasProperty),REG_JNI(register_android_graphics_ColorFilter),REG_JNI(register_android_graphics_DrawFilter),REG_JNI(register_android_graphics_FontFamily),REG_JNI(register_android_graphics_Interpolator),REG_JNI(register_android_graphics_LayerRasterizer),REG_JNI(register_android_graphics_MaskFilter),REG_JNI(register_android_graphics_Matrix),REG_JNI(register_android_graphics_Movie),REG_JNI(register_android_graphics_NinePatch),REG_JNI(register_android_graphics_Paint),REG_JNI(register_android_graphics_Path),REG_JNI(register_android_graphics_PathMeasure),REG_JNI(register_android_graphics_PathEffect),REG_JNI(register_android_graphics_Picture),REG_JNI(register_android_graphics_PorterDuff),REG_JNI(register_android_graphics_Rasterizer),REG_JNI(register_android_graphics_Region),REG_JNI(register_android_graphics_Shader),REG_JNI(register_android_graphics_SurfaceTexture),REG_JNI(register_android_graphics_Typeface),REG_JNI(register_android_graphics_Xfermode),REG_JNI(register_android_graphics_YuvImage),REG_JNI(register_android_graphics_pdf_PdfDocument),REG_JNI(register_android_graphics_pdf_PdfEditor),REG_JNI(register_android_graphics_pdf_PdfRenderer),REG_JNI(register_android_database_CursorWindow),REG_JNI(register_android_database_SQLiteConnection),REG_JNI(register_android_database_SQLiteGlobal),REG_JNI(register_android_database_SQLiteDebug),REG_JNI(register_android_os_Debug),REG_JNI(register_android_os_FileObserver),REG_JNI(register_android_os_MessageQueue),REG_JNI(register_android_os_SELinux),REG_JNI(register_android_os_Trace),REG_JNI(register_android_os_UEventObserver),REG_JNI(register_android_net_LocalSocketImpl),REG_JNI(register_android_net_NetworkUtils),REG_JNI(register_android_net_TrafficStats),REG_JNI(register_android_os_MemoryFile),REG_JNI(register_com_android_internal_os_ZygoteInit),REG_JNI(register_com_android_internal_os_Zygote),REG_JNI(register_com_android_internal_util_VirtualRefBasePtr),REG_JNI(register_android_hardware_Camera),REG_JNI(register_android_hardware_camera2_CameraMetadata),REG_JNI(register_android_hardware_camera2_legacy_LegacyCameraDevice),REG_JNI(register_android_hardware_camera2_legacy_PerfMeasurement),REG_JNI(register_android_hardware_camera2_DngCreator),REG_JNI(register_android_hardware_SensorManager),REG_JNI(register_android_hardware_SerialPort),REG_JNI(register_android_hardware_SoundTrigger),REG_JNI(register_android_hardware_UsbDevice),REG_JNI(register_android_hardware_UsbDeviceConnection),REG_JNI(register_android_hardware_UsbRequest),REG_JNI(register_android_hardware_location_ActivityRecognitionHardware),REG_JNI(register_android_media_AudioRecord),REG_JNI(register_android_media_AudioSystem),REG_JNI(register_android_media_AudioTrack),REG_JNI(register_android_media_JetPlayer),REG_JNI(register_android_media_RemoteDisplay),REG_JNI(register_android_media_ToneGenerator),REG_JNI(register_android_opengl_classes),REG_JNI(register_android_server_NetworkManagementSocketTagger),REG_JNI(register_android_ddm_DdmHandleNativeHeap),REG_JNI(register_android_backup_BackupDataInput),REG_JNI(register_android_backup_BackupDataOutput),REG_JNI(register_android_backup_FileBackupHelperBase),REG_JNI(register_android_backup_BackupHelperDispatcher),REG_JNI(register_android_app_backup_FullBackup),REG_JNI(register_android_app_ActivityThread),REG_JNI(register_android_app_NativeActivity),REG_JNI(register_android_view_InputChannel),REG_JNI(register_android_view_InputEventReceiver),REG_JNI(register_android_view_InputEventSender),REG_JNI(register_android_view_InputQueue),REG_JNI(register_android_view_KeyEvent),REG_JNI(register_android_view_MotionEvent),REG_JNI(register_android_view_PointerIcon),REG_JNI(register_android_view_VelocityTracker),REG_JNI(register_android_content_res_ObbScanner),REG_JNI(register_android_content_res_Configuration),REG_JNI(register_android_animation_PropertyValuesHolder),REG_JNI(register_com_android_internal_content_NativeLibraryHelper),REG_JNI(register_com_android_internal_net_NetworkStatsFactory),
};

    这里使用了宏REG_JNI,将函数名传递给结构体RegJNIRec的mProc函数指针。这些函数最终调用了jniRegisterNativeMethods方法注册JNI方法。

    至此,JNI方法注册就分析到这里。

3、开启java世界

    在AndroidRuntime的start方法中是通过CallStaticVoidMethod这个JNI函数调用ZygoteInit文件的main函数来开启java世界的。ZygoteInit文件位于frameworks/base/core/java/com/android/internal/os/ZygoteInit.java,定位到main方法,代码如下:

    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);/* 注册zygote服务所需的Socket,用来接收请求 */EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());preload();/* 加载class资源和resource资源 */EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());// Finish profiling the zygote initialization.SamplingProfilerIntegration.writeZygoteSnapshot();// Do an initial gc to clean up after startupgc();/* 强制gc */// Disable tracing so that forked processes do not inherit stale tracing tags from// Zygote.Trace.setTracingEnabled(false);if (startSystemServer) {startSystemServer(abiList, socketName);/* 启动system_server */}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();/* zygote异常退出 */throw ex;}}
ZygoteInit的main方法主要做了5部分工作:
  • 注册zygote的Socket;
  • 预加载Class资源和Resource资源;
  • 启动system_server;
  • 执行runSelectLoop方法;
  • 执行MethodAndArgsCaller的run方法,该方法将执行SystemServer的main函数。
    这5部分的工作是Android应用程序启动和运行的基础。通过zygote的Socket,ActivityManagerService便可以通过zygote启动新的应用进程;通过预加载资源,让不同应用程序共享框架层资源,提高运行效率;启动system_server,将开启Android的Native system service和Java System Service,这是应用框架层运行的基础;通过runSelectLoop,便可以处理新的应用程序运行请求。

三、Zygote开启java世界

1、注册Zygote的Socket

    注册zygote服务所需要的Socket,是通过registerZygoteSocket函数来实现的,该函数绑定了zygote中的Socket,用于接收ActivityManagerService中启动应用程序的请求。

    /*** 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);}}}
     这里的socketName是在main函数中定义的“zygote”,因此fullSocketName为“ANDROID_SOCKET_zygote”,通过调用System.getenv(fullSocketName)函数,返回在系统环境变量值定义的变量的字符串值;最后根据对应的文件描述符生成一个LocalServerSocket对象,即zygote服务端的Socket,最终该Socket将与ActivityManagerService通信,是java世界启动新进程的通道。

2、预加载Class资源和Resource资源

    在ZygoteInit的main方法中创建完Socket后,便进行预加载资源,这部分功能是通过preload函数完成的,在preload函数中封装了对preloadClasses()和preloadResource()等函数的调用。

    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");}
2.1、preloadClasses()

    该函数用于加载preloaded-classes文件中指定的java类,代码如下:

    /*** 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.使用VMRuntime的setTargetHeapUtilization函数可以增强程序堆内存的处理效率。float defaultUtilization = runtime.getTargetHeapUtilization();runtime.setTargetHeapUtilization(0.8f);// Start with a clean slate.System.gc();runtime.runFinalizationSync();Debug.startAllocCounting();//统计两点间的内存分配情况,使用Debug.startAllocCounting()、Debug.stopAllocCounting()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);}}
       其中,PRELOADED_CLASSES是被定义的全局变量“/system/etc/preloaded-classes”,这是一个文件,用来存放预加载的类;根据该文件的内容,我们可以得知函数中读取出来的line便是一个类,通过反射机制加载读取出来的类。preloaded-classes文件中有2000行以上,读取每一行并加载每一行指定的类需要花费较长的时间,这也是影响系统启动速度慢的原因。不过这些类预先加载到内存中,当新的应用程序启动时,可以共享这部分资源,这样便加快了应用程序启动和运行速度。

2.2、preloadResources()

    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.getSystem();/*初始化全局变量mResources*/mResources.startPreloading();/*设置初始状态,防止重复加载资源*/if (PRELOAD_RESOURCES) {/*PRELOAD_RESOURCES设置在zygote初始化时是否加载Resource,初始值为true*/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();}}
     preloadResources共加载了两种资源:drawables和color。这些资源定义在frameworks/base/core/res/res/values/array.xml文件中,最终会被编译进frameworks-res.apk中。

3、启动system_server进程

    ZygoteInit的main函数中加载完共享资源后,便开始启动system_server进程,该进程是理解框架层的基础。Android中所有的系统服务都是由该进程启动的,它的异常退出会直接导致zygote“自杀”,这样整个java世界便会崩溃。system_server进程的启动入口是startSystemServer,代码如下:

    /*** 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启动system_server的命令行参数 */String args[] = {"--setuid=1000",/*设置用户ID为1000,即SYSTEM_UID,系统进程*/"--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);/*将args数组分解成Arguments类型的对象*/ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);/* Request to fork the system server process.调用系统函数fork()创建子进程system_server */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);/*在子进程中调用handleSystemServerProcess*/}return true;/*在父进程中返回*/}
       到这里zygote做了第一次分裂,fork出系统服务的总管system_server进程。这个过程分为以下两个重要的步骤:通过forkSystemServer创建system_server子进程;在子进程中调用handleSystemServerProcess方法。

3.1、调用forkSystemServer创建system_server子进程。

    创建system_server的第一步便是调用Zygote类的forkSystemServer方法,该方法位于frameworks/base/core/java/com/android/internal/os/Zygote.java类中,在forkSystemServer方法中通过调用Native方法nativeForkSystemServer来完成启动system_server进程的任务。代码如下:

    /*** 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);
    nativeForkSystemServer的JNI层代码实现位于/dalvik/vm/native/dalvik_system_Zygote.cpp中,函数名为Dalvik_dalvik_system_Zygote_forkSystemServer,代码如下:
static void Dalvik_dalvik_system_Zygote_forkSystemServer(  const u4* args, JValue* pResult)  
{  pid_t pid;  pid = forkAndSpecializeCommon(args, true); //启动system_server进程  /* The zygote process checks whether the child process has died or not. */  if (pid > 0) {//pid大于0,说明是在父进程中   int status;    ALOGI("System server process %d has been created", pid);  gDvm.systemServerPid = pid;  //在虚拟机中记录system_server进程的ID/* There is a slight window that the system server process has crashed * but it went unnoticed because we haven't published its pid yet. So * we recheck here just to make sure that all is well. */  if (waitpid(pid, &status, WNOHANG) == pid) {//堵塞,等待system_server进程   ALOGE("System server process %d has died. Restarting Zygote!", pid);  kill(getpid(), SIGKILL);//一旦上面的等待返回,说明进程pid(system_server)已终止,此时Zygote杀死自己   }  }  RETURN_INT(pid);  
}  
    该函数的功能主要分为两部分:一是启动system_server进程;二是监控system_server进程的启动结果。system_server进程的启动是由forkAndSpecializeCommon完成的,在forkAndSpecializeCommon函数中调用系统函数fork创建子进程,并且在创建前设置信号处理函数setSignalHandler(),在setSignalHandler方法中调用系统函数sigaction处理SIGCHLD信号(子进程退出信号),指定处理函数sigchldHandler,如果system_server出现异常,就会自杀。

3.2、在子进程中调用handleSystemServerProcess方法

    system_server进程启动之后,便开始执行handleSystemServerProcess方法,代码如下:

    /*** Finish remaining work for the newly forked system server process.*/private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)throws ZygoteInit.MethodAndArgsCaller {closeServerSocket();//关闭fork时从zygote继承下来的socket// 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);//system_server}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 */}
    在handleSystemServerProcess方法中做了一些清理和初始化工作,接着调用RuntimeInit.zygoteInit方法。

    /*** 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.<p>** Current recognized args:* <ul>*   <li> <code> [--] <start class name>  <args>* </ul>** @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);}
       在zygoteInit方法中封装了四个方法的调用,对应四个步骤实现不同的初始化操作,分别介绍如下:

    1)、redirectLogStreams方法重定向标准I/O操作,重定向到Android标准的日志系统。

    /*** Redirect System.out and System.err to the Android log.*/public static void redirectLogStreams() {System.out.close();System.setOut(new AndroidPrintStream(Log.INFO, "System.out"));System.err.close();System.setErr(new AndroidPrintStream(Log.WARN, "System.err"));}
    2)、commonInit方法初始化一些通用设置

    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() {@Overridepublic 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();//Android log配置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;}
    3)、nativeZygoteInit方法开启Binder通信

    nativeZygoteInit是一个native方法,其JNI实现方法位于AndroidRuntime.cpp中,方法名为com_android_internal_os_RuntimeInit_nativiZygoteInit,代码如下:

static void com_android_internal_os_RuntimeInit_nativeZygoteInit(JNIEnv* env, jobject clazz)
{gCurRuntime->onZygoteInit();
}

    这里调用onZygoteInit方法,这里onZygoteInit是一个虚函数,直接调用子类AppRuntime.onZygoteInit,代码位于frameworks/base/cmd/app_process/App_main.cpp中。

    virtual void onZygoteInit(){// Re-enable tracing now that we're no longer in Zygote.atrace_set_tracing_enabled(true);sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");proc->startThreadPool();//创建PoolThread对象,用于Binder通信}
    4)、invokeStaticMain方法抛出异常

    applicationInit方法做了虚拟机设置,并转化了参数后,直接调用invokeStaticMain方法,这里直接分析invokeStaticMain,代码如下:

    /*** 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 className Fully-qualified class name* @param argv Argument vector for main()* @param classLoader the classLoader to load {@className} with*/private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {Class<?> cl;try {cl = Class.forName(className, true, classLoader);//利用java反射机制加载类信息,className是com.android.server.SystemServer} 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 });//加载SystemServer类的main函数} 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();//利用java的反射机制,判断main函数的修饰符是否是static和publicif (! (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);//抛出异常}
    invokeStaticMain方法利用java的反射机制加载类的信息后,最后抛出一个MethodAndArgsCaller异常,那么在哪里进行的异常捕获呢?

4、执行MethodAndArgsCaller的run方法

    在ZygoteInit的main方法中捕获了MethodAndArgsCaller异常,便执行run方法。

    public static void main(String argv[]) {try {......registerZygoteSocket(socketName);......preload();......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;}}
   分析下MethodAndArgsCaller类中都做了哪些事情。

    /*** 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 Exceptionimplements 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 });//利用java反射机制调用这个方法} 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);}}}
    MethodAndArgsCaller类既是一个异常类又是一个线程,异常处理代码中直接调用了这个线程的run方法,并执行了传入的com.android.server.SystemServer类的main方法。invokeStaticMain方法抛出异常的目的是执行com.android.server.SystemServer类的main方法,通过这种方式,可以直接从调用栈中跳出,并返回到ZygoteInit的main方法中。

    system_server绕了一大圈执行了SystemServer的main方法,那么SystemServer又承担了什么使命呢?首先分析一下com.android.server.SystemServer类的main方法中做了哪些事情,代码位于:\frameworks\base\services\java\com\android\server\SystemServer.java,如下:

    /*** The main entry point from zygote.*/public static void main(String[] args) {new SystemServer().run();}
    private void run() {// If a device's clock is before 1970 (before 0), a lot of// APIs crash dealing with negative numbers, notably// java.io.File#setLastModified, so instead we fake it and// hope that time from cell towers or NTP fixes it shortly.if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {Slog.w(TAG, "System clock is before 1970; setting to 1970.");SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);}// Here we go!Slog.i(TAG, "Entered the Android system server!");EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());// In case the runtime switched since last boot (such as when// the old runtime was removed in an OTA), set the system// property so that it is in sync. We can't do this in// libnativehelper's JniInvocation::Init code where we already// had to fallback to a different runtime because it is// running as root and we need to be the system user to set// the property. http://b/11463182SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());// Enable the sampling profiler.if (SamplingProfilerIntegration.isEnabled()) {//判断是否开启性能统计SamplingProfilerIntegration.start();//启动性能统计mProfilerSnapshotTimer = new Timer();mProfilerSnapshotTimer.schedule(new TimerTask() {@Overridepublic void run() {SamplingProfilerIntegration.writeSnapshot("system_server", null);//结束统计并生成结果文件}}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);}// Mmmmmm... more memory!申请更多的内存VMRuntime.getRuntime().clearGrowthLimit();// The system server has to run all of the time, so it needs to be// as efficient as possible with its memory usage.提高堆内存使用效率VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);// Some devices rely on runtime fingerprint generation, so make sure// we've defined it before booting further.Build.ensureFingerprintProperty();// Within the system server, it is an error to access Environment paths without// explicitly specifying a user.Environment.setUserRequired(true);// Ensure binder calls into the system always run at foreground priority.BinderInternal.disableBackgroundScheduling(true);// Prepare the main looper thread (this thread).android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_FOREGROUND);android.os.Process.setCanSelfBackground(false);Looper.prepareMainLooper();// Initialize native services.加载libandroid_servers.so库文件System.loadLibrary("android_servers");nativeInit();// Check whether we failed to shut down last time we tried.// This call may not return.performPendingShutdown();// Initialize the system context.createSystemContext();// Create the system service manager.mSystemServiceManager = new SystemServiceManager(mSystemContext);LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);// Start services.try {startBootstrapServices();startCoreServices();startOtherServices();} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;}// For debug builds, log event loop stalls to dropbox for analysis.if (StrictMode.conditionallyEnableDebugLogging()) {Slog.i(TAG, "Enabled StrictMode for system server main thread.");}// Loop forever.Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

    nativeInit是本地方法,具体实现位于\frameworks\base\services\core\jni\com_android_server_SystemServer.cpp,只是启动Sensor Service。SensorService是C语言实现的系统服务,因此成为Native System Service。

static void android_server_SystemServer_nativeInit(JNIEnv* env, jobject clazz) {char propBuf[PROPERTY_VALUE_MAX];property_get("system_init.startsensorservice", propBuf, "1");if (strcmp(propBuf, "1") == 0) {// Start the sensor serviceSensorService::instantiate();}
}
















    









http://www.ppmy.cn/news/172532.html

相关文章

4. camera调试日志

注: 1. 37m androidM/N/O 驱动及hal代码一样&#xff0c;可以公用2. 37m 与37t 驱动代码一样&#xff0c;hal层代码格式不一样3. 37m 与39 驱动代码一样&#xff0c;只有头文件#include "kd_camera_hw.h" --> #include "kd_camera_typedef.h"hal 层代码…

FreeRTOS系统下LwIP-1.4.1的移植

转载至 基于FreeRTOS与MQTT的物联网技术应用系列——步进电机控制&#xff08;四&#xff09;FreeRTOS系统下LwIP-1.4.1的移植 怕以后查找麻烦&#xff0c;特粘贴至此~~~~~~ 本文使用的网卡PHY芯片型号是DP83848&#xff0c;工作在MII接口模式&#xff0c;时钟频率是25MHz。 …

转://Oracle 11gR2 硬件导致重新添加节点

一、环境描述&#xff1a; 这是一套五年前部署的双节点单柜11g RAC&#xff0c;当时操作系统盘是一块164g的单盘&#xff0c;没有做RAID。 OS: RedHat EnterPrise 5.5 x86_x64 GI : Oracle Grid Infrastructure 11.2.0.3x86_x64 DB: Oracle Database EnterPrise 11.2.0.3x86_64…

11gR2 硬件导致重新添加节点

http://blog.csdn.net/wuweilong/article/details/53897208 一、环境描述&#xff1a; 这是一套五年前部署的双节点单柜11g RAC&#xff0c;当时操作系统盘是一块164g的单盘&#xff0c;没有做RAID。 OS: RedHat EnterPrise 5.5 x86_x64 GI : Oracle Grid Infrastructure 11.2…

Android图形合成和显示系统---基于高通MSM8k MDP4平台

介绍了Android SurfaceFlinger层次以下的图形合成和显示系统&#xff0c;主要基于高通MSM8k MDP4x平台。 做为Android Display专题。SurfaceFlinger的详细介绍参见链接文章。 Android GDI之SurfaceFlinger SurfaceFinger按英文翻译过来就是Surface投递者。SufaceFlinger的构成并…

C++primer plus部分解答

第二章&#xff1a;开始学习 C //ex2.1--display your name andaddress #include<iostream>int main(void) { usingnamespace std; cout<<"My name is liao chunguang and I live in hunanchenzhou.\n”&#xff1b; } //ex2.2--convert the furlong units to…

C语言-typedef关键字

一.typedef 关键字 typedef是在C语言允许为一个数据类型起一个新的别名。它本身是一种存储类的关键字,与auto extern,mutable、static、register 等关键字不能出现在同一个表达式中。 二、typedef用法 示例&#xff1a; 对于数据类型使用例如&#xff1a; 对于指针的使用例如…

CASS10.1 野外操作码

一、引论 CASS10.1的野外操作码由描述实体属性的野外地物码和一些描述连接关系的野外连接码组成。CASS10.1专门有一个野外操作码定义文件JCODE.DEF&#xff0c;该文件是用来描述野外操作码于CASS10.1内部编码的对应关系的&#xff0c;用户可编辑此文件使之符合自己的要求。 文…