虚拟地址相关问题明了解释

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

早期的内存分配机制

在早期的计算机中,要运行一个程序,会把这些程序全都装入内存,程序都是直接运行在内存上的,也就是说程序中访问的内存地址都是实际的物理内存地址。当计算机同时运行多个程序时,必须保证这些程序用到的内存总量要小于计算机实际物理内存的大小。

那当程序同时运行多个程序时,操作系统是如何为这些程序分配内存的呢?下面通过实例来说明当时的内存分配方法:

某台计算机总的内存大小是128M,现在同时运行两个程序A和B,A需占用内存10M,B需占用内存110.计算机在给程序分配内存时会采取这样的方法:先将内存中的前10M分配给程序A,接着再从内存中剩余的118M中划分出110M分配给程序B。这种分配方法可以保证程序A和程序B都能运行,但是这种简单的内存分配策略问题很多。

这里写图片描述

早期的内存分配方法:

问题1 :进程地址空间不隔离。由于程序都是直接访问物理内存,所以恶意程序可以随意修改别的进程的内存数据,以达到破坏的目的。有些非恶意的,但是有bug的程序也可能不小心修改了其它程序的内存数据,就会导致其它程序的运行出现异常。这种情况对用户来说是无法容忍的,因为用户希望使用计算机的时候,其中一个任务失败了,至少不能影响其它的任务。

问题2 :内存使用效率低。在A和B都运行的情况下,如果用户又运行了程序C,而程序C需要20M大小的内存才能运行,而此时系统只剩下8M的空间可供使用,所以此时系统必须在已运行的程序中选择一个将该程序的数据暂时拷贝到硬盘上,释放出部分空间来供程序C使用,然后再将程序C的数据全部装入内存中运行。想象得到,在这个过程中,有大量的数据在装入装出,导致效率十分低下。

问题3 :程序运行的地址不确定。当内存中的剩余空间可以满足程序C的要求后,操作系统会在剩余空间中随机分配一段连续的20M大小的空间给程序C使用,因为是随机分配的,所以程序运行的地址是不确定的。

分段

为 了解决上述问题,人们想到了一种变通的方法,就是增加一个中间层,利用一种间接的地址访问方法访问物理内存。按照这种方法,程序中访问的内存地址不再是实际的物理内存地址,而是一个虚拟地址,然后由操作系统将这个虚拟地址映射到适当的物理内存地址上。这样,只要操作系统处理好虚拟地址到物理内存地址的映射,就可以保证不同的程序最终访问的内存地址位于不同的区域,彼此没有重叠,就可以达到内存地址空间隔离的效果。

当创建一个进程时,操作系统会为该进程分配一个 4GB 大小的虚拟进程地址空间。之所以是 4GB ,是因为在 32 位的操作系统中,一个指针长度是 4 字节,而 4 字节指针的寻址能力是从 0x00000000~0xFFFFFFFF,最大值 0xFFFFFFFF 表示的即为 4GB 大小的容量。与虚拟地址空间相对的,还有一个物理地址空间,这个地址空间对应的是真实的物理内存。如果你的计算机上安装了 512M 大小的内存,那么这个物理地址空间表示的范围是 0x00000000~0x1FFFFFFF 。当操作系统做虚拟地址到物理地址映射时,只能映射到这一范围,操作系统也只会映射到这一范围。当进程创建时,每个进程都会有一个自己的 4GB 虚拟地址空间。要注意的是这个 4GB 的地址空间是“虚拟”的,并不是真实存在的,而且每个进程只能访问自己虚拟地址空间中的数据,无法访问别的进程中的数据,通过这种方法实现了进程间的地址隔离。那是不是这 4GB 的虚拟地址空间应用程序可以随意使用呢?很遗憾,在 Windows 系统下,这个虚拟地址空间被分成了 4 部分: NULL 指针区、用户区、 64KB 禁入区、内核区。

1)NULL指针区 (0x00000000~0x0000FFFF): 如果进程中的一个线程试图操作这个分区中的数据,CPU就会引发非法访问。他的作用是,调用 malloc 等内存分配函数时,如果无法找到足够的内存空间,它将返回 NULL。而不进行安全性检查。它只是假设地址分配成功,并开始访问内存地址 0x00000000(NULL)。由于禁止访问内存的这个分区,因此会发生非法访问现象,并终止这个进程的运行。

2)用户模式分区 ( 0x00010000~0xBFFEFFFF):这个分区中存放进程的私有地址空间。一个进程无法以任何方式访问另外一个进程驻留在这个分区中的数据 (相同 exe,通过 copy-on-write 来完成地址隔离)。(在windows中,所有 .exe 和动态链接库都载入到这一区域。系统同时会把该进程可以访问的所有内存映射文件映射到这一分区)。

2)隔离区 (0xBFFF0000~0xBFFFFFFF):这个分区禁止进入。任何试图访问这个内存分区的操作都是违规的。微软保留这块分区的目的是为了简化操作系统的现实。

3)内核区 (0xC0000000~0xFFFFFFFF):这个分区存放操作系统驻留的代码。线程调度、内存管理、文件系统支持、网络支持和所有设备驱动程序代码都在这个分区加载。这个分区被所有进程共享。

应用程序能使用的只是用户区而已,大约 2GB 左右 ( 最大可以调整到 3GB) 。内核区为 2GB ,内核区保存的是系统线程调度、内存管理、设备驱动等数据,这部分数据供所有的进程共享,但应用程序是不能直接访问的。

人们之所以要创建一个虚拟地址空间,目的是为了解决进程地址空间隔离的问题。但程序要想执行,必须运行在真实的内存上,所以,必须在虚拟地址与物理地址间建立一种映射关系。这样,通过映射机制,当程序访问虚拟地址空间上的某个地址值时,就相当于访问了物理地址空间中的另一个值。人们想到了一种分段(Sagmentation) 的方法,它的思想是在虚拟地址空间和物理地址空间之间做一一映射。比如说虚拟地址空间中某个 10M 大小的空间映射到物理地址空间中某个 10M 大小的空间。这种思想理解起来并不难,操作系统保证不同进程的地址空间被映射到物理地址空间中不同的区域上,这样每个进程最终访问到的。

物理地址空间都是彼此分开的。通过这种方式,就实现了进程间的地址隔离。还是以实例说明,假设有两个进程 A 和 B ,进程 A 所需内存大小为 10M ,其虚拟地址空间分布在 0x00000000 到 0x00A00000 ,进程 B 所需内存为 100M ,其虚拟地址空间分布为 0x00000000 到 0x06400000 。那么按照分段的映射方法,进程 A 在物理内存上映射区域为 0x00100000 到 0x00B00000 ,,进程 B 在物理内存上映射区域为0x00C00000 到 0x07000000 。于是进程 A 和进程 B 分别被映射到了不同的内存区间,彼此互不重叠,实现了地址隔离。从应用程序的角度看来,进程 A 的地址空间就是分布在 0x00000000 到 0x00A00000 ,在做开发时,开发人员只需访问这段区间上的地址即可。应用程序并不关心进程 A 究竟被映射到物理内存的那块区域上了,所以程序的运行地址也就是相当于说是确定的了。 下图显示的是分段方式的内存映射方法:
这里写图片描述

分段方式的内存映射方法

这种分段的映射方法虽然解决了上述中的问题一和问题三,但并没能解决问题二,即内存的使用效率问题。在分段的映射方法中,每次换入换出内存的都是整个程序, 这样会造成大量的磁盘访问操作,导致效率低下。所以这种映射方法还是稍显粗糙,粒度比较大。实际上,程序的运行有局部性特点,在某个时间段内,程序只是访问程序的一小部分数据,也就是说,程序的大部分数据在一个时间段内都不会被用到。基于这种情况,人们想到了粒度更小的内存分割和映射方法,这种方法就是分页 (Paging) 。

分页

分页的基本方法是,将地址空间分成许多的页。每页的大小由 CPU 决定,然后由操作系统选择页的大小。目前 Inter 系列的 CPU 支持 4KB 或 4MB 的页大小,而 PC上目前都选择使用 4KB 。按这种选择, 4GB 虚拟地址空间共可以分成 1048576 页, 512M 的物理内存可以分为 131072 个页。显然虚拟空间的页数要比物理空间的页数多得多。

在分段的方法中,每次程序运行时总是把程序全部装入内存,而分页的方法则有所不同。分页的思想是程序运行时用到哪页就为哪页分配内存,没用到的页暂时保留在硬盘上。当用到这些页时再在物理地址空间中为这些页分配内存,然后建立虚拟地址空间中的页和刚分配的物理内存页间的映射。

下面通过介绍一个可执行文件的装载过程来说明分页机制的实现方法。一个可执行文件 (PE 文件 ) 其实就是一些编译链接好的数据和指令的集合,它也会被分成很多页,在 PE 文件执行的过程中,它往内存中装载的单位就是页。当一个 PE 文件被执行时,操作系统会先为该程序创建一个 4GB 的进程虚拟地址空间。前面介绍过,虚拟地址空间只是一个中间层而已,它的功能是利用一种映射机制将虚拟地址空间映射到物理地址空间,所以,创建 4GB 虚拟地址空间其实并不是要真的创建空间,只是要创建那种映射机制所需要的数据结构而已,这种数据结构就是页目和页表。

当创建完虚拟地址空间所需要的数据结构后,进程开始读取 PE 文件的第一页。在PE 文件的第一页包含了 PE 文件头和段表等信息,进程根据文件头和段表等信息,将 PE 文件中所有的段一一映射到虚拟地址空间中相应的页 (PE 文件中的段的长度都是页长的整数倍 ) 。这时 PE 文件的真正指令和数据还没有被装入内存中,操作系统只是据 PE 文件的头部等信息建立了 PE 文件和进程虚拟地址空间中页的映射关系而已。当 CPU 要访问程序中用到的某个虚拟地址时,当 CPU 发现该地址并没有相相关联的物理地址时, CPU 认为该虚拟地址所在的页面是个空页面, CPU 会认为这是个页错误 (Page Fault) , CPU 也就知道了操作系统还未给该 PE 页面分配内存,CPU 会将控制权交还给操作系统。操作系统于是为该 PE 页面在物理空间中分配一个页面,然后再将这个物理页面与虚拟空间中的虚拟页面映射起来,然后将控制权再还给进程,进程从刚才发生页错误的位置重新开始执行。由于此时已为 PE 文件的那个页面分配了内存,所以就不会发生页错误了。随着程序的执行,页错误会不断地产生,操作系统也会为进程分配相应的物理页面来满足进程执行的需求。

分页方法的核心思想就是当可执行文件执行到第 x 页时,就为第 x 页分配一个内存页 y ,然后再将这个内存页添加到进程虚拟地址空间的映射表中 , 这个映射表就相当于一个 y=f(x) 函数。应用程序通过这个映射表就可以访问到 x 页关联的 y 页了。

逻辑地址、线性地址、物理地址和虚拟地址的区别

逻辑地址(Logical Address) 是指由程式产生的和段相关的偏移地址部分。例如,你在进行 C 语言指针编程中,能读取指针变量本身值( &操作 ),实际上这个值就是逻辑地址,他是相对于你当前进程数据段的地址,不和绝对物理地址相干。只有在 Intel 实模式下,逻辑地址才和物理地址相等(因为实模式没有分段或分页机制,cpu不进行自动地址转换);逻辑也就是在Intel保护模式下程式执行代码段限长内的偏移地址(假定代码段、数据段如果完全相同)。应用程式员仅需和逻辑地址打交道,而分段和分页机制对你来说是完全透明的,仅由系统编程人员涉及。应用程式员虽然自己能直接操作内存,那也只能在操作系统给你分配的内存段操作。

线性地址(Linear Address) 是逻辑地址到物理地址变换之间的中间层。程式代码会产生逻辑地址,或说是段中的偏移地址,加上相应段的基地址就生成了一个线性地址。如果启用了分页机制,那么线性地址能再经变换以产生一个物理地址。若没有启用分页机制,那么线性地址直接就是物理地址。Intel 80386 的线性地址空间容量为 4G(2的32次方即32根地址总线寻址)。

物理地址(Physical Address) 是指出目前 CPU 外部地址总线上的寻址物理内存的地址信号,是地址变换的最终结果地址。如果启用了分页机制,那么线性地址会使用页目录和页表中的项变换成物理地址。如果没有启用分页机制,那么线性地址就直接成为物理地址了。

虚拟内存(Virtual Memory)是指计算机呈现出要比实际拥有的内存大得多的内存量。因此他允许程式员编制并运行比实际系统拥有的内存大得多的程式。这使得许多大型项目也能够在具有有限内存资源的系统上实现。一个非常恰当的比喻是:你不必非常长的轨道就能让一列火车从上海开到北京你只需要足够长的铁轨(比如说3公里)就能完成这个任务。采取的方法是把后面的铁轨即时铺到火车的前面,只要你的操作足够快并能满足需求,列车就能象在一条完整的轨道上运行。这也就是虚拟内存管理需要完成的任务。在Linux0.11内核中,给每个程式(进程)都划分了总容量为64MB的虚拟内存空间。因此程式的逻辑地址范围是0x0000000到0x4000000。有时我们也把逻辑地址称为虚拟地址。因为状语从句:虚拟内存空间的概念类似,逻辑地址也是和实际物理内存容量无 的。逻辑地址和物理地址的“差距”是0xC0000000,是由于虚拟地址 - >线性地址 - >物理地址映射正好差这个值。这个值是由操作系统指定的。机理逻辑地址(或称为虚拟地址)到线性地址是由CPU的段机制自动转换的。如果没有开启分页管理,则线性地址就是物理地址。如果开启了分页管理,那么系统程式需要参和线性地址到物理地址的转换过程。具体是通过设置页目录表和页表项进行的。

分页内存管理——虚拟地址到物理地址的转换 前言: 此篇博客内容在于着重帮助读者明白在分页内存管理下,虚拟地址和物理地址转换计算是怎么进行的。 有关分页内存管理的其他知识请事先参阅有关资料。^_^ 一、在计算之前需要了解如下知识: 1.对每个程序而言,就好像自己独占了内存空间从0到极限的计算机。 虚拟地址都是以0开始到某个上限结束。这是因为某个程序在何种硬件设备的机器上运行并不能事先确定(因为写出来的程序一般都... 阅读详情

相关推荐

【Linux】进程虚拟地址空间

答:是的 ❗ 本质上,(因为有地址空间的存在,所以上层申请空间,其实是在地址空间上申请的,物理内存可以甚至一个字节都不给你,而当你真正进行对物理地址空间访问的时候,才执行内存的相关管理算法,帮你申请内存,构建页表映射关系),然后才让你进行内存访问的。如果私生子要的钱太多,富豪不给,私生子也只会认为是父亲不想给我。假设有一个富豪,他有 10 亿美元的家产,而这个富豪他有 3 个私生子,但这 3个私生子此之间并不知道对方的存在,这个富豪对他的每个私生子都说过同一句话:儿子,这10亿的家产未来都是你的。

2401_87541984的博客 766

存储容量与地址空间的计算

比如,告诉你某FLASH存储容量8MB,地址空间范围是m~0xFFFFFFFF,让求m 先算出8M对应的16进制数,内存是按照字节编址的,8MB=810241024Byte=2^20*8 =16^5 * 8; 以上写成16进制数为 0x80 0000 求m m=0xFFFFFFFF-0x800000+1 =0xFFFFFFFF+1-0x800000 =0xFF800000 其他的题目,同以上一样的道理,可以算最高地址,也可以根据地址空间范围计算存储容量,还是相对简单的 ...

weixin_42212753的博客 1万+

OS实战笔记(4)-- 虚拟地址和物理地址的转换

本笔记会先从个人角度出发谈谈虚拟地址出现的原因,然后会对X86的保护模式和长模式下虚拟地址相关知识进行总结。

vivo01的专栏 4832

世上最强最全Linux 内存总结(用户空间:malloc、内核空间:vmalloc kmalloc slab buddy )

深入理解Linux内存分配 为了写一个用户层程序,你也许会声明一个全局变量,这个全局变量可能是一个int类型也可能是一个数组,而声明之后你有可能会先初始化它,也有可能放在之后用到它的时候再初始化。除此之外,你有可能会选择在函数内部去声明局部变量,又或者为变量动态申请内存。 不管你在用户程序中采取哪种方式申请内存,这些都对应着不同的内存分配方式以及不同的数据段,如果再加上代码段,就构成了一个完整的进程。由此可见,一个完整的进程在内存空间中对应着不同的数据区,具体来说,对应着五种不同的数据区: 代码段,存放操作

qq_41945905的博客 2282

虚拟内存机制,页&页框&页表 物理地内存和虚拟内存的联系

虚拟内存机制 计算机的存储系统 为什么要有虚拟内存 在早期的计算机中,是没有虚拟内存的概念的。我们要运行一个程序,会把程序全部装入内存,然后运行。 当运行多个程序时,经常会出现一下问题: 进程地址空间不隔离,没有权限保护 由于程序都是直接访问物理内存,所以一个进程可以修改其他的内存数据,甚至修改内核地址空间中的数据。 内存使用效率低 当内存空间不足时,要将其他程序暂时拷贝到硬盘,然后将新的程序装入装出,内存使用效率会十分低下。 程序运行的地址不确定 因为内存地址是随机分配的,所以程序运行的地址也是不确

hhb442的博客 3899

linux kernel 内存分配管理机制以及分配机制

其实计算机的核心就是运算, 但是为了复杂的运算毕竟只靠cpu 自带的寄存器 云算力毕竟有限,所以就有了内存的出现。 下面我们呢先见一下linux 中内存的分布情况。 在内核空间中,内核 地址16~896M 其他有iomap 和dma 一、内核空间 1、页 页(page)是内核的内存管理基本单位。(linux/mm_types.h) struct page { unsigned long flags; // 页标志符 struct address_space *mapping; // 该页...

sdc20102010的博客 1838

F28335第四篇——存储器及CMD文件

地址映射

我想想的博客 1万+

彻底搞懂虚拟内存,虚拟地址虚拟地址空间

程序经过编译后,变成了可执行的文件,可执行文件主要包括代码和数据两部分,代码是只读的,数据则是可读可写的。 可执行文件由操作系统加载到内存中,交由CPU去执行,现在问题来了,CPU怎么去访问代码和数据?,访问的方式经历过四个阶段: 1.直接访问 2.段基址+段偏移地址 3.段选择子+段偏移地址 4.虚拟地址 现代操作系统采用的是虚拟地址,这也是本篇文章阐述的重点,但虚拟地址是由1~3阶段发展而来的,所以也有必要阐述1~3三种访问方式。 直接访问 直接访问很好理解,程序经过编译后,生成了可执行

一口Linux的专栏 2万+

【Linux】虚拟地址空间

本文介绍了Linux虚拟地址空间的概念及其工作原理。通过代码示例展示了虚拟地址空间分布,验证了父子进程共享相同虚拟地址但实际物理地址不同的现象。文章解释了写时拷贝机制的作用:1)减少进程创建时间;2)避免内存浪费。深入剖析了虚拟地址空间的本质是mm_struct结构体,以及操作系统通过vm_area_struct管理不同内存区域的机制。最后阐述了虚拟地址空间的三大优势:1)将无序物理地址有序化;2)提供内存保护机制;3)解耦进程与内存管理。文章还补充说明了挂起状态时内存的处理方式。

记录学习与成长 1869

Windows虚拟地址相关笔记

Windows虚拟地址空间 32位 虚拟空间最大为4GB,默认为: 2GB用户进程空间(低位0x00000000-0x7FFFFFFF) 2GB系统空间(高位0x80000000-0xFFFFFFFF) 处理器存取模式划分为用户模式Ring3和内核模式Ring0 用户应用程序一般在用户模式,访问空间局限于用户区。 操作系统内核代码运行在内核模式,可以访问所有内存空间,可使用所有处理器指令。 用户区...

Veni Vidi Vici 446

虚拟地址空间

虚拟地址空间1. 虚拟地址空间1.1 定义1.2 背景(为什么需要虚拟地址空间?)1.3 大小1.4 组成1.5 页表映射物理内存1.5.1 局部性原理(动态装入的基本原理)1.5.2 两个映射1.5.3 页错误 1. 虚拟地址空间 1.1 定义 地址空间就是每个进程所能访问的内存地址范围,是一段连续的虚拟内存空间。 1.2 背景(为什么需要虚拟地址空间?) 因为早期的计算机运行方式是直接将程序运行在物理内存上。这就存在三个问题问题1. 地址空间不隔离 所有进程都直接访问物理地址,程序使用的物理空间不

哈尼的博客 6584

Linux:虚拟地址空间

程序经过编译后,变成了可执行的文件,可执行文件主要包括代码和数据两部分,代码是只读的,数据则是可读可写的。可执行文件由操作系统加载到内存中,交由CPU去执行,现在问题来了,CPU怎么去访问代码和数据?,访问的方式经历过四个阶段:1.直接访问2.段基址+段偏移地址3.段选择子+段偏移地址4.虚拟地址现代操作系统采用的是虚拟地址,这也是本篇文章阐述的重点,但虚拟地址是由前三阶段发展而来的,所以也有必要阐述前三种访问方式。

2301_80641185的博客 960

# 虚拟地址空间区域划分​

虚拟内存物理内存转换及虚拟内存地址空间的内存分布

L221578的博客 1697

虚拟地址物理地址等众多地址及MMU相关知识

虚拟地址物理地址等众多地址及MMU相关知识先聊聊存储器STM32单片机存储器关于编译器生成的文件数据在存储器上的存储结构物理地址、虚拟地址、线性地址和逻辑地址物理地址虚拟地址逻辑地址线性地址这些地址之间的关系总结加载地址/存储地址和运行地址/链接地址加载地址运行地址MMU什么是MMU最后 先聊聊存储器 存储器是单片机结构的重要组成部分,存储器是用来存储编译好的程序代码和数据的,有了存储器单片机系统才具有记忆功能。按照存储介质的特性,可以分“易失性存储器”和“非易失性存储器”两类。易失性存储器断电后,里面存储

weixin_45905650的博客 2165

学习系统编程No.5【虚拟地址空间】

一文带你搞懂什么是虚拟地址空间

狂小伍的博客 2068

Linux相关概念和易错知识点(13)(进程地址空间、页表、虚拟地址

编译器、用户访问、进程管理,你能想到的一切操作,它们都只能拿着虚拟地址进行访问和操作,这些操作都会传到页表的手上,一切对物理空间的访问都需要页表来管理。我们new出来的堆区空间本质上也不是在开辟物理空间,而只是在进程地址空间里做文章,这个空间本质是虚拟的,最终是靠页表帮我们创建的物理空间。,且不同的系统、编译器存在不同程度的优化,因此我们要接受理论和实践上的差别。我们先来认识常见的划分方式。,也就意味着无论我们修改数据,实际指向的代码大概率是不会被拷贝的,就算被拷贝,只要内容不变,也不影响上述结论,同时。

SGlow0708的博客 1302

Linux内核固定虚拟地址映射

前面我们说到,为kernel image设置了虚实地址转换表,并且开启了mmu。但是现在从虚拟地址空间CPU只能看到kernel image,如果此时想访问其他物理地址空间怎么办?用ioremap吗?要知道,此时内存子系统还没有初始化,ioremap无法工作。为了解决这一问题,Linux内核定义了一段固定的虚拟地址空间,所谓固定就是说在编译时就确定的,内核启动早期会将某些物理地址映射到这段固定虚拟...

liuhangtiant的博客 3378

进程虚拟地址空间

关键词:进程虚拟地址空间,进程描述符,页表,分段式,段页式。 在进入正式的内容之前,我们先了解一个重要的概念——进程描述符PCB。 在Linux操作系统中,描述进程的结构体叫做task_struct。Linux操作系统通过task_struct感知进程的存在。 task_struct结构体中的内容: 标识符:描述本进程的唯一标识符,又来区别本进程与其他进程。 状态:进程的状态,退出信号等等 优先级:相对于其它进程的优先级。 程序计数器:存储着程序中下一条将要执行的指令的地址。 内存指针

qq_56713382的博客 2998

(2024 更新) 选择最佳美国虚拟地址服务的完整指南

做为独立开发者,分享一些常用的美国虚拟地址服务

qq_27521105的博客 4564
上一篇: STL中容器list的sort方法详解
下一篇: 腾讯内推三面经验分享
山农
博客等级 码龄9年 44粉丝 50原创
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值