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),
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/378765.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Java--数据结构】二叉树oj题(上)

前言 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 判断是否是相同的树 oj链接 要判断树是否一样&#xff0c;要满足3个条件 根的 结构 和 值 一样左子树的结构和值一样右子树的结构和值一样 所以就可以总结以下思路…

【Pytorch】RNN for Name Classification

参考学习来自&#xff1a; https://pytorch.org/tutorials/intermediate/char_rnn_classification_tutorial.htmlRNN完成姓名分类https://download.pytorch.org/tutorial/data.zip 导入库 import glob # 用于查找符合规则的文件名 import os import unicodedata import stri…

【Powershell】超越限制:获取Azure AD登录日志

你是否正在寻找一种方法来追踪 Azure Active Directory&#xff08;Azure AD&#xff09;中用户的登录活动&#xff1f; 如果是的话&#xff0c;查看Azure AD用户登录日志最简单的方法是使用Microsoft Entra管理中心。打开 https://entra.microsoft.com/&#xff0c;然后进入 监…

taro小程序terser-webpack-plugin插件不生效(vue2版本)

背景 最近在做公司内部的小程序脚手架&#xff0c;为了兼容老项目和旧项目&#xff0c;做了vue2taro,vue3taro两个模板&#xff0c;发现terser-webpack-plugin在vue2和vue3中的使用方式并不相同&#xff0c;同样的配置在vue3webpack5中生效&#xff0c;但是在vue2webpack4中就…

学习Python的IDE功能--(一)入门导览

项目视图是主要工具窗口之一。它包含项目目录、SDK 特定的外部库和临时文件。点击带条纹的按钮可以预览演示项目。您也可以按Alt1打开。点击以打开项目视图&#xff0c;展开项目目录以查看项目文件。双击以打开welcome.py。 切换到"学习"工具窗口继续学习本课次。…

ELK企业级日志分析

目 录 一、ELK简介 1.1 elasticsearch简介 1.2 logstash简介 1.3 kibana简介 1.4 ELK的好处 1.5 ELK的工作原理 二、部署ELK 2.1 部署elasticsearch(集群) 2.1.1 修改配置文件 2.1.2 修改系统参数 2.1.2.1 修改systemmd服务管理器 2.1.2.2 性能调优参数 2.1.2.3 …

文献阅读:tidyomics 生态系统:增强组学数据分析

文献介绍 文献题目&#xff1a; The tidyomics ecosystem: enhancing omic data analyses 研究团队&#xff1a; Stefano Mangiola&#xff08;澳大利亚沃尔特和伊丽莎霍尔医学研究所&#xff09;、Michael I. Love&#xff08;美国北卡罗来纳大学教堂山分校&#xff09;、Ant…

k8s集群新增节点

目前集群状态 如K8S 集群搭建中规划的集群一样 Masternode01node02IP192.168.100.100192.168.100.101192.168.100.102OSCent OS 7.9Cent OS 7.9Cent OS 7.9 目前打算新增节点node03 Masternode01node02node03IP192.168.100.100192.168.100.101192.168.100.102192.168.100.1…

Golang | Leetcode Golang题解之第240题搜索二维矩阵II

题目&#xff1a; 题解&#xff1a; func searchMatrix(matrix [][]int, target int) bool {m, n : len(matrix), len(matrix[0])x, y : 0, n-1for x < m && y > 0 {if matrix[x][y] target {return true}if matrix[x][y] > target {y--} else {x}}return f…

STM32 GPIO的工作原理

STM32的GPIO管脚有下面8种可能的配置:&#xff08;4输入 2 输出 2 复用输出) &#xff08;1&#xff09;浮空输入_IN_FLOATING 在上图上&#xff0c;阴影的部分处于不工作状态&#xff0c;尤其是下半部分的输出电路&#xff0c;实际上是与端口处于隔离状态。黄色的高亮部分显示…

C#统一委托Func与Action

C#在System命名空间下提供两个委托Action和Func&#xff0c;这两个委托最多提供16个参数&#xff0c;基本上可以满足所有自定义事件所需的委托类型。几乎所有的 事件 都可以使用这两个内置的委托Action和Func进行处理。 Action委托&#xff1a; Action定义提供0~16个参数&…

stm32入门-----初识stm32

目录 前言 ARM stm32 1.stm32家族 2.stm32的外设资源 3.命名规则 4.系统结构 5.引脚定义 6.启动配置 7.STM32F103C8T6芯片 8.STM32F103C8T6芯片原理图与最小系统电路 前言 已经很久没跟新了&#xff0c;上次发文的时候是好几个月之前了&#xff0c;现在我是想去学习st…

怎样减少视频的容量 怎样减少视频内存保持清晰度

在数字媒体时代&#xff0c;视频内容已经成为人们日常交流和信息传递的重要方式。然而&#xff0c;视频往往占用大量存储空间&#xff0c;给我们的设备带来不小的负担。如何在不损失视频质量的前提下&#xff0c;减少视频文件的大小呢&#xff1f;本文将为你揭秘几个实用的技巧…

定制开发AI智能名片商城微信小程序在私域流量池构建中的应用与策略

摘要 在数字经济蓬勃发展的今天&#xff0c;私域流量已成为企业竞争的新战场。定制开发AI智能名片商城微信小程序&#xff0c;作为私域流量池构建的创新工具&#xff0c;正以其独特的优势助力企业实现用户资源的深度挖掘与高效转化。本文深入探讨了定制开发AI智能名片商城微信…

数据结构(5.2_2)——二叉树的性质

常见考点1: 设非空二叉树中度为0、1和2的结点个数分别为n0、n1和n2&#xff0c;则n0n21(叶子结点比二分支结点多一个) 常见考点2&#xff1a; 二叉树第一层至多右 有个结点(i>1) m叉树第一层至多右 有个结点(i>1) 常见考点3&#xff1a; 高度为h的二叉树至多有个结点…

23年oppo提前批笔试真题-构造二阶行列式

构造二阶行列式 题目描述 小欧希望你构造一个二阶行列式&#xff0c;满足行列式中每个数均为不超过 20 的正整数&#xff0c;且行列式的值恰好等于x。你能帮帮她吗? 输入描述 一个正整数x。-1000 < x < 1000 输出描述 如果无解&#xff0c;请输出-1。否则输出任意合…

ELK日志管理与应用

目录 一.ELK收集nginx日志 二.收集tomcat日志 三.Filebeat 一.ELK收集nginx日志 1.搭建好ELKlogstashkibana架构 2.关闭防火墙和selinux systemctl stop firewalld setenforce 0 3.安装nginx [rootlocalhost ~]# yum install epel-release.noarch -y [rootlocalhost …

好用的AI搜索引擎

1. 360AI 搜索 访问 360AI 搜索: https://www.huntagi.com/sites/1706642948656.html 360AI 搜索介绍&#xff1a; 360AI 搜索&#xff0c;新一代智能答案引擎&#xff0c;值得信赖的智能搜索伙伴&#xff0c;为复杂搜索提供专业支持&#xff0c;解锁更相关、更全面的答案。AI…

Elasticsearch基础(三)

目录 1.DSL查询文档 1.1.DSL查询分类 1.2.全文检索查询 1.3.精准查询 1.4.地理坐标查询 1.5.复合查询 2.搜索结果处理 2.1.排序 2.2.分页 2.3.高亮 2.4.总结 3.RestClient查询文档 3.1.快速入门 3.2.match查询 3.3.精确查询 3.4.布尔查询 3.5.排序、分页 3.6.…

live555 rtsp服务器实战之doGetNextFrame

live555关于RTSP协议交互流程 live555的核心数据结构值之闭环双向链表 live555 rtsp服务器实战之createNewStreamSource live555 rtsp服务器实战之doGetNextFrame 注意&#xff1a;该篇文章可能有些绕&#xff0c;最好跟着文章追踪下源码&#xff0c;不了解源码可能就是天书…