Android系统启动流程--system_server进程的启动流程

news2024/9/23 3:18:13

     紧接上一篇zygote进程的启动流程,上一篇的结尾提到zygote进程中会fock出一个system_server进程,用于启动和管理Android系统中大部分的系统服务,本篇就来分析system_server进程是如何创建并运行的以及它都做了哪些重要的工作。

//文件路径:frameworks\base\core\java\com\android\internal\os\ZygoteInit.java	
public static void main(String argv[]) {
    
            //创建服务端的socket,等待AMS发出的创建应用进程的消息   (ZygoteServer.java)
            zygoteServer = new ZygoteServer(isPrimaryZygote);

            if (startSystemServer) {
				//创建SystemServer进程的入口
                Runnable r = forkSystemServer(abiList, zygoteSocketName, zygoteServer);

                // child (system_server) process.
                if (r != null) {
                    //执行runnable的run方法
                    r.run();
                    return;
                }
            }
}
        

继续跟入forkSystemServer()方法:


private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {

            /* Hardcoded command line to start the system server */
        //启动systemserver的参数
        String args[] = {
                "--setuid=1000",
                "--setgid=1000",
                "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,"
                        + "1024,1032,1065,3001,3002,3003,3006,3007,3009,3010,3011",
                "--capabilities=" + capabilities + "," + capabilities,
                "--nice-name=system_server", //进程名
                "--runtime-args",
                "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
                "com.android.server.SystemServer",  //这里留个印象,fork出进程后,会调到这个文件中。
        };
        
        ZygoteArguments parsedArgs = null;
        try {
            //解析上面写死的参数
            parsedArgs = new ZygoteArguments(args);

            //fork SystemServer进程
            //需要注意的是,返回值pid不是当前进程的id,而是当前进程的子进程的id,若pid=0说明当前进程是systemserver进程,因为它没有子进程所以返回0
			//而pid>0,说明当前进程是zygote进程,因为它有子进程,且子进程是systemserver
            pid = Zygote.forkSystemServer(
                    parsedArgs.mUid, parsedArgs.mGid,
                    parsedArgs.mGids,
                    parsedArgs.mRuntimeFlags,
                    null,
                    parsedArgs.mPermittedCapabilities,
                    parsedArgs.mEffectiveCapabilities);
          //从这里开始,下面的代码会在两个进程中执行,fork操作可理解为将zygote进程复制了一个称作systemserver进程
          if (pid == 0) {   //pid=0,就是systemserver进程,下文会看到
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }
            //因为zygote中创建的socket在systemServer进程中用不到,所以关闭socket资源
            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs); //将上面的参数传入
        }



}

继续跟入Zygote.forkSystemServer():

//文件路径:frameworks\base\core\java\com\android\internal\os\Zygote.java

static int forkSystemServer(int uid, int gid, int[] gids, int runtimeFlags,
            int[][] rlimits, long permittedCapabilities, long effectiveCapabilities) {
        ZygoteHooks.preFork();
        //这里转到native层去fork进程
        int pid = nativeForkSystemServer(
                uid, gid, gids, runtimeFlags, rlimits,
                permittedCapabilities, effectiveCapabilities);

        // Set the Java Language thread priority to the default value for new apps.
        Thread.currentThread().setPriority(Thread.NORM_PRIORITY);

        ZygoteHooks.postForkCommon();
        return pid;
    }

上一篇中,zygote进程中做的比较重要事儿之一就是注册jni方法,趁热打铁来看下java层的native方法是怎么找到对应的native层的方法的。

//文件路径:sframeworks/base/core/jni/AndroidRuntime.cpp

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{ 
    /*
     * Register android functions.注册jni的入口在这里
     */
    if (startReg(env) < 0) {  //注册JNI方法,系统api中涉及的native方法都是在这里注册的
        ALOGE("Unable to register all android natives\n");
        return;
    }

}


int AndroidRuntime::startReg(JNIEnv* env)
{
   if (register_jni_procs(gRegJNI, NELEM(gRegJNI), env) < 0) {
        env->PopLocalFrame(NULL);
        return -1;
    }

}

//这个数组中保存了,注册android系统中各个部分注册jni的函数指针
static const RegJNIRec gRegJNI[] = {
    ...
    //和本次native方法相关的是这个,其实也比较好找,是对应的Zygote.java文件的包名
    REG_JNI(register_com_android_internal_os_Zygote),
}

继续跟进register_com_android_internal_os_Zygote():

//文件路径: frameworks/base/core/jni/com_android_internal_os_Zygote.cpp

int register_com_android_internal_os_Zygote(JNIEnv* env) {
  gZygoteClass = MakeGlobalRefOrDie(env, FindClassOrDie(env, kZygoteClassName));
  gCallPostForkSystemServerHooks = GetStaticMethodIDOrDie(env, gZygoteClass,
                                                          "callPostForkSystemServerHooks",
                                                          "(I)V");
  gCallPostForkChildHooks = GetStaticMethodIDOrDie(env, gZygoteClass, "callPostForkChildHooks",
                                                   "(IZZLjava/lang/String;)V");

  return RegisterMethodsOrDie(env, "com/android/internal/os/Zygote", gMethods, NELEM(gMethods));
}

//其中gMethods这个数组是线索,它定义了Zygote.java中所有的native方法与c++中方法的对应关系
static const JNINativeMethod gMethods[] = {
        //这个就是此次寻找的目标
        {"nativeForkSystemServer", "(II[II[[IJJ)I",
         (void*)com_android_internal_os_Zygote_nativeForkSystemServer},
        ...
}




static jint com_android_internal_os_Zygote_nativeForkSystemServer(
        JNIEnv* env, jclass, uid_t uid, gid_t gid, jintArray gids,
        jint runtime_flags, jobjectArray rlimits, jlong permitted_capabilities,
        jlong effective_capabilities) {

      //进一步进行fork操作,此方法中就会调用系统的fork函数,到此就不再深入分析了
      pid_t pid = ForkCommon(env, true,
                         fds_to_close,
                         fds_to_ignore,
                         true);

       // 从这个注释可得知 pid=0的是system_server进程
      if (pid == 0) {
          // System server prcoess does not need data isolation so no need to
          // know pkg_data_info_list.
     
      } else if (pid > 0) { //pid>0的是zygote进程
          // The zygote process checks whether the child process has died or not.
          ALOGI("System server process %d has been created", pid);
          gSystemServerPid = pid;

      }

}


回到最开始的地方,forkSystemServer()中fork出了system_server进程后,执行了handleSystemServerProcess(parsedArgs)方法。

//文件路径:frameworks\base\core\java\com\android\internal\os\ZygoteInit.java	
private static Runnable handleSystemServerProcess(ZygoteArguments parsedArgs) {

     ...
     ClassLoader cl = null;
            if (systemServerClasspath != null) {
				//创建pathclassLoader,类加载器
                cl = createPathClassLoader(systemServerClasspath, parsedArgs.mTargetSdkVersion);

                Thread.currentThread().setContextClassLoader(cl);
            }

            /*
             * Pass the remaining arguments to SystemServer.
             */
            return ZygoteInit.zygoteInit(parsedArgs.mTargetSdkVersion,
                    parsedArgs.mDisabledCompatChanges,
                    parsedArgs.mRemainingArgs, cl);

}

继续跟入ZygoteInit.zygoteInit():

//frameworks\base\core\java\com\android\internal\os\ZygoteInit.java	


public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {
        if (RuntimeInit.DEBUG) {
            Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
        }

        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
        RuntimeInit.redirectLogStreams();
        //初始化运行环境
        RuntimeInit.commonInit();
		//开启binder线程池
        ZygoteInit.nativeZygoteInit();
        
        return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
                classLoader);
    }

继续跟入RuntimeInit.applicationInit():

//文件路径:	frameworks\base\core\java\com\android\internal\os\RuntimeInit.java
protected static Runnable applicationInit(int targetSdkVersion, long[] disabledCompatChanges,
            String[] argv, ClassLoader classLoader) {


      ...
      return findStaticMain(args.startClass, args.startArgs, classLoader);
}


 protected static Runnable findStaticMain(String className, String[] argv,
            ClassLoader classLoader) {
        Class<?> cl;

        try {
            //这里关键点就来了,还记得在fork进程前有个String[]里面定义的启动systemserver的多个参数吗?
            //classname就是一层层传递进来的 "com.android.server.SystemServer" 
            cl = Class.forName(className, true, classLoader);
        } catch (ClassNotFoundException ex) {
            throw new RuntimeException(
                    "Missing class when invoking static main " + className,
                    ex);
        }

        Method m;
        try {
            // 通过反射拿到SystemServer类的main()
            m = cl.getMethod("main", new Class[] { String[].class });
        } 

        //将runnable返回
        return new MethodAndArgsCaller(m, argv);
}


还记得上面在pid=0,也就是在systemserver进程中会执行r.run();这个r就是MethodAndArgsCaller,因此打开run():

//文件路径:	frameworks\base\core\java\com\android\internal\os\RuntimeInit.java	


public void run() {
            try {
                //很简单,通过反射执行SystemServer的main()
                mMethod.invoke(null, new Object[] { mArgs });
            } 
}

到此,zygote进程fork出了systemserver进程,android系统启动流程也从zygote阶段过渡到了systemserver阶段,下面我们就可以看看在systemserver进程中主要都做了哪些事儿。

//文件路径 frameworks/base/services/java/com/android/server/systemServer.java
 /**
  * The main entry point from zygote. zygote fork出systemserver进程后,systemserver部分代码的入口
  */
  public static void main(String[] args) {
      new SystemServer().run();
  }

  //主要操作在run方法中
  private void run() {
        //创建looper
        Looper.prepareMainLooper();
        
        // Initialize native services. 加载动态库libandroid_servers.so
        System.loadLibrary("android_servers");

        //step1:创建系统上下文
        createSystemContext();
       
        // Create the system service manager. 用于启动和管理下面的服务
        mSystemServiceManager = new SystemServiceManager(mSystemContext);
        mSystemServiceManager.setStartInfo(mRuntimeRestart,
                    mRuntimeStartElapsedTime, mRuntimeStartUptime);
        LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
        // 准备一个线程池,用于并行执行systemserver进程初始化中的多个任务
        SystemServerInitThreadPool.start();


        // Start services. systemserver进程中最重要的事,启动一下三种类型中的各个服务
        try {
            t.traceBegin("StartServices");
            startBootstrapServices(t);  //引导服务 AMS PMS
            startCoreServices(t);  //核心服务(主要是系统自己用到的服务)
            startOtherServices(t);  //其他服务 WMS 
        }

        // looper循环,保证此进程不会退出(结束)
        Looper.loop();

  }


---------------------------step1-----------------------------------
private void createSystemContext() {
        //通过ActivityThread创建上下文,ActivityThread相对于进程的关系,就好像CEO相对于公司的关系
        // 它掌握着公司(进程)的资源以及调度执行的权利
        ActivityThread activityThread = ActivityThread.systemMain();
        mSystemContext = activityThread.getSystemContext();
		//设置默认主题
        mSystemContext.setTheme(DEFAULT_SYSTEM_THEME);

        final Context systemUiContext = activityThread.getSystemUiContext();
        systemUiContext.setTheme(DEFAULT_SYSTEM_THEME);
    }

  

下面我们来具体看下SystemServiceManager是如何管理这些系统服务的?

在此之前,我们先总结一下几个名称相近的概念:

  • systemserver:是一个进程,用来管理众多系统服务
  • SystemServiceManager:systemserver管理服务的具体实现类
  • SystemService:是系统服务的标准,即系统服务想要被SystemServiceManager管理,就必须实现这个标准,各个系统都是SystemService的扩展。
  • serviceManager:是一个进程,binder驱动的管理者,所有系统服务的Binder,都要注册进来

OK,分清这些概念后,我们以ATMS(ActivityManagerTaskService),这个与应用层开发者最密切的系统服务为例,来看SystemServiceManager是如何管理系统服务的。


private void startBootstrapServices(@NonNull TimingsTraceAndSlog t) {
      //mSystemServiceManager已经在上一步进行创建
      ActivityTaskManagerService atm = mSystemServiceManager.startService(
                ActivityTaskManagerService.Lifecycle.class).getService();
      ...
}

//------------------------- 我们先看前半句startService()--------------------------------
//	frameworks\base\services\core\java\com\android\server\SystemServiceManager.java
public <T extends SystemService> T startService(Class<T> serviceClass) {
        try {
            //得到对应的类名,此处为ActivityTaskManagerService.Lifecycle
            final String name = serviceClass.getName();
            
            final T service;
            try {
                //通过反射创建ActivityTaskManagerService.Lifecycle对象
                Constructor<T> constructor = serviceClass.getConstructor(Context.class);
                service = constructor.newInstance(mContext);
            }
            //继续调用重载方法
            startService(service);
            //将ActivityTaskManagerService.Lifecycle实例返回
            return service;
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER);
        }
    }

 private final ArrayList<SystemService> mServices = new ArrayList<SystemService>();
 public void startService(@NonNull final SystemService service) {
        // 将service(这里是ActivityTaskManagerService.Lifecycle对象)存入list集合
        mServices.add(service);
        // Start it.
        long time = SystemClock.elapsedRealtime();
        try {
            //调用ActivityTaskManagerService.Lifecycle的onStart()
            service.onStart();
        }
        warnIfTooLong(SystemClock.elapsedRealtime() - time, service, "onStart");
 }


//文件路径:	frameworks\base\services\core\java\com\android\server\wm\ActivityTaskManagerService.java

public static final class Lifecycle extends SystemService {
        private final ActivityTaskManagerService mService;

        public Lifecycle(Context context) {
            super(context);
            //创建ActivityTaskManagerService实例
            mService = new ActivityTaskManagerService(context);
        }

        @Override
        public void onStart() {
            //将ActivityTaskManagerService注册到serviceManager进程,(serviceManager进程是在init进程中解析init.rc文件后启动的,不熟悉的小伙伴可以去翻下我的另一篇:serviceManager进程的启动流程)     //这个流程在下面单独展开,以免妨碍我们跟踪主线流程
            publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);
            //调用ActivityTaskManagerService的start()
            mService.start();
        }
}

private void start() {
        //mInternal 是在ActivityTaskManagerService的构造方法中创建的,它是LocalService类型
        //LocalServices中维护了一个ArrayMap,用于存储LocalService,LocalService封装了一些ATMS能提供的服务的具体实现逻辑,后面可能专门写一篇来分析ATMS。
        LocalServices.addService(ActivityTaskManagerInternal.class, mInternal);
    }

//到这里我们知道前半句反回了ActivityTaskManagerService.Lifecycle实例

//-------------------------后半句getService()--------------------------------------------
//后半句非常简单,就是将上面创建的ActivityTaskManagerService实例返回了出去

public static final class Lifecycle extends SystemService {
        private final ActivityTaskManagerService mService;
        public ActivityTaskManagerService getService() {
            return mService;
        }
}



    

上面还留了一个问题,即服务创建后怎么注册到serviceManager进程?

//调用入口ActivityTaskManagerService.Lifecycle.onStart()
publishBinderService(Context.ACTIVITY_TASK_SERVICE, mService);

//文件路径:frameworks\base\services\core\java\com\android\server\SystemService.java	
//重载方法调用,最终走到这个方法
// name: "activity_task"
// service: ActivityTaskManagerService对象
// allowIsolated:false
// dumpPriority: DUMP_FLAG_PRIORITY_DEFAULT
protected final void publishBinderService(String name, IBinder service,
            boolean allowIsolated, int dumpPriority) {
        ServiceManager.addService(name, service, allowIsolated, dumpPriority);
}


//文件路径:frameworks\base\core\java\android\os\ServiceManager.java	
public static void addService(String name, IBinder service, boolean allowIsolated,
            int dumpPriority) {
        try {
            getIServiceManager().addService(name, service, allowIsolated, dumpPriority);
        } catch (RemoteException e) {
            Log.e(TAG, "error in addService", e);
        }
}


private static IServiceManager getIServiceManager() {
        if (sServiceManager != null) {
            return sServiceManager;
        }

        // Find the service manager
        sServiceManager = ServiceManagerNative
                .asInterface(Binder.allowBlocking(BinderInternal.getContextObject()));
        return sServiceManager;
    }


getIServiceManager()这个流程在Android IPC Binder机制学习(一)中分析过,这是个跨进程通信,最终会调到以下位置:

//文件位置:frameworks\native\cmds\servicemanage\ServiceManager.cpp	

Status ServiceManager::addService(const std::string& name, const sp<IBinder>& binder, bool allowIsolated, int32_t dumpPriority) {
    
    ...
    // Overwrite the old service if it exists
    // ServiceMap mNameToService; 定义在ServiceManager.h中,这样serviceManager进程就保存了ATMS的binder对象。
    mNameToService[name] = Service {
        .binder = binder,
        .allowIsolated = allowIsolated,
        .dumpPriority = dumpPriority,
        .debugPid = ctx.debugPid,
    };
    ...

}

还有一个点简单提一下,上面提到所有的系统服务都扩展自SystemService类,具体实现分为两类:

  1. 直接继承SystemService,如PowerManagerService
  2. 写一个内部类,让内部类继承SystemService,内部类再持有外部类service,如ATMS,主要原因在于java的单继承,某些service需要继承IBinder。

到这里对SystemServer进程的分析就结束了。

总结:systemserver进程主要做了哪些事儿

  1. 创建系统上下文,设置默认主题
  2. 创建SystemServiceManager,用于管理众多系统服务
  3. 启动三大类系统服务,并将服务注册到serviceManager进程

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Show, Attend, and Tell | a PyTorch Tutorial to Image Captioning代码调试(跑通)

Show, Attend, and Tell | a PyTorch Tutorial to Image Captioning代码调试&#xff08;跑通&#xff09; 文章目录 Show, Attend, and Tell | a PyTorch Tutorial to Image Captioning代码调试&#xff08;跑通&#xff09;前言1. 创建、安装所用的包1.1 创建环境&#xff0c…

【深度学习】OCR文本识别

OCR文字识别定义 OCR&#xff08;optical character recognition&#xff09;文字识别是指电子设备&#xff08;例如扫描仪或数码相机&#xff09;检查纸上打印的字符&#xff0c;然后用字符识别方法将形状翻译成计算机文字的过程&#xff1b;即&#xff0c;对文本资料进行扫描…

【数据结构】二叉树经典oj题

&#x1f680;write in front&#x1f680; &#x1f4dc;所属专栏&#xff1a;初阶数据结构 &#x1f6f0;️博客主页&#xff1a;睿睿的博客主页 &#x1f6f0;️代码仓库&#xff1a;&#x1f389;VS2022_C语言仓库 &#x1f3a1;您的点赞、关注、收藏、评论&#xff0c;是对…

B. Make Them Equal(Codeforces Round 673 (Div. 1))

传送门 题意&#xff1a; 思路&#xff1a; 首先判断是否能够操作达到目的&#xff1a;即所有的数都相等。 不能达到有两种情况&#xff1a; 1&#xff1a;所有数之和对n取余不等于0 2: 每个ai都是小于i的&#xff0c;例如n5, a[]{0,1,2,3,4}。因为每个数都是小于 i 的&am…

idea中的 debug 中小功能按钮都代表的意思

1.step over 步过----->一行一行的往下走,如果这一行中有方法那么不会进入该方法,直接一行一行往下走,除非你在该方法中打入断点 2.step into 步入—>可以进入方法内部,但是只能进入自己写的方法内部,而不会进入方法的类库中 3.Force step into 强制步入---->可以步…

编译livox ros driver2(ROS2、livox、rviz、ubuntu22.04)

1. 编译Livox-SDK2 官方地址&#xff1a;https://github.com/Livox-SDK/Livox-SDK2 执行一下命令&#xff1a; git clone https://github.com/Livox-SDK/Livox-SDK2.git cd ./Livox-SDK2/ mkdir build cd build cmake .. && make sudo make install 如上就安装完成了…

嵌入式【CPLD】5M570ZT100C5N、5M1270ZF256C5N、5M2210ZF256C5N采用独特的非易失性架构,低成本应用设计。

英特尔MAX V CPLD 采用独特的非易失性架构&#xff0c;提供低功耗片上功能&#xff0c;适用于以边缘为中心的应用。MAX V CPLD系列能够在单位空间中提供大量 I/O 和逻辑。这些设备还使用了低成本绿色封装技术&#xff0c;封装大小只有 20 毫米。 MAX V系列的关键应用包括&…

PCL点云库(1) - 简介与数据类型

目录 1.1 简介 1.2 PCL安装 1.2.1 安装方法 1.2.2 测试程序 1.3 PCL数据类型 1.4 PCL中自定义point类型 1.4.1 增加自定义point的步骤 1.4.2 完整代码 1.1 简介 来源&#xff1a;PCL&#xff08;点云库&#xff09;_百度百科 PCL&#xff08;Point Cloud Library&…

个推打造消息推送专项运营提升方案,数据驱动APP触达效果升级

“数智化运营”能力已成为企业的核心竞争力之一。借助数据和算法&#xff0c;构建完善的数智化运营体系&#xff0c;企业可增强用户洞察和科学决策能力&#xff0c;提高日常运营效率和投入产出比。近半年&#xff0c;个推精准把握行业客户的切实需求&#xff0c;将“数智化运营…

分析型数据库:MPP 数据库的概念、技术架构与未来发展方向

随着企业数据量的增多&#xff0c;为了配合企业的业务分析、商业智能等应用场景&#xff0c;从而驱动数据化的商业决策&#xff0c;分析型数据库诞生了。由于数据分析一般涉及的数据量大&#xff0c;计算复杂&#xff0c;分析型数据库一般都是采用大规模并行计算或者分布式计算…

css的属性选择器

文章目录 属性选择器的原理简单的语法介绍子串值&#xff08;Substring value&#xff09;属性选择器 CSS 属性选择器的最基本用法复杂一点的用法层叠选择多条件复合选择伪正则写法配合 **:not()** 伪类重写行内样式 组合用法&#xff1a;搭配伪元素提升用户体验角标功能属性选…

基于51单片机的智能晾衣架的设计与实现(源码+论文)_kaic

【摘要】随着社会和市场的变化&#xff0c;我国经济的快速发展和房地产行业的快速扩张&#xff0c;使得装修家居行业飞速发展&#xff0c;在行业高速发展的同时&#xff0c;消费者家居智能化要求也在日益发展。随着科学技术的进步和电子技术的发展&#xff0c;单片机作为智能控…

Stable Diffusion一键安装器,只有2.3M

最近AI画图真的是太火了&#xff0c;但是Midjourney收费之后大家就不知道去哪里能用AI画图了&#xff0c; Stable Diffusion很多人听过&#xff0c;但是安装特别麻烦。所以为大家介绍一款软件&#xff0c;一键安装SD。 Stable Diffusion一键安装器_SD一键启动器-Stable Diffus…

LeetCode:459. 重复的子字符串 —【2、KMP算法】

&#x1f34e;道阻且长&#xff0c;行则将至。&#x1f353; &#x1f33b;算法&#xff0c;不如说它是一种思考方式&#x1f340; 算法专栏&#xff1a; &#x1f449;&#x1f3fb;123 一、&#x1f331;459. 重复的子字符串 题目描述&#xff1a;给定一个非空的字符串 s &…

Docker数据管理与Docker镜像的创建

目录 1.管理数据的方式 1.数据卷 2.数据卷容器 3.容器互联&#xff08;使用centos镜像&#xff09; 2.Docker镜像的创建 1.基于现有镜像创建 2.基于本地模板创建 3.基于Dockerfile创建 4.Dockerfile案例 总结 1.管理数据的方式 容器中管理数据主要有两种方式&#xff1…

c++作业

自己定义mystring类实现string功能 #include <iostream> #include<cstring> using namespace std;class myString {private:char *str; //记录c风格的字符串int size; //记录字符串的实际长度public://无参构造myString():size(10){str new …

tomcat服务搭建

系列文章目录 文章目录 系列文章目录一、Tomcat1.核心功能 二、Tomcat服务搭建1.Tomcat服务2.Tomcat 虚拟主机配置1.创建 kgc 和 benet 项目目录和文件2.修改 Tomcat 主配置文件 server.xml3.客户端浏览器访问验证 三、Tomcat多实例部署 一、Tomcat 1.核心功能 1.connector&a…

Spring Bean生命周期源码之包扫描、创建BeanDefinition、合并BeanDefinition源码

文章目录 Bean生命周期源码生成BeanDefinitionSpring容器启动时创建单例Bean合并BeanDefinition Bean生命周期源码 我们创建一个ApplicationContext对象时&#xff0c;这其中主要会做两件时间&#xff1a;包扫描得到BeanDefinition的set集合&#xff0c;创建非懒加载的单例Bea…

体验ChatGPT在具体应用场景下的能力与表现——vuedraggable的move多次触发问题

当下人工智能模型在满天飞&#xff0c;今天拿一个具体的应用场景&#xff0c;来体验下ChatGPT的能力与表现&#xff0c;看看是否能解决实际问题。 顺便填一下之前遇到的一个具体的坑&#xff1a;vuedraggable的move多次触发问题。 背景 背景是这样的&#xff0c;实现低代码开…

Hadoop启动相关命令

Hadoop启动相关配置 文章目录 Hadoop启动相关配置格式化节点的情况什么情况下Hadoop需要进行格式化节点&#xff1f; Hadoop启动步骤Hadoop的启动步骤只是start-dfs.sh即可吗 *hdfs*的web管理页面参数说明参数的评价场景 格式化节点的情况 什么情况下Hadoop需要进行格式化节点…