操作系统整合版

首先:这是一篇我看了别人的笔记整合的版本。有遗漏的希望大家补充
引用的文章
https://blog.csdn.net/weixin_43664418/article/details/102092119


进程和线程
定义:
  进程是具有一定独立功能的程序关于某个数据集合上的一次运行活动,进程是系统进行资源分配和调度的一个独立单位.
  线程是进程的一个实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位.线程自己基本上不拥有系统资源,只拥有一点在运行中必不可少的资源(如程序计数器,一组寄存器和栈),但是它可与同属一个进程的其他的线程共享进程所拥有的全部资源.

  1. 进程和线程的关系

    • 一个线程只能属于一个进程,而一个进程可以有多个线程,但至少有一个线程。线程是操作系统可识别的最小执行和调度单位。
    • 资源分配给进程,同一进程的所有线程共享该进程的所有资源。同一进程中的多个线程共享代码段(代码和常量),数据段(全局变量和静态变量),扩展段(堆存储)。但是每个线程拥有自己的栈段,栈段又叫运行时段,用来存放所有局部变量和临时变量。
    • 处理机分给线程,即真正在处理机上运行的是线程。
    • 线程在执行过程中,需要协作同步。不同进程的线程间要利用消息通信的办法实现同步。
  2. 进程和线程的区别

    • 进程有自己的独立地址空间,线程没有 进程是资源分配的最小单位,线程是CPU调度的最小单位
    • 进程和线程通信方式不同(线程之间的通信比较方便。同一进程下的线程共享数据(比如全局变量,静态变量),通过这些数据来通信不仅快捷而且方便,当然如何处理好这些访问的同步与互斥正是编写多线程程序的难点。而进程之间的通信只能通过进程通信的方式进行。)
    • 进程上下文切换开销大,线程开销小
    • 一个进程挂掉了不会影响其他进程,而线程挂掉了会影响其他线程
    • 对进程进程操作一般开销都比较大,对线程开销就小了

进程间的通信
进程之间的信息交换(信息量少则一个状态或数值,多者则是成千上万个字节)。
  高级进程通信指:用户可以利用操作系统所提供的一组通信命令传送大量数据的一种通信方式。操作系统隐藏了进程通信的实现细节。或者说,通信过程对用户是透明的。
  低级进程通信:对于用信号量进行的进程间的互斥和同步,由于其所交换的信息量少而被归结为低级通信

  1. 管道(pipe)及命名管道(named pipe):
    管道可用于具有亲缘关系的父子进程间的通信,有名管道除了具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

  2. 信号(signal):
    信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;

  3. 消息队列:
    消息队列是消息的链接表,它克服了上两种通信方式中信号量有限的缺点,具有写权限得进程可以按照一定得规则向消息队列中添加新信息;对消息队列有读权限得进程则可以从消息队列中读取信息;

  4. 共享内存:
    可以说这是最有用的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据得更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等;

  5. 信号量: 主要作为进程之间及同一种进程的不同线程之间得同步和互斥手段;

  6. 套接字: 这是一种更为一般得进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

几种方式的比较:
管道:速度慢、容量有限
消息队列:容量收到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题。
信号量:不能传递复杂信息,只能用来同步。
共享内存:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全。


线程间通信

  1. 同步
    多个线程通过synchronized通讯,类似于共享内存
  2. while轮询
    线程A不断改变条件,线程B不断查看条件是否满足需求(比方说=5),从而实现通讯。效率不高,因为B一直在查看,没做别的
  3. wait/notify
    进入阻塞,而不是像轮询一样一直占用CPU资源
  4. 管道通信
    通过管道,将一个线程的消息发送个另一个线程

什么是死锁?死锁产生的条件?

  1. 死锁的概念
    在两个或者多个并发进程中,如果每个进程持有某种资源而又等待其它进程释放它或它们现在保持着的资源,在未改变这种状态之前都不能向前推进,称这一组进程产生了死锁。通俗的讲,就是两个或多个进程无限期的阻塞、相互等待的一种状态。

  2. 死锁产生的四个必要条件

    互斥:至少有一个资源必须属于非共享模式,即一次只能被一个进程使用;若其他申请使用该资源,那么申请进程必须等到该资源被释放为止;

    占有并等待:一个进程必须占有至少一个资源,并等待另一个资源,而该资源为其他进程所占有;

    非抢占:进程不能被抢占,即资源只能被进程在完成任务后自愿释放

    循环等待:若干进程之间形成一种头尾相接的环形等待资源关系

  3. 死锁的处理基本策略和常用方法
    解决死锁的基本方法主要有 预防死锁、避免死锁、检测死锁、解除死锁 、鸵鸟策略 等。

  4. 死锁预防
    死锁预防的基本思想是 只要确保死锁发生的四个必要条件中至少有一个不成立,就能预防死锁的发生,具体方法包括:

    • 打破互斥条件:允许进程同时访问某些资源。但是,有些资源是不能被多个进程所共享的,这是由资源本身属性所决定的,因此,这种办法通常并无实用价值。
    • 打破占有并等待条件:可以实行资源预先分配策略(进程在运行前一次性向系统申请它所需要的全部资源,若所需全部资源得不到满足,则不分配任何资源,此进程暂不运行;只有当系统能满足当前进程所需的全部资源时,才一次性将所申请资源全部分配给该线程)或者只允许进程在没有占用资源时才可以申请资源(一个进程可申请一些资源并使用它们,但是在当前进程申请更多资源之前,它必须全部释放当前所占有的资源)。但是这种策略也存在一些缺点:在很多情况下,无法预知一个进程执行前所需的全部资源,因为进程是动态执行的,不可预知的;同时,会降低资源利用率,导致降低了进程的并发性。
    • 打破非抢占条件:允许进程强行从占有者哪里夺取某些资源。也就是说,但一个进程占有了一部分资源,在其申请新的资源且得不到满足时,它必须释放所有占有的资源以便让其它线程使用。这种预防死锁的方式实现起来困难,会降低系统性能。
    • 打破循环等待条件:实行资源有序分配策略。对所有资源排序编号,所有进程对资源的请求必须严格按资源序号递增的顺序提出,即只有占用了小号资源才能申请大号资源,这样就不回产生环路,预防死锁的发生。
  5. 死锁避免的基本思想 
    死锁避免的基本思想是动态地检测资源分配状态,以确保循环等待条件不成立,从而确保系统处于安全状态。所谓安全状态是指:如果系统能按某个顺序为每个进程分配资源(不超过其最大值),那么系统状态是安全的,换句话说就是,如果存在一个安全序列,那么系统处于安全状态。资源分配图算法和银行家算法是两种经典的死锁避免的算法,其可以确保系统始终处于安全状态。其中,资源分配图算法应用场景为每种资源类型只有一个实例(申请边,分配边,需求边,不形成环才允许分配),而银行家算法应用于每种资源类型可以有多个实例的场景。

  6. 死锁解除
    死锁解除的常用两种方法为进程终止和资源抢占。所谓进程终止是指简单地终止一个或多个进程以打破循环等待,包括两种方式:终止所有死锁进程和一次只终止一个进程直到取消死锁循环为止;所谓资源抢占是指从一个或多个死锁进程那里抢占一个或多个资源,此时必须考虑三个问题:

    • 选择一个牺牲品
    • 回滚:回滚到安全状态
    • 饥饿(在代价因素中加上回滚次数,回滚的越多则越不可能继续被作为牺牲品,避免一个进程总是被回滚)

进程状态

  • 就绪状态:进程已获得除处理机以外的所需资源,等待分配处理机资源;
  • 运行状态:占用处理机资源运行,处于此状态的进程数小于等于CPU数;
  • 阻塞状态: 进程等待某种条件,在条件满足之前无法执行;
    请添加图片描述

线程状态

  1. 新建(NEW):新创建了一个线程对象。

  2. 可运行(RUNNABLE):线程对象创建后,其他线程(比如main线程)调用了该对象的start()方法。该状态的线程位于可运行线程池中,等待被线程调度选中,获取cpu 的使用权 。

  3. 运行(RUNNING):可运行状态(runnable)的线程获得了cpu 时间片(timeslice) ,执行程序代码。

  4. 阻塞(BLOCKED):阻塞状态是指线程因为某种原因放弃了cpu 使用权,也即让出了cpu timeslice,暂时停止运行。直到线程进入可运行(runnable)状态,才有机会再次获得cpu timeslice 转到运行(running)状态。阻塞的情况分三种:

    (一). 等待阻塞:运行(running)的线程执行o.wait()方法,JVM会把该线程放入等待队列(waitting queue)中。
    (二). 同步阻塞:运行(running)的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入锁池(lock pool)中。
    (三). 其他阻塞:运行(running)的线程执行Thread.sleep(long ms)或t.join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入可运行(runnable)状态。

  5. 死亡(DEAD):线程run()、main() 方法执行结束,或者因异常退出了run()方法,则该线程结束生命周期。死亡的线程不可再次复生。

在给定的时间点上,一个线程只能处于一种状态。


进程同步

进程同步的主要任务:是对多个相关进程在执行次序上进行协调,以使并发执行的诸进程之间能有效地共享资源和相互合作,从而使程序的执行具有可再现性。

同步机制遵循的原则:

(1)空闲让进;

(2)忙则等待(保证对临界区的互斥访问);

(3)有限等待(有限代表有限的时间,避免死等);

(4)让权等待,(当进程不能进入自己的临界区时,应该释放处理机,以免陷入忙等状态)。


上下文切换

  • 对于单核单线程CPU而言,在某一时刻只能执行一条CPU指令。上下文切换(Context
    Switch)是一种将CPU资源从一个进程分配给另一个进程的机制。从用户角度看,计算机能够并行运行多个进程,这恰恰是操作系统通过快速上下文切换造成的结果。在切换的过程中,操作系统需要先存储当前进程的状态(包括内存空间的指针,当前执行完的指令等等),再读入下一个进程的状态,然后执行此进程。

为什么进程上下文切换比线程上下文切换代价高?

  1. 进程切换分两步:
    切换页目录以使用新的地址空间
    切换内核栈和硬件上下文

对于linux来说,线程和进程的最大区别就在于地址空间,对于线程切换,第1步是不需要做的,第2是进程和线程切换都要做的。

切换的性能消耗:

  • 线程上下文切换和进程上下问切换一个最主要的区别是线程的切换虚拟内存空间依然是相同的,但是进程切换是不同的。这两种上下文切换的处理都是通过操作系统内核来完成的。内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
  • 另外一个隐藏的损耗是上下文的切换会扰乱处理器的缓存机制。简单的说,一旦去切换上下文,处理器中所有已经缓存的内存地址一瞬间都作废了。还有一个显著的区别是当你改变虚拟内存空间的时候,处理的页表缓冲(processor’s Translation Lookaside Buffer (TLB))或者相当的神马东西会被全部刷新,这将导致内存的访问在一段时间内相当的低效。但是在线程的切换中,不会出现这个问题。

分页和分段有什么区别(内存管理)?

  • 段式存储管理是一种符合用户视角的内存分配管理方案。在段式存储管理中,将程序的地址空间划分为若干段(segment),如代码段,数据段,堆栈段;这样每个进程有一个二维地址空间,相互独立,互不干扰。段式管理的优点是:没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)

  • 页式存储管理方案是一种用户视角内存与物理内存相分离的内存分配管理方案。在页式存储管理中,将程序的逻辑地址划分为固定大小的页(page),而物理内存划分为同样大小的帧,程序加载时,可以将任意一页放入内存中任意一个帧,这些帧不必连续,从而实现了离散分离。页式存储管理的优点是:没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满)。

两者的不同点:

  1. 目的不同:分页是由于系统管理的需要而不是用户的需要,它是信息的物理单位;分段的目的是为了能更好地满足用户的需要,它是信息的逻辑单位,它含有一组其意义相对完整的信息;
  2. 大小不同:页的大小固定且由系统决定,而段的长度却不固定,由其所完成的功能决定;
  3. 地址空间不同: 段向用户提供二维地址空间;页向用户提供的是一维地址空间;
  4. 信息共享:段是信息的逻辑单位,便于存储保护和信息的共享,页的保护和共享受到限制;
  5. 内存碎片:页式存储管理的优点是没有外碎片(因为页的大小固定),但会产生内碎片(一个页可能填充不满);而段式管理的优点是没有内碎片(因为段大小可变,改变段大小来消除内碎片)。但段换入换出时,会产生外碎片(比如4k的段换5k的段,会产生1k的外碎片)。

操作系统中进程调度策略

  • FCFS(先来先服务,队列实现,非抢占的):先请求CPU的进程先分配到CPU
    优点:公平、简单(FIFO队列)、非抢占
    缺点:不适合交互式
  • SJF(最短作业优先调度算法):平均等待时间最短,但难以知道下一个CPU区间长度
  • 优先级调度算法(可以是抢占的,也可以是非抢占的):优先级越高越先分配到CPU,相同优先级先到先服务,存在的主要问题是:低优先级进程无穷等待CPU,会导致无穷阻塞或饥饿;解决方案:老化
  • 时间片轮转调度算法(可抢占的):队列中没有进程被分配超过一个时间片的CPU时间,除非它是唯一可运行的进程。如果进程的CPU区间超过了一个时间片,那么该进程就被抢占并放回就绪队列。
    缺点:上下文切换次数较多;时间片太大,响应时间太长;吞吐量变小,周转时间变长;当时间片过长时,退化为FCFS
  • 多级队列调度算法:将就绪队列分成多个独立的队列,每个队列都有自己的调度算法,队列之间采用固定优先级抢占调度。其中,一个进程根据自身属性被永久地分配到一个队列中。
  • 多级反馈队列调度算法:与多级队列调度算法相比,其允许进程在队列之间移动:若进程使用过多CPU时间,那么它会被转移到更低的优先级队列;在较低优先级队列等待时间过长的进程会被转移到更高优先级队列,以防止饥饿发生。
    过程:
    1. 进程在进入待调度的队列时,首先进入优先级最高的Q1等待;
    2. 首先调度优先级高的队列中的进程。若高优先级中队列中已没有调度的进程,则调度次优先级队列中的进程
    3. 对于同一个队列中的各个进程,按照时间片轮转法调度
    4. 在低优先级的队列中的进程在运行时,又有新到达的作业,那么在运行完这个时间片后,CPU马上分配给新到达的作业(抢占式)

进程同步与互斥

  • 互斥:指某一个资源同时只允许一个访问者对其进行访问,具有唯一性和排它性。但互斥无法限制访问者对资源的访问顺序,即访问是无序的
  • 同步:是指在互斥的基础上(大多数情况下),通过其它机制实现访问者对资源的有序访问。大多数情况下,同步已经实现了互斥,特别是所有写入资源的情况必定是互斥的。少数情况是指可以允许多个访问者同时访问资源。
    同步:体现的是一种协作性。互斥:体现的是排它性。

进程同步机制

  1. 信号量机制
    一个信号量只能置一次初值,以后只能对之进行p操作或v操作。 由此也可以看到,信号量机制必须有公共内存,不能用于分布式操作系统,这是它最大的弱点。
  2. 自旋锁
    旋锁是为了保护共享资源提出的一种锁机制。 调用者申请的资源如果被占用,即自旋锁被已经被别的执行单元保持,则调用者一直循环在那里看是否该自旋锁的保持着已经释放了锁,自旋锁是一种比较低级的保护数据结构和代码片段的原始方式,可能会引起以下两个问题;
    • 死锁
    • 过多地占用CPU资源
  3. 管程
    信号量机制功能强大,但使用时对信号量的操作分散,而且难以控制,读写和维护都很困难。因此后来又提出了一种集中式同步进程——管程。其基本思想是将共享变量和对它们的操作集中在一个模块中,操作系统或并发程序就由这样的模块构成。这样模块之间联系清晰,便于维护和修改,易于保证正确性。
  4. 会合
    进程直接进行相互作用
  5. 分布式系统
    由于在分布式操作系统中没有公共内存,因此参数全为值参,而且不可为指针。

线程同步方式

  • 互斥量
    Synchronized/Lock:采用互斥对象机制,只有拥有互斥对象的线程才有访问公共资源的权限。因为互斥对象只有一个,所以可以保证公共资源不会被多个线程同时访问

  • 信号量 Semphare:
    它允许同一时刻多个线程访问同一资源,但是需要控制同一时刻访问此资源的最大线程数量

  • 事件(信号),Wait/Notify:
    通过通知操作的方式来保持多线程同步,还可以方便的实现多线程优先级的比较操作


虚拟内存

  1. 内存的发展历程
    没有内存抽象(单进程,除去操作系统所用的内存之外,全部给用户程序使用) —> 有内存抽象(多进程,进程独立的地址空间,交换技术(内存大小不可能容纳下所有并发执行的进程)
    )—> 连续内存分配(固定大小分区(多道程序的程度受限),可变分区(首次适应,最佳适应,最差适应),碎片) —> 不连续内存分配(分段,分页,段页式,虚拟内存)

  2. 虚拟内存
    虚拟内存允许执行进程不必完全在内存中。虚拟内存的基本思想是:每个进程拥有独立的地址空间,这个空间被分为大小相等的多个块,称为页(Page),每个页都是一段连续的地址。这些页被映射到物理内存,但并不是所有的页都必须在内存中才能运行程序。当程序引用到一部分在物理内存中的地址空间时,由硬件立刻进行必要的映射;当程序引用到一部分不在物理内存中的地址空间时,由操作系统负责将缺失的部分装入物理内存并重新执行失败的命令。这样,对于进程而言,逻辑上似乎有很大的内存空间,实际上其中一部分对应物理内存上的一块(称为帧,通常页和帧大小相等),还有一些没加载在内存中的对应在硬盘上,如图5所示。
    注意,请求分页系统、请求分段系统和请求段页式系统都是针对虚拟内存的,通过请求实现内存与外存的信息置换。

在这里插入图片描述

由图可以看出,虚拟内存实际上可以比物理内存大。当访问虚拟内存时,会访问MMU(内存管理单元)去匹配对应的物理地址(比如图5的0,1,2)。如果虚拟内存的页并不存在于物理内存中(如图5的3,4),会产生缺页中断,从磁盘中取得缺的页放入内存,如果内存已满,还会根据某种算法将磁盘中的页换出。

  1. 页面置换算法

    FIFO先进先出算法:在操作系统中经常被用到,比如作业调度(主要实现简单,很容易想到);
    LRU(Least recently use)最近最少使用算法:根据使用时间到现在的长短来判断;
    LFU(Least frequently use)最少使用次数算法:根据使用次数来判断;
    OPT(Optimal replacement)最优置换算法:理论的最优,理论;就是要保证置换出去的是不再被使用的页,或者是在实际内存中最晚使用的算法。

  2. 虚拟内存的应用与优点
    虚拟内存很适合在多道程序设计系统中使用,许多程序的片段同时保存在内存中。当一个程序等待它的一部分读入内存时,可以把CPU交给另一个进程使用。虚拟内存的使用可以带来以下好处:
    在内存中可以保留多个进程,系统并发度提高
    解除了用户与内存之间的紧密约束,进程可以比内存的全部空间还大

  3. 颠簸
    颠簸本质上是指频繁的页调度行为,具体来讲,进程发生缺页中断,这时,必须置换某一页。然而,其他所有的页都在使用,它置换一个页,但又立刻再次需要这个页。因此,会不断产生缺页中断,导致整个系统的效率急剧下降,这种现象称为颠簸(抖动)。

    内存颠簸的解决策略包括:

  • 如果是因为页面替换策略失误,可以修改替换算法来解决这个问题;
  • 如果是因为运行的程序太多,造成程序无法同时将所有频繁访问的页面调入内存,则要降低多道程序的数量;
  • 否则,还剩下两个办法:终止该进程或增加物理内存容量

局部性原理

  1. 时间上的局部性:最近被访问的页在不久的将来还会被访问;
  2. 空间上的局部性:内存中被访问的页周围的页也很可能被访问。

中断和轮询

  1. 中断的定义
    指在计算机执行期间,系统内发生任何非寻常的或非预期的急需处理事件,使得CPU暂时中断当前正在执行的程序而转去执行相应的事件处理程序,待处理完毕后又返回原来被中断处继续执行或调度新的进程执行的过程

  2. 轮询的定义
    定时对各种设备轮流询问一遍有无处理要求


临界区和冲突解决

  1. 临界资源的定义:
    一次仅允许一个进程使用的资源

  2. 临界区的定义:
    每个进程中访问临界资源的那段程序

  3. 解决冲突:
    如果有若干进程要求进入空闲的临界区,一次仅允许一个进程进入
    任何时候,处于临界区内的进程不可多于一个
    进入临界区的进程要在有限时间内退出,以便其它进程能及时进入自己的临界区
    如果进程不能进入自己的临界区,则应让出CPU,避免进程出现“忙等”现象


缓冲区

  1. 缓冲区溢出:
    指当计算机向缓冲区内填充数据时超过了缓冲区本身的容量,溢出的数据覆盖在合法数据上

  2. 缓冲区溢出的危害:
    程序崩溃导致拒绝服务、跳转并且执行一段恶意代码

  3. 缓冲区溢出的原因:
    程序中没有仔细检查用户输入的参数


多进程编程和多线程编程优缺点

多进程编程和多钱程编程,都可以使用并行机制来提升系统的运行效率。二者的区别在于运行时所占的内存分布不同,多钱程是共用一套内存的代码块区间;而多进程是各用一套独立的内存区间。

多进程的优点是稳定性好,一个子进程崩溃了,不会影响主进程以及其余进程。基于这个特性,常常会用多进程来实现守护服务器的功能。

多进程编程也有不足,即创建进程的代价非常大,因为操作系统要给每个进程分配固定的资源,并且操作系统对进程的总数会有一定的限制,若进程过多,操作系统调度都会存在问题,会造成假死状态。

多线程编程的优点是效率较高一些,适用于批处理任务等功能;不足之处在于,任何一个线程崩溃都可能造成整个进程的崩溃,因为它们共享了进程的内存资源池。

既然多线程编程和多进程编程各有优缺点,因此它们分别适用于不同的场景。比如说,对于计算密集型的任务,多进程效率会更高一下;而对于IO密集型的任务(比如文件操作,网络爬虫),采用多线程编程效率更高。为什么是这样呢?

其实也不难理解。对于 IO 密集型操作,大部分消耗时间其实是等待时间,在等待时间中,Python会释放GIL供新的线程使用,实现了线程间的切换;相反对于 CPU 密集型代码,2 个 CPU 干活肯定比一个 CPU 快很多。

在大型的计算机集群系统中,通常都会将多进程程序分布运行在不同的计算机上协同工作。而每一台计算机上的进程内部,又会由多个线程来并行工作。

注意,对于任务数来说,无论是多进程编程或者多线程编程,其进程数或线程数都不能太多:

对于多进程编程来说,操作系统在切换任务时,会有一系列的保护现场措施,这要花费相当多的系统资源,若任务过多,则大部分资源都被用做干这些了,结果就是所有任务都做不好;
多线程编程也不是线程个数越多效率越高,通过下面的公式可以计算出线程数量最优的一个参考值:
请添加图片描述






  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值