android系统SystemServer进程启动流程分析

news2025/2/22 10:10:00

目录

一,SystemServer整体框架

二,SystemServer启动源码分析

2.1,重要的概念

2.2,启动入口

2.3,创建对应进程的binder

三,binder驱动和binder线程池

四,SystemServer真正启动方法

4.1 SystemServer main方法里面主要做了几件事情

1)创建SystemServiceManager管理所有的服务

2)创建上下文createSystemContext,

3)启动核心服务和其他服务

4)Looper.prepareMainLooper()

5)Looper.loop()

4.2 分析startBootstrapServices

4.3 分析startBootstrapServices

4.4 分析startBootstrapServices

五,结束语


一,SystemServer整体框架

1.1,SystemServer类图

1.1,SystemServer时序图

二,SystemServer启动源码分析

2.1,重要的概念

在开始讲解源码之前,需要搞清楚几个重要的概念

对象含义备注
SystemServerSystemServer是一个进程,这个进程是Android系统中的核心进程,该进程由zygote进程在开机时fork而出,管理了系统的核心服务,整个安卓系统只有一个SystemServer进程

属于进程级别

是一个单独的进程

SystemServiceManagerSystemServiceManager是为了管理SystemServer进程里面运行的90+多个服务(Services)设置的一个管理者角色,这个仅仅用于同一个进程之间使用普通的java对象
LocalServicesLocalServices是Android系统中用于在同一进程中注册和获取LocalService的工具类。它维护了一个ArrayMap来存储不同类型的LocalService实例,确保每个类型只对应一个实例。通过addService()方法注册服务,getService()方法获取服务。这些服务不是Binder对象仅限于同一进程内通信,类似于ServiceManager但更私有化,和ServiceManager最本质的区别是ServiceManager管理的服务都是binder服务,这些服务都是可以在不同进程之间跑的对象

管理的是普通的服务,只能在同一个进程之间使用的服务,不具备进程之间通信能力的服务,各个系统service在启动的过程中都会调用LocalServices.addService()把自己的local service注册到LocalServices中,比如AMS,WMS,PMS
注册到LocalServices中的service不是Binder对象,只能在同一个进程中使用,除此之外它的的使用方式和ServiceManager相似。

普通的java对象

SystemServiceRegistry

Android应用层将系统服务注册相关的API放在了SystemServiceRegistry类中,因为android系统的代码特别庞大,为了给上层应用开发者提供可以很方便的获取到被SystemServer进程管理的所有服务,那么通过

SystemServiceRegistry可以方便获取

比如ActivityManager am = (ActivityManager)getSystemService(Context.ACTIVITY_SERVICE);

最终是调用到ServiceManager获取对应服务的Ibinder对象

,普通的java对象

SystemService为了统一管理SystemServer进程运行的所有服务提供的一个统一接口对象普通的java对象
ServiceManagerServiceManager  android系统的服务守护进程,管理了整个android系统的所有Ibinder对象是一个单独的进程,和Zygote平级,都是由Init进程fork出来的

上面说了这么多,一图胜千言,下面看具体的

2.2,启动入口

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

的main方法里面

if (startSystemServer) {
                Runnable r = forkSystemServer(abiList, socketName, zygoteServer);

                // {@code r == null} in the parent (zygote) process, and {@code r != null} in the
                // child (system_server) process.
                if (r != null) {
                    r.run();
                    return;
                }
            }

fork出来,之所以后面可以通过java反射的方式调用到SystemServer,是因为在fork之前传递的参数重明确的说明了对象com.android.server.SystemServer,通知指定了运行所在的进程是在--nice-name=system_server

private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        ....................................................................................
        String args[] = {
            "--setuid=1000",
            "--setgid=1000",
            "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1021,1023,1032,3001,3002,3003,3006,3007,3009,3010",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "com.android.server.SystemServer",
        };
        ....................................................................................

最后通过反射的方法调用MethodAndArgsCaller对象以后调用到SystemServer的main方法

static class MethodAndArgsCaller implements Runnable {
        /** method to call */
        private final Method mMethod;

        /** argument array */
        private final String[] mArgs;

        public MethodAndArgsCaller(Method method, String[] args) {
            mMethod = method;
            mArgs = args;
        }

        public void run() {
            try {
                mMethod.invoke(null, new Object[] { mArgs });

2.3,创建对应进程的binder

上面只是完成了SystemServer的启动,但是此时SystemServer并不具备进程之间通信的能力,所以在SystemServer启之后,立刻打开binder,做进程映射,

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

 public static final Runnable zygoteInit(int targetSdkVersion, 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();
        ZygoteInit.nativeZygoteInit();
        return RuntimeInit.applicationInit(targetSdkVersion, argv, classLoader);
    }

此时通过nativeZygoteInit调用到jni,frameworks/base/core/jni/AndroidRuntime.cpp

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

最后调用到/Volumes/aosp/android-8.1.0_r52/frameworks/base/cmds/app_process/app_main.cpp

virtual void onZygoteInit()
{
    sp<ProcessState> proc = ProcessState::self();
    ALOGV("App process: starting thread pool.\n");
    proc->startThreadPool();
}

/Volumes/aosp/android-8.1.0_r52/frameworks/native/libs/binder/ProcessState.cpp

ProcessState::ProcessState(const char *driver)
    : mDriverName(String8(driver))
    , mDriverFD(open_driver(driver))
    , mVMStart(MAP_FAILED)
    , mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
    , mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
    , mExecutingThreadsCount(0)
    , mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
   ............................
{
    if (mDriverFD >= 0) {
............................
        mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
...................................
        }
    }
............................v
}

可以看到这里其实就是做了三件事情

1)打开binder驱动,和binder通信,通过

status_t result = ioctl(fd, BINDER_VERSION, &vers);

2)mmap内存映射

mVMStart = mmap(0, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);

3)设置binder驱动最多可以创建的线程数,

result = ioctl(fd, BINDER_SET_MAX_THREADS, &maxThreads);

#define BINDER_VM_SIZE ((1 * 1024 * 1024) - sysconf(_SC_PAGE_SIZE) * 2)
#define DEFAULT_MAX_BINDER_THREADS 15

所以通过这里可以看到binder之前通信的时候最大的传递数据大小是1M-8K(同步),异步场景下是减半,binder驱动最多可以创建的线程数是15个,不包含主线程,包含的话是16个,上面这部分牵扯到binder驱动源码的分析,后续会用单独的文章讲解,敬请期待

2.4 创建binder线程池

/Volumes/aosp/android-8.1.0_r52/frameworks/native/libs/binder/ProcessState.cpp

 virtual void onZygoteInit()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    }

启动binder线程池 

void ProcessState::startThreadPool()
{
    AutoMutex _l(mLock);
    if (!mThreadPoolStarted) {
        mThreadPoolStarted = true;
        spawnPooledThread(true);
    }
}

设置binder线程名字:Binder:%d_%X", pid, s,通过这段代码可以看到binder线程的名字是从0递增

void ProcessState::spawnPooledThread(bool isMain)
{
    if (mThreadPoolStarted) {
        String8 name = makeBinderThreadName();
        ALOGV("Spawning new pooled thread, name=%s\n", name.string());
        sp<Thread> t = new PoolThread(isMain);
        t->run(name.string());
    }
}

可以看到 PoolThread本身就是一个线程,在创建进程的时候此时启动的线程就是主线程,也是唯一的一个主线程,ProcessState是Application在Framework Native层面的对象,代表一个进程


class PoolThread : public Thread
{
public:
    explicit PoolThread(bool isMain)
        : mIsMain(isMain)
    {
    }
    
protected:
    virtual bool threadLoop()
    {
        IPCThreadState::self()->joinThreadPool(mIsMain);
        return false;
    }
    
    const bool mIsMain;
};

在ProcessState里面调用了run方法以后,最后调用到了/Volumes/aosp/android-8.1.0_r52/system/core/libutils/Threads.cpp的run----->_threadLoop

status_t Thread::run(const char* name, int32_t priority, size_t stack)
{
    ................................................

    bool res;
    if (mCanCallJava) {
        res = createThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    } else {
        res = androidCreateRawThreadEtc(_threadLoop,
                this, name, priority, stack, &mThread);
    }

     ................................................
    return NO_ERROR;
}
int Thread::_threadLoop(void* user)
{
    ----------------------------------------------------------------------

    do {
        bool result;
        if (first) {
            first = false;
            self->mStatus = self->readyToRun();
            result = (self->mStatus == NO_ERROR);

            if (result && !self->exitPending()) {
               ----------------------------------------------------------------------

                result = self->threadLoop();
            }
        } else {
            result = self->threadLoop();
        }

         
    } while(strong != 0);

    return 0;
}

到这里以后会开始binder线程池,通过/Volumes/aosp/android-8.1.0_r52/frameworks/native/libs/binder/IPCThreadState.cpp ,IPCThreadState.cpp代表一个IPC线程池管理

IPCThreadState* IPCThreadState::self()
{
   ---------------------------------------------

    pthread_mutex_lock(&gTLSMutex);
    if (!gHaveTLS) {
        int key_create_value = pthread_key_create(&gTLS, threadDestructor);
        if (key_create_value != 0) {
            pthread_mutex_unlock(&gTLSMutex);
   ---------------------------------------------

            return NULL;
        }
        gHaveTLS = true;
    }
    pthread_mutex_unlock(&gTLSMutex);
    goto restart;
}

到这里为止才开始创建了一个真正的线程pthread_key_create,目前这种场景下只是创建了一个线程,所以读者有时候觉得joinThreadPool线程池不太明白,其实joinThreadPool后续还会被binder驱动调用,创建更多的线程,所以这个方法的名字叫做线程池

对于isMain=true的情况下, command为BC_ENTER_LOOPER,代表的是Binder主线程,不会退出的线程;
对于isMain=false的情况下,command为BC_REGISTER_LOOPER,表示是由binder驱动创建的线程。

Binder系统中可分为3类binder线程:

Binder主线程:进程创建过程会调用startThreadPool()过程中再进入spawnPooledThread(true),来创建Binder主线程。编号从1开始,也就是意味着binder主线程名为binder_1,并且主线程是不会退出的。
Binder普通线程:是由Binder Driver来根据是否有空闲的binder线程来决定是否创建binder线程,回调spawnPooledThread(false) ,isMain=false,该线程名格式为binder_x。
Binder其他线程:其他线程是指并没有调用spawnPooledThread方法,而是直接调用IPC.joinThreadPool(),将当前线程直接加入binder线程队列。例如: mediaserver和servicemanager的主线程都是binder线程,但system_server的主线程并非binder线程

对于joinThreadPoo其实最主要做了三件事情

1)processPendingDerefs 清除队列的引用

2)getAndExecuteCommand,如果有下一条命令的话,执行下一条命令,没有的话在这里等待,只有主进程创建的主线程会一直在这里等待,非主线程出现timeout则线程退出

3)mOut.writeInt32(BC_EXIT_LOOPER);推出循环以后通知binde驱动

4)talkWithDriver 和binder驱动通信交互数据

void IPCThreadState::joinThreadPool(bool isMain)
{
    LOG_THREADPOOL("**** THREAD %p (PID %d) IS JOINING THE THREAD POOL\n", (void*)pthread_self(), getpid());

    mOut.writeInt32(isMain ? BC_ENTER_LOOPER : BC_REGISTER_LOOPER);

    status_t result;
    do {
        processPendingDerefs();
        // now get the next command to be processed, waiting if necessary
        result = getAndExecuteCommand();

        if (result < NO_ERROR && result != TIMED_OUT && result != -ECONNREFUSED && result != -EBADF) {
            ALOGE("getAndExecuteCommand(fd=%d) returned unexpected error %d, aborting",
                  mProcess->mDriverFD, result);
            abort();
        }

        // Let this thread exit the thread pool if it is no longer
        // needed and it is not the main process thread.
        if(result == TIMED_OUT && !isMain) {
            break;
        }
    } while (result != -ECONNREFUSED && result != -EBADF);

    LOG_THREADPOOL("**** THREAD %p (PID %d) IS LEAVING THE THREAD POOL err=%d\n",
        (void*)pthread_self(), getpid(), result);

    mOut.writeInt32(BC_EXIT_LOOPER);
    talkWithDriver(false);
}

这块需要结合binder驱动源码来讲解,参考下面

三,binder驱动和binder线程池

binder驱动源码android-msm-angler-3.10-oreo-r6/msm/drivers/staging/android/binder.c

四,SystemServer真正启动方法

4.1 SystemServer main方法里面主要做了几件事情

1)创建SystemServiceManager管理所有的服务

2)创建上下文createSystemContext,

这里可以看到会创建ActivityThread,因为systemserver是一个进程和app一样,都有自己的looper

,同时把自己也注入到ActivityThread,让ActivityThread持有自己的IBinder对象

mSystemServiceManager = new SystemServiceManager(mSystemContext);
            mSystemServiceManager.setRuntimeRestarted(mRuntimeRestart);
            LocalServices.addService(SystemServiceManager.class, mSystemServiceManager);
            // Prepare the thread pool for init tasks that can be parallelized
            SystemServerInitThreadPool.get();

3)启动核心服务和其他服务

try {
            traceBeginAndSlog("StartServices");
            startBootstrapServices();
            startCoreServices();
            startOtherServices();
            SystemServerInitThreadPool.shutdown();

4)Looper.prepareMainLooper()

5)Looper.loop()

永远循环,这样子SystemServer进程才不会推出

4.2 分析startBootstrapServices

.....................................
Installer installer = mSystemServiceManager.startService(Installer.class);
   

 
        mSystemServiceManager.startService(DeviceIdentifiersPolicyService.class);
    
        mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();
        mActivityManagerService.setSystemServiceManager(mSystemServiceManager);
        mActivityManagerService.setInstaller(installer);
   

    
        mPowerManagerService = mSystemServiceManager.startService(PowerManagerService.class);
 
        mActivityManagerService.initPowerManagement();
    

     
        mSystemServiceManager.startService(LightsService.class);
        
        mDisplayManagerService = 
       mSystemServiceManager.startService(DisplayManagerService.class);
    
        
 mSystemServiceManager.startBootPhase(SystemService.PHASE_WAIT_FOR_DEFAULT_DISPLAY);
         .....................................

4.3 分析startBootstrapServices

mSystemServiceManager.startService(DropBoxManagerService.class);
      
mSystemServiceManager.startService(BatteryService.class);
       
mSystemServiceManager.startService(UsageStatsService.class);
mActivityManagerService.setUsageStatsManager(
LocalServices.getService(UsageStatsManagerInternal.class));
       
mWebViewUpdateService = mSystemServiceManager.startService(WebViewUpdateService.class);
mPackageManagerService = PackageManagerService.main(mSystemContext, installer,
                mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);
        mFirstBoot = mPackageManagerService.isFirstBoot();

      mActivityManagerService = mSystemServiceManager.startService(
                ActivityManagerService.Lifecycle.class).getService();

AMS启动之后会通过mActivityManagerService.setSystemProcess();

添加到ServiceManager

public void setSystemProcess() {
        try {
            ServiceManager.addService(Context.ACTIVITY_SERVICE, this, true);
            ServiceManager.addService(ProcessStats.SERVICE_NAME, mProcessStats);
            ServiceManager.addService("meminfo", new MemBinder(this));
            ServiceManager.addService("gfxinfo", new GraphicsBinder(this));
            ServiceManager.addService("dbinfo", new DbBinder(this));
            if (MONITOR_CPU_USAGE) {
                ServiceManager.addService("cpuinfo", new CpuBinder(this));
            }
            ServiceManager.addService("permission", new PermissionController(this));
            ServiceManager.addService("processinfo", new ProcessInfoService(this));

            ApplicationInfo info = mContext.getPackageManager().getApplicationInfo(
                    "android", STOCK_PM_FLAGS | MATCH_SYSTEM_ONLY);
            mSystemThread.installSystemApplicationInfo(info, getClass().getClassLoader());

          
                updateLruProcessLocked(app, false, null);
               
            }
        } catch (PackageManager.NameNotFoundException e) {
            throw new RuntimeException(
                    "Unable to find android system package", e);
        }
    }

4.4 分析startBootstrapServices

  

wm = WindowManagerService.main(context, inputManager,
                    mFactoryTestMode != FactoryTest.FACTORY_TEST_LOW_LEVEL,
                    !mFirstBoot, mOnlyCore, new PhoneWindowManager());
            ServiceManager.addService(Context.WINDOW_SERVICE, wm);
            ServiceManager.addService(Context.INPUT_SERVICE, inputManager);

可以看到所有的Service在启动以后,都会添加到ServiceManager进程来管理,通过这里就可以看到ServiceManager是整个android系统的大管家

五,结束语

鉴于作者水平有限,文章之中难免有错误或者遗漏地方,欢迎大家批评指正,也欢迎大家讨论,积极评论哈,谢谢

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

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

相关文章

Oracle 深入理解Lock和Latch ,解析访问数据块全流程

Oracle 锁机制介绍 根据保护对象的不同&#xff0c;单实例Oracle数据库锁可以分为以下几大类&#xff1a; DML lock&#xff08;data locks&#xff0c;数据锁&#xff09;&#xff1a;用于保护数据的完整性&#xff1b; DDL lock&#xff08;dictionary locks&#xff0c;字典…

如何基于transformers库通过训练Qwen/DeepSeek模型的传统分类能力实现文本分类任务

文章目录 模型与环境准备文档分析源码解读模型训练及推理方式进阶:CPU与显存的切换进阶:多卡数据并行训练🔑 DDP 训练过程核心步骤🚫 DDP 不适用于模型并行⚖️ DDP vs. Model Parallelism⚙️ 解决大模型训练的推荐方法🎉进入大模型应用与实战专栏 | 🚀查看更多专栏…

Unity中一个节点实现植物动态(Shader)

1 . 核心思路就操作顶点作往复运动&#xff1b; 核心代码&#xff1a; half stage1 dot(positionOS, float3(0, 1, 0)) * _Strength; half stage2 sin(dot(positionOS, float3(1, 0, 0)) * _Strength _Time.y * _Speed); half stage3 stage1 * stage2 * float3(0.001,…

PrimeTime:工具简介

相关阅读 PrimeTimehttps://blog.csdn.net/weixin_45791458/category_12900271.html?spm1001.2014.3001.5482 PrimeTime是PrimeTime Suite中的一个工具&#xff0c;能够执行全芯片级、门级的静态时序分析&#xff0c;这是芯片设计和分析流程中的一个关键部分。该工具通过检查…

【拜读】Tensor Product Attention Is All You Need姚期智团队开源兼容RoPE位置编码

姚期智团队开源新型注意力&#xff1a;张量积注意力&#xff08;Tensor Product Attention&#xff0c;TPA&#xff09;。有点像一种「动态的LoRA」&#xff0c;核心思路在于利用张量分解来压缩注意力机制中的 Q、K、V 表示&#xff0c;同时保留上下文信息&#xff0c;减少内存…

Docker-技术架构演进之路

目录 一、概述 常见概念 二、架构演进 1.单机架构 2.应用数据分离架构 3.应用服务集群架构 4.读写分离 / 主从分离架构 5.引入缓存 —— 冷热分离架构 6.垂直分库 7.业务拆分 —— 微服务 8.容器化引入——容器编排架构 三、尾声 一、概述 在进行技术学习过程中&am…

用Chrome Recorder轻松完成自动化测试脚本录制

前言 入门自动化测试,录制回放通常是小白测试首先用到的功能。而录制回放工具也一直是各大Web自动化测试必然会着重提供的一块功能。 早期WinRunner、QTP这样的工具,自动化测试可以说是围绕录制回放开展的。近年像Selenium也提供有录制工具 Selenium IDE,Playwright也包含…

python中的异常-模块-包

文章目录 异常异常的定义异常捕获语法捕获常规异常捕获指定异常捕获多个异常捕获所有异常异常else异常finally 异常传递总结 模块概念导入自定义模块及导入main方法all变量 总结 包自定义包定义pycharm中建包的基本步骤导入方式 第三方包 异常 异常的定义 当检测到一个错误时…

【GPU驱动】OpenGLES图形管线渲染机制

OpenGLES图形管线渲染机制 OpenGL/ES 的渲染管线也是一个典型的图形流水线&#xff08;Graphics Pipeline&#xff09;&#xff0c;包括多个阶段&#xff0c;每个阶段都负责对图形数据进行处理。管线的核心目标是将图形数据转换为最终的图像&#xff0c;这些图像可以显示在屏幕…

ssm-day06 ssm整合

从springMVC总结再回顾一下 60节 整合就是应用框架&#xff0c;并且把这个框架放到IOC容器中 web容器&#xff1a;装springMVC和controller相关的web组件 root容器&#xff1a;装业务和持久层相关的组件 子容器可以引用父容器中的组件&#xff0c;父容器不能调子容器 一个容器…

AI 编程助手 cursor的系统提示词 prompt

# Role 你是一名极其优秀具有10年经验的产品经理和精通java编程语言的架构师。与你交流的用户是不懂代码的初中生&#xff0c;不善于表达产品和代码需求。你的工作对用户来说非常重要&#xff0c;完成后将获得10000美元奖励。 # Goal 你的目标是帮助用户以他容易理解的…

ollama如何安全卸载,解决Ollama unins000.msg is missing

春节后在本地电脑安装了Ollama的客户端&#xff0c;每次开机自启&#xff0c;影响开机速度&#xff0c;而且本地的模型不如联网的回答效果好&#xff0c;果断选择了卸载&#xff0c;但是今天卸载发现提示下方的错误。根据此文章可以解决当前的问题。 根据此文章可以解决当前的…

网络安全设备防护原理 网络安全防护装置

&#x1f345; 点击文末小卡片 &#xff0c;免费获取网络安全全套资料&#xff0c;资料在手&#xff0c;涨薪更快 防火墙 简介 网络层的防护设备&#xff0c;依照特殊的规则允许或者限制传输的数据通过 是由软件和硬件设备组合而成&#xff0c;在内部网和外部网之间、专用网…

Python的那些事第二十八篇:数据分析与操作的利器Pandas

Pandas:数据分析与操作的利器 摘要 Pandas是基于Python的开源数据分析库,广泛应用于数据科学、机器学习和商业智能等领域。它提供了高效的数据结构和丰富的分析工具,能够处理结构化数据、时间序列数据以及复杂的数据转换任务。本文从Pandas的基础概念入手,深入探讨其核心…

学习threejs,使用MeshBasicMaterial基本网格材质

&#x1f468;‍⚕️ 主页&#xff1a; gis分享者 &#x1f468;‍⚕️ 感谢各位大佬 点赞&#x1f44d; 收藏⭐ 留言&#x1f4dd; 加关注✅! &#x1f468;‍⚕️ 收录于专栏&#xff1a;threejs gis工程师 文章目录 一、&#x1f340;前言1.1 ☘️THREE.MeshBasicMaterial 二…

【git-hub项目:YOLOs-CPP】本地实现05:项目移植

ok&#xff0c;经过前3个博客&#xff0c;我们实现了项目的跑通。 但是&#xff0c;通常情况下&#xff0c;我们的项目都是需要在其他电脑上也跑通&#xff0c;才对。 然而&#xff0c;经过测试&#xff0c;目前出现了2 个bug。 项目一键下载【⬇️⬇️⬇️】&#xff1a; 精…

【python】协程(coroutine)

协程&#xff08;coroutine&#xff09;可以理解为一个可以中途暂停保存当前执行状态信息并可以从此处恢复执行的函数&#xff0c;多个协程共用一个线程执行&#xff0c;适合执行需要“等待”的任务。 所以严格意义上&#xff0c;多个协程同一时刻也只有一个在真正的执行&#…

【编译器】-LLVMIR

概述 LLVM 是一种基于静态单赋值 (SSA) 的表示形式&#xff0c;提供类型安全、低级操作、灵活性以及干净地表示“所有”高级语言的能力。 LLVM IR 是一门低级语言&#xff0c;语法类似于汇编任何高级编程语言&#xff08;如C&#xff09;都可以用LLVM IR表示基于LLVM IR可以很…

java面试场景问题

还在补充&#xff0c;这几天工作忙&#xff0c;闲了会把答案附上去&#xff0c;也欢迎各位大佬评论区讨论 1.不用分布式锁如何防重复提交 方法 1&#xff1a;基于唯一请求 ID&#xff08;幂等 Token&#xff09; 思路&#xff1a;前端生成 一个唯一的 requestId&#xff08;…

python pandas下载

pandas pandas:就是一个可以处理数据的 python 库 核心功能&#xff1a; 数据的清洗&#xff1a;处理丢失值&#xff0c;重复值数据分析&#xff1a;计算和统计信息&#xff0c;或分组汇总数据可视化&#xff1a;结合 图标库&#xff08;Matplotlib&#xff09;完成数据可视化…