GDB Linux Kernel Awareness

GDB Linux Kernel Awareness

1、GDB Kernel Awareness介绍

GDB Linux Kernel Awareness称为GDB内核感知,它属于GDB调试的扩展部分,它有什么作用呢?
我们平常使用GDB调试的时候,一般都是查看某些寄存器,打一些断点,看某些内存空间,一步一步调试驱动或者应用程序;
那么对于Linux这么庞大的系统,我们调试时,很多数据结构相互包含,我们调试的时候,GDB并不知道我们调试的是什么,不知道是在调试线程,还是在调试时钟等,这就是GDB Linux Kernel Awareness的作用。
举个例子:
比如我们在使用gdb的时候,想查看当前运行的所有进程,该怎么办?
带着问题往下看吧。

2、内核配置

为了构建能够调试的内核,我们需要配置以下几个参数

  • CONFIG_DEBUG_INFO:包含内核调试信息,这个选项在幕后为gcc使用的编译器参数增加了-g选项。
  • CONFIG_GDB_SCRIPTS:提供GDB Scripts用于调试内核
# 开启kernel debug info
 Kernel hacking  ---> 
     [*] Kernel debugging
     Compile-time checks and compiler options  --->
         [*] Compile the kernel with debug info
         [*]   Provide GDB scripts for kernel debuggin

打开相应配置后,编译内核,编译结束后会在内核根目录下生成vmlinux-gdb.py文件。

3、连接GDB

我使用的是Jlink,直接与开发板连接

在这里插入图片描述

# Ubuntu的一个终端用于连接开发板
 JLinkGDBServer -device Cortex-A7 -endian little -if SWD
 
 # 另一个终端用于调试
 arm-linux-gnueabihf-gdb vmlinux
 add-auto-load-safe-path ./      # 设置内核输出路径
 source ./vmlinux-gdb.py         # 一定要指定./ 当前目录,否则可能会报错
 target remote :2331

问题一
注意:某些发行版可能会将gdb脚本的自动加载限制在已知的安全目录中。 如果gdb报告拒绝加载vmlinux-gdb.py(相关命令找不到),请将:
add-auto-load-safe-path /path/to/linux-build
问题二
加载时报错:找不到linux.utils模块

(gdb) source vmlinux-gdb.py
 Traceback (most recent call last):
   File "vmlinux-gdb.py", line 25, in <module>
     import linux.utils
 ImportError: No module named linux.utils

需要加上当前路径:source ./vmlinux-gdb.py
这里使用的gdb script脚本,就是scripts/gdb/下面的脚本。

3、内核调试

如果前面都没有报错,我们就能够可以调试内核了,通过apropos lx查看目前所支持的调试命令。

apropos lx      # 查看支持的扩展命令
 function lx_current -- Return current task
 function lx_module -- Find module by name and return the module variable
 function lx_per_cpu -- Return per-cpu variable
 function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
 function lx_thread_info -- Calculate Linux thread_info from task variable
 function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid
 lx-cmdline --  Report the Linux Commandline used in the current kernel
 lx-cpus -- List CPU status arrays
 lx-dmesg -- Print Linux kernel log buffer
 lx-fdtdump -- Output Flattened Device Tree header and dump FDT blob to the filename
 lx-iomem -- Identify the IO memory resource locations defined by the kernel
 lx-ioports -- Identify the IO port resource locations defined by the kernel
 lx-list-check -- Verify a list consistency
 lx-lsmod -- List currently loaded modules
 lx-mounts -- Report the VFS mounts of the current process namespace
 lx-ps -- Dump Linux tasks
 lx-symbols -- (Re-)load symbols of Linux kernel and currently loaded modules
 lx-version --  Report the Linux Version of the current kernel

3.1 lx-cmdline

lx-cmdline:查看传入内核的参数

(gdb) lx-cmdline
 loglevel=8 console=ttyS1,115200 rootwait root=/dev/mmcblk0p1 rootfstype=ext4 boottype=spinor spiafficpu=2 mtdparts=spi0.0:192k(boot)ro,4416k(system)ro,4m(rootfs),20m(firmware),3456k(data),512k(rp),128k(pstore) uboot_msg=2020.10-rc1-g40c17dce6d-dirty(01/12/2024-11:45:56)

3.2 lx-cpus

lx-cpus:查看cpu状态

(gdb) lx-cpus
 Possible CPUs : [0, 1, 2]
 Present CPUs  : [0, 1, 2]
 Online CPUs   : [0, 1, 2]
 Active CPUs   : [0, 1, 2]

3.3 lx-dmesg

lx-dmesg:查看linux内核log

3.4 lx-fdtdump

lx-fdtdump:查看设备树,并dump出来

(gdb) lx-fdtdump
 fdt_magic:         0xD00DFEED
 fdt_totalsize:     0xC000
 off_dt_struct:     0x38
 off_dt_strings:    0xA764
 off_mem_rsvmap:    0x28
 version:           17
 last_comp_version: 16
 Dumped fdt blob to fdtdump.dtb

3.5 lx-iomem

lx-iomem:IO内存资源分配

(gdb) lx-iomem
10000000-17ffffff : System RAM
  10008000-109fffff : Kernel code
  10b00000-10baefc7 : Kernel data
40002000-4000201f : serial
40006000-4000601f : serial

3.6 lx-lsmod

lx-lsmod:查看加载模块

3.7 lx-mounts

lx-mounts:查看所有mount的文件系统

(gdb) lx-mounts
rootfs / rootfs rw 0 0
/dev/root / ext4 rw,relatime 0 0
devtmpfs /dev devtmpfs rw,relatime 0 0
proc /proc proc rw,nodiratime,relatime 0 0
devpts /dev/pts devpts rw,relatime 0 0
sysfs /sys sysfs rw,relatime 0 0

3.8 lx-ps

lx-ps:查看各个进程

(gdb) lx-ps
0xc0b07fc0 <init_task> 0 swapper/0
0xc78e0000 1 init
0xc78e0600 2 kthreadd
0xc78e0c00 3 rcu_gp

3.9 lx-symbols

lx-symbols:当前加载的模块

3.10 lx-version

lx-version:当前版本

除了直接调用命令外,一些提供的function函数也能直接使用。
支持的函数如下:

function lx_current -- Return current task
function lx_module -- Find module by name and return the module variable
function lx_per_cpu -- Return per-cpu variable
function lx_task_by_pid -- Find Linux task by PID and return the task_struct variable
function lx_thread_info -- Calculate Linux thread_info from task variable
function lx_thread_info_by_pid -- Calculate Linux thread_info from task variable found by pid

3.11 lx_task_by_pid

lx_task_by_pid:根据PID查找每个task的状态,其结构体类型为struct task_struct
我们通过lx-ps可以查看到每个进程的pid,然后直接调用该函数,能够获取指定进程的各个字段信息。
调用指令:p $lx_task_by_pid(pid编号)

(gdb) lx-ps
0xc0b07fc0 <init_task> 0 swapper/0
0xc78e0000 1 init
0xc78e0600 2 kthreadd
0xc78e0c00 3 rcu_gp
0xc78e1200 4 rcu_par_gp
0xc78e1800 5 kworker/0:0
0xc78e2a00 8 mm_percpu_wq
0xc78e3000 9 ksoftirqd/0
0xc78e3600 10 rcu_preempt
0xc78e3c00 11 rcu_sched
0xc78e4200 12 rcu_bh
0xc78e4800 13 migration/0
......

0xc7a8d400 105 syslogd
0xc7a8e000 109 klogd
.......

(gdb) p $lx_task_by_pid(109)
$6 = {state = 1, stack = 0xc6a48000, usage = {counter = 2}, flags = 4194560, ptrace = 0, wake_entry = {next = 0x0}, on_cpu = 0, wakee_flips = 144, wakee_flip_decay_ts = 0,
  last_wakee = 0xc7a8d400, recent_used_cpu = 0, wake_cpu = 0, on_rq = 0, prio = 120, static_prio = 120, normal_prio = 120, rt_priority = 0, sched_class = 0xc070225c <fair_sched_class>,
  se = {load = {weight = 1024, inv_weight = 4194304}, runnable_weight = 1024, run_node = {__rb_parent_color = 1, rb_right = 0x0, rb_left = 0x0}, group_node = {next = 0xc7a8e098,
      prev = 0xc7a8e098}, on_rq = 0, exec_start = 5726624435, sum_exec_runtime = 19151794, vruntime = 668774412, prev_sum_exec_runtime = 18865627, nr_migrations = 6,
    statistics = {<No data fields>}, avg = {last_update_time = 5726623744, load_sum = 327, runnable_load_sum = 327, util_sum = 285696, period_contrib = 342, load_avg = 0,
      runnable_load_avg = 0, util_avg = 0, util_est = {enqueued = 1, ewma = 13}}}, rt = {run_list = {next = 0xc7a8e140, prev = 0xc7a8e140}, timeout = 0, watchdog_stamp = 0,
    time_slice = 25, on_rq = 0, on_list = 0, back = 0x0}, dl = {rb_node = {__rb_parent_color = 3349733728, rb_right = 0x0, rb_left = 0x0}, dl_runtime = 0, dl_deadline = 0, dl_period = 0,
    dl_bw = 0, dl_density = 0, runtime = 0, deadline = 0, flags = 0, dl_throttled = 0, dl_boosted = 0, dl_yielded = 0, dl_non_contending = 0, dl_overrun = 0, dl_timer = {node = {node = {
          __rb_parent_color = 3349733808, rb_right = 0x0, rb_left = 0x0}, expires = 0}, _softexpires = 0, function = 0xc0156688 <dl_task_timer>, base = 0xc7ed0800, state = 0 '\000',
      is_rel = 0 '\000', is_soft = 0 '\000'}, inactive_timer = {node = {node = {__rb_parent_color = 3349733856, rb_right = 0x0, rb_left = 0x0}, expires = 0}, _softexpires = 0,
      function = 0xc0157e88 <inactive_task_timer>, base = 0xc7ed0800, state = 0 '\000', is_rel = 0 '\000', is_soft = 0 '\000'}}, policy = 0, nr_cpus_allowed = 3, cpus_allowed = {bits = {
      7}}, rcu_read_lock_nesting = 0, rcu_read_unlock_special = {b = {blocked = 0 '\000', need_qs = 0 '\000', exp_need_qs = 0 '\000', pad = 0 '\000'}, s = 0}, rcu_node_entry = {
    next = 0xc7a8e224, prev = 0xc7a8e224}, rcu_blocked_node = 0x0, rcu_tasks_nvcsw = 0, rcu_tasks_holdout = 0 '\000', rcu_tasks_idx = 0 '\000', rcu_tasks_idle_cpu = -1,
  rcu_tasks_holdout_list = {next = 0xc7a8e23c, prev = 0xc7a8e23c}, sched_info = {<No data fields>}, tasks = {next = 0xc6802044, prev = 0xc7a8d644}, pushable_tasks = {prio = 140,
    prio_list = {next = 0xc7a8e250, prev = 0xc7a8e250}, node_list = {next = 0xc7a8e258, prev = 0xc7a8e258}}, pushable_dl_tasks = {__rb_parent_color = 3349733984, rb_right = 0x0,
    rb_left = 0x0}, mm = 0xc6a24600, active_mm = 0xc6a24600, vmacache = {seqnum = 6, vmas = {0x0, 0x0, 0xc699bea0, 0xc699b9c0}}, exit_state = 0, exit_code = 0, exit_signal = 17,
  pdeath_signal = 0, jobctl = 0, personality = 8388608, sched_reset_on_fork = 0, sched_contributes_to_load = 0, sched_migrated = 0, sched_remote_wakeup = 0, in_execve = 0, in_iowait = 0,
  brk_randomized = 0, atomic_flags = 0, restart_block = {fn = 0xc012f254 <do_no_restart_syscall>, {futex = {uaddr = 0x0, val = 0, flags = 0, bitset = 0, time = 0, uaddr2 = 0x0},
      nanosleep = {clockid = 0, type = TT_NONE, {rmtp = 0x0, compat_rmtp = 0x0}, expires = 0}, poll = {ufds = 0x0, nfds = 0, has_timeout = 0, tv_sec = 0, tv_nsec = 0}}}, pid = 109,
  tgid = 109, stack_canary = 3015467484, real_parent = 0xc78e0000, parent = 0xc78e0000, children = {next = 0xc7a8e2f4, prev = 0xc7a8e2f4}, sibling = {next = 0xc795e8fc,
    prev = 0xc7a8d6fc}, group_leader = 0xc7a8e000, ptraced = {next = 0xc7a8e308, prev = 0xc7a8e308}, ptrace_entry = {next = 0xc7a8e310, prev = 0xc7a8e310}, thread_pid = 0xc7a91680,
  pid_links = {{next = 0x0, pprev = 0xc7a91688}, {next = 0x0, pprev = 0xc7a9168c}, {next = 0x0, pprev = 0xc7960390}, {next = 0x0, pprev = 0xc7960394}}, thread_group = {next = 0xc7a8e33c,
    prev = 0xc7a8e33c}, thread_node = {next = 0xc7bf4f0c, prev = 0xc7bf4f0c}, vfork_done = 0x0, set_child_tid = 0x0, clear_child_tid = 0x0, utime = 12000000, stime = 4000000, gtime = 0,
  prev_cputime = {utime = 0, stime = 0, lock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}, nvcsw = 13, nivcsw = 4, start_time = 2611642126,
  real_start_time = 2611643042, min_flt = 47, maj_flt = 0, cputime_expires = {utime = 0, stime = 0, sum_exec_runtime = 0}, cpu_timers = {{next = 0xc7a8e3c0, prev = 0xc7a8e3c0}, {
      next = 0xc7a8e3c8, prev = 0xc7a8e3c8}, {next = 0xc7a8e3d0, prev = 0xc7a8e3d0}}, ptracer_cred = 0x0, real_cred = 0xc7953900, cred = 0xc7953900, comm = "klogd\000stop-daem",
  nameidata = 0x0, sysvsem = {undo_list = 0x0}, sysvshm = {shm_clist = {next = 0xc7a8e3fc, prev = 0xc7a8e3fc}}, last_switch_count = 0, last_switch_time = 0, fs = 0xc7a91640,
  files = 0xc69a3700, nsproxy = 0xc0b10e38 <init_nsproxy>, signal = 0xc7bf4f00, sighand = 0xc6a3ca80, blocked = {sig = {0, 0}}, real_blocked = {sig = {0, 0}}, saved_sigmask = {sig = {0,
      0}}, pending = {list = {next = 0xc7a8e438, prev = 0xc7a8e438}, signal = {sig = {0, 0}}}, sas_ss_sp = 0, sas_ss_size = 0, sas_ss_flags = 2, task_works = 0x0, audit_context = 0x0,
  seccomp = {<No data fields>}, parent_exec_id = 4, self_exec_id = 5, alloc_lock = {{rlock = {raw_lock = {{slock = 917518, tickets = {owner = 14, next = 14}}}}}}, pi_lock = {raw_lock = {{
        slock = 3407924, tickets = {owner = 52, next = 52}}}}, wake_q = {next = 0x0}, pi_waiters = {rb_root = {rb_node = 0x0}, rb_leftmost = 0x0}, pi_top_task = 0x0, pi_blocked_on = 0x0,
  journal_info = 0x0, bio_list = 0x0, plug = 0x0, reclaim_state = 0x0, backing_dev_info = 0x0, io_context = 0x0, ptrace_message = 0, last_siginfo = 0x0, ioac = {<No data fields>},
  robust_list = 0x0, pi_state_list = {next = 0xc7a8e4b0, prev = 0xc7a8e4b0}, pi_state_cache = 0x0, perf_event_ctxp = {0x0, 0x0}, perf_event_mutex = {owner = {counter = 0}, wait_lock = {{
        rlock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}}, osq = {tail = {counter = 0}}, wait_list = {next = 0xc7a8e4d0, prev = 0xc7a8e4d0}}, perf_event_list = {
    next = 0xc7a8e4d8, prev = 0xc7a8e4d8}, rseq = 0x0, rseq_len = 0, rseq_sig = 0, rseq_event_mask = 5, tlb_ubc = {<No data fields>}, rcu = {next = 0x0, func = 0x0}, splice_pipe = 0x0,
  task_frag = {page = 0x0, offset = 0, size = 0}, nr_dirtied = 0, nr_dirtied_pause = 32, dirty_paused_when = 0, timer_slack_ns = 50000, default_timer_slack_ns = 50000,
  curr_ret_stack = -1, curr_ret_depth = -1, ret_stack = 0x0, ftrace_timestamp = 0, trace_overrun = {counter = 0}, tracing_graph_pause = {counter = 0}, trace = 0, trace_recursion = 0,
  utask = 0x0, pagefault_disabled = 0, oom_reaper_list = 0x0, thread = {address = 0, trap_no = 0, error_code = 0, debug = {hbp = {0x0 <repeats 32 times>}}}}

要想获取单独字段,也支持p $lx_task_by_pid(PID).segment,如下:

(gdb) p $lx_task_by_pid(109).state
$15 = 1

3.12 lx_thread_info

lx_thread_info:根据线程symbol,来获取对应的线程信息

(gdb) p $lx_thread_info(init_task)
$1 = {flags = 3232760088, preempt_count = -962472448, addr_limit = 3232759868,
  task = 0xc0b00018 <init_thread_info+24>, cpu = 3222359180, cpu_domain = 3222359880, cpu_context = {r4 = 3354095296,
    r5 = 3232792512, r6 = 3221241856, r7 = 2147483653, r8 = 0, r9 = 3232760088, sl = 3232759900, fp = 3232759872,
    sp = 3222359388, pc = 3222359056, extra = {5, 3232779272}}, syscall = 3780247554,
  used_cp = "\030\001\260\300\024\001\260\300`\000\260\300\264N\021\300", tp_value = {3222359224, 1879031663},
  fpstate = {hard = {save = {0 <repeats 33 times>, 3230004071, 0}}, soft = {save = {0 <repeats 33 times>, 3230004071,
        0}}}, vfpstate = {hard = {fpregs = {18446744072099725747, 18446744072647344460, 13884598857006842236,
        13839929358871960484, 18446744072647344512, 16236039623716376576, 3232760192, 2305839092203543999, 4294967295,
        13884599283270811653, 13884599200604225920, 16236039618442095632, 18446744072099725747, 13763000461244235857,
        3232779272, 13884599303683441060, 13864590854932982872, 13872761850891862016, 16257997536730546176,
        13873206006344843264, 13884599475482132916, 13864591164170628308, 13884599544201609724, 13864591713921042068,
        0, 0, 0, 0, 13884600282932981419, 13884770600926314496, 24707596288, 13884599853439255060},
      fpexc = 3222702248, fpscr = 3222701308, fpinst = 3229757099, fpinst2 = 3232760420, cpu = 3232760380}}}

3.13 lx_thread_info_by_pid

lx_thread_info_by_pid:根据指定的PID,查找每个线程的状态,其结构体为struct thread_info

(gdb) p $lx_thread_info_by_pid(109)
$12 = {flags = 0, preempt_count = 2, addr_limit = 3204448256, task = 0xc7a8e000, cpu = 0, cpu_domain = 0, cpu_context = {r4 = 3354095296, r5 = 3349733376, r6 = 3349730304,
    r7 = 3332523520, r8 = 3332523008, r9 = 3228196620, sl = 3228574208, fp = 3332677396, sp = 3332677312, pc = 3228196228, extra = {0, 0}}, syscall = 0,
  used_cp = "\000\000\000\000\000\000\000\000\000\000\001\001\000\000\000", tp_value = {3069935712, 0}, fpstate = {hard = {save = {0 <repeats 35 times>}}, soft = {save = {
        0 <repeats 35 times>}}}, vfpstate = {hard = {fpregs = {7021710432085763445, 7598814389921932660, 3473436823868759924, 8443739334891561264, 7598245610908836467,
        8318823019731641718, 2320822906498477929, 4991486266103057747, 0 <repeats 24 times>}, fpexc = 1073741824, fpscr = 0, fpinst = 3233240588, fpinst2 = 3332677208, cpu = 0}}}

3.14 current_task

current_task:该命令仅arm64和x86架构支持,使用方法如下:

(gdb) p $lx_current().pid
    $1 = 4998
    (gdb) p $lx_current().comm
    $2 = "modprobe\000\000\000\000\000\000\000"

3.15 lx_module

lx_module:查找指定模块,并返回struct module类型变量。
使用方法: p $lx_module(“module_name”)
例子如下,通过lx-lsmod获取所有模块名称,然后调用lx_module来获取该模块信息。

(gdb) lx-lsmod
Address    Module                  Size  Used by
0xbf026000 arobot_udc             28672  0
0xbf011000 udc_core               53248  1 arobot_udc
0xbf00a000 usb_common             16384  1 udc_core
0xbf000000 gpio_keys              20480  0

(gdb) p $lx_module("gpio_keys")
$19 = {state = MODULE_STATE_LIVE, list = {next = 0xc0b19144 <modules>, prev = 0xbf00c004}, name = "gpio_keys", '\000' <repeats 50 times>, mkobj = {kobj = {name = 0xc6a9d5c0 "gpio_keys",
      entry = {next = 0xbf00c04c, prev = 0xc7a3e104}, parent = 0xc7a3710c, kset = 0xc7a37100, ktype = 0xc0b10d30 <module_ktype>, sd = 0xc6918180, kref = {refcount = {refs = {
            counter = 4}}}, state_initialized = 1, state_in_sysfs = 1, state_add_uevent_sent = 1, state_remove_uevent_sent = 0, uevent_suppress = 0}, mod = 0xbf0030c0,
    drivers_dir = 0xc698bfc0, mp = 0x0, kobj_completion = 0x0}, modinfo_attrs = 0xc6b82c00, version = 0x0, srcversion = 0x0, holders_dir = 0xc6a9d640, syms = 0x0, crcs = 0x0,
  num_syms = 0, param_lock = {owner = {counter = 0}, wait_lock = {{rlock = {raw_lock = {{slock = 0, tickets = {owner = 0, next = 0}}}}}}, osq = {tail = {counter = 0}}, wait_list = {
      next = 0xbf003164, prev = 0xbf003164}}, kp = 0x0, num_kp = 0, num_gpl_syms = 0, gpl_syms = 0x0, gpl_crcs = 0x0, async_probe_requested = false, gpl_future_syms = 0x0,
  gpl_future_crcs = 0x0, num_gpl_future_syms = 0, num_exentries = 0, extable = 0x0, init = 0xbf006000, core_layout = {base = 0xbf000000 <gpio_keys_close>, size = 20480, text_size = 8192,
    ro_size = 12288, ro_after_init_size = 12288, mtn = {mod = 0xbf0030c0, node = {node = {{__rb_parent_color = 3204497689, rb_right = 0x0, rb_left = 0x0}, {
            __rb_parent_color = 3204497701, rb_right = 0x0, rb_left = 0x0}}}}}, init_layout = {base = 0x0, size = 0, text_size = 0, ro_size = 0, ro_after_init_size = 0, mtn = {
      mod = 0xbf0030c0, node = {node = {{__rb_parent_color = 1, rb_right = 0xbf00c118, rb_left = 0xbf0031d8}, {__rb_parent_color = 1, rb_right = 0xbf00c124, rb_left = 0xbf0031e4}}}}},
  arch = {unwind = {0xc6a9d480, 0x0, 0xc6a9d4c0, 0xc6a9d500, 0x0, 0x0, 0x0}}, taints = 0, kallsyms = 0xbf003244, core_kallsyms = {symtab = 0xbf004000, num_symtab = 78,
    strtab = 0xbf0044e0 ""}, sect_attrs = 0xc6a11400, notes_attrs = 0xc6a9df80, args = 0xc6a9d540 "", percpu = 0x0, percpu_size = 0, num_tracepoints = 0, tracepoints_ptrs = 0x0,
  jump_entries = 0x0, num_jump_entries = 0, num_trace_bprintk_fmt = 0, trace_bprintk_fmt_start = 0x0, trace_events = 0x0, num_trace_events = 0, trace_evals = 0x0, num_trace_evals = 0,
  num_ftrace_callsites = 22, ftrace_callsites = 0xbf0027d4, source_list = {next = 0xbf003294, prev = 0xbf003294}, target_list = {next = 0xbf00329c, prev = 0xbf00329c}, exit = 0xbf0012f4,
  refcnt = {counter = 1}}

目前可供GDB调试的命令还比较少,我们也可以根据规则自己编写我们需要的调试指令。

  • 29
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值