Android系统源码分析-从init进程开始

从源码角度看Android系统init进程启动过程 init进程是Linux系统中用户空间的第一个进程,进程号为1。Kernel启动后,在用户空间启动init进程,并调用/system/core/init.cpp中的main方法执行一些重要的工作。 备注:本文将结合Android8.0的源码看init进程的启动过程以及init进程做了哪些重要工作。 1. init进程启动前系统的启动流程 在引入init进程前,我们需要大致了解系统是如何走到init... 阅读详情

简介 

Android系统底层用的是Linux,和Linux一样,init进程是Linux系统用户进程的第一个进程,它是由Linux内核(kenerl)启动的。其它所有的用户进程都是init进程的子进程。
我们从init进程来分析Android系统的启动流程。

 

init进程代码分析: 

init进程是被kernel中的kernel_init函数通过execve系统调用进行的初始化。

init进程的代码位置:
system/core/init/init.c 

init的main函数:

 (1) 创建并挂载文件系统相关目录,并且赋予root权限(0755); 例如,代码中的:

    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

也就是说,这些目录是在Android系统运行期间由init进程创建的,而不是在编译期间创建。 

(2)创建标准输入输出设备节点文件,即

open_devnull_stdio();

 这个函数会在/dev目录下创建__null__设备节点文件,然后,标准输入输出,标准错误输出会重定向到这个设备节点中。

(3) 简析init.rc,%hardware%.rc文件。init.rc文件是init进程启动后执行的脚本文件。在rc文件中,启动了很多守护进程,例如,服务管理器servicemanager,rild,installd......等等。init.rc它主要包含五种类型语句: 
Action、Commands、Services、Options和Import。

解析init.rc的语句是:

init_parse_config_file("/init.rc");

(4) 初始化设备id;

(5)初始化属性(property)服务;

property_init()

Android系统中的所有进程共享系统属性值,这些属性值被保存在共享内存中,通过调用 property_init()函数,来初始化属性值;

(6) 启动property服务;

(7) 循环处理device/property,进行事件处理。包括重启(restart_processes()函数)或终止子进程,并发送SIGCHLD信号等。

整个流程图如下:

init.c main函数代码:
int main(int argc, char **argv)
{
    int fd_count = 0;
    struct pollfd ufds[4];
    char *tmpdev;
    char* debuggable;
    char tmp[32];
    int property_set_fd_init = 0;
    int signal_fd_init = 0;
    int keychord_fd_init = 0;
    bool is_charger = false;

    if (!strcmp(basename(argv[0]), "ueventd"))
        return ueventd_main(argc, argv);

    /* clear the umask */
    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);

    mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");
    mkdir("/dev/pts", 0755);
    mkdir("/dev/socket", 0755);
    mount("devpts", "/dev/pts", "devpts", 0, NULL);
    mount("proc", "/proc", "proc", 0, NULL);
    mount("sysfs", "/sys", "sysfs", 0, NULL);

        /* indicate that booting is in progress to background fw loaders, etc */
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

        /* We must have some place other than / to create the
         * device nodes for kmsg and null, otherwise we won't
         * be able to remount / read-only later on.
         * Now that tmpfs is mounted on /dev, we can actually
         * talk to the outside world.
         */
    open_devnull_stdio();
    klog_init();
    property_init();

    get_hardware_name(hardware, &revision);

    process_kernel_cmdline();

......

    for(;;) {
        int nr, i, timeout = -1;

        execute_one_command();
        restart_processes();

        if (!property_set_fd_init && get_property_set_fd() > 0) {
            ufds[fd_count].fd = get_property_set_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            property_set_fd_init = 1;
        }
        if (!signal_fd_init && get_signal_fd() > 0) {
            ufds[fd_count].fd = get_signal_fd();
            ufds[fd_count].events = POLLIN;
            ufds[fd_count].revents = 0;
            fd_count++;
            signal_fd_init = 1;
        }
    ......

    return 0;
}

在文件系统创建完成后,就进入了loop循环,在循环中,进行了一系列的fd(文件描述符)的初始化和设置。这些文件fd是struct pollfd结构体类型的,主要有{ device _fd , property_set_fd , signal_recv_fd , keychord_fd }4种。

device _fd:

 主要设置包括sdcard在内的socket。

property_set_fd:

 property的cs架构:

#define PROP_SERVICE_NAME "property_service"

signal_recv_fd and signal_fd:

//signal_recv_fd and signal_fd:

/* create a signalling mechanism for the sigchld handler */
    if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) == 0) {
        signal_fd = s[0];
        signal_recv_fd = s[1];
        fcntl(s[0], F_SETFD, FD_CLOEXEC);
        fcntl(s[0], F_SETFL, O_NONBLOCK);
        fcntl(s[1], F_SETFD, FD_CLOEXEC);
        fcntl(s[1], F_SETFL, O_NONBLOCK);
    }

……
//read signal
read(signal_recv_fd, tmp, sizeof(tmp);
while (!wait_for_one_process(0)); //阻塞

这些文件系统,socket等的初始化都ok后,其它进程会和这些资源进行进程间通信。

下面,是一个典型的进程列表:

USER     PID   PPID  VSIZE RSS   WCHAN    PC         NAME

root     1     0     548   196   c00b8c14 0000d5cc S /init

root     2     0     0     0     c006bf70 00000000 S kthreadd

......

root     724   2     0     0     c0216908 00000000 S mmcqd

root     726   1     772   180   c019dbc4 afe0c1dc S /system/bin/sh

system   727   1     840   188   c022d8a0 afe0c47c S /system/bin/servicemanager

root     729   1     1920  336   ffffffff afe0c1dc S /system/bin/mountd

root     730   1     704   176   c0257854 afe0ce0c S /system/bin/debuggerd

root     731   1     4132  628   c027e2f8 afe0ce0c S /opl/bin/tcmd

root     732   1     852   248   c00b92b0 afe0c5a4 S /opl/bin/adapter

radio    733   1     12796 648   ffffffff beaab18c S /system/bin/rild

root     734   1     72000 14172 c00b92b0 afe0c5a4 S zygote

root     735   1     33848 4512  ffffffff afe0c47c S /system/bin/mediaserver

root     736   1     1080  216   c00b8c14 bedc021c S /system/bin/dbus-daemon

root     737   1     832   208   c02b6e80 afe0c1dc S /system/bin/installd

root     740   1     856   260   c00b92b0 afe0c5a4 S /opl/bin/bpd

root     741   1     828   172   c00b8c14 afe0d27c S /opl/bin/battmond

root     768   1     720   272   c02265ec afe0c1dc S /system/bin/logcat

root     769   1     716   264   c02265ec afe0c1dc S /system/bin/logcat

root     816   2     0     0     c0068eec 00000000 S battery.0

system   825   734   574128 28360 ffffffff afe0c47c S system_server

radio    877   734   158260 20040 ffffffff afe0d404 S com.android.phone

app_5    879   734   100888 13616 ffffffff afe0d404 S android.process.acore

system   882   734   144664 24296 ffffffff afe0d404 S android.process.omsservice

app_45   884   734   92304 10932 ffffffff afe0d404 S com.motorola.motohome

app_22   890   734   117068 30228 ffffffff afe0d404 S oms.home

app_3    918   734   98760 12652 ffffffff afe0d404 S oms.widgetmanager

app_5    928   734   100888 13336 ffffffff afe0d404 S com.android.inputmethod.borqs

app_24   930   734   105176 19168 ffffffff afe0d404 S com.db4o.servo.search

app_18   960   734   104180 15208 ffffffff afe0d404 S com.android.mms

app_8    979   734   118860 14044 ffffffff afe0d404 S android.process.media

app_9    991   734   91980 12264 ffffffff afe0d404 S com.android.alarmclock

app_15   998   734   103144 12908 ffffffff afe0d404 S oms.dcd

system   1018  734   94732 13792 ffffffff afe0d404 S oms.dm

app_14   1025  734   95636 13036 ffffffff afe0d404 S com.android.calendar

app_42   1041  734   93292 11316 ffffffff afe0d404 S com.motorola.smsautoreg

app_40   1090  734   97152 15192 ffffffff afe0d404 S com.motorola.mtc

app_38   1102  734   93832 12868 ffffffff afe0d404 S com.streamezzo.browser.android

app_26   1115  734   96596 15084 ffffffff afe0d404 S oms.mediacenter

app_37   1126  734   98208 15212 ffffffff afe0d404 S com.hyfsoft.docviewer

app_20   1146  734   99260 15320 ffffffff afe0d404 S com.android.music

app_47   1157  734   100204 15964 ffffffff afe0d404 S com.motorola.camera

app_11   1183  734   122672 23576 ffffffff afe0d404 S com.android.browser

app_6    1199  734   117032 20388 ffffffff afe0d404 S oms.mobilemusic

system   1244  734   99292 15940 ffffffff afe0d404 S com.android.settings

app_23   1311  734   96932 16004 ffffffff afe0d404 S oms.bru

root     1334  2     0     0     c0216908 00000000 S mmcqd

app_8    1351  734   100308 15876 ffffffff afe0d404 S com.android.camera

app_1    1424  734   111904 17024 ffffffff afe0d404 S oms.messaging

app_4    1436  734   101172 15504 ffffffff afe0d404 S oms.mail

app_2    1484  734   100716 18128 ffffffff afe0d404 S com.ms

app_16   1663  734   101024 16748 ffffffff afe0d404 S oms.android.filemanager

root     1684  1     3364  176   ffffffff 0000e8f4 S /sbin/adbd

root     1692  1684  776   348   c0059cd4 afe0d0ac S /system/bin/sh

root     1724  1692  920   356   00000000 afe0c1dc R ps

可见,所有的进程都是init的子进程。
列举几个重要的进程:
servicemanager:所有的服务都要最终注册在这个进程中,有它来进行管理这些服务。
rild:发短信,打电话等应用,会和rild进行通信,而rild再和baseband模块进行通信,从而实现发短信,打电话功能。
zygote:Android系统中最重要的进程,所有的上层应用都是它孵化出来的。
system_server:即Android framework所在的进程。包括一系列框架服务,例如,PackageManager,ActivityManager,BatteryManager...等等。

init.rc:

init.rc部分代码截取: 

import /init.${ro.hardware}.rc
import /init.usb.rc
import /init.trace.rc

on early-init
    # Set init and its forked children's oom_adj.
    write /proc/1/oom_adj -16

    start ueventd

# create mountpoints
    mkdir /mnt 0775 root system

on init

sysclktz 0

loglevel 3

# setup the global environment
    export PATH /sbin:/vendor/bin:/system/sbin:/system/bin:/system/xbin
    export LD_LIBRARY_PATH /vendor/lib:/system/lib
    export ANDROID_BOOTLOGO 1
    export ANDROID_ROOT /system
    export ANDROID_ASSETS /system/app
    export ANDROID_DATA /data

......

    mkdir /system
    mkdir /data 0771 system system
    mkdir /cache 0770 system cache
    mkdir /config 0500 root root

......

## Daemon processes to be run by init.
##
service ueventd /sbin/ueventd
    class core
    critical

service console /system/bin/sh
    class core
    console
    disabled
    user shell
    group log

on property:ro.debuggable=1
    start console

# adbd is controlled via property triggers in init.<platform>.usb.rc
service adbd /sbin/adbd
    class core
    disabled

# adbd on at boot in emulator
on property:ro.kernel.qemu=1
    start adbd

service servicemanager /system/bin/servicemanager
    class core
    user system
    group system

......

 分析:

1. 在init.rc中,创建了系统需要的系统文件夹,例如/system, /data, 并赋予相应的权限。

2. 设置了全局环境变量,例如

export ANDROID_ROOT /system
export ANDROID_ASSETS /system/app
export ANDROID_DATA /data

3. 用service指令启动了很多守护进程,例如,ueventd,sh,adbd,servicemanager......等。从底层架构的角度来看,正是这些守护进程,对上提供了丰富的功能,对下层,大部分守护进程都是通过系统调用去与kenel进行交互,从而实现在整个系统中,能够进行进程间的通信。


了解底层的运作机制,对于理解上层代码,乃至整个系统,都非常有益。

Android底层启动解析 过去的这一年多里,做过VR,AR,现在又回到了Android客户端,开始做AI相关的应用,Android方面的知识也开始重新捡了起来,所以在这儿撸一遍Android的底层源码,巩固加强一下在自己Android方面的技能。 在撸Android的源码之前,我们先要有两件事要准备,第一件事是有一份Android的源码,我一般是通过线上(http://androidxref.com/)查看源码的,这个网... 阅读详情

相关推荐

SystemServer的启动过程

了解SystemManger之前,我们知道 Android系统是基于Linux内核的,而在Linux系统中,所有的进程都是init进程的子孙进程,也就是说,所有的进程都是直接或者间接地由init进程fork出来的。Zygote进程也不例外,它是在系统启动的过程,由init进程创建的。在系统启动脚本system/core/rootdir/init.rc文件中。 系统启动时init进程会创建Zyg...

点滴积累 2863

Android源码分析 - init进程

本篇以android-11.0.0_r25作为基础解析PC启动会通过BIOS引导,从0x7c00处找到以0xaa55为结尾的引导程序启动。而Android通常使用在移动设备上,没有PC的BIOS,取而代之的是BootLoader。在CPU上电复位完成后,会从一个固定的地址加载一段程序,即BootLoader,不同的CPU可能这个地址不同。BootLoader是一段引导程序,其中最为常见的为U-boot,它一般会先检测用户是否按下某些特别按键,这些特别按键是uboot在编译时预先被约定好的,用于进入调试模式。

qq_34231329的博客 1236

【Linux0.11代码分析】06 之 kernel 1号进程 init() 代码分析

【Linux0.11代码分析】06 之 kernel 1号进程 init() 代码分析

|~~~热爱生活、努力学习的小伙汁~~~| 1271

Android源码学习---init

Android源码之init源码分析,init解析zygote,Android属性服务

m0_56366502的博客 1092

利用Skipper跨层追踪技术高效定位LVS Short问题

本文详细介绍了如何利用Skipper跨层追踪技术高效定位LVS Short问题。通过解析Skipper的跨层trace net原理和实战操作步骤,帮助芯片设计工程师快速识别版图中的短路问题,显著提升验证效率。文章还提供了处理大规模设计的高级技巧和常见问题排查方法。

weixin_29239661的博客 509

PaddleOCR PP-OCRv5实战:5分钟搞定身份证信息提取(附完整代码)

本文详细介绍了如何利用PaddleOCR的PP-OCRv5模型,在5分钟内快速搭建身份证信息提取流程。文章从环境搭建、核心代码实战出发,深入解析了PP-OCRv5针对证件识别的优化特性,并提供了从OCR结果到结构化数据的完整处理方案与避坑指南,帮助开发者高效实现AI驱动的自动化信息提取。

salt9的博客 782

Android P (9.0) 之Init进程源码分析

概述 众所周知,init进程是Android系统的第一个用户进程,Android启动流程大致如下: 今天我们来分析一下用户空间的第一个进程init进程。 init进程主要提供以下几个功能: 挂载文件系统、生成部分设备节点、创建目录 属性服务 处理子进程终止 分析和运行init.rc(等等rc文件) init进程代码路径为 system/core/init/init.cpp。今天我们主要是...

wangzaieee的博客 6146

无人机WIFI集群组网技术详解及成本分析

无人机WIFI集群组网技术是指利用无人机作为移动平台,通过集成高性能的WIFI模块,实现多架无人机之间以及无人机与地面控制站之间的无线通信组网。在协议层面,无人机WIFI集群组网需要遵循标准的无线通信协议,如IEEE 802.11系列(即WIFI标准),同时可能还需定制或优化路由协议、媒体访问控制(MAC)协议等,以确保高效、可靠的数据传输。数据安全与隐私保护:无人机集群组网中传输的数据可能涉及敏感信息,需采取加密、认证等安全措施,确保数据传输的安全性和用户隐私的保护。根据性能不同,价格差异较大。

专注多旋翼、复合翼无人机技术研究,从事低空经济行业。 1583

Android中的init进程源码分析

init进程启动时main方法的具体执行流程详细注释

h397318057的博客 392

揭秘大型语言模型:从神经元到思维透明的奇妙旅程

这篇博客带你走进大型语言模型的内部世界,从神经元的小任务,到功能向量的团队合作,再到层间的协作和思维的透明化。掌握这些知识,能让你在开发中更高效地优化AI,在研究中更深入地探索模型的奥秘。建议多动手实践,把这些理论变成自己的能力!

数据与算法架构提升之路专栏 1520

自制电吉他效果器 DIY PCB(二)电路图分析

自制DIY电吉他效果器 PCB(二)电路图分析 要自制一款效果器,看懂电路图是必须的,即使你已经有了完整的设计电路图,在电路的绘制已久焊接的时候电路知识都会用到。 在效果器电路中,比较简单的是过载失真类效果器的电路。混响延迟等与声相有关的由于电路比较复杂本人也没有尝试过。其他的例如EQ、哇音等等效果器复杂程度依设计各不相同,在能做出过载失真类效果器后,可以自行尝试。 我这里主要为大家介绍失真效果器...

qq_41342525的博客 2万+

Android系统启动过程

转自:http://www.cnblogs.com/bastard/archive/2012/08/28/2660389.html 首先Android框架架构图:(来自网上,我觉得这张图看起来很清晰)    Linux内核启动之后就到Android Init进程,进而启动Android相关的服务和应用。 启动的过程如下图所示:(图片来自网上,后面有地址)     

easyer2012的专栏 962

大学生作业:80C51存储器数据传输实验:基本指令和数据传送

大学生作业:80C51存储器数据传输实验,报价 200 文章目录实验一、80C51存储器数据传输实验实验目的:实验内容一:基本指令实验内容二:数据传送 实验一、80C51存储器数据传输实验 实验目的: 熟悉软件的集成开发环境 掌握单片机软件设计流程,掌握存储器结构及各窗口之间的联系 掌握内部数据存储器、外部数据存储器的数据传送特点和应用; 实验内容一:基本指令 用三种方法实现将累加器A内容改为20H ORG 0000H LJMP MAIN ORG 0030H MAIN: ;方法一使用寄

weixin_54707168的博客 5101

李洋谈Android系统启动过程剖析

1. 系统启动流程简介 在Linux内核启动后,init 1(1号进程)将作为第一个用户空间(Linux虚拟内存的大小为232(在32位的x86机器上),内核将这4G字节的空间分为两部分。最高的1G字节供内核使用,称为“内核空间”。而较低的3G字节供各个进程使用,称为“用户空间”。)的进程来启动Android系统,该启动流程可以分为如下5个阶段,如下图: 点击查看大图 (

zwj0403的专栏 1309

ENVI入门系列教程---一、数据预处理---1.1基本操作--5.x以后的界面(新界面)

every blog every motto: You never know how strong you really are until being strong is the only choice you have。 0. 前言 本系列教程旨在系统学习并梳理ENVI。说来惭愧,作为一名GISer,对ENVI这类软件还不能熟练掌握,这也是本系列教程的初衷。种一棵树最好的时间是十年以前,其次是...

胡侃有料的博客 1万+

AI之KE:Knowledge Engineering知识工程的简介、发展、应用案例之详细攻略

AI之KE:Knowledge Engineering知识工程的简介、发展、应用案例之详细攻略 目录 Knowledge Engineering知识工程的简介 0.1、What 0.2、How 0.3、Why 1、知识工程过程的5个活动 2、知识图谱的应用 Knowledge Engineering知识工程的发展 1、前知识工程时期——1950-1970时期:图...

头部AI社区如有邀博主AI主题演讲请私信—心比天高,仗剑走天涯,保持热爱,奔赴向梦想!低调,专注,谦虚,自律,反思,成长,还算比较正能量的博主,公益免费传播…内心特别想在AI界做出一些可以推进历史进程影响力的技术(兴趣使然,有点小情怀,也有点使命感呀 5335

Android 10.0系统启动之init进程-[Android取经之路]

摘要:init进程是linux系统中用户空间的第一个进程,进程号为1.当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。 阅读本文大约需要花费50分钟。 文章的内容主要还是从源码进行分析,虽然又臭又长,但是如果想要学习Android系统源码,这是必要...

IngresGe 的专栏 2万+

VC中sqlite的使用

在VC中对sqlite的使用,实现数据库大批数据的快速插入,读取数据库中的数据在ListCtrl中显示

Android 9(P)之init进程启动源码分析指南之一

      Android P之init进程启动源码分析指南 前言   此时的我吃着火锅唱着歌,进行着Android P(此P非彼P,Android 9)的适配工作。我真的只能说每次Android版本的迭代更新,都是对我们的一次炼狱般的摧残啊,各种适配啊,我真的想说fuck the coding。但是吐槽归吐槽,为了我热爱的coding事业,让我们愉快的适配起来。本篇将从源码角度来分析分析Android P的init进程启动流程,这个和其它Android版本还是有蛮大区别的。 注意:本文演示的代码是An

IT先森 5542

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

ssm框架搭建

这个是一个在idea上成功跑起来的ssm框架项目,保存在这里方便以后使用直接下载

安卓init进程详解

安卓init进程详解

似霰的博客 2557

TMS_WebGMaps_1.9.8.0_Full_Source

TMS_WebGMaps_1.9.8.0_Full_Source 全源码 delphi cbuilder

Android init 启动进程分析

Android init 启动进程主要分三个阶段分析: 概述,Init如何被启动 Init进程启动的源码分析 rc语法分析 1.概述: init进程是linux系统中用户空间的第一个进程,进程号为1. 当bootloader启动后,启动kernel,kernel启动完后,在用户空间启动init进程,再通过init进程,来读取init.rc中的相关配置,从而来启动其他相关进程以及其他操作。 init进程被赋予了很多重要工作,init进程启动主要分为两个阶段: 第一个阶段完成以下内容: ..

u010783226的专栏 3216

Android7.0 init进程源码分析

主要对Android 7.0中,init进程的源码进行分析。

橡果生长记的博客 1万+

【Android】【Android启动流程之init进程】

待补充 面试问题回答内容。

super_sun_shine的博客 2023

Android 9 (P)之init进程启动源码分析指南之二

      Android P之init进程启动源码分析指南之二 前言   在上一篇章Android P之init进程启动源码分析指南之一中我们讲解了init启动的第一阶段工作,在第一阶段中init主要做了如下几方面的工作: ueventd/watchdogd跳转判断以及其它初始化 创建并挂载相关的文件系统 初始化内核Log系统 文件系统挂载 SELinux Init初始化 第一阶段收尾和第二阶段准备工作 在本篇章中我们将要讲解init的第二阶段相关内容。 注意:本文演示的代码是Android

IT先森 4120

Android 9 (P)之init进程启动源码分析指南之三

      Android P之init进程启动源码分析指南之三 前言   在前面的篇章Android P之init进程启动源码分析指南之一和Android P之init进程启动源码分析指南之二讲解了init进程经过前面两个阶段以后,已经建立了相关的文件系统,属性系统,SELinux安全策略系统。但是我们知道init进程做的远远不止这些,还要启动一些Android的native service系统服务及其其他相关的操作,但是如果都是像属性系统和SELinux系统那样一行行代码去做,显得有点杂乱繁琐,而且

IT先森 4395
上一篇: Android基础#6: Android AndroidManifest清单文件介绍
下一篇: Android基础#7: Toast工具类的实现,以及设置Toast文字居中显示
liranke
博客等级 码龄20年 4939粉丝 594原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

liranke

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值