[日更-2019.4.22、23、24] cm-14.1 Android系统启动过程分析(三)-SystemServer进程启动过程...

本文主要是介绍[日更-2019.4.22、23、24] cm-14.1 Android系统启动过程分析(三)-SystemServer进程启动过程...,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

声明

  • 前阶段在项目中涉及到了Android系统定制任务,Android系统定制前提要知道Android系统是如何启动的。
  • 本文参考了一些书籍的若干章节,比如《Android进阶解密-第2章-Android系统启动》、《深入理解Android虚拟机-第8/9/10章-init进程详解/Dalvik VM的进程系统/Dalvik VM运作流程详解》、《深入理解Android系统-第6/7/8章-init启动进程详解/Zygote进程详解/System进程详解》等
  • 本文使用的代码是LineageOS的cm-14.1,对应Android 7.1.2,可以参考我的另一篇博客:如何下载Nexus5的LineageOS14.1(cm-14.1)系统源码并编译、刷机
  • 很多代码注释待详细写

0 写在前面的

    在上一篇《[日更-2019.4.20、21] cm-14.1 Android系统启动过程分析(二)-Zygote进程启动过程》的第3.2节启动了SystemServer,Zygote让SystemServer启动是为了让它去创建Android的系统服务:AMS、WMS、PMS等。

        

                  Zygote启动SystemServer的时序图(盗一张图)

1 Zygote进程启动SystemServer

    分析startSystemServer方法最终调用的方法handleSystemServerProcess,源码目录为:~/LineageOS/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中:

    /*** Finish remaining work for the newly forked system server process.*/private static void handleSystemServerProcess(ZygoteConnection.Arguments parsedArgs)throws ZygoteInit.MethodAndArgsCaller {//关闭Zygote进程创建的Socket(SystemServer进程是由Zygote进程fork而来,所以拥有Zygote进程的地址空间,//因此也会得到Zygote进程创建的Socket,这个Socket对SystemServer进程没用,所以要关闭它);closeServerSocket();// set umask to 0077 so new files and directories will default to owner-only permissions.Os.umask(S_IRWXG | S_IRWXO);if (parsedArgs.niceName != null) {Process.setArgV0(parsedArgs.niceName);}final String systemServerClasspath = Os.getenv("SYSTEMSERVERCLASSPATH");if (systemServerClasspath != null) {performSystemServerDexOpt(systemServerClasspath);}if (parsedArgs.invokeWith != null) {String[] args = parsedArgs.remainingArgs;// If we have a non-null system server class path, we'll have to duplicate the// existing arguments and append the classpath to it. ART will handle the classpath// correctly when we exec a new process.if (systemServerClasspath != null) {String[] amendedArgs = new String[args.length + 2];amendedArgs[0] = "-cp";amendedArgs[1] = systemServerClasspath;System.arraycopy(parsedArgs.remainingArgs, 0, amendedArgs, 2, parsedArgs.remainingArgs.length);}WrapperInit.execApplication(parsedArgs.invokeWith,parsedArgs.niceName, parsedArgs.targetSdkVersion,VMRuntime.getCurrentInstructionSet(), null, args);} else {ClassLoader cl = null;if (systemServerClasspath != null) {//创建了PathClassLoader,关于它将在后面介绍ClassLoader时分析;cl = createSystemServerClassLoader(systemServerClasspath,parsedArgs.targetSdkVersion);Thread.currentThread().setContextClassLoader(cl);}/** Pass the remaining arguments to SystemServer.* 调用ZygoteInit的zygoteinit方法;*/RuntimeInit.zygoteInit(parsedArgs.targetSdkVersion, parsedArgs.remainingArgs, cl);}/* should never reach here */}

    分析zygoteinit方法,源码目录为:~/LineageOS/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中:

    /*** The main function called when started through the zygote process. This* could be unified with main(), if the native code in nativeFinishInit()* were rationalized with Zygote startup.<p>** Current recognized args:* <ul>*   <li> <code> [--] &lt;start class name&gt;  &lt;args&gt;* </ul>** @param targetSdkVersion target SDK version* @param argv arg strings*/public static final void zygoteInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application from zygote");Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "RuntimeInit");redirectLogStreams();commonInit();//1-1 调用Native层方法启动Binder线程池,这样SystemServer进程就可以使用Binder与其他进程进行通信了;nativeZygoteInit();//1.2 进入SystemServer的main方法applicationInit(targetSdkVersion, argv, classLoader);}

1.1 如何启动Binder线程池

    在代码注释1-1处nativeZygoteInit方法是RuntimeInit.java的一个Native方法,其对应的Native层的JNI函数为com_android_internal_os_RuntimeInit_nativeZygoteInit函数,源码目录:~/LineageOS/frameworks/base/core/jni/AndroidRuntime.cpp中

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

    其中,gCurRuntime为AndroidRuntime类型的指针,具体是AndroidRuntime类型的子类AppRuntime,其定义在源码目录:~/LineageOS/frameworks/base/cmds/app_process/app_main.cpp中

    virtual void onZygoteInit(){sp<ProcessState> proc = ProcessState::self();ALOGV("App process: starting thread pool.\n");//1-1-1 启动一个Binder线程池,proc->startThreadPool();}

1.2 如何进入SystemServer的main方法

    在代码注释1-2处调用了RuntimeInit的applicationInit方法,该方法在源码目录:~/LineageOS/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中:

    private static void applicationInit(int targetSdkVersion, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {// If the application calls System.exit(), terminate the process// immediately without running any shutdown hooks.  It is not possible to// shutdown an Android application gracefully.  Among other things, the// Android runtime shutdown hooks close the Binder driver, which can cause// leftover running threads to crash before the process actually exits.nativeSetExitWithoutCleanup(true);// We want to be fairly aggressive about heap utilization, to avoid// holding on to a lot of memory that isn't needed.VMRuntime.getRuntime().setTargetHeapUtilization(0.75f);VMRuntime.getRuntime().setTargetSdkVersion(targetSdkVersion);final Arguments args;try {args = new Arguments(argv);} catch (IllegalArgumentException ex) {Slog.e(TAG, ex.getMessage());// let the process exitreturn;}// The end of of the RuntimeInit event (see #zygoteInit).Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);// Remaining arguments are passed to the start class's static main//1-2-1 application方法中主要调用了invokeStaticMain方法;invokeStaticMain(args.startClass, args.startArgs, classLoader);}

    在代码注释1-2处调用了invokeStaticMain方法,在源码目录:~/LineageOS/frameworks/base/core/java/com/android/internal/os/RuntimeInit.java中

    /*** Invokes a static "main(argv[]) method on class "className".* Converts various failing exceptions into RuntimeExceptions, with* the assumption that they will then cause the VM instance to exit.** @param className Fully-qualified class name* @param argv Argument vector for main()* @param classLoader the classLoader to load {@className} with*/private static void invokeStaticMain(String className, String[] argv, ClassLoader classLoader)throws ZygoteInit.MethodAndArgsCaller {Class<?> cl;try {//1-2-2 通过反射得到SystemServer类,ClassName为com.android.server.SystemServer,返回的的cl为SystemServer类;cl = Class.forName(className, true, classLoader);} catch (ClassNotFoundException ex) {throw new RuntimeException("Missing class when invoking static main " + className,ex);}Method m;try {//1-2-3 找到SystemServer的main方法;m = cl.getMethod("main", new Class[] { String[].class });} catch (NoSuchMethodException ex) {throw new RuntimeException("Missing static main on " + className, ex);} catch (SecurityException ex) {throw new RuntimeException("Problem getting static main on " + className, ex);}int modifiers = m.getModifiers();if (! (Modifier.isStatic(modifiers) && Modifier.isPublic(modifiers))) {throw new RuntimeException("Main method is not public and static on " + className);}/** This throw gets caught in ZygoteInit.main(), which responds* by invoking the exception's run() method. This arrangement* clears up all the stack frames that were required in setting* up the process.*///1-2-4 将找到的main函数传入到MethodAndArgsCaller异常中并抛出该异常(截获MethodAndArgsCaller异常的代码在ZygoteInit.java的main函数中)throw new ZygoteInit.MethodAndArgsCaller(m, argv);}

    在代码注释1-2-4 处截获MethodAndArgsCaller异常的代码在ZygoteInit.java的main函数中,这个main方法会调用SystemServer的main方法,源码目录:~/LineageOS/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java中:

    public static void main(String argv[]) {// Mark zygote start. This ensures that thread creation will throw// an error.ZygoteHooks.startZygoteNoThreadCreation();try {Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygoteInit");RuntimeInit.enableDdms();// Start profiling the zygote initialization.SamplingProfilerIntegration.start();boolean startSystemServer = false;String socketName = "zygote";String abiList = null;for (int i = 1; i < argv.length; i++) {if ("start-system-server".equals(argv[i])) {startSystemServer = true;} else if (argv[i].startsWith(ABI_LIST_ARG)) {abiList = argv[i].substring(ABI_LIST_ARG.length());} else if (argv[i].startsWith(SOCKET_NAME_ARG)) {socketName = argv[i].substring(SOCKET_NAME_ARG.length());} else {throw new RuntimeException("Unknown command line argument: " + argv[i]);}}if (abiList == null) {throw new RuntimeException("No ABI list supplied.");}registerZygoteSocket(socketName);Trace.traceBegin(Trace.TRACE_TAG_DALVIK, "ZygotePreload");EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_START,SystemClock.uptimeMillis());preload();EventLog.writeEvent(LOG_BOOT_PROGRESS_PRELOAD_END,SystemClock.uptimeMillis());Trace.traceEnd(Trace.TRACE_TAG_DALVIK);// Finish profiling the zygote initialization.SamplingProfilerIntegration.writeZygoteSnapshot();// Do an initial gc to clean up after startupTrace.traceBegin(Trace.TRACE_TAG_DALVIK, "PostZygoteInitGC");gcAndFinalize();Trace.traceEnd(Trace.TRACE_TAG_DALVIK);Trace.traceEnd(Trace.TRACE_TAG_DALVIK);// Disable tracing so that forked processes do not inherit stale tracing tags from// Zygote.Trace.setTracingEnabled(false);// Zygote process unmounts root storage spaces.Zygote.nativeUnmountStorageOnInit();ZygoteHooks.stopZygoteNoThreadCreation();if (startSystemServer) {startSystemServer(abiList, socketName);}Log.i(TAG, "Accepting command socket connections");runSelectLoop(abiList);closeServerSocket();} catch (MethodAndArgsCaller caller) {//1-2-5 捕获到MethodAndArgsCaller异常时调用MethodAndArgsCaller的run方法,MethodAndArgsCaller是ZygoteInit.java的静态内部类;caller.run();} catch (Throwable ex) {Log.e(TAG, "Zygote died with exception", ex);closeServerSocket();throw ex;}}

    在代码注释1-2-5 处的MethodAndArgsCaller是ZygoteInit.java的内部类:

    /*** Helper exception class which holds a method and arguments and* can call them. This is used as part of a trampoline to get rid of* the initial process setup stack frames.*/public static class MethodAndArgsCaller extends Exceptionimplements Runnable {/** method to call */private final Method mMethod;/** argument array */private final String[] mArgs;public MethodAndArgsCaller(Method method, String[] args) {mMethod = method;mArgs = args;}public void run() {try {//其中mMethod指的是SystemServer的main方法,调用了mMethod的invoke方法后,SystemServer的main方法就会被动态调用,SystemServer进程就进入了SystemServer的main方法中了;mMethod.invoke(null, new Object[] { mArgs });} catch (IllegalAccessException ex) {throw new RuntimeException(ex);} catch (InvocationTargetException ex) {Throwable cause = ex.getCause();if (cause instanceof RuntimeException) {throw (RuntimeException) cause;} else if (cause instanceof Error) {throw (Error) cause;}throw new RuntimeException(ex);}}}

有一个疑问了在代码注释1-2-4为什么要以这种抛出异常的形式代用SystemServer的main方法呢?

    因为这种抛出异常的处理会清除所有的设置过程需要的堆栈帧,而让SystemServer的main方法看起来像是SystemServer进程的入口方法。在Zygote启动了SystemServer后,SystemServer进程已经做了很多准备工作,而这些工作都是在SystemServer的main方法调用之前做的,这使得SystemServer的main方法看起来不像是SystemServer进程的入口方法,而这种抛出异常交给ZygoteInit.java的main方法处理,会让SystemServer的main方法看起来像是SystemServer进程的入口方法;(可以参考下图)

        

                  Zygote启动SystemServer的时序图(盗一张图)

2 SystemServer进程都做了什么?

    进入源码目录:~/LineageOS/frameworks/base/services/java/com/android/server/SystemServer.java中查看SystemServer的main方法:

    /*** The main entry point from zygote.*/public static void main(String[] args) {//main方法中只调用了SystemServer的main方法;new SystemServer().run();}
    private void run() {try {Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "InitBeforeStartServices");// If a device's clock is before 1970 (before 0), a lot of// APIs crash dealing with negative numbers, notably// java.io.File#setLastModified, so instead we fake it and// hope that time from cell towers or NTP fixes it shortly.if (System.currentTimeMillis() < EARLIEST_SUPPORTED_TIME) {Slog.w(TAG, "System clock is before 1970; setting to 1970.");SystemClock.setCurrentTimeMillis(EARLIEST_SUPPORTED_TIME);}// If the system has "persist.sys.language" and friends set, replace them with// "persist.sys.locale". Note that the default locale at this point is calculated// using the "-Duser.locale" command line flag. That flag is usually populated by// AndroidRuntime using the same set of system properties, but only the system_server// and system apps are allowed to set them.//// NOTE: Most changes made here will need an equivalent change to// core/jni/AndroidRuntime.cppif (!SystemProperties.get("persist.sys.language").isEmpty()) {final String languageTag = Locale.getDefault().toLanguageTag();SystemProperties.set("persist.sys.locale", languageTag);SystemProperties.set("persist.sys.language", "");SystemProperties.set("persist.sys.country", "");SystemProperties.set("persist.sys.localevar", "");}// Here we go!Slog.i(TAG, "Entered the Android system server!");EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN, SystemClock.uptimeMillis());// In case the runtime switched since last boot (such as when// the old runtime was removed in an OTA), set the system// property so that it is in sync. We can't do this in// libnativehelper's JniInvocation::Init code where we already// had to fallback to a different runtime because it is// running as root and we need to be the system user to set// the property. http://b/11463182SystemProperties.set("persist.sys.dalvik.vm.lib.2", VMRuntime.getRuntime().vmLibrary());// Enable the sampling profiler.if (SamplingProfilerIntegration.isEnabled()) {SamplingProfilerIntegration.start();mProfilerSnapshotTimer = new Timer();mProfilerSnapshotTimer.schedule(new TimerTask() {@Overridepublic void run() {SamplingProfilerIntegration.writeSnapshot("system_server", null);}}, SNAPSHOT_INTERVAL, SNAPSHOT_INTERVAL);}// Mmmmmm... more memory!VMRuntime.getRuntime().clearGrowthLimit();// The system server has to run all of the time, so it needs to be// as efficient as possible with its memory usage.VMRuntime.getRuntime().setTargetHeapUtilization(0.8f);// Some devices rely on runtime fingerprint generation, so make sure// we've defined it before booting further.Build.ensureFingerprintProperty();// Within the system server, it is an error to access Environment paths without// explicitly specifying a user.Environment.setUserRequired(true);// Within the system server, any incoming Bundles should be defused// to avoid throwing BadParcelableException.BaseBundle.setShouldDefuse(true);// Ensure binder calls into the system always run at foreground priority.BinderInternal.disableBackgroundScheduling(true);// Increase the number of binder threads in system_serverBinderInternal.setMaxThreads(sMaxBinderThreads);// Prepare the main looper thread (this thread).android.os.Process.setThreadPriority(android.os.Process.THREAD_PRIORITY_FOREGROUND);android.os.Process.setCanSelfBackground(false);//创建消息LooperLooper.prepareMainLooper();// Initialize native services.//加载动态库libandroid_servers.soSystem.loadLibrary("android_servers");// Check whether we failed to shut down last time we tried.// This call may not return.performPendingShutdown();// Initialize the system context.//创建系统的ContextcreateSystemContext();// Create the system service manager.//创建SystemServiceManager对系统服务进行创建、启动、生命周期管理;mSystemServiceManager = new SystemServiceManager(mSystemContext);mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}// Start services.try {Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartServices");//启动引导服务;startBootstrapServices();//启动核心服务;startCoreServices();//启动其他服务,其他服务是一些非必须或不需要立即启动的服务;startOtherServices();} catch (Throwable ex) {Slog.e("System", "******************************************");Slog.e("System", "************ Failure starting system services", ex);throw ex;} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}// For debug builds, log event loop stalls to dropbox for analysis.if (StrictMode.conditionallyEnableDebugLogging()) {Slog.i(TAG, "Enabled StrictMode for system server main thread.");}// Loop forever.Looper.loop();throw new RuntimeException("Main thread loop unexpectedly exited");}

    参考文章:Android 系统服务一览表

cm14-1 引导服务作用
Installer系统安装APK时的一个服务类,启动完成Installer服务之后才能启动其他系统服务
ActivityManagerServiceAndroid framework框架核心服务,管理整个框架中任务、进程管理, Intent解析等的核心实现,管理四大组建的生命周期。
PowerManagerService电源管理服务
LightsService光感应传感器服务
DisplayManagerService用于管理全局显示生命周期,决定在已连接的物理设备如何配置逻辑显示,并且通知系统和应用状态的改变
UserManagerService多用户模式管理
SensorService为系统提供各种感应服务
PackageManagerService用来对APK进行安装、解析、删除、卸载等操作
......
cm14-1 核心服务作用
BatteryService管理电池相关系统服务
UsageStatsService收集用户使用的每个App的频率及时长
WebViewUpdateServiceWebView更新服务
cm14-1 其他服务作用
VibratorService振动器服务
NetworkStatsService网络统计相关
NetworkPolicyManagerService维护网络使用策略
ConnectivityService网络连接状态服务
NsdService网络服务搜索
WindowManagerServiceAndroid framework框架核心服务,窗口管理服务
SerialService对串口的设备进行操作
NetworkTimeUpdateService监视网络时间,当网络时间变化时更新本地时间
CommonTimeManagementService管理本地常见的时间服务的配置,在网络配置变化时重新配置本地服务
InputManagerService以前在WindowManagerService中,现在独立了出来,用户处理事件分发
ConsumerIrService远程控制,通过红外等控制周围的设备(例如电视等)
CameraService摄像头相关服务
AlarmManagerService全局定时器管理服务
LocationManagerService定位管理服务
AudioService音频相关管理服务
......

2.1 利用SystemServiceManager的startService方法启动

    这些系统服务的启动逻辑是相似的,以DisplayManagerService为例分析,其代码为:

        // Display manager is needed to provide display metrics before package manager// starts up.mDisplayManagerService = mSystemServiceManager.startService(DisplayManagerService.class);

    SystemServiceManager的startService方法启动了DisplayManagerService,其在源码目录:~/LineageOS/frameworks/base/services/core/java/com/android/server /SystemServiceManager.java

    /*** Creates and starts a system service. The class must be a subclass of* {@link com.android.server.SystemService}.** @param serviceClass A Java class that implements the SystemService interface.* @return The service instance, never null.* @throws RuntimeException if the service fails to start.*/@SuppressWarnings("unchecked")public <T extends SystemService> T startService(Class<T> serviceClass) {try {final String name = serviceClass.getName();Slog.i(TAG, "Starting " + name);Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "StartService " + name);// Create the service.if (!SystemService.class.isAssignableFrom(serviceClass)) {throw new RuntimeException("Failed to create " + name+ ": service must extend " + SystemService.class.getName());}final T service;try {Constructor<T> constructor = serviceClass.getConstructor(Context.class);service = constructor.newInstance(mContext);} catch (InstantiationException ex) {throw new RuntimeException("Failed to create service " + name+ ": service could not be instantiated", ex);} catch (IllegalAccessException ex) {throw new RuntimeException("Failed to create service " + name+ ": service must have a public constructor with a Context argument", ex);} catch (NoSuchMethodException ex) {throw new RuntimeException("Failed to create service " + name+ ": service must have a public constructor with a Context argument", ex);} catch (InvocationTargetException ex) {throw new RuntimeException("Failed to create service " + name+ ": service constructor threw an exception", ex);}// 注册Service,mServices是一个存储SystemService类型的ArrayList;mServices.add(service);// Start it.try {//启动Service,调用PowerManagerService的onStart方法完成启动PowerManagerService;service.onStart();} catch (RuntimeException ex) {throw new RuntimeException("Failed to start service " + name+ ": onStart threw an exception", ex);}return service;} finally {Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);}}

2.2 利用Service自身main方法启动

    以PancakeManagerService为例:

        // Start the package manager.traceBeginAndSlog("StartPackageManagerService");//直接调用PackageManagerService的main方法;mPackageManagerService = PackageManagerService.main(mSystemContext, installer,mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);mFirstBoot = mPackageManagerService.isFirstBoot();mPackageManager = mSystemContext.getPackageManager();Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);

其main方法在源码目录:~/LineageOS/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java中

public static PackageManagerService main(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// Self-check for initial settings.//自检初始的设置;PackageManagerServiceCompilerMapping.checkProperties();//先将Package管理服务PackageManagerService启动起来;PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);m.enableSystemUserPackages();//将该Package管理服务注册到ServiceManager中,这样其它组件就可以通过ServiceManager来获取它的访问接口了,ServiceManager用来管理系统中各种Service,用于系统C/S架构中的Binder通信机制;ServiceManager.addService("package", m);return m;}

2.3 SystemServer进程小结

    SystemServer进程被Zygote创建后的主要用来:

  1. 启动Binder线程池,这样就可与其他进程通信;
  2. 创建SystemServiceManager,用于对系统的服务进行创建、启动、生命周期的管理;
  3. 启动各种系统服务(引导服务、核心服务、其他服务);

Enjoy it !!

    下一篇分析下Launcher的启动过程

转载于:https://my.oschina.net/XiaoMaPedro/blog/3043293

这篇关于[日更-2019.4.22、23、24] cm-14.1 Android系统启动过程分析(三)-SystemServer进程启动过程...的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/321623

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

作业提交过程之HDFSMapReduce

作业提交全过程详解 (1)作业提交 第1步:Client调用job.waitForCompletion方法,向整个集群提交MapReduce作业。 第2步:Client向RM申请一个作业id。 第3步:RM给Client返回该job资源的提交路径和作业id。 第4步:Client提交jar包、切片信息和配置文件到指定的资源提交路径。 第5步:Client提交完资源后,向RM申请运行MrAp

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

MySQL数据库宕机,启动不起来,教你一招搞定!

作者介绍:老苏,10余年DBA工作运维经验,擅长Oracle、MySQL、PG、Mongodb数据库运维(如安装迁移,性能优化、故障应急处理等)公众号:老苏畅谈运维欢迎关注本人公众号,更多精彩与您分享。 MySQL数据库宕机,数据页损坏问题,启动不起来,该如何排查和解决,本文将为你说明具体的排查过程。 查看MySQL error日志 查看 MySQL error日志,排查哪个表(表空间

springboot3打包成war包,用tomcat8启动

1、在pom中,将打包类型改为war <packaging>war</packaging> 2、pom中排除SpringBoot内置的Tomcat容器并添加Tomcat依赖,用于编译和测试,         *依赖时一定设置 scope 为 provided (相当于 tomcat 依赖只在本地运行和测试的时候有效,         打包的时候会排除这个依赖)<scope>provided

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

内核启动时减少log的方式

内核引导选项 内核引导选项大体上可以分为两类:一类与设备无关、另一类与设备有关。与设备有关的引导选项多如牛毛,需要你自己阅读内核中的相应驱动程序源码以获取其能够接受的引导选项。比如,如果你想知道可以向 AHA1542 SCSI 驱动程序传递哪些引导选项,那么就查看 drivers/scsi/aha1542.c 文件,一般在前面 100 行注释里就可以找到所接受的引导选项说明。大多数选项是通过"_

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss