当前位置: 首页 > news >正文

Android Framework学习笔记(4)----Zygote进程

Zygote的启动流程

Init进程启动后,会加载并执行init.rc文件。该.rc文件中,就包含启动Zygote进程的Action。详见“RC文件解析”章节

根据Zygote对应的RC文件,可知Zygote进程是由/system/bin/app_process程序来创建的。

app_process大致处理流程如下,详见“app_process解析”章节

  1. 创建AppRuntime
  2. 整理传参,并将相应参数传给Runtime或Zygote进程。
  3. 使用Runtime调用start方法,传入“com.android.internal.os.ZygoteInit”类和zygote传参,启动zygote。

class AppRuntime 继承自 public AndroidRuntime。调用start方法后,将执行以下步骤。详见"AndroidRuntime解析"章节

  1. 搜集JVM参数,启动VM
  2. 注册JNI方法。
  3. 利用java的反射,调用com.android.internal.os.ZygoteInit类中的main方法,创建zygote进程。

Zygote代码分析

RC文件解析

Init.rc中的Zygote内容

init.rc中涉及Zygote的部分有五处。从代码中可以看出,在初始化晚期阶段late-init,会触发Zygote-start行为,而该行为会创建zygote,以及zygote_secondary两个进程。

import /system/etc/init/hw/init.${ro.zygote}.rcimport /system/etc/init/hw/init.boringssl.${ro.zygote}.rc...on late-inittrigger zygote-start...on zygote-startwait_for_prop odsign.verification.done 1# A/B update verifier that marks a successful boot.exec_start update_verifierstart statsdstart netdstart zygotestart zygote_secondary...on userspace-reboot-resumetrigger userspace-reboot-fs-remounttrigger post-fs-datatrigger zygote-starttrigger early-boottrigger boot

关于如何启动这两个进程,可以参照init.${ro.zygote}.rc。${ro.zygote}的值,可以通过shell指令“getprop ro.zygote”获取。以下图为例,zygote64_32代表64模式为主,32位模式为辅。

init.zygote64_32.rc

引用了init.zygote64.rc

声明了一个名字为“zygote_secondary”的service。该service将有/system/bin/app_process进程来启动。其中以“-”开头的是JVM参数,-Xzygote /system/bin。以“--”开头的是进程参数,--zygote --socket-name=zygote_secondary --enable-lazy-preload。

创建了两个socket,名字为zygote_secondary和usap_pool_secondary。

import /system/etc/init/hw/init.zygote64.rcservice zygote_secondary /system/bin/app_process32 -Xzygote /system/bin --zygote --socket-name=zygote_secondary --enable-lazy-preloadclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote_secondary stream 660 root systemsocket usap_pool_secondary stream 660 root systemonrestart restart zygotetask_profiles ProcessCapacityHigh MaxPerformance

init.zygote64.rc

声明了一个名字为“zygote”的service。该service将有/system/bin/app_process进程来启动。其中以“-”开头的是JVM参数,-Xzygote /system/bin。以“--”开头的是进程参数,--zygote --start-system-server --socket-name=zygote。

创建了两个socket,名字为zygote和usap_pool_primary。

service zygote /system/bin/app_process64 -Xzygote /system/bin --zygote --start-system-server --socket-name=zygoteclass mainpriority -20user rootgroup root readproc reserved_disksocket zygote stream 660 root systemsocket usap_pool_primary stream 660 root systemonrestart exec_background - system system -- /system/bin/vdc volume abort_fuseonrestart write /sys/power/state on# NOTE: If the wakelock name here is changed, then also# update it in SystemSuspend.cpponrestart write /sys/power/wake_lock zygote_kwlonrestart restart audioserveronrestart restart cameraserveronrestart restart mediaonrestart restart --only-if-running media.tuneronrestart restart netdonrestart restart wificondtask_profiles ProcessCapacityHigh MaxPerformancecritical window=${zygote.critical_window.minute:-off} target=zygote-fatal

init.boringssl.zygote64_32.rc

包含的都是在某些条件下触发的自测进程。了解即可。

on init && property:ro.product.cpu.abilist32=*exec_start boringssl_self_test32
on init && property:ro.product.cpu.abilist64=*exec_start boringssl_self_test64
on property:apexd.status=ready && property:ro.product.cpu.abilist32=*exec_start boringssl_self_test_apex32
on property:apexd.status=ready && property:ro.product.cpu.abilist64=*exec_start boringssl_self_test_apex64

app_process解析 

frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{if (!LOG_NDEBUG) {String8 argv_String;for (int i = 0; i < argc; ++i) {argv_String.append("\"");argv_String.append(argv[i]);argv_String.append("\" ");}ALOGV("app_process main with argv: %s", argv_String.c_str());}//步骤1:创建Runtime.--------------------------------------------AppRuntime runtime(argv[0], computeArgBlockSize(argc, argv));// Process command line arguments// ignore argv[0]argc--;argv++;// 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.//// As an exception to the above rule, anything in "spaced commands"// goes to the vm even though it has a space in it.const char* spaced_commands[] = { "-cp", "-classpath" };// Allow "spaced commands" to be succeeded by exactly 1 argument (regardless of -s).bool known_command = false;//步骤2:解析传参,将'-'开头的参数传给Runtime.--------------------------------------------int i;for (i = 0; i < argc; i++) {if (known_command == true) {runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add known option '%s'", argv[i]);known_command = false;continue;}for (int j = 0;j < static_cast<int>(sizeof(spaced_commands) / sizeof(spaced_commands[0]));++j) {if (strcmp(argv[i], spaced_commands[j]) == 0) {known_command = true;ALOGV("app_process main found known command '%s'", argv[i]);}}if (argv[i][0] != '-') {break;}if (argv[i][1] == '-' && argv[i][2] == 0) {++i; // Skip --.break;}runtime.addOption(strdup(argv[i]));// The static analyzer gets upset that we don't ever free the above// string. Since the allocation is from main, leaking it doesn't seem// problematic. NOLINTNEXTLINEALOGV("app_process main add option '%s'", argv[i]);}// Parse runtime arguments.  Stop at first unrecognized option.bool zygote = false;bool startSystemServer = false;bool application = false;String8 niceName;String8 className;//步骤3:整理zygote进程的传参.--------------------------------------------++i;  // Skip unused "parent dir" argument.while (i < argc) {const char* arg = argv[i++];if (strcmp(arg, "--zygote") == 0) {zygote = true;niceName = ZYGOTE_NICE_NAME;} 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 if (strncmp(arg, "--", 2) != 0) {className = arg;break;} else {--i;break;}}//步骤4:根据整理的zygote传参,判断是否是zygote进程,并根据判断结果,补充“--abi-list”和“start-system-server”传参.--------------------------------------------Vector<String8> args;if (!className.empty()) {// 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"));runtime.setClassNameAndArgs(className, argc - i, argv + i);if (!LOG_NDEBUG) {String8 restOfArgs;char* const* argv_new = argv + i;int argc_new = argc - i;for (int k = 0; k < argc_new; ++k) {restOfArgs.append("\"");restOfArgs.append(argv_new[k]);restOfArgs.append("\" ");}ALOGV("Class name = %s, args = %s", className.c_str(), restOfArgs.c_str());}} 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.empty()) {runtime.setArgv0(niceName.c_str(), true /* setProcName */);}
//步骤5:使用runtime,调用“com.android.internal.os.ZygoteInit”类,并传入传参,启动zygote.--------------------------------------------if (zygote) {runtime.start("com.android.internal.os.ZygoteInit", args, zygote);} else if (!className.empty()) {runtime.start("com.android.internal.os.RuntimeInit", args, zygote);} else {fprintf(stderr, "Error: no class name or --zygote supplied.\n");app_usage();LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");}
}

AndroidRuntime解析

frameworks/base/core/jni/AndroidRuntime.cpp

Runtime的start方法

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ALOGD(">>>>>> START %s uid %d <<<<<<\n",className != NULL ? className : "(unknown)", getuid());static const String8 startSystemServer("start-system-server");// Whether this is the primary zygote, meaning the zygote which will fork system server.bool primary_zygote = false;//步骤1:必要的参数及环境变量检查。-----------------------------------/** '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) {primary_zygote = true;/* 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 /system does not exist.");return;}setenv("ANDROID_ROOT", rootDir, 1);}const char* artRootDir = getenv("ANDROID_ART_ROOT");if (artRootDir == NULL) {LOG_FATAL("No ART directory specified with ANDROID_ART_ROOT environment variable.");return;}const char* i18nRootDir = getenv("ANDROID_I18N_ROOT");if (i18nRootDir == NULL) {LOG_FATAL("No runtime directory specified with ANDROID_I18N_ROOT environment variable.");return;}const char* tzdataRootDir = getenv("ANDROID_TZDATA_ROOT");if (tzdataRootDir == NULL) {LOG_FATAL("No tz data directory specified with ANDROID_TZDATA_ROOT environment variable.");return;}//const char* kernelHack = getenv("LD_ASSUME_KERNEL");//ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);//步骤2:启动VM。-----------------------------------/* start the virtual machine */JniInvocation jni_invocation;jni_invocation.Init(NULL);JNIEnv* env;if (startVm(&mJavaVM, &env, zygote, primary_zygote) != 0) {return;}onVmCreated(env);/** Register android functions.*/
//步骤3:注册JNI方法。-----------------------------------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;stringClass = env->FindClass("java/lang/String");assert(stringClass != NULL);strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);assert(strArray != NULL);classNameStr = env->NewStringUTF(className);assert(classNameStr != NULL);env->SetObjectArrayElement(strArray, 0, classNameStr);for (size_t i = 0; i < options.size(); ++i) {jstring optionsStr = env->NewStringUTF(options.itemAt(i).c_str());assert(optionsStr != NULL);env->SetObjectArrayElement(strArray, i + 1, optionsStr);}//步骤4:利用java的反射,调用com.android.internal.os.ZygoteInit类中的main方法,创建zygote进程。-----------------------------------/** Start VM.  This thread becomes the main thread of the VM, and will* not return until the VM exits.*/char* slashClassName = toSlashClassName(className != NULL ? 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 0if (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");
}

启动VM

该方法有600+行,都是收集VM参数,并调用JNI_CreateJavaVM启动VM。参数是一个可以做性能优化的点。了解即可。

int AndroidRuntime::startVm(JavaVM** pJavaVM, JNIEnv** pEnv, bool zygote, bool primary_zygote)
{JavaVMInitArgs initArgs;char propBuf[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 usejitOptsBuf[sizeof("-Xusejit:")-1 + PROPERTY_VALUE_MAX];char jitpthreadpriorityOptsBuf[sizeof("-Xjitpthreadpriority:")-1 + PROPERTY_VALUE_MAX];char jitmaxsizeOptsBuf[sizeof("-Xjitmaxsize:")-1 + PROPERTY_VALUE_MAX];char jitinitialsizeOptsBuf[sizeof("-Xjitinitialsize:")-1 + PROPERTY_VALUE_MAX];char jitthresholdOptsBuf[sizeof("-Xjitthreshold:")-1 + PROPERTY_VALUE_MAX];char jitprithreadweightOptBuf[sizeof("-Xjitprithreadweight:")-1 + PROPERTY_VALUE_MAX];char jittransitionweightOptBuf[sizeof("-Xjittransitionweight:")-1 + PROPERTY_VALUE_MAX];char hotstartupsamplesOptsBuf[sizeof("-Xps-hot-startup-method-samples:")-1 + PROPERTY_VALUE_MAX];char saveResolvedClassesDelayMsOptsBuf[sizeof("-Xps-save-resolved-classes-delay-ms:")-1 + PROPERTY_VALUE_MAX];char profileMinSavePeriodOptsBuf[sizeof("-Xps-min-save-period-ms:")-1 + PROPERTY_VALUE_MAX];char profileMinFirstSaveOptsBuf[sizeof("-Xps-min-first-save-ms:") - 1 + PROPERTY_VALUE_MAX];char profileInlineCacheThresholdOptsBuf[sizeof("-Xps-inline-cache-threshold:") - 1 + PROPERTY_VALUE_MAX];char madviseWillNeedFileSizeVdex[sizeof("-XMadviseWillNeedVdexFileSize:")-1 + PROPERTY_VALUE_MAX];char madviseWillNeedFileSizeOdex[sizeof("-XMadviseWillNeedOdexFileSize:")-1 + PROPERTY_VALUE_MAX];char madviseWillNeedFileSizeArt[sizeof("-XMadviseWillNeedArtFileSize:")-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 foregroundHeapGrowthMultiplierOptsBuf[sizeof("-XX:ForegroundHeapGrowthMultiplier=")-1 + PROPERTY_VALUE_MAX];char finalizerTimeoutMsOptsBuf[sizeof("-XX:FinalizerTimeoutMs=")-1 + PROPERTY_VALUE_MAX];char threadSuspendTimeoutOptsBuf[sizeof("-XX:ThreadSuspendTimeout=")-1 + PROPERTY_VALUE_MAX];char cachePruneBuf[sizeof("-Xzygote-max-boot-retry=")-1 + PROPERTY_VALUE_MAX];char dex2oatXmsImageFlagsBuf[sizeof("-Xms")-1 + PROPERTY_VALUE_MAX];char dex2oatXmxImageFlagsBuf[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 dex2oatThreadsBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];char dex2oatThreadsImageBuf[sizeof("-j")-1 + PROPERTY_VALUE_MAX];char dex2oatCpuSetBuf[sizeof("--cpu-set=")-1 + PROPERTY_VALUE_MAX];char dex2oatCpuSetImageBuf[sizeof("--cpu-set=")-1 + PROPERTY_VALUE_MAX];char dex2oat_isa_variant_key[PROPERTY_KEY_MAX];char dex2oat_isa_variant[sizeof("--instruction-set-variant=") -1 + PROPERTY_VALUE_MAX];char dex2oat_isa_features_key[PROPERTY_KEY_MAX];char dex2oat_isa_features[sizeof("--instruction-set-features=") -1 + PROPERTY_VALUE_MAX];char dex2oatFlagsBuf[PROPERTY_VALUE_MAX];char dex2oatImageFlagsBuf[PROPERTY_VALUE_MAX];char extraOptsBuf[PROPERTY_VALUE_MAX];char perfettoHprofOptBuf[sizeof("-XX:PerfettoHprof=") + PROPERTY_VALUE_MAX];char perfettoJavaHeapStackOptBuf[sizeof("-XX:PerfettoJavaHeapStackProf=") + PROPERTY_VALUE_MAX];enum {kEMDefault,kEMIntPortable,kEMIntFast,kEMJitCompiler,} executionMode = kEMDefault;char localeOption[sizeof("-Duser.locale=") + PROPERTY_VALUE_MAX];char lockProfThresholdBuf[sizeof("-Xlockprofthreshold:")-1 + PROPERTY_VALUE_MAX];char nativeBridgeLibrary[sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX];char cpuAbiListBuf[sizeof("--cpu-abilist=") + PROPERTY_VALUE_MAX];char corePlatformApiPolicyBuf[sizeof("-Xcore-platform-api-policy:") + PROPERTY_VALUE_MAX];char methodTraceFileBuf[sizeof("-Xmethod-trace-file:") + PROPERTY_VALUE_MAX];char methodTraceFileSizeBuf[sizeof("-Xmethod-trace-file-size:") + PROPERTY_VALUE_MAX];std::string fingerprintBuf;char javaZygoteForkLoopBuf[sizeof("-XX:ForceJavaZygoteForkLoop=") + PROPERTY_VALUE_MAX];char jdwpProviderBuf[sizeof("-XjdwpProvider:") - 1 + PROPERTY_VALUE_MAX];char opaqueJniIds[sizeof("-Xopaque-jni-ids:") - 1 + PROPERTY_VALUE_MAX];char bootImageBuf[sizeof("-Ximage:") - 1 + PROPERTY_VALUE_MAX];// Read if we are using the profile configuration, do this at the start since the last ART args// take precedence.std::string profile_boot_class_path_flag =server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,PROFILE_BOOT_CLASS_PATH,/*default_value=*/"");bool profile_boot_class_path;switch (ParseBool(profile_boot_class_path_flag)) {case ParseBoolResult::kError:// Default to the system property.profile_boot_class_path =GetBoolProperty("dalvik.vm.profilebootclasspath", /*default_value=*/false);break;case ParseBoolResult::kTrue:profile_boot_class_path = true;break;case ParseBoolResult::kFalse:profile_boot_class_path = false;break;}if (profile_boot_class_path) {addOption("-Xcompiler-option");addOption("--count-hotness-in-compiled-code");addOption("-Xps-profile-boot-class-path");addOption("-Xps-profile-aot-code");addOption("-Xjitsaveprofilinginfo");}std::string use_jitzygote_image_flag =server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,ENABLE_JITZYGOTE_IMAGE,/*default_value=*/"");// Use the APEX boot image for boot class path profiling to get JIT samples on BCP methods.// Also use the APEX boot image if it's explicitly enabled via configuration flag.const bool use_apex_image = profile_boot_class_path || (use_jitzygote_image_flag == "true");if (use_apex_image) {ALOGI("Using JIT Zygote image: '%s'\n", kJitZygoteImageOption);addOption(kJitZygoteImageOption);} else if (parseRuntimeOption("dalvik.vm.boot-image", bootImageBuf, "-Ximage:")) {ALOGI("Using dalvik.vm.boot-image: '%s'\n", bootImageBuf);} else {ALOGI("Using default boot image");}std::string disable_lock_profiling =server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,DISABLE_LOCK_PROFILING,/*default_value=*/ "");if (disable_lock_profiling == "true") {addOption(kLockProfThresholdRuntimeOption);ALOGI("Disabling lock profiling: '%s'\n", kLockProfThresholdRuntimeOption);} else {ALOGI("Leaving lock profiling enabled");}const bool checkJni = GetBoolProperty("dalvik.vm.checkjni", false);if (checkJni) {ALOGD("CheckJNI is ON");/* extended JNI checking */addOption("-Xcheck:jni");/* with -Xcheck:jni, this provides a JNI function call trace *///addOption("-verbose:jni");}const bool odsignVerificationSuccess = GetBoolProperty("odsign.verification.success", false);if (!odsignVerificationSuccess) {addOption("-Xdeny-art-apex-data-files");}property_get("dalvik.vm.execution-mode", propBuf, "");if (strcmp(propBuf, "int:portable") == 0) {executionMode = kEMIntPortable;} else if (strcmp(propBuf, "int:fast") == 0) {executionMode = kEMIntFast;} else if (strcmp(propBuf, "int:jit") == 0) {executionMode = kEMJitCompiler;}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");// On Android, we always want to allow loading the PerfettoHprof plugin.// Even with this option set, we will still only actually load the plugin// if we are on a userdebug build or the app is debuggable or profileable.// This is enforced in art/runtime/runtime.cc.//// We want to be able to disable this, because this does not work on host,// and we do not want to enable it in tests.parseRuntimeOption("dalvik.vm.perfetto_hprof", perfettoHprofOptBuf, "-XX:PerfettoHprof=","true");// Enable PerfettoJavaHeapStackProf in the zygoteparseRuntimeOption("dalvik.vm.perfetto_javaheap", perfettoJavaHeapStackOptBuf,"-XX:PerfettoJavaHeapStackProf=", "true");if (primary_zygote) {addOption("-Xprimaryzygote");}/** The default starting and maximum size of the heap.  Larger* values should be specified in a product property override.*/parseRuntimeOption("dalvik.vm.heapstartsize", heapstartsizeOptsBuf, "-Xms", "4m");parseRuntimeOption("dalvik.vm.heapsize", heapsizeOptsBuf, "-Xmx", "16m");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=");/* Foreground heap growth multiplier option */parseRuntimeOption("dalvik.vm.foreground-heap-growth-multiplier",foregroundHeapGrowthMultiplierOptsBuf,"-XX:ForegroundHeapGrowthMultiplier=");/** Finalizer and thread suspend timeouts.*/parseRuntimeOption("dalvik.vm.finalizer-timeout-ms",finalizerTimeoutMsOptsBuf,"-XX:FinalizerTimeoutMs=");parseRuntimeOption("dalvik.vm.thread-suspend-timeout-ms",threadSuspendTimeoutOptsBuf,"-XX:ThreadSuspendTimeout=");/** JIT related options.*/parseRuntimeOption("dalvik.vm.usejit", usejitOptsBuf, "-Xusejit:");parseRuntimeOption("dalvik.vm.jitmaxsize", jitmaxsizeOptsBuf, "-Xjitmaxsize:");parseRuntimeOption("dalvik.vm.jitinitialsize", jitinitialsizeOptsBuf, "-Xjitinitialsize:");parseRuntimeOption("dalvik.vm.jitthreshold", jitthresholdOptsBuf, "-Xjitthreshold:");parseRuntimeOption("dalvik.vm.jitpthreadpriority",jitpthreadpriorityOptsBuf,"-Xjitpthreadpriority:");addOption("-Xjitsaveprofilinginfo");parseRuntimeOption("dalvik.vm.jitprithreadweight",jitprithreadweightOptBuf,"-Xjitprithreadweight:");parseRuntimeOption("dalvik.vm.jittransitionweight", jittransitionweightOptBuf,"-Xjittransitionweight:");/** Use default platform configuration as limits for madvising,* when no properties are specified.*/parseRuntimeOption("dalvik.vm.madvise.vdexfile.size",madviseWillNeedFileSizeVdex,"-XMadviseWillNeedVdexFileSize:");parseRuntimeOption("dalvik.vm.madvise.odexfile.size",madviseWillNeedFileSizeOdex,"-XMadviseWillNeedOdexFileSize:");parseRuntimeOption("dalvik.vm.madvise.artfile.size",madviseWillNeedFileSizeArt,"-XMadviseWillNeedArtFileSize:");/** Profile related options.*/parseRuntimeOption("dalvik.vm.hot-startup-method-samples", hotstartupsamplesOptsBuf,"-Xps-hot-startup-method-samples:");parseRuntimeOption("dalvik.vm.ps-resolved-classes-delay-ms", saveResolvedClassesDelayMsOptsBuf,"-Xps-save-resolved-classes-delay-ms:");parseRuntimeOption("dalvik.vm.ps-min-save-period-ms", profileMinSavePeriodOptsBuf,"-Xps-min-save-period-ms:");parseRuntimeOption("dalvik.vm.ps-min-first-save-ms", profileMinFirstSaveOptsBuf,"-Xps-min-first-save-ms:");parseRuntimeOption("dalvik.vm.ps-inline-cache-threshold", profileInlineCacheThresholdOptsBuf,"-Xps-inline-cache-threshold:");property_get("ro.config.low_ram", propBuf, "");if (strcmp(propBuf, "true") == 0) {addOption("-XX:LowMemoryMode");}/** Garbage-collection related options.*/parseRuntimeOption("dalvik.vm.gctype", gctypeOptsBuf, "-Xgc:");// If it set, honor the "enable_generational_cc" device configuration;// otherwise, let the runtime use its default behavior.std::string enable_generational_cc =server_configurable_flags::GetServerConfigurableFlag(RUNTIME_NATIVE_BOOT_NAMESPACE,ENABLE_GENERATIONAL_CC,/*default_value=*/ "");if (enable_generational_cc == "true") {addOption(kGenerationalCCRuntimeOption);} else if (enable_generational_cc == "false") {addOption(kNoGenerationalCCRuntimeOption);}parseRuntimeOption("dalvik.vm.backgroundgctype", backgroundgcOptsBuf, "-XX:BackgroundGC=");/** Enable/disable zygote native fork loop.*/parseRuntimeOption("dalvik.vm.force-java-zygote-fork-loop",javaZygoteForkLoopBuf,"-XX:ForceJavaZygoteForkLoop=");/** Enable debugging only for apps forked from zygote.*/if (zygote) {// Set the JDWP provider and required arguments. By default let the runtime choose how JDWP is// implemented. When this is not set the runtime defaults to not allowing JDWP.addOption("-XjdwpOptions:suspend=n,server=y");parseRuntimeOption("dalvik.vm.jdwp-provider",jdwpProviderBuf,"-XjdwpProvider:","default");}// Only pass an explicit opaque-jni-ids to apps forked from zygoteif (zygote) {parseRuntimeOption("dalvik.vm.opaque-jni-ids",opaqueJniIds,"-Xopaque-jni-ids:","swapable");}parseRuntimeOption("dalvik.vm.lockprof.threshold",lockProfThresholdBuf,"-Xlockprofthreshold:");if (executionMode == kEMIntPortable) {addOption("-Xint:portable");} else if (executionMode == kEMIntFast) {addOption("-Xint:fast");} else if (executionMode == kEMJitCompiler) {addOption("-Xint:jit");}// Extra options for JIT.parseCompilerOption("dalvik.vm.dex2oat-filter", dex2oatCompilerFilterBuf,"--compiler-filter=", "-Xcompiler-option");parseCompilerOption("dalvik.vm.dex2oat-threads", dex2oatThreadsBuf, "-j", "-Xcompiler-option");parseCompilerOption("dalvik.vm.dex2oat-cpu-set", dex2oatCpuSetBuf, "--cpu-set=","-Xcompiler-option");// Copy the variant.sprintf(dex2oat_isa_variant_key, "dalvik.vm.isa.%s.variant", ABI_STRING);parseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,"--instruction-set-variant=", "-Xcompiler-option");// Copy the features.sprintf(dex2oat_isa_features_key, "dalvik.vm.isa.%s.features", ABI_STRING);parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,"--instruction-set-features=", "-Xcompiler-option");/** When running with debug.generate-debug-info, add --generate-debug-info to the compiler* options so that both JITted code and the boot image, if it is compiled on device, will* include native debugging information.*/property_get("debug.generate-debug-info", propBuf, "");bool generate_debug_info = (strcmp(propBuf, "true") == 0);if (generate_debug_info) {addOption("-Xcompiler-option");addOption("--generate-debug-info");}// The mini-debug-info makes it possible to backtrace through compiled code.bool generate_mini_debug_info = property_get_bool("dalvik.vm.minidebuginfo", 0);if (generate_mini_debug_info) {addOption("-Xcompiler-option");addOption("--generate-mini-debug-info");}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);// Extra options for boot image generation.parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xms", dex2oatXmsImageFlagsBuf,"-Xms", "-Ximage-compiler-option");parseCompilerRuntimeOption("dalvik.vm.image-dex2oat-Xmx", dex2oatXmxImageFlagsBuf,"-Xmx", "-Ximage-compiler-option");parseCompilerOption("dalvik.vm.image-dex2oat-filter", dex2oatImageCompilerFilterBuf,"--compiler-filter=", "-Ximage-compiler-option");// If there is a dirty-image-objects file, push it.if (hasFile("/system/etc/dirty-image-objects")) {addOption("-Ximage-compiler-option");addOption("--dirty-image-objects=/system/etc/dirty-image-objects");}parseCompilerOption("dalvik.vm.image-dex2oat-threads", dex2oatThreadsImageBuf, "-j","-Ximage-compiler-option");parseCompilerOption("dalvik.vm.image-dex2oat-cpu-set", dex2oatCpuSetImageBuf, "--cpu-set=","-Ximage-compiler-option");// The runtime may compile a boot image, when necessary, not using installd. Thus, we need// to pass the instruction-set-features/variant as an image-compiler-option.// Note: it is OK to reuse the buffer, as the values are exactly the same between//       * compiler-option, used for runtime compilation (DexClassLoader)//       * image-compiler-option, used for boot-image compilation on deviceparseCompilerOption(dex2oat_isa_variant_key, dex2oat_isa_variant,"--instruction-set-variant=", "-Ximage-compiler-option");parseCompilerOption(dex2oat_isa_features_key, dex2oat_isa_features,"--instruction-set-features=", "-Ximage-compiler-option");if (generate_debug_info) {addOption("-Ximage-compiler-option");addOption("--generate-debug-info");}if (generate_mini_debug_info) {addOption("-Ximage-compiler-option");addOption("--generate-mini-debug-info");}property_get("dalvik.vm.image-dex2oat-flags", dex2oatImageFlagsBuf, "");parseExtraOpts(dex2oatImageFlagsBuf, "-Ximage-compiler-option");/* Set the properties for locale */{strcpy(localeOption, "-Duser.locale=");const std::string locale = readLocale();strncat(localeOption, locale.c_str(), PROPERTY_VALUE_MAX);addOption(localeOption);}// Trace files are stored in /data/misc/trace which is writable only in debug mode.property_get("ro.debuggable", propBuf, "0");if (strcmp(propBuf, "1") == 0) {property_get("dalvik.vm.method-trace", propBuf, "false");if (strcmp(propBuf, "true") == 0) {addOption("-Xmethod-trace");parseRuntimeOption("dalvik.vm.method-trace-file",methodTraceFileBuf,"-Xmethod-trace-file:");parseRuntimeOption("dalvik.vm.method-trace-file-siz",methodTraceFileSizeBuf,"-Xmethod-trace-file-size:");property_get("dalvik.vm.method-trace-stream", propBuf, "false");if (strcmp(propBuf, "true") == 0) {addOption("-Xmethod-trace-stream");}}}// Native bridge library. "0" means that native bridge is disabled.//// Note: bridging is only enabled for the zygote. Other runs of//       app_process may not have the permissions to mount etc.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 (zygote && strcmp(propBuf, "0") != 0) {snprintf(nativeBridgeLibrary, sizeof("-XX:NativeBridge=") + PROPERTY_VALUE_MAX,"-XX:NativeBridge=%s", propBuf);addOption(nativeBridgeLibrary);}#if defined(__LP64__)const char* cpu_abilist_property_name = "ro.product.cpu.abilist64";
#elseconst char* cpu_abilist_property_name = "ro.product.cpu.abilist32";
#endif  // defined(__LP64__)property_get(cpu_abilist_property_name, propBuf, "");if (propBuf[0] == '\0') {ALOGE("%s is not expected to be empty", cpu_abilist_property_name);return -1;}snprintf(cpuAbiListBuf, sizeof(cpuAbiListBuf), "--cpu-abilist=%s", propBuf);addOption(cpuAbiListBuf);// Dalvik-cache pruning counter.parseRuntimeOption("dalvik.vm.zygote.max-boot-retry", cachePruneBuf,"-Xzygote-max-boot-retry=");// If set, the property below can be used to enable core platform API violation reporting.property_get("persist.debug.dalvik.vm.core_platform_api_policy", propBuf, "");if (propBuf[0] != '\0') {snprintf(corePlatformApiPolicyBuf,sizeof(corePlatformApiPolicyBuf),"-Xcore-platform-api-policy:%s",propBuf);addOption(corePlatformApiPolicyBuf);}/** Retrieve the build fingerprint and provide it to the runtime. That way, ANR dumps will* contain the fingerprint and can be parsed.* Fingerprints are potentially longer than PROPERTY_VALUE_MAX, so parseRuntimeOption() cannot* be used here.* Do not ever re-assign fingerprintBuf as its c_str() value is stored in mOptions.*/std::string fingerprint = GetProperty("ro.build.fingerprint", "");if (!fingerprint.empty()) {fingerprintBuf = "-Xfingerprint:" + fingerprint;addOption(fingerprintBuf.c_str());}initArgs.version = JNI_VERSION_1_4;initArgs.options = mOptions.editArray();initArgs.nOptions = mOptions.size();initArgs.ignoreUnrecognized = JNI_FALSE;/** 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) {ALOGE("JNI_CreateJavaVM failed\n");return -1;}return 0;
}

注册JNI

将gRegJNI数组里的JNI方法进行注册。RegJNIRec 包含100+个JNI方法。

/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{ATRACE_NAME("RegisterAndroidNatives");/** 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);//核心步骤:将gRegJNI数组里的JNI方法进行注册。if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {env->PopLocalFrame(NULL);return -1;}env->PopLocalFrame(NULL);//createJavaThread("fubar", quickTest, (void*) "hello");return 0;
}
static const RegJNIRec gRegJNI[] = {REG_JNI(register_com_android_internal_os_RuntimeInit),REG_JNI(register_com_android_internal_os_ZygoteInit_nativeZygoteInit),REG_JNI(register_android_os_SystemClock),REG_JNI(register_android_util_CharsetUtils),REG_JNI(register_android_util_EventLog),REG_JNI(register_android_util_Log),REG_JNI(register_android_util_MemoryIntArray),REG_JNI(register_android_app_admin_SecurityLog),REG_JNI(register_android_content_AssetManager),REG_JNI(register_android_content_StringBlock),REG_JNI(register_android_content_XmlBlock),REG_JNI(register_android_content_res_ApkAssets),REG_JNI(register_android_content_res_ResourceTimer),REG_JNI(register_android_text_AndroidCharacter),REG_JNI(register_android_text_Hyphenator),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_os_PerformanceHintManager),REG_JNI(register_android_os_HidlMemory),REG_JNI(register_android_os_HidlSupport),REG_JNI(register_android_os_HwBinder),REG_JNI(register_android_os_HwBlob),REG_JNI(register_android_os_HwParcel),REG_JNI(register_android_os_HwRemoteBinder),REG_JNI(register_android_os_NativeHandle),REG_JNI(register_android_os_ServiceManager),REG_JNI(register_android_os_ServiceManagerNative),REG_JNI(register_android_os_storage_StorageManager),REG_JNI(register_android_service_DataLoaderService),REG_JNI(register_android_view_DisplayEventReceiver),REG_JNI(register_android_view_Surface),REG_JNI(register_android_view_SurfaceControl),REG_JNI(register_android_view_SurfaceControlHdrLayerInfoListener),REG_JNI(register_android_view_SurfaceSession),REG_JNI(register_android_view_InputApplicationHandle),// This must be called after register_android_view_SurfaceControl since it has a dependency// on the Java SurfaceControl object that references a native resource via static request.REG_JNI(register_android_view_InputWindowHandle),REG_JNI(register_android_view_CompositionSamplingListener),REG_JNI(register_android_view_TextureView),REG_JNI(register_android_view_TunnelModeEnabledListener),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_EGL15),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_opengl_jni_GLES32),REG_JNI(register_android_graphics_classes),REG_JNI(register_android_graphics_BLASTBufferQueue),REG_JNI(register_android_graphics_GraphicBuffer),REG_JNI(register_android_graphics_GraphicsStatsService),REG_JNI(register_android_graphics_SurfaceTexture),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_database_SQLiteRawStatement),REG_JNI(register_android_os_Debug),REG_JNI(register_android_os_FileObserver),REG_JNI(register_android_os_GraphicsEnvironment),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_os_MemoryFile),REG_JNI(register_android_os_SharedMemory),REG_JNI(register_android_os_incremental_IncrementalManager),REG_JNI(register_com_android_internal_content_om_OverlayConfig),REG_JNI(register_com_android_internal_content_om_OverlayManagerImpl),REG_JNI(register_com_android_internal_net_NetworkUtilsInternal),REG_JNI(register_com_android_internal_os_ClassLoaderFactory),REG_JNI(register_com_android_internal_os_LongArrayMultiStateCounter),REG_JNI(register_com_android_internal_os_LongMultiStateCounter),REG_JNI(register_com_android_internal_os_Zygote),REG_JNI(register_com_android_internal_os_ZygoteCommandBuffer),REG_JNI(register_com_android_internal_os_ZygoteInit),REG_JNI(register_com_android_internal_security_VerityUtils),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_DngCreator),REG_JNI(register_android_hardware_camera2_impl_CameraExtensionJpegProcessor),REG_JNI(register_android_hardware_camera2_utils_SurfaceUtils),REG_JNI(register_android_hardware_display_DisplayManagerGlobal),REG_JNI(register_android_hardware_HardwareBuffer),REG_JNI(register_android_hardware_OverlayProperties),REG_JNI(register_android_hardware_SensorManager),REG_JNI(register_android_hardware_SerialPort),REG_JNI(register_android_hardware_SyncFence),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_AudioDeviceAttributes),REG_JNI(register_android_media_AudioEffectDescriptor),REG_JNI(register_android_media_AudioSystem),REG_JNI(register_android_media_AudioRecord),REG_JNI(register_android_media_AudioTrack),REG_JNI(register_android_media_AudioAttributes),REG_JNI(register_android_media_AudioProductStrategies),REG_JNI(register_android_media_AudioVolumeGroups),REG_JNI(register_android_media_AudioVolumeGroupChangeHandler),REG_JNI(register_android_media_MediaMetrics),REG_JNI(register_android_media_MicrophoneInfo),REG_JNI(register_android_media_RemoteDisplay),REG_JNI(register_android_media_ToneGenerator),REG_JNI(register_android_media_audio_common_AidlConversion),REG_JNI(register_android_media_midi),REG_JNI(register_android_opengl_classes),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_Activity),REG_JNI(register_android_app_ActivityThread),REG_JNI(register_android_app_NativeActivity),REG_JNI(register_android_util_jar_StrictJarFile),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_MotionPredictor),REG_JNI(register_android_view_PointerIcon),REG_JNI(register_android_view_VelocityTracker),REG_JNI(register_android_view_VerifiedKeyEvent),REG_JNI(register_android_view_VerifiedMotionEvent),REG_JNI(register_android_content_res_ObbScanner),REG_JNI(register_android_content_res_Configuration),REG_JNI(register_android_animation_PropertyValuesHolder),REG_JNI(register_android_security_Scrypt),REG_JNI(register_com_android_internal_content_F2fsUtils),REG_JNI(register_com_android_internal_content_NativeLibraryHelper),REG_JNI(register_com_android_internal_os_FuseAppLoop),REG_JNI(register_com_android_internal_os_KernelAllocationStats),REG_JNI(register_com_android_internal_os_KernelCpuBpfTracking),REG_JNI(register_com_android_internal_os_KernelCpuTotalBpfMapReader),REG_JNI(register_com_android_internal_os_KernelCpuUidBpfMapReader),REG_JNI(register_com_android_internal_os_KernelSingleProcessCpuThreadReader),REG_JNI(register_com_android_internal_os_KernelSingleUidTimeReader),REG_JNI(register_android_window_WindowInfosListener),REG_JNI(register_android_window_ScreenCapture),REG_JNI(register_jni_common),REG_JNI(register_android_tracing_PerfettoDataSource),REG_JNI(register_android_tracing_PerfettoDataSourceInstance),REG_JNI(register_android_tracing_PerfettoProducer),
};

相关文章:

Android Framework学习笔记(4)----Zygote进程

Zygote的启动流程 Init进程启动后&#xff0c;会加载并执行init.rc文件。该.rc文件中&#xff0c;就包含启动Zygote进程的Action。详见“RC文件解析”章节。 根据Zygote对应的RC文件&#xff0c;可知Zygote进程是由/system/bin/app_process程序来创建的。 app_process大致处…...

澎湃算力 玩转AI 华为昇腾AI开发板——香橙派OriengePi AiPro边缘计算案例评测

澎湃算力 玩转AI 华为昇腾AI开发板 香橙派OriengePi AiPro 边缘计算案例评测 人工智能&#xff08;AI&#xff09;技术正以前所未有的速度改变着我们的生活、工作乃至整个社会的面貌。作为推动这一变革的关键力量&#xff0c;边缘计算与AI技术的深度融合正成为行业发展的新趋势…...

<数据集>铁轨缺陷检测数据集<目标检测>

数据集格式&#xff1a;VOCYOLO格式 图片数量&#xff1a;844张 标注数量(xml文件个数)&#xff1a;844 标注数量(txt文件个数)&#xff1a;844 标注类别数&#xff1a;3 标注类别名称&#xff1a;[Spalling, Squat, Wheel Burn] 序号类别名称图片数框数1Spalling3315522…...

第2章 矩阵

A 乘以此列向量&#xff0c;1的位置依次往下&#xff0c;所以A的列向量全为0 B C、D 取BE 要统一...

抖音seo短视频矩阵源码系统开发搭建----开源+二次开发

抖音seo短视频矩阵源码系统开发搭建 是一项技术密集型工作&#xff0c;需要对大数据处理、人工智能等领域有深入了解。该系统开发过程中需要用到多种编程语言&#xff0c;如Java、Python等。同时&#xff0c;需要使用一些框架和技术&#xff0c;如Hadoop、Spark、PyTorch等&am…...

【ELK】简述

ELK 堆栈的作用 大规模日志管理与分析 随着系统规模的扩大&#xff0c;应用程序、服务器和网络设备会产生大量的日志数据。ELK 堆栈可以集中收集、存储和索引这些分散在不同服务器和系统中的日志&#xff0c;方便快速检索和分析&#xff0c;帮助您快速定位系统故障、异常事件和…...

PyTorch张量数值计算

文章目录 1、张量基本运算2、阿达玛积3、点积运算4、指定运算设备⭐5、解决在GPU运行PyTorch的问题 &#x1f343;作者介绍&#xff1a;双非本科大三网络工程专业在读&#xff0c;阿里云专家博主&#xff0c;专注于Java领域学习&#xff0c;擅长web应用开发、数据结构和算法&am…...

Dockerfile相关命令

Dockerfile Dockerfile 是一个用来构建Docker镜像的文本文件&#xff0c;包含了一系列构建镜像所需的指令和参数。 指令详解 Dockerfile 指令说明FROM指定基础镜像&#xff0c;用于后续的指令构建&#xff0c;必须为第一个命令MAINTAINER指定Dockerfile的作者/维护者。&…...

【AI教程-吴恩达讲解Prompts】第1篇 - 课程简介

文章目录 简介Prompt学习相关资源 两类大模型原则与技巧 简介 欢迎来到面向开发者的提示工程部分&#xff0c;本部分内容基于吴恩达老师的《Prompt Engineering for Developer》课程进行编写。《Prompt Engineering for Developer》课程是由吴恩达老师与 OpenAI 技术团队成员 I…...

Leetcode - 周赛406

目录 一&#xff0c;3216. 交换后字典序最小的字符串 二&#xff0c;3217. 从链表中移除在数组中存在的节点 三&#xff0c;3218. 切蛋糕的最小总开销 I 四&#xff0c;3219. 切蛋糕的最小总开销 II 一&#xff0c;3216. 交换后字典序最小的字符串 本题要求交换一次相邻字符…...

【JavaScript 算法】拓扑排序:有向无环图的应用

&#x1f525; 个人主页&#xff1a;空白诗 文章目录 一、算法原理二、算法实现方法一&#xff1a;Kahn算法方法二&#xff1a;深度优先搜索&#xff08;DFS&#xff09;注释说明&#xff1a; 三、应用场景四、总结 拓扑排序&#xff08;Topological Sorting&#xff09;是一种…...

Fastgpt本地或服务器私有化部署常见问题

一、错误排查方式 遇到问题先按下面方式排查。 docker ps -a 查看所有容器运行状态,检查是否全部 running,如有异常,尝试docker logs 容器名查看对应日志。容器都运行正常的,docker logs 容器名 查看报错日志带有requestId的,都是 OneAPI 提示错误,大部分都是因为模型接…...

基于深度学习的股票预测

基于深度学习的股票预测是一项复杂且具有挑战性的任务&#xff0c;涉及金融数据的分析和预测。其目的是利用深度学习模型来预测股票价格的走势&#xff0c;从而帮助投资者做出更为准确的投资决策。以下是对这一领域的系统介绍&#xff1a; 1. 任务和目标 股票预测的主要任务和…...

UNiapp 微信小程序渐变不生效

开始用的一直是这个&#xff0c;调试一直没问题&#xff0c;但是重新启动就没生效&#xff0c;经查询这个不适合小程序使用&#xff1a;不适合没生效 background-image:linear-gradient(to right, #33f38d8a,#6dd5ed00); 正确使用下面这个&#xff1a; 生效&#xff0c;适合…...

FinClip 率先入驻 AWS Marketplace,加速全球市场布局

近日&#xff0c;凡泰极客旗下的小程序数字管理平台 FinClip 已成功上线亚马逊云科技&#xff08;AWS&#xff09;Marketplace。未来&#xff0c;FinClip 将主要服务于海外市场的开放银行、超级钱包、财富管理、社交电商、智慧城市解决方案等领域。 在全球市场的多样性需求推动…...

ChatGPT对话:Windows如何将Python训练模型转换为TensorFlow.js格式

【编者按】编者目前正在做手机上的人工智能软件&#xff0c;第一次做这种工作&#xff0c;从一些基本工作开始与ChatGPT交流。对初学者应该有帮助。 一天后修改文章补充内容&#xff1a; 解决TensorFlow 2.X与TensorFlow Decision Forests版本冲突问题&#xff1a; 在使用tens…...

封装网络请求 鸿蒙APP HarmonyOS ArkTS

一、效果展示 通过在页面直接调用 userLogin(params) 方法&#xff0c;获取登录令牌 二、申请网络权限 访问网络时候首先需要申请网络权限&#xff0c;需要修改 src/main 目录下的 module.json5 文件&#xff0c;加入 requestPermissions 属性&#xff0c;详见官方文档 【声明权…...

2024年度上半年中国汽车保值率报告

来源&#xff1a;中国汽车流通协会&精真估 近期历史回顾&#xff1a; 2024上半年房地产企业数智化转型报告.pdf 2024国产院线电影路演数据洞察报告.pdf 空间数据智能大模型研究-2024年中国空间数据智能战略发展白皮书.pdf 2024年全球资产管理报告 2024年中型律师事务所的法…...

Go语言之内存分配

文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ 文章收录在网站&#xff1a;http://hardyfish.top/ Go 语言程序所管理的虚拟内存空间会被分为两部分&#xff1a;堆内…...

北京交通大学《深度学习》专业课,实验3卷积、空洞卷积、残差神经网络实验

一、实验要求 1. 二维卷积实验&#xff08;平台课与专业课要求相同&#xff09; ⚫ 手写二维卷积的实现&#xff0c;并在至少一个数据集上进行实验&#xff0c;从训练时间、预测精 度、Loss变化等角度分析实验结果&#xff08;最好使用图表展示&#xff09; ⚫ 使用torch.nn…...

WPF中UI元素继承关系

在 WPF&#xff08;Windows Presentation Foundation&#xff09;框架中&#xff0c;UI 元素是基于一个层次化的类结构构建的&#xff0c;这个结构以 FrameworkElement 类为核心&#xff0c;大多数 UI 元素都是 FrameworkElement 或其派生类的子类。FrameworkElement 类本身又继…...

qml 实现一个listview

主要通过qml实现listvie功能&#xff0c;主要包括右键菜单&#xff0c;滚动条&#xff0c;拖动改变内容等&#xff0c;c 与 qml之间的变量和函数的调用。 main.cpp #include <QQuickItem> #include <QQmlContext> #include "testlistmodel.h" int main…...

【Leetcode】十六、深度优先搜索 宽度优先搜索 :二叉树的层序遍历

文章目录 1、深度优先搜索算法2、宽度优先搜索算法3、leetcode102&#xff1a;二叉树的层序遍历4、leetcode107&#xff1a;二叉树的层序遍历II5、leetcode938&#xff1a;二叉搜索树的范围和 1、深度优先搜索算法 深度优先搜索&#xff0c;即DFS&#xff0c;从root节点开始&a…...

Ruby教程

Ruby是一种动态的、面向对象的、解释型的脚本语言&#xff0c;以其简洁和易读性而闻名。Ruby的设计哲学强调程序员的生产力和代码的可读性&#xff0c;同时也融合了功能性和面向对象编程的特性。 以下是一个基础的Ruby教程&#xff0c;涵盖了一些基本概念和语法&#xff1a; …...

react + pro-components + ts完成单文件上传和批量上传

上传部分使用的是antd中的Upload组件,具体如下: GradingFilingReportUpload方法是后端已经做好文件流,前端只需要调用接口即可 单文件上传 <Uploadkey{upload_${record.id}}showUploadList{false}accept".xlsx"maxCount{1}customRequest{({ file }) > {const …...

暑假第一周——ZARA仿写

iOS学习 前言首页&#xff1a;无限轮播图商城&#xff1a;分类我的&#xff1a;自定义cell总结 前言 结束了UI的基础学习&#xff0c;现在综合运用开始写第一个demo&#xff0c;在实践中提升。 首页&#xff1a;无限轮播图 先给出效果&#xff1a; 无限轮播图&#xff0c;顾…...

github.com/antchfx/jsonquery基本使用

要在 GitHub 上使用 antchfx/jsonquery 库来查找 JSON 文档中的元素&#xff0c;首先需要了解这个库的基本用法。jsonquery 是一个用于查询 JSON 数据的 Go 语言库&#xff0c;允许使用 XPath 表达式来查找和选择 JSON 数据中的元素。 以下是一些基本步骤和示例&#xff0c;演…...

【python虚拟环境管理】【mac m3】使用poetry管理python项目

文章目录 一. 为什么选择poetry二. poetry相关操作1. 创建并激活环境2. 依赖包管理2.1. 安装项目依赖1.2. 管理不同开发环境的依赖1.3. 依赖维护1.4. 项目相关 Poetry是Python中用于依赖管理和打包的工具。它允许您声明项目所依赖的库&#xff0c;并将为您管理&#xff08;安装…...

《JavaSE》---16.<抽象类接口Object类>

目录 前言 一、抽象类 1.1什么是抽象类 1.2抽象类代码实现 1.3 抽象类特点 1.4抽象类的作用 二、接口 2.1什么是接口 2.2接口的代码书写 2.3 接口使用 2.4 接口特点 2.5 实现多个接口 快捷键&#xff08;ctrl i &#xff09;&#xff1a; 2.6接口的好处 2.7 接…...

简单修改,让UE4/5着色器编译速度变快

简单修改&#xff0c;让UE4/5着色器编译速度变快 目录 简单修改&#xff0c;让UE4/5着色器编译速度变快 一、问题描述 二、解决方法 &#xff08;一&#xff09;硬件升级 &#xff08;二&#xff09;调整相关设置和提升优先级 1.调整相关设置 &#xff08;1&#xff09…...

wordpress接口发布文章/企业网站模板 免费

在开发高并发系统时有三把利器用来保护系统&#xff1a;缓存、降级和限流。之前已经有一些文章介绍过缓存和限流了。本文将详细聊聊降级。当访问量剧增、服务出现问题&#xff08;如响应时间慢或不响应&#xff09;或非核心服务影响到核心流程的性能时&#xff0c;仍然需要保证…...

第三方做公司网站/2345网址导航电脑版

sessionid, uuid 2个字段上都有索引&#xff0c;但是查询条件 (sessionidxxx or uuidyyy) 不能使用索引 > 拆成2个sql sessionidxxx, uuidyyy 将查询出来的结果合并 /*** param array $a* param array $b* param callable|NULL $compare* return array*/private function…...

小区网站建设/网店代运营哪个好

概述&#xff1a;实力是一种客观存在&#xff0c;而霸权不同&#xff0c;霸权的基础虽然是实力&#xff0c;但它更是一种感知&#xff0c;是其他国家对他实力的感知。 一个大国&#xff0c;当然要积累实力&#xff0c;但重要的是通过战略运作&#xff0c;来经营国际社会对你霸权…...

建设部门户网站/自己怎么做关键词优化

一、前言最近公司在预研设备app端与服务端的交互方案&#xff0c;主要方案有服务端和app端通过阿里iot套件实现消息的收发&#xff1b;服务端通过极光推送主动给app端推消息&#xff0c;app通过rest接口与服务端进行交互&#xff1b;服务端与app通过mqtt消息队列来实现彼此的消…...

网站模板 简洁/制作网站的全过程

SIP有多种定义和解释,其中一说是多芯片堆叠的3D封装内系统集成,在芯片的正方向堆叠2片以上互连的裸芯片的封装。SIP是强调封装内包含了某种系统的功能封装&#xff0c;3D封装仅强调在芯片方向上的多芯片堆叠,如今3D封装已从芯片堆叠发展到封装堆叠,扩大了3D封装的内涵。     …...

mac上怎么安装wordpress/渠道网

以前不知道在循环中可以使用标签。最近遇到后&#xff0c;举得还是有其独特的用处的。我这么说的意思是说标签在循环中可以改变循环执行的流程。而这种改变不是我们以前单独使用break或者是continue能够达到的。下面还是看看实例吧。 outer1: for(int i 0;i<4;i){ Syste…...