man 7 sched文档翻译

man 7 sched

描述:
    从linux 2.6.23开始, 默认的调度器是CFS, 即"Completely Fair Scheduler", 完全公平调度器. 它取代了之前的O(1)调度器.
    
    API Summary
        linux提供下面的系统调用来控制CPU调度器的行为, 策略以及进程的优先级(准确地讲, 是线程, thread).
        nice(2): 为调用的线程设置一个新的nice值, 并返回新的ncie值.
        getpriority(2): 返回一个被指定用户拥有的线程, 进程组或线程组的nice值.
        setpriority(2): 设置被指定用户拥有的线程, 进程组或线程组的nice值.
        sched_setscheduler(2): 设置指定线程的调度策略和参数.
        sched_getscheduler(2): 返回指定线程的调度策略.
        sched_setparam(2): 设置指定线程的调度参数.
        sched_getparam(2): 获取指定线程的调度参数.
        sched_get_priority_max(2): 返回指定调度策略的最大可用优先级.
        sched_get_priority_min(2): 返回指定调度策略的最小可用优先级.
        sched_rr_get_interval(2): 获取在"round-robin"调度策略下指定线程的时间片(quantum).
        sched_yield(2): 放弃CPU, 使得其他线程可以执行.
        sched_setaffinity(2): 设置指定线程的CPU亲和性.
        sched_getaffinity(2): 获取指定线程的CPU亲和行.
        sched_setattr(2): 设置指定线程的调度策略和参数. 这个函数为linux特有, 提供了sched_setscheduler()和sched_setparam()功能的集合.
        sched_getattr(2): 获取指定线程的调度策略和参数. 这个函数为linux特有, 提供了sched_getscheduler()和sched_getparam()功能的集合.

调度策略:
    调度器是内核的一个组件, 它决定CPU接下来将要运行的线程. 每个线程都有一个调度策略和静态调度优先级(sche_priority). 调度器基于系统中所有线程的调度策略和静态优先级做出决策.
    对于调度策略为SCHED_OTHER, SCHED_IDLE和SCHED_BATCH的线程, sched_priority不用于调度决策(it must be specified as 0).
    采用实时策略(SCEHD_FIFO和SCHED_RR)的进程有一个sched_priority值, 范围为1(low)~99(high). 注意: POSIX.1对实时策略仅要求支持32个优先等级, 有些系统也确实仅支持32个. 为了程序的可移植性, 应使用sched_get_priority_min()和sched_get_priority_max()来获取指定策略的可支持优先级范围.
    调度器为每个sched_priority维护了一个可运行线程的列表, 为了选出下一个接下来运行的线程, 调度器在非空的, 具有最高静态优先级的列表中选择处于列表头的线程.
    线程的调度策略决定它被插入到对应列表(具有相同静态优先级的线程的列表)的何处以及怎样被插入.
    所有调度均可抢占, 如果一个高静态优先级的线程就绪, 则当前线程将被抢占并返回其对应的进程列表中. 调度策略仅决定线程在列表内的顺序.
    
SCHED_FIFO: 先入先出调度
    SCHED_FIFO只能在静态优先级大于0时使用, 这意味着当SCHED_FIFO线程就绪时, 它将立即抢占正在运行的SCHED_OTHER, SCHED_BATCH或SCHED_IDLE线程. SCHED_FIFO是一个没有时间片机制的简单的调度算法. 在这种调度策略下, 有如下这些规则:
    1) 当一个线程被更高优先级的线程抢占后, 它将被存放在相应优先级线程列表的头, 一旦更高优先级的线程被阻塞后, 它将马上被唤醒.
    2) 当一个线程被阻塞后, 它将被插入到对应优先级线程列表的末尾.
    3) 当调用sched_setscheduler(), sched_setparam(), sched_setattr(), pthread_setschedparam()或pthread_setschedprio()使正在运行或处于就绪态的线程(通过参数pid指定)优先级发生变化时: 若优先级提高, 它将被插入到新优先级对应的线程列表的末尾, 因此, 它有可能抢占正在运行的线程; 若线程优先级不变, 则它在运行列表中的位置也不变; 若线程优先级降低, 则它将被插入到新的优先级对应的列表的头部.
    4) 调用sched_yield()的线程将被插入到对应列表的尾部.
    其他事件均无法改变线程在列表中的位置.
    使用SCHED_FIFO的线程将在以下情况被阻塞: 执行IO请求, 被高优先级线程抢占或调用sched_yield().
    
SCEHD_RR: 时间片轮转调度(Round-robin scheduling)
    SCHED_RR是SCHED_FIFO的一个简单增强版. 以上关于SCHED_FIFO的描述均可适用, 除了每个线程每次只能运行一个(最大)时间片. 若一个线程已经运行的时间大于等于最大时间片, 则它将被移动到队列的末尾. 一个被高优先级线程抢占的SCHED_RR线程将在下次执行时消耗剩余的时间片. 时间片的长度可以通过sched_rr_get_interval()获取.
    

SCHED_DEADLINE: Sporadic task model deadline scheduling
    从3.14开始, linux提供最终期限调度策略(SCHED_DEADLINE). 这个策略现在用GEDF(Global Earliest Deadline First)结合CBS(Constant Bandwidth Server)实现. 可以使用linux系统调用sched_setattr()和sched_getattr()来设置或获取这个策略的属性.
    零散任务(sporadic task)是这样一种任务: 它有一系列的作业(job), 其中每个作业在每个周期最多被激活一次. 每个作业也有一个相对最后期限(relative deadline), 在这个deadline之前, 它应该完成执行; 它还有一个运算时间(computation time), 代表了执行该作业需要的CPU时间. 一个任务因为作业必须被执行而唤醒的时刻称为到达时间(arrival time). 任务开始执行的时间称为开始时间(start time). 绝对最后期限(absolute deadline)等于到达时间加上相对最后期限.
    下图表示了各个变量的关系:

           arrival/wakeup                    absolute deadline
                |    start time                    |
                |        |                         |
                v        v                         v
           -----x--------xooooooooooooooooo--------x--------x---
                         |<- comp. time ->|
                |<------- relative deadline ------>|
                |<-------------- period ------------------->|

    当使用setattr()设置线程的调度策略为SCHED_DEADLINE时, 我们可以指定三个参数: 运行时间(Runtime), 最后期限(Deadline)和周期(Period). 这些参数不必与前面所提的名词严格对应: 实践中通常设置平均的运算时间比运算时间稍大(对于硬实时任务应设置为最长的运算时间), 最后期限设置为相对最后期限, 周期设置为任务的运行周期. 这样, 当SCHED_DEADLINE策略调度时, 有下图所示的关系:

           arrival/wakeup                    absolute deadline
                |    start time                    |
                |        |                         |
                v        v                         v
           -----x--------xooooooooooooooooo--------x--------x---
                         |<-- Runtime ------->|
                |<----------- Deadline ----------->|
                |<-------------- Period ------------------->|

    这三个最后期限调度参数对应着sched_attr结构中的sched_runtime, sched_deadline和sched_period域, 可查阅sched_setattr(2). 这些域的单位是纳秒. 如果sched_period被指定为0, 则它将被设置为与sched_deadline相同的值. 
    内核要求: sched_runtime <= sched_deadline <= sched_period
    此外, 在当前的实现中, 这些参数必须大于1024(比1ms稍大, 实现的精度如此), 且小于2^63. 若参数不合法, 则sched_setattr(2)以EINVAL错误退出.
    CBS保证了任务之间没有干扰, 这通过压制(throttling)那些运行时间试图超过Runtime的任务实现.
    为了确保该调度策略的运行, 内核必须要避免一种情况: 线程设置了SCHED_DEADLINE, 但它在给定的限制下根本不可行. 为此, 当设置或改变线程的调度策略为SCHED_DEADLINE时, 内核会执行准入测试. 准入测试会计算该线程是否可设置为该策略, 若不能, sched_setattr(2)以EBUSY退出.
    例如, 必须满足(不充分)总的利用率小于等于全部cpu空闲率的总和, 这样每个线程可以在每个周期最大化地运行Runtime时间, 线程的利用率等于Runtime/Period.
    为了保证SCHED_DEADLINE策略线程的顺利运行, 这些线程具有系统中的最高优先级(user controllable);当有任何一个SCHED_DEADLINE线程就绪时, 它将抢占使用其他策略的线程.
    使用SCEHD_DEADLINE策略的线程调用fork(2)时将以错误EAGAIN失败, 除非它设置了reset-on-fork标志.
    使用SCHED_DEADLINE策略的线程调用sched_yield(2)时将让出当前作业并等待下一个周期.

SCHED_OTHER: Default Linux time-sharing scheduling
    SCEHD_OTHER仅可被用于静态优先级为0的线程中(也就是说使用实时策略的线程总是比SCHED_OTHER线程优先级高). SCHED_OTHER是标准的Linux分时调度, 它用于那些不需要特殊实时机制的线程.
    被运行的线程从静态优先级为0的列表中基于动态优先级被选出, 动态优先级仅在该列表内部使用. 静态优先级基于基于nice值, 并且在线程准备运行但被调度器拒绝的每个时间片增加. 这保证了在所有使用该策略的线程中的公平性.
    在Linux内核源码中, SCHED_OTHER策略被命名为SCHED_NORMAL. 

nice值
    nice值在调度时影响CPU调度器偏向或疏远一个进程. 它影响使用SCHED_OTHER和SCHED_BATCH策略的进程. nice值可以通过nice(2), setpriority(2)或scehd_setattr(2)修改.
    根据POSIX.1, nice值是进程的属性, 也就是说, 在一个进程中的线程共享一个nice值. 然而, 在Linux中, nice值是线程的属性: 同一个进程的不同线程可能有不同的nice值.
    各种UNIX系统中nice值的范围不尽相同. 在现代Linux中, 范围是-20(高优先级)到+19(低优先级). 在一些其他的系统中, 该范围是-20到+20. 在早期Linux内核中(Linux 2.0以前), 该范围是负无穷到+15.
    在不同的UNIX系统以及不同的Linux内核版本中, nice值对SCHED_OTHER进程的相对调度级别的影响都不仅相同.
    随着2.6.23版本内核中CFS调度器的到来, Linux实现了一个算法, 它使nice值之间的差异影响更大. 在当前的实现中, 当两个进程之间的nice值相差1个单位时, 调度器将偏向于高优先级进程1.25个调度等级. This causes very low nice values (+19) to truly provide  lit-
    tle  CPU  to a process whenever there is any other higher priority load
    on the system, and makes high nice values (-20) deliver most of the CPU
    to applications that require it (e.g., some audio applications).

    On  Linux, the RLIMIT_NICE resource limit can be used to define a limit
       to which an unprivileged process's nice value can be raised; see  setr-
       limit(2) for details.

    更多nice值的细节可以查看auto-group特性和group scheduling的章节.

SCHED_BATCH: Scheduling batch process
    (从Linux 2.6.16开始) SCHED_BATCH 仅可用于静态优先级为0时. 这个策略和SCHE_OTHER相同, 都是根据动态优先级(基于nice值)调度的. 差别是这个策略中调度器假设线程都是CPU密集型的. 因此, 调度器将对唤醒行为进行较小的调度惩罚, 使该进程在调度决策中受到轻微的疏远.
    这个策略对于那些非交互型又不想降低nice值的负载, 以及想要一个确定性的, 没有交互造成的额外抢占的调度策略的进程很有用.

SCHED_IDLE: Scheduling very low priority jobs
    (从Linux 2.6.23开始)SCHED_IDLE仅可用于静态优先级为0时;进程的nice值对该策略没有影响.
    该策略倾向于运行优先级极低的任务(甚至比SCHED_OTHER和SCHED_BATCH中+19优先级还低)

为子进程重设调度策略
    每个线程有一个reset-on-fork调度标志. 当设置这个标志时, 通过fork(2)创建的子进程不继承调度策略. reset-on-fork标志可以通过下列方法设置:
    * 调用sched_setscheduler(2)时, 设置SCHED_REST_ON_FORK(将它与其他标志使用或操作).
    * 调用sched_setattr(2)时, 在attr.sched_flags中指定SCHED_FLAG_RESET_ON_FORK.
    注意这两个API用的常量名字不一样. reset-on-fork设置的结果分别可以通过sched_getscheduler(2)和sched_getattr(2)获取.
    reset-on-fork标志是为媒体回放应用设计的, 可用来防止应用通过创建多个子进程来躲避RLIMIT_RTTIME资源限制(可查阅getrlimit(2)).
    更准确地说, 如果reset-on-fork标志被设置, 则下列规则将被应用:
    * 若调用的进程使用的调度策略是SCHED_FIFO或SCHED_RR, 则子进程将被设置为SCHED_OTHER.
    * 若调用的进程nice值为负, 则子进程nice值将被设置为0.
    在reset-on-fork标志被使能后, 只有该线程有CAP_SYS_NICE能力时, 它才可以被reset. 使用fork(2)创建子进程时, 该标志被disable

特权和资源限制
    在Linux 2.6.12之前的系统中, 仅有特权(CAP_SYS_NICE)的线程可以设置非0的静态优先级(即实时调度策略). 非特权线程唯一可以做的改变是设置SCHED_OTHER策略, 并且要求调用方的有效用户ID和目标线程(要改变调度策略的线程)的真实(real)或者有效(effictive)用户ID匹配.
    为了设置或修改SCHED_DEALINE策略, 线程必须拥有特权.
    从Linux 2.6.12开始, RLIMIT_RTPRIO资源限制定义了SCHED_RR和SCHED_FIFO策略非特权线程静态优先级的范围. 改变调度策略及优先级的规则如下:
    * 若一个非特权线程有非0的RLIMIT_RTPRIO软限制, 则它可以改变调度策略和优先级, 但有如下限制: 其优先级不能大于当前优先级和RLIMIT_RTPRIO软限制中的最大值.
    * 若RLIMIT_RTPRIO软限制为0, 则仅被允许的操作是降低优先级或切换到非实时策略.
    * 同样, 其他的线程也可以做出这些更改, 只要其有效用户ID和被更改的线程真实(real)或有效用户ID相匹配.
    * SCEHD_IDLE有特殊规则. 在2.6.39之前的内核, 在该策略下运行的非特权线程不能改变其策略, 无论它的RLIMIT_RTPRIO软限制是何值. 在之后的版本中, 非特权线程可以切换到SCHED_BATCH或SCHED_OTHER, 只要它的nice值在RLIMIT_NICE允许的范围内(可查阅getrlimit(2)).
    特权线程(CAP_SYS_NICE)忽略RLIMIT_RTPRIO限制;像更老的内核一样, 它们可以任意改变调度策略和优先级. 查看getrlimit(2)获取更多关于RLIMIT_RTPRIO的更多信息.

限制实时进程和deadline进程的CPU使用
    SCHED_FIFO, SCHED_RR或SCHED_DEADLINE策略下的非阻塞死循环可能阻塞所有其他线程的CPU访问. Linux 2.6.25之前, 避免一个实时进程冻结系统的唯一办法是运行一个更高静态优先级的shell(在console中). 这允许紧急杀死非阻塞的实时应用程序或使其恰当的终止.
    从2.6.25开始, 有其他的方法可以处理失控的实时进程或deadline进程. 其中之一是使用RLIMIT_RTTIME资源限制来设置实时进程可消耗的CPU时间范围. 可查看getrlimit(2).
    从2.6.25开始, Linux还提供了两个/proc文件, 可以保留一定的CPU时间将其用于非实时进程. 这样保留的CPU时间可以用于一个root shell来把失控的进程杀掉. 这些文件指定的时间值单位都是微秒: 
    /proc/sys/kernel/sched_rt_period_us
        这个文件指定一个相当于100% CPU带宽的调度周期. 这个值的范围是1~INT_MAX, 给出了一个从1微秒到大约35分钟的调整范围. 默认值是1000000(1秒).
    /proc/sys/kernel/sched_rt_runtime_us
        这个值指定了调度周期中的多少可用于实时进程和deadline进程. 该值可以设置为-1~INT_MAX-1. 指定为-1使得可运行时间等于调度周期;也就是没有CPU时间为非实时进程保留(也就是和Linux 2.6.25之前的行为相同). 默认值是950000(0.95秒), 意味着5%的CPU时间为非实时进程保留.

响应时间
    一个正在阻塞等待IO的高优先级线程在被再次调度之前有一个确定的响应时间. 设备驱动程序的开发者可以使用"slow interrupt"中断处理程序极大地减少该时间.

其他
    fork(2)的子进程继承调度策略和参数. 使用execve(2)时调度策略和参数被保存.
    实时进程通常需要memory lock来避免paging delays; 可通过mlock(2)或mlockall(2)实现.


autogroup特性
    从Linux 2.6.38开始, 内核提供一个叫做autogroup的特性, 用来提高系统面对多进程, CPU密集时的桌面交互性能, 例如当Linux内核使用多个并行构建进程时(即make(1) -j标志).
    这个特性与CFS调度器共同作用, 并且要求内核配置CONFIG_SCHED_AUTOGROUP. 在正在运行的系统上, 这个特性可以通过写文件/proc/sys/kernel/sched_autogroup_enabled使能或关闭; 0表示关闭, 1为使能. 默认为1, 除非内核用noautogroup参数启动.
    当通过setsid(2)创建新会话时, 一个新的autogroup被创建;例如, 当开启一个新的中断窗口时. 使用fork(2)创建的新进程继承它父亲的autogroup成员. 这样, 在同一个会话中的所有进程是同一个autogroup的成员. 当这个group中最后一个进程结束时, autogroup被自动销毁.
    当autogroup被使能时, autogroup中的所有成员被放入同一内核调度器任务组(task group), CFS调度器执行的算法使CPU周期在不同任务组之间的分配平等. 这样做对交互性桌面性能的好处可用下例说明.
    假设有两个autogroup竞争同一CPU(即, 要么使用的是单CPU系统, 要么在SMP系统中使用了taskset(1)限制所有的进程运行在同一CPU上). 第一个autogrouop使用make -j10构建内核, 因而有10个CPU密集型进程. 另一个包含一个CPU密集型进程: 视频播放器. autogroup特性使得每个autogroup拥有一半的CPU周期. 也就是说, 视频播放器有50%的CPU时间, 而不是9%, 后者将使视频播放的性能下降. 在SMP系统中, 该场景更复杂, 但影响类似: 调度器在任务组之间分配CPU时间, 这样包含很多CPU密集型进程的autogroup不会以牺牲其他工作为代价运行.
    一个进程的任务组成员可以通过下面的文件查看: /proc/[pid]/autogroup:
        $ cat /proc/1/autogroup
               /autogroup-1 nice 0
    这个文件也可以用来修改分配给一个组的CPU带宽. 这通过向设置autogroup的nice值的文件中写入一个"nice"范围的值实现. 允许的范围是+19(低优先级)到-20(高优先级). (写入不该范围的值将使write(2)以错误EINVAL失败退出)
    autogroup的nice值的设置与进程的nice值有相同的含义, 但是它是基于与其他autogroup的nice值的相对值在autogroup之间分配CPU时间. 对于一个在autogroup中的进程, 它分配到的CPU周期是autogroup的nice值(autogroup相比)与进程nice值(与同一autogroup中其他进程的nice值相比)的乘积.
    调用cgroup(7)CPU控制器将进程放在除根CPU cgroup之外的其他cgroup中将覆盖autogroup的影响.
    autogroup特性仅对非实时调度策略(SCHED_OTHER, SCHED_BATCH和SCHED_IDLE)下的进程进行分组. 它不会对实时策略和deadline策略下的进程分组. 这些进程的调度行为如前所述.

nice值和分组调度
    当调度非实时进程(即SCHED_OTHER, SCHED_BATCH, SCHED_IDLE)时, 若内核配置了CONFIG_GROUP_SCHED(这是典型情况), CFS调度器使用称为组调度(group scheduling)的技术.
    在组调度下, 线程在任务组中被调度. 任务组有层级关系, 它的根在系统的初始任务组, 也就是根任务组(root task group). 任务组在如下场景中形成:
    * CPU cgroup中的所有线程组成一个任务组. 这个任务组的父亲是相应的父cgroup的任务组.
    * 如果autogroup被使能, 那么所有被置于一个autogroup(即相同的会话, 像setsid(2)设置的那样)的线程组成一个任务组.  因此, 每个新的autogroup都是一个独立的task group. 根任务组是所有这种autogroup的父任务组. 
    * 如果autogroup被使能, 那么根任务组由根CPU cgroup中的所有进程组成, 进程也可能被隐形地放在一个新的autogroup中. (不知怎么翻译, 原文: If autogrouping is enabled, then the root task group consists of all processes  in the root CPU cgroup that were not otherwise implicitly placed into a new autogroup.)
    * 如果autogroup未被使能, 那么根任务组由根CPU cgroup中的所有进程组成.
    * 如果组调度未被使能(即内核为配置CONFIG_FAIR_GROUP_SCHED), 那么系统中所有进程被放入同一个任务组中.
    在组调度中, 一个线程的nice值仅对同一任务组中与其他线程的相对调度决策有影响. 就UNIX系统中nice值的传统语义而言, 这会产生令人惊讶的效果. 尤其是, 当autogroup被使能时(很多发行版默认如此), setpriority(2)或nice(1)仅对该进程与同一会话(典型情况是同一终端窗口)中的其他进程的相对调度起作用.
    相反, 对于两个在不同会话中的(例如, 它们在不同的中断窗口, 每个窗口绑定到不同的autogroup中)CPU密集型的进程来说, 改变其中一个会话中的进程的nice值对另一个会话中的相对调度没有影响. 一个可能有用的妥协方案是为一个终端会话中的所有进程修改autogroup的nice值:
    echo 10 > /proc/self/autogroup

Linux主线中的real-time feature
    从内核版本2.6.18开始, Linux逐渐具备实时能力, 这些能力大多来自实时抢占补丁. 在所有的补丁被完全合入主线之前, 它们必须被安装以实现最好的实时性能. 这些补丁被命名为: patch-kernelversion-rtpatchversion, 它们可以从这里下载: <http://www.kernel.org/pub/linux/kernel/projects/rt/>
    在没有补丁的情况下,在将其完全包含到主线内核中之前,内核配置仅提供三个抢占类CONFIG_PREEMPT_NONE、CONFIG_PREEMPT_VOLUMENTARY和CONFIG_PRE-EMPT_DESKTOP,它们分别不提供、部分提供和显著减少最坏情况下的调度延迟。
    打上补丁或在它们合入主线之后, 额外的配置项CONFIG_PREEMPT_RT变得可用. 当它被选择时, Linux变为常规的实时操作系统. FIFO和RR调度策略被用来运行拥有真实时优先级的线程, 提供最小的最坏情况下的latency.

NOTES:
    cgroup(7) CPU控制器可以用来限制进程组的CPU消耗.
    最初, 标准Linux倾向于设计为处理后台任务/交互应用/低实时要求应用的通用操作系统. 虽然Linux内核2.6允许内核抢占, 也引入了新的O(1)调度器保证调度所需的时间固定且与活动任务数无关, 直到内核版本2.6.17, 真实时计算才变得可能.

SEE ALSO
    chcpu(1), chrt(1), lscpu(1), ps(1), taskset(1), top(1), getpriority(2),
       mlock(2), mlockall(2), munlock(2), munlockall(2), nice(2),
       sched_get_priority_max(2), sched_get_priority_min(2),
       sched_getaffinity(2), sched_getparam(2), sched_getscheduler(2),
       sched_rr_get_interval(2), sched_setaffinity(2), sched_setparam(2),
       sched_setscheduler(2), sched_yield(2), setpriority(2),
       pthread_getaffinity_np(3), pthread_getschedparam(3),
       pthread_setaffinity_np(3), sched_getcpu(3), capabilities(7), cpuset(7)

    Programming for the real  world  -  POSIX.4  by  Bill  O.  Gallmeister,
       O'Reilly & Associates, Inc., ISBN 1-56592-074-0.

       The    Linux   kernel   source   files   Documentation/scheduler/sched-
       deadline.txt,               Documentation/scheduler/sched-rt-group.txt,
       Documentation/scheduler/sched-design-CFS.txt,                       and
       Documentation/scheduler/sched-nice-design.txt

COLOPHON
       This page is part of release 5.05 of the Linux  man-pages  project.   A
       description  of  the project, information about reporting bugs, and the
       latest    version    of    this    page,    can     be     found     at
       https://www.kernel.org/doc/man-pages/.

Linux                             2019-08-02                          SCHED(7)


 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值