Android系统启动流程概览

这篇具有很好参考价值的文章主要介绍了Android系统启动流程概览。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

第一节 Android系统启动流程

Boot Rom —— Bootloader —— Linux Kernel —— init进程 —— Zygote进程(dalvik/ art)—— systemServer —— Apps

init进程

init 进程是Linux系统中,用户空间启动的第一个进程。

  1. 创建并挂载一些文件目录
  2. 启动属性服务
  3. 解析 init.rc 配置文件,启动 Zygote 进程

挂载 seLinux 文件目录,创建seLinux,加载 安全策略
启动内核日志
启动属性系统,从文件读取属性
创建epoll

第二节 init.rc 解析

包含五种类型语句:

  1. Action(包含command)
  2. Commands
  3. Services(将由 init进程 启动的服务)
  4. Options(服务的选项)
  5. Import (其他配置文件)

Zygote服务 也在init.rc中配置:
//进程名为Zygote,执行的真正程序是 /system/bin/app_process

service zygote /system/bin/app_process -Xzygote /system/bin --zygote --start-system-server           
    class main        //classname为main
    priority -20
    user root
    group root readproc reserved_disk
    socket zygote stream 660 root system
    socket usap_pool_primary stream 660 root system
    onrestart exec_background - system system -- /system/bin/vdc volume abort_fuse
    onrestart write /sys/power/state on
    onrestart restart audioserver
    onrestart restart cameraserver
    onrestart restart media
    onrestart restart netd
    onrestart restart wificond
    writepid /dev/cpuset/foreground/tasks

第三节 Zygote进程的启动过程

zygote受精卵,用于孵化子进程。所有APP及Systemserver进程 都由zygote 通过Linux的fork() 函数孵化出来的。
Zygote进程是Android系统中第一个带有art 虚拟机的进程,Zygote 与 SystemServer 进程以socket的方式进行通信。
Zygote是C/S模型的服务端,主要负责创建 Java 虚拟机,加载系统资源,启动 SystemServer 及其他进程。

启动一个应用的流程:
点击应用图标 —— AMS —— 发出socket —— Zygote —— linux fork() 新进程

/frameworks/base/cmds/app_process/app_main.cpp
App_main.main() 有两种启动模式:

  1. Zygote初始化模式
  2. application模式

  • Zygote初始化模式,会启动 SystemServer ,并指定自己的 socket 名字为 zygote
  • application模式,就是启动普通应用,参数有 class 名字及参数

(C++世界)(Zygote Service)App_main() —— AndroidRumtime.start() 【startVm() —— startReg() —— callMain() 】—— (Java世界开始)ZygoteInit.main() 【registerZygoteSocket() —— preload() —— gc() —— startSystemServer() —— runSelectLoop()】

  1. AndroidRumtime.start() —— startVm() —— startReg():AndroidRumtime::Start() 函数中将启动 JavaVM,并注册所有FWK相关的系统 JNI 接口。为 Java 世界做好准备。
  2. ZygoteInit.main() registerZygoteSocket() —— preload() —— gc() —— startSystemServer() —— runSelectLoop():第一次进入Java世界,运行 ZygoteInit.java::main() 初始化Zygote,并创建 Socket 的 Server 端。preload 常用的 Java 类库、系统 resources,执行 gc() 清理内存,为 fork 子进程做好准备。然后 fork 出子进程,在子进程中 初始化 SystemServer,初始化的过程中 启动 Android 系统所有的 Service。
  3. (与此同时,Zygote 继续在后台监听 Socket 等待新的应用启动请求。)
  4. (与此同时,Zygote 监听 SystemServer 的 SIGHID信号,一旦SystemServer挂掉,立即 kill 掉 Zygote 自己。init 会重启 Zygote,再启动 SystemServer,使系统恢复正常。 )
  5. AMS ready后,寻找系统的 “Startup” Application,向 Zygote 发请求。
  6. Zygote fork 出“Startup” Application,也就是启动 Launcher 应用,然后显示出Home页面。

在fork SystemServer之前执行了 gc 操作,使得被 fork 出来的子进程有尽可能少的垃圾内存。


ZygoteInit的启动入口是:
app_main.cpp(也就是Zygote的C++进程) ::main() —— AppRuntime.start(“com.android.internal.os.ZygoteInit”)

AndroidRuntime包括了:从下到上 libc、JNI、Java-VM、Java-lib

3.1 AndroidRuntime.start 做了三件事:

  1. startVm():
  2. startReg():
  3. callMain():

3.1.1 startVm:

cmds/app_process.cpp
base/core/jni/AndroidRuntime.cpp startvm()
art/runtime/java_vm_ext.cc JNI_CreateJavaVM() 返回JniEnv JavaVm给Native代码,这样就可以访问java接口了
art/runtime/runtime.cc 提供art虚拟机运行时:创建堆管理对象gc::Heap、创建Java虚拟机对象JavaVmExt、为vm添加JniEnvHandler、连接主线程、创建类连接器

3.1.2 startReg:

主要做两件事:

  1. 设置创建可访问Java的线程方法
  2. 注册所有的JNI方法
3.1.2.1 设置创建可访问Java的线程方法

system/core/libutils/Threads.cpp::run() native层可以创建两种Thread:一种可以调用java方法的线程,一种是纯native的线程。
可调用java的线程,会调用vm 将线程与 JNIEnv 绑定,使之可通过JNIEnv调用java方法。

// base/core/jni/androidRuntime.cpp
/*
 * Makes the current thread visible to the VM.
 *
 * The JNIEnv pointer returned is only valid for the current thread, and
 * thus must be tucked into thread-local storage.
 */
static int javaAttachThread(const char* threadName, JNIEnv** pEnv)
{
    JavaVMAttachArgs args;
    JavaVM* vm;
    vm = AndroidRuntime::getJavaVM();
    result = vm->AttachCurrentThread(pEnv, (void*) &args);
    if (result != JNI_OK)
        ALOGI("NOTE: attach of thread '%s' failed\n", threadName);

    return result;
}

从数据结构上看,thread.h 类中,有一个 JNIEnvExt* 的指针变量 jni_env, 表明一个thread与一个jni_env关联。

// runtime/thread.h
struct PACKED(sizeof(void*)) tls_ptr_sized_values {
    // Every thread may have an associated JNI environment
    JNIEnvExt* jni_env;
}
3.1.2.2 注册所有的JNI方法

除了系统的JNI接口(javacore nativehelper)FWK还有大量的Native实现,所有这些接口一次性通过startReg()来完成
base/core/jni/androidRuntime.cpp::startReg()

/*
 * Register android native functions with the VM.
 */
/*static*/ int AndroidRuntime::startReg(JNIEnv* env)
{
	......
	register_jni_procs(gRegJNI, NELEM(gRegJNI), env)
}
static int register_jni_procs(const RegJNIRec array[], size_t count, JNIEnv* env)
{
    for (size_t i = 0; i < count; i++) {
        if (array[i].mProc(env) < 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_EventLog),
    REG_JNI(register_android_util_Log),
    REG_JNI(register_android_util_MemoryIntArray),
    REG_JNI(register_android_util_PathParser),
    REG_JNI(register_android_util_StatsLog),
    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_text_AndroidCharacter), 
    ... ...
}

// frameworks/base/core/jni/android_util_Log.cpp
int register_android_util_Log(JNIEnv* env)
{
    jclass clazz = FindClassOrDie(env, "android/util/Log");
    levels.verbose = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "VERBOSE", "I"));
    levels.debug = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "DEBUG", "I"));
    levels.info = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "INFO", "I"));
    levels.warn = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "WARN", "I"));
    levels.error = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ERROR", "I"));
    levels.assert = env->GetStaticIntField(clazz, GetStaticFieldIDOrDie(env, clazz, "ASSERT", "I"));
    return RegisterMethodsOrDie(env, "android/util/Log", gMethods, NELEM(gMethods));
}

/*
 * Register native methods using JNI.
 */
/*static*/ int AndroidRuntime::registerNativeMethods(JNIEnv* env,
    const char* className, const JNINativeMethod* gMethods, int numMethods)
{
    return jniRegisterNativeMethods(env, className, gMethods, numMethods);
}

// libnativehelper/JNIHelp.cpp
extern "C" int jniRegisterNativeMethods(C_JNIEnv* env, const char* className, constJNINativeMethod* gMethods, int numMethods){
    JNIEnv* e = reinterpret_cast<JNIEnv*>(env);
    (*env)->RegisterNatives(e, c.get(), gMethods, numMethods)
	... ...
}

3.1.3 callMain()

// frameworks/base/core/java/com.android.internal.os.RuntimeInit.java
public static final void main(String[] argv) {
		......
        /*
         * Now that we're running in interpreted code, call back into native code
         * to run the system.
         */
        nativeFinishInit();
        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }
// frameworks/base/core/jni/AndroidRuntime.cpp
/*
 * Code written in the Java Programming Language calls here from main().
 */
static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{
    gCurRuntime->onStarted();
}

// frameworks/base/cmds/app_process/app_main.cpp
virtual void onStarted()
{
    AndroidRuntime* ar = AndroidRuntime::getRuntime();
    ar->callMain(mClassName, mClass, mArgs);
}

callMain() 将调起Java世界的 com.android.internal.os.ZygoteInit 类的main()方法。

3.2 (Java世界开始)ZygoteInit.main()

public static void main(String argv[]) {
        ZygoteServer zygoteServer = new ZygoteServer();

        final Runnable caller;
        try {
            boolean startSystemServer = false;
            String socketName = "zygote";
            String abiList = null;
            boolean enableLazyPreload = false;
            for (int i = 1; i < argv.length; i++) {
                if ("start-system-server".equals(argv[i])) {
                    startSystemServer = true;
                } else if ("--enable-lazy-preload".equals(argv[i])) {
                    enableLazyPreload = 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]);
                }
            }

			// 注册Server socket
            zygoteServer.registerServerSocketFromEnv(socketName); 
			if (!enableLazyPreload) {
                bootTimingsTraceLog.traceBegin("ZygotePreload");
                // 加载类到内存
                preload(bootTimingsTraceLog); 
            }
			preloadTextResources();
			// 垃圾回收
			gcAndFinalize();
            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;
                }
            }

            Log.i(TAG, "Accepting command socket connections");

            // The select loop returns early in the child process after a fork and
            // loops forever in the zygote.
            caller = zygoteServer.runSelectLoop(abiList);
        } catch (Throwable ex) {
            Log.e(TAG, "System zygote died with exception", ex);
            throw ex;
        } finally {
            zygoteServer.closeServerSocket();
        }
    }

preload是Android 启动最耗时的部分之一

/**
     * Prepare the arguments and forks for the system server process.
     *
     * Returns an {@code Runnable} that provides an entrypoint into system_server code in the
     * child process, and {@code null} in the parent.
     */
    private static Runnable forkSystemServer(String abiList, String socketName,
            ZygoteServer zygoteServer) {
        /* Hardcoded command line to start the system server */
        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",
            "--capabilities=" + capabilities + "," + capabilities,
            "--nice-name=system_server",
            "--runtime-args",
            "--target-sdk-version=" + VMRuntime.SDK_VERSION_CUR_DEVELOPMENT,
            "com.android.server.SystemServer",
        };
        ZygoteConnection.Arguments parsedArgs = null;
        int pid;
        try {
            parsedArgs = new ZygoteConnection.Arguments(args);
            ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
            ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);

            boolean profileSystemServer = SystemProperties.getBoolean(
                    "dalvik.vm.profilesystemserver", false);
            if (profileSystemServer) {
                parsedArgs.runtimeFlags |= Zygote.PROFILE_SYSTEM_SERVER;
            }

            /* Request to fork the system server process */
            pid = Zygote.forkSystemServer(
                    parsedArgs.uid, parsedArgs.gid,
                    parsedArgs.gids,
                    parsedArgs.runtimeFlags,
                    null,
                    parsedArgs.permittedCapabilities,
                    parsedArgs.effectiveCapabilities);
        } catch (IllegalArgumentException ex) {
            throw new RuntimeException(ex);
        }
        /* For child process */
        if (pid == 0) {
            if (hasSecondZygote(abiList)) {
                waitForSecondaryZygote(socketName);
            }

            zygoteServer.closeServerSocket();
            return handleSystemServerProcess(parsedArgs);
        }
        return null;
    }

第四节 SystemServer 启动流程

SystemServer 是 Zygote fork出来的第一个 Java 进程,此进程非要重要,提供了 Android 系统所有的核心服务。
SystemServer 中运行着AMS,WMS 等系统服务,Binder-x 之类的服务线程,UI-thread InputReader

SystemServer 创建:ActivityThread、SystemContext、SystemUIContext
ContextImpl 是 Context 的实现类,其中封装了生成 4 种常用的 createContext 方法:

  • createSystemContext()
  • createSystemUiContext()
  • createAppContext()
  • createActivityContext()

启动Service

startBootstrapServices();
startCoreServices();
startOtherServices();
  • startBootstrapServices():启动系统启动所需的一小堆关键服务(AMS,PMS,RecoverySystemService,LightsService,DisplayManagerService,UserManagerService,OverlayManagerService)。这些服务具有复杂的相互依赖关系,这就是为什么我们在这里将它们全部初始化的原因。除非您的服务也与这些依赖关系纠缠在一起,否则它应该在其他函数中进行初始化。
  • startCoreServices():启动一些在引导过程中没有互相交互的基本服务(BatteryService, UsageStatsService, WebViewUpdateService, BinderCallsStatsService)。
  • startOtherServices():启动一些杂七杂八的东西(ConnectivityService,WMS,InputManagerService,TelephonyRegistry)

Zygote会在后台观察 SystemServer ,一旦 System 挂掉了,将其回收,然后重启 Zygote 自己。
Watchdog 也可能因某种原因,将 SystemServer Kill 掉,并重启。屏幕会黑屏一段时间。

在 Unix-like 系统,父进程必须用 waitpid 等待子进程退出,否则子进程将变成 Zombie(僵尸)进程,不仅系统资源泄漏,而且系统将崩溃。没有 SystemServer 所有 Android 应用程序都无法运行。
waitpid() 是一个阻塞函数,所以通常处理是 在 signal 函数里无阻塞调用。
每个子进程退出时 —— 发出 SIGCHID 信号 —— Zygote 会杀掉自己 —— 系统给所有子进程发送 SIGHUP 信号 —— 各子进程杀掉自己退出当前进程(子进程中的 Daemon 进程设置 SIG_IGN参数忽略 SIGHUP 信号继续运行)。

第五节 其他小点

5.1 什么情况下 Zygote 进程会重启呢?

ServiceManager、SystemServer、SurfaceFlinger、Zygote自己 进程被杀

5.2 fork函数

返回值 == 0:成功创建子进程,并进入子进程执行
返回值 > 0:成功创建子进程,返回子进程id,并继续沿原父进程执行
返回值 < 0:创建失败
失败原因主要有:进程数超过系统上限(EAGAIN),系统内存不足(ENOMEM)
fork() 出的子进程,是父进程的一个copy,继承了整个进程的地址空间:包括进程上下文、进程堆栈、打开的文件描述符、信号控制设定、进程优先级、进程组号等。与父进程不同的只有进程号、计时器等少量信息。由上可见,fork() 函数的开销是很大的。

5.3 写时拷贝(copy-on-write)

Linux 的 fork 采用写时拷贝。写时拷贝是一种可能推迟甚至避免拷贝的技术。内核此时并不复制整个进程的地址空间,而是让子进程共享父进程的地址空间。只有在需要写入时,才会复制地址空间,使子进程拥有自己的地址空间。

panic
内核出现异常会导致1 进程死亡,如果是中断上下文的异常或系统关键资源受到破坏,通常会导致2 内核挂起,不再响应任何事件。

5.4 内核空间、用户空间

在linux中, 将最高的1G字节(从虚拟地址0xC0000000到0xFFFFFFFF),供内核使用,称为“内核空间”。而将较低的3G字节(从虚拟地址 0x00000000到0xBFFFFFFF),供各个进程使用,称为“用户空间)
内核非法使用了用户空间的地址故存在问题。

5.5 epoll

epoll
epoll_create、epoll_wait、epoll_ctl文章来源地址https://www.toymoban.com/news/detail-462098.html

5.6 内核的异常级别

1. bug
2. Oops
3. panic
  • Bug:是指那些不符合内核的正常设计,但内核能够检测出来并且对系统运行不会产生影响的问题,比如在原子上下文中休眠。
  • Oops:程序在内核态时,进入一种异常情况,比如引用非法指针导致的数据异常,数组越界导致的取指异常,此时异常处理机制能够捕获此异常,并将系统关键信息打印到串口上,正常情况下Oops消息会被记录到系统日志中去。
    Oops发生时,进程处在内核态,很可能正在访问系统关键资源,并且获取了一些锁,当进程由于Oops异常退出时,无法释放已经获取的资源,导致其他需要获取此资源的进程挂起,对系统的正常运行造成影响。通常这种情况,系统处在不稳定的状态,很可能崩溃。
  • panic:当Oops发生在中断上下文中或者在进程0和1中,系统将彻底挂起,因为中断服务程序异常后,将无法恢复,这种情况即称为内核panic。另外当系统设置了panic标志时,无论Oops发生在中断上下文还是进程上下文,都将导致内核Panic。由于在中断复位程序中panic后,系统将不再进行调度,Syslogd将不会再运行,因此这种情况下,Oops的消息仅仅打印到串口上,不会被记录在系统日志中。

到了这里,关于Android系统启动流程概览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 笔记:Android 9系统启动流程

    当电源键按下时,引导芯片代码(汇编指令)会从预定的地方(固化在ROM)开始执行,将引导程序 BootLoader 加载到 RAM中,然后执行 BootLoader 是在 Android 操作系统开始前的一个小程序,主要作用是把系统OS拉起来并运行 位置: bootablebootloader 当 Linux系统被 BootLoader 程序拉起,

    2024年02月14日
    浏览(39)
  • Android T 远程动画显示流程其二——系统侧动画启动流程

    接着上篇文章分析 Android T 远程动画显示流程其一 下面,我们以从桌面点击一个应用启动的场景来分析远程动画的流程,窗口添加的流程见Android T WMS窗口相关流程 这里我们从AppTransitionController.handleAppTransitionReady方法开始跟踪代码流程 代码路径:framework/services/core/java/com/and

    2024年03月28日
    浏览(45)
  • 基于Android13的系统启动流程分析(三)之FirstStageMain阶段

    Android13系统启动阶段大致分为FirstStageMain阶段和SecondStageMain,此章主要讲FirstStageMain阶段 (若分析有误敬请指教) 本章讲解的方向和你将收获的知识: 用户空间进程的调用流程 当进程挂掉后该如何处理 何时挂载上的基本文件系统和文件系统小知识 FirstStageMain阶段会挂载上什

    2024年02月10日
    浏览(58)
  • 基于Android13的系统启动流程分析(四)之SecondStageMain阶段

    Android13系统启动阶段大致分为FirstStageMain阶段和SecondStageMain,此章主要讲SecondStageMain阶段 (若分析有误敬请指教) 在基于Android13的系统启动流程分析(三)之FirstStageMain阶段已经讲解过android系统启动的基本介绍了,这里不再单独介绍了 我们先看是怎么进入该阶段的,仍然是

    2023年04月24日
    浏览(49)
  • Android 12系统源码_窗口管理(一)WindowManagerService的启动流程

    WindowManagerService是Android系统中重要的服务,它是WindowManager的管理者,WindowManagerService无论对于应用开发还是Framework开发都是重要的知识点,究其原因是因为WindowManagerService有很多职责,每个职责都会涉及重要且复杂的系统,这使得WindowManagerService就像一个十字路口的交通灯一样

    2024年02月11日
    浏览(46)
  • 基于Android13的系统启动流程分析(一)之SeLinux权限介绍

    学习Android系统启动流程之前先学习一下SeLinux权限系统,步入正题 本章讲解的方向和你将收获的知识: 什么是SeLinux系统,SeLinux的简介和介绍 SeLinux系统的主要作用和存在的意义,是基于哪个版本开始推行该方案的 如果遇到了SeLinux权限问题该如何解决,有几种解决方案 SeLi

    2024年02月04日
    浏览(89)
  • Android NDK开发详解之调试和性能分析的系统跟踪概览

    “系统跟踪”就是记录短时间内的设备活动。系统跟踪会生成跟踪文件,该文件可用于生成系统报告。此报告有助于您了解如何最有效地提升应用或游戏的性能。 有关进行跟踪和性能分析的全面介绍,请参阅 Perfetto 文档中的跟踪 101 页面。 Android 平台提供了多种不同的跟踪记

    2024年02月06日
    浏览(52)
  • Android启动流程优化 中篇

    本文链接:https://blog.csdn.net/feather_wch/article/details/131587046 1、我们可以优化部分 Application构建到主界面onWindowFocusChanged 2、启动方式(官方) 冷启动 热启动 温启动 3、怎么样算是卡顿? 卡顿:2-5-8原则 2秒以内:流程 2-5秒:可以接受 5-8秒:有些卡顿 8秒以上:非常卡顿,没办法接

    2024年02月12日
    浏览(49)
  • Android:启动流程

    第一步:启动电源以及系统启动 当电源按下,引导芯片代码开始从预定义的地方(固化在ROM)开始执行。加载引导程序到RAM,然后 执行 第二步:引导程序 引导程序是在Android操作系统开始运行前的一个小程序。引导程序是运行的第一个程序,因此它是针 对特定的主板与芯片的

    2023年04月16日
    浏览(39)
  • Android Activity的启动流程(Android-10)

    在Android开发中,我们经常会用到startActivity(Intent)方法,但是你知道startActivity(Intent)后Activity的启动流程吗?今天就专门讲一下最基础的startActivity(Intent)看一下Activity的启动流程,同时由于Launcher的启动后续和这里基本类似,就记录在一起。注意本章都是基于Android-10来讲解的。

    2024年01月17日
    浏览(47)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包