netdata:proc/stat

1059 篇文章 274 订阅

proc/stat文件解析

proc/stat 该文件包含了所有CPU活动的信息,该文件中的所有值都是从系统启动开始累计到当前时刻。不同内核版本中该文件的格式可能不大一致,以下通过实例来说明数据该文件中各字段的含义。

$ cat /proc/stat
//CPU指标:user,nice, system, idle, iowait, irq, softirq
cpu  41470 33 12790 159518 8654 0 373 0 0 0
cpu0 20650 15 6220 79957 4281 0 141 0 0 0
cpu1 20819 18 6569 79560 4372 0 231 0 0 0
intr 1120015 165 644 0 0 0 0 0 0 1 0 0 0 38995 0 0 0 6965 26054 205 727 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 633 266 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
ctxt 1867303
btime 1611747797
processes 4785
procs_running 4
procs_blocked 0
softirq 1043783 1 643973 229 4241 26658 0 884 126691 0 241106

  • 当前系统共有2个处理器
  • intr :系统启动以来的所有interrupts的次数情况
  • ctxt: 系统上下文切换次数
  • btime:当前时间,此处指为1611747797,转换北京时间为 2021-01-27 19:43:17 存疑
  • processes:系统启动后所创建过的进程数量。当短时间该值特别大,系统可能出现异常
  • procs_running:处于Runnable状态的进程个数
  • procs_blocked:处于等待I/O完成的进程个数

在这里插入图片描述
第一行的数值表示的是CPU总的使用情况(第一行所有的数相加),所以我们只要用第一行的数字计算就可以了。下表解析第一行各数值的含义(单位: jiffies):

jiffies是内核中的一个全局变量,用来记录自系统启动一来产生的节拍数,在linux中,一个节拍大致可理解为操作系统进程调度的最小时间片,不同linux内核可能值有不同,通常在1ms到10ms之间

  • user (41470):从系统启动开始累计到当前时刻,处于用户态的运行时间,不包含 nice值为负进程。
  • nice (33) : 从系统启动开始累计到当前时刻,nice值为负的进程所占用的CPU时间
  • system (12790) :从系统启动开始累计到当前时刻,处于核心态的运行时间
  • idle (159518) 从系统启动开始累计到当前时刻,除IO等待时间以外的其它等待时间
  • iowait (8654) 从系统启动开始累计到当前时刻,IO等待时间(since 2.5.41)
  • irq (0) 从系统启动开始累计到当前时刻,硬中断时间(since 2.6.0-test4)
  • softirq (373) 从系统启动开始累计到当前时刻,软中断时间(since 2.6.0-test4)

获取当前系统有几个处理器

在这里插入图片描述
1、procfile.h

#pragma once

#include <stdio.h>
#include <elf.h>
#include <glob.h>

#define FILENAME_MAX 4096
#define PROCFILE_FLAG_DEFAULT             0x00000000
#define PROCFILE_FLAG_NO_ERROR_ON_FILE_IO 0x00000001

// ----------------------------------------------------------------------------
// An array of lines
typedef struct {
    size_t words;   // how many words this line has
    size_t first;   // the id of the first word of this line
    // in the words array
} ffline;
typedef struct {
    size_t len;     // used entries
    size_t size;    // capacity
    ffline lines[]; // array of lines
} pflines;

// ----------------------------------------------------------------------------
// An array of words

typedef struct {
    size_t len;     // used entries
    size_t size;    // capacity
    char *words[];  // array of pointers
} pfwords;



// ----------------------------------------------------------------------------
// The procfile

typedef enum procfile_separator {
    PF_CHAR_IS_SEPARATOR,
    PF_CHAR_IS_NEWLINE,
    PF_CHAR_IS_WORD,
    PF_CHAR_IS_QUOTE,
    PF_CHAR_IS_OPEN,
    PF_CHAR_IS_CLOSE
} PF_CHAR_TYPE;


typedef struct {
    char filename[FILENAME_MAX + 1]; // 不填充  until profile_filename() is called

    uint32_t flags;
    int fd;               // the file desriptor
    size_t len;           // 我们放入data中的字节
    size_t size;          // 我们分配data中的字节
    pflines *lines;
    pfwords *words;
    PF_CHAR_TYPE separators[256];
    char data[];          // allocated buffer to keep file contents
} procfile;

//返回当前行数
#define procfile_lines(ff) ((ff)->lines->len)
//返回文件的第n个单词或空字符串
#define procfile_word(ff, word) (((word) < (ff)->words->len) ? (ff)->words->words[(word)] : "")

//返回第n行中的单词数
#define procfile_linewords(ff, line) (((line) < procfile_lines(ff)) ? (ff)->lines->lines[(line)].words : 0)
//返回当前行的第n个单词
#define procfile_lineword(ff, line, word) (((line) < procfile_lines(ff) && (word) < procfile_linewords((ff), (line))) ? procfile_word((ff), (ff)->lines->lines[(line)].first + (word)) : "")


procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) ;
procfile *procfile_readall(procfile *ff) ;


void procfile_close(procfile *ff);

2、procfile.c

//
// Created by oceanstar on 2021/1/4.
//

#include <fcntl.h>
#include "procfile.h"
#include "stdio.h"
#include <memory.h>
#include <stdlib.h>
#include <ctype.h>
#include <zconf.h>

#define likely(x)  __builtin_expect((x), 1)
#define unlikely(x)  __builtin_expect((x), 0)



#define NEVERNULL __attribute__((returns_nonnull))
#define NOINLINE __attribute__((noinline))

#define PFLINES_INCREASE_STEP 10
#define PFWORDS_INCREASE_STEP 200
#define PROCFILE_INCREMENT_BUFFER 512
size_t procfile_max_lines = PFLINES_INCREASE_STEP;
size_t procfile_max_words = PFWORDS_INCREASE_STEP;
size_t procfile_max_allocation = PROCFILE_INCREMENT_BUFFER;


int procfile_adaptive_initial_allocation = 0;
int procfile_open_flags = O_RDONLY;


NEVERNULL static inline pflines *pflines_new(void) {
    size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_words : PFLINES_INCREASE_STEP;

    pflines *new = malloc(sizeof(pflines) + size * sizeof(ffline));
    new->len = 0;
    new->size = size;
    return new;
}

NEVERNULL static inline pfwords *pfwords_new(void) {
    // debug(D_PROCFILE, PF_PREFIX ":   initializing words");

    size_t size = (procfile_adaptive_initial_allocation) ? procfile_max_words : PFWORDS_INCREASE_STEP;

    pfwords *new = malloc(sizeof(pfwords) + size * sizeof(char *));
    new->len = 0;
    new->size = size;
    return new;
}

NOINLINE static void procfile_set_separators(procfile *ff, const char *separators) {
    static PF_CHAR_TYPE def[256];
    static char initilized = 0;

    if(unlikely(!initilized)) {
        // this is thread safe
        // if initialized is zero, multiple threads may be executing
        // this code at the same time, setting in def[] the exact same values
        int i = 256;
        while(i--) {
            if(unlikely(i == '\n' || i == '\r'))
                def[i] = PF_CHAR_IS_NEWLINE;

            else if(unlikely(isspace(i) || !isprint(i)))
                def[i] = PF_CHAR_IS_SEPARATOR;

            else
                def[i] = PF_CHAR_IS_WORD;
        }

        initilized = 1;
    }

    // copy the default
    PF_CHAR_TYPE *ffs = ff->separators, *ffd = def, *ffe = &def[256];
    while(ffd != ffe)
        *ffs++ = *ffd++;

    // set the separators
    if(unlikely(!separators))
        separators = " \t=|";

    ffs = ff->separators;
    const char *s = separators;
    while(*s)
        ffs[(int)*s++] = PF_CHAR_IS_SEPARATOR;
}
char *procfile_filename(procfile *ff) {
    if(ff->filename[0]) return ff->filename;

    char buffer[FILENAME_MAX + 1];
    snprintf(buffer, FILENAME_MAX, "/proc/self/fd/%d", ff->fd);

    ssize_t l = readlink(buffer, ff->filename, FILENAME_MAX);
    if(unlikely(l == -1))
        snprintf(ff->filename, FILENAME_MAX, "unknown filename for fd %d", ff->fd);
    else
        ff->filename[l] = '\0';

    // on non-linux systems, something like this will be needed
    // fcntl(ff->fd, F_GETPATH, ff->filename)

    return ff->filename;
}

static inline void pflines_free(pflines *fl) {
    free(fl);
}
static inline void pfwords_free(pfwords *fw) {
    free(fw);
}
static inline void pflines_reset(pflines *fl) {
    fl->len = 0;
}
static inline void pfwords_reset(pfwords *fw) {
    fw->len = 0;
}
NEVERNULL static inline size_t *pflines_add(procfile *ff) {
    pflines *fl = ff->lines;
    if(unlikely(fl->len == fl->size)) {
        ff->lines = fl = realloc(fl, sizeof(pflines) + (fl->size + PFLINES_INCREASE_STEP) * sizeof(ffline));
        fl->size += PFLINES_INCREASE_STEP;
    }

    ffline *ffl = &fl->lines[fl->len++];
    ffl->words = 0;
    ffl->first = ff->words->len;

    return &ffl->words;
}
static inline void pfwords_add(procfile *ff, char *str) {
    // debug(D_PROCFILE, PF_PREFIX ":   adding word No %d: '%s'", fw->len, str);

    pfwords *fw = ff->words;
    if(unlikely(fw->len == fw->size)) {
        // debug(D_PROCFILE, PF_PREFIX ":   expanding words");

        ff->words = fw = realloc(fw, sizeof(pfwords) + (fw->size + PFWORDS_INCREASE_STEP) * sizeof(char *));
        fw->size += PFWORDS_INCREASE_STEP;
    }

    fw->words[fw->len++] = str;
}

NOINLINE static void procfile_parser(procfile *ff) {
    char  *s = ff->data                 // our current position
    , *e = &ff->data[ff->len]       // the terminating null
    , *t = ff->data;                // the first character of a word (or quoted / parenthesized string)

    PF_CHAR_TYPE *separators = ff->separators;

    char quote = 0;                     // the quote character - only when in quoted string
    size_t opened = 0;                  // counts the number of open parenthesis

    size_t *line_words = pflines_add(ff);

    while(s < e) {
        PF_CHAR_TYPE ct = separators[(unsigned char)(*s)];

        // this is faster than a switch()
        // read more here: http://lazarenko.me/switch/
        if(likely(ct == PF_CHAR_IS_WORD)) {
            s++;
        }
        else if(likely(ct == PF_CHAR_IS_SEPARATOR)) {
            if(!quote && !opened) {
                if (s != t) {
                    // separator, but we have word before it
                    *s = '\0';
                    pfwords_add(ff, t);
                    (*line_words)++;
                    t = ++s;
                }
                else {
                    // separator at the beginning
                    // skip it
                    t = ++s;
                }
            }
            else {
                // we are inside a quote or parenthesized string
                s++;
            }
        }
        else if(likely(ct == PF_CHAR_IS_NEWLINE)) {
            // end of line

            *s = '\0';
            pfwords_add(ff, t);
            (*line_words)++;
            t = ++s;

            // debug(D_PROCFILE, PF_PREFIX ":   ended line %d with %d words", l, ff->lines->lines[l].words);

            line_words = pflines_add(ff);
        }
        else if(likely(ct == PF_CHAR_IS_QUOTE)) {
            if(unlikely(!quote && s == t)) {
                // quote opened at the beginning
                quote = *s;
                t = ++s;
            }
            else if(unlikely(quote && quote == *s)) {
                // quote closed
                quote = 0;

                *s = '\0';
                pfwords_add(ff, t);
                (*line_words)++;
                t = ++s;
            }
            else
                s++;
        }
        else if(likely(ct == PF_CHAR_IS_OPEN)) {
            if(s == t) {
                opened++;
                t = ++s;
            }
            else if(opened) {
                opened++;
                s++;
            }
            else
                s++;
        }
        else if(likely(ct == PF_CHAR_IS_CLOSE)) {
            if(opened) {
                opened--;

                if(!opened) {
                    *s = '\0';
                    pfwords_add(ff, t);
                    (*line_words)++;
                    t = ++s;
                }
                else
                    s++;
            }
            else
                s++;
        }
        else{
            printf("(%s) %dInternal Error: procfile_readall() does not handle all the cases.", __FUNCTION__ , __LINE__);
            exit(0);
        }
    }

    if(likely(s > t && t < e)) {
        // the last word
        if(unlikely(ff->len >= ff->size)) {
            // we are going to loose the last byte
            s = &ff->data[ff->size - 1];
        }

        *s = '\0';
        pfwords_add(ff, t);
        (*line_words)++;
        // t = ++s;
    }
}
void procfile_close(procfile *ff) {
    if(unlikely(!ff)) return;

    printf( "(%s)%d:: Closing file '%s'", __FUNCTION__ , __LINE__, procfile_filename(ff));

    if(likely(ff->lines)) pflines_free(ff->lines);
    if(likely(ff->words)) pfwords_free(ff->words);

    if(likely(ff->fd != -1)) close(ff->fd);
    free(ff);
}
procfile *procfile_readall(procfile *ff) {
    ff->len = 0;    // zero the used size
    ssize_t r = 1;  // read at least once

    while(r > 0) {
        ssize_t s = ff->len;
        ssize_t x = ff->size - s;

        if(unlikely(!x)) {
            printf( "(%s)%d: Expanding data buffer for file '%s'.\n", __FUNCTION__ , __LINE__, procfile_filename(ff));
            ff = realloc(ff, sizeof(procfile) + ff->size + PROCFILE_INCREMENT_BUFFER);
            ff->size += PROCFILE_INCREMENT_BUFFER;
        }

        printf( "(%s)%d: Reading file '%s', from position %zd with length %zd\n", __FUNCTION__ , __LINE__,  procfile_filename(ff), s, (ssize_t)(ff->size - s));
        r = read(ff->fd, &ff->data[s], ff->size - s);
        if(unlikely(r == -1)) {
            if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO)))
                printf( "(%s)%d:  Cannot read from file '%s' on fd %d\n" , __FUNCTION__ , __LINE__,  procfile_filename(ff), ff->fd);
            procfile_close(ff);
            return NULL;
        }

        ff->len += r;
    }
    if(unlikely(lseek(ff->fd, 0, SEEK_SET) == -1)) {
        if(unlikely(!(ff->flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO)))
            printf( "(%s)%d:Cannot rewind on file '%s'.\n", __FUNCTION__ , __LINE__, procfile_filename(ff));
        procfile_close(ff);
        return NULL;
    }

    pflines_reset(ff->lines);
    pfwords_reset(ff->words);
    procfile_parser(ff);

    if(unlikely(procfile_adaptive_initial_allocation)) {
        if(unlikely(ff->len > procfile_max_allocation)) procfile_max_allocation = ff->len;
        if(unlikely(ff->lines->len > procfile_max_lines)) procfile_max_lines = ff->lines->len;
        if(unlikely(ff->words->len > procfile_max_words)) procfile_max_words = ff->words->len;
    }

    return ff;
}



procfile *procfile_open(const char *filename, const char *separators, uint32_t flags) {
    int fd = open(filename, procfile_open_flags, 0666);
    if(unlikely(fd == -1)) {
        if(unlikely(!(flags & PROCFILE_FLAG_NO_ERROR_ON_FILE_IO)))
            printf("(%s)%d: Cannot open file '%s'",  __FUNCTION__ , __LINE__, filename);
        return NULL;
    }


    size_t size = (unlikely(procfile_adaptive_initial_allocation)) ? procfile_max_allocation : PROCFILE_INCREMENT_BUFFER;
    procfile *ff = malloc(sizeof(procfile) + size);

    ff->filename[0] = '\0';

    ff->fd = fd;
    ff->size = size;
    ff->len = 0;
    ff->flags = flags;

    ff->lines = pflines_new();
    ff->words = pfwords_new();

    procfile_set_separators(ff, separators);

    return ff;
}

3、main.c

#include <stdio.h>
#include <string.h>
#include "procfile.h"

#define likely(x)  __builtin_expect((x), 1)
#define unlikely(x)  __builtin_expect((x), 0)


long get_system_cpus(void) {
    int processors = 1;
    processors = 1;
    char *filename = "/proc/stat";
    procfile *ff = procfile_open(filename, NULL, PROCFILE_FLAG_DEFAULT);
    if(!ff) {
        printf("Cannot open file '%s'. Assuming system has %d processors.\n", filename, processors);
        return processors;
    }

    ff = procfile_readall(ff);
    if(!ff) {
        printf("Cannot open file '%s'. Assuming system has %d processors.\n", filename, processors);
        return processors;
    }

    processors = 0;
    unsigned int i;
    for(i = 0; i < procfile_lines(ff); i++) {
        if(!procfile_linewords(ff, i)) continue;

        if(strncmp(procfile_lineword(ff, i, 0), "cpu", 3) == 0) processors++;
    }
    processors--;
    if(processors < 1) processors = 1;

    procfile_close(ff);

    printf("System has %d processors.", processors);
    return processors;
}



int main(int argc, char **argv)
{
    get_system_cpus();
}

在这里插入图片描述
分析:
在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值