Linux服务器进程管理与监控(top|kill|ps|nohup)

本文详细介绍了Linux服务器上的进程管理与监控,包括进程的前台与后台执行、如何将命令放入后台、后台命令管理,以及如何终止进程,如kill、killall、pkill命令的使用。此外,还重点讲解了top命令监控服务器状态的功能和交互式操作。

【1】基本介绍

① 前台与后台

在LINUX中,每个执行的程序(代码)都称为一个进程。每一个进程都分配一个ID号。每一个进程,都会对应一个父进程,而这个父进程可以复制多个子进程。例如www服务器。

每个进程都可能以两种方式存在的:前台与后台。所谓前台进程就是用户目前的屏幕上可以进行操作的。后台进程则是实际在操作,但由于屏幕上无法看到的进程,通常使用后台方式执行。

一般系统的服务都是以后台进程的方式存在,而且都会常驻在系统中。直到关机才才结束。

其实服务本质就是后台进程。

服务(service) 本质就是进程,但是是运行在后台的,通常都会监听某个端口,等待其它程序的请求,比如(mysql , sshd 防火墙等),因此我们又称为守护进程,是Linux中非常重要的知识点

如下所示是Windows任务管理器下进程任务查看图:
在这里插入图片描述

② 后台管理

前台是指当前可以操控和执行命令的这个操作环境,后台是指工作可以自行运行,但是不能直接用ctrl+c来终止它,只能使用fg/bg来调用工作。

当前的登录终端,只能管理当前终端的工作,而不能管理其他登录终端的工作。如tty1登录的终端是不能管理tty2终端中的工作的。

放入后台的命令必须可以持续运行一段时间,这样我们才能捕捉和操作这个工作。如果把ls命令放入后台执行,它很快就会执行完成,我们很难操作它。

放入后台执行的命令不能和前台用户有交互或需要前台输入,否则放入后台只能暂停,而不能执行。比如vi命令放入后台只能暂停,而不能执行,因为vi需要前台输入信息。top命令也不能放入后台执行,而只能放入后台暂停,因为top命令需要和前台有交互。


③ 命令放入后台

① “命令 &”,把命令放入后台执行

第一种把命令放入后台的方法是在命令后面加入“空格&”,这种方法放入后台的命令,在后台是执行状态。但是注意,放入后台执行的命令不能与前台有交互,否则这个命令是不能在后台执行的。

实例如下:

[root@localhost ~]# find / -name install.log &
  [1]      30365
# [工作号] 进程号

find命令放入后台执行,每个后台命令会分配一个工作号,命令既然可以执行,就会有进程产生,所以也会有进程号。

#使用jobs -l可以查看后台进程
[root@VM_0_12_centos ~]# jobs -l
[1]+ 30365 Done                    find / -name install.log

证明后台这个任务已经完成了,当然命令如果有执行结果的话,也会显示到操作终端上。[1]是这个命令的工作号,+号代表这个任务是最近一个被放入后台的工作。


② 命令执行过程中按ctrl+z快捷键

第二种方法是在命令执行过程中按ctrl+z快捷键,命令在后台是暂停状态。使用这种方法放入后台的命令,就算是不和前台有交互,能在后台执行的命令,也是暂停状态,ctrl+z快捷键就是暂停的快捷键。

实例如下:

[root@VM_0_12_centos ~]# top
执行ctrl z
[1]+  Stopped                 top
[root@VM_0_12_centos ~]# jobs -l
[1]+  2325 Stopped (signal)        top
[root@VM_0_12_centos ~]#

#提示命令被放入后台,工作号是1,状态是暂停。而且虽然top命令没有结束,也能取得
#控制台权限

④ 后台命令管理

查看后台的工作

[root@VM_0_12_centos ~]# jobs -l
[1]+  2325 Stopped (signal)        top

将后台暂停的工作恢复到前台执行

[root@localhost ~]# fg %工作号
参数:
%工作号: %号可以省略,但是注意工作号和PID的区别

把后台暂停的工作恢复到后台执行

[root@localhost ~]# bg %工作号

后台命令脱离登录终端运行

我们已经知道把命令放入后台,只能在当前登录终端执行。那如果我是远程管理的服务器,在远程终端中执行了后台命令,这时我退出登录,这个后台命令还能继续执行吗?当然是不行的,这个后台命令会被终止。但是我们确实需要在远程终端中执行某些后台命令,该如何执行呢?

  • 第一种方法是把需要后台执行的命令加入/etc/rc.local文件,让系统在启动时执行这个后台程序。这种方法的问题是,服务器时不能随便重启的,万一有临时后台任务,就不能执行。
  • 第二种方法是使用系统定时任务,让系统在指定的时间执行某个后台命令。这样放入后台的命令与终端无关,是不依赖登录终端的。
  • 最后一种方法是使用nohup命令。

nohup命令的作用就是让后台工作在离开操作终端时,也能够正确的在后台执行。命令的格式如下:

[root@localhost ~]# nohup [命令] &

【2】终止进程

系统中可以识别的信号较多,我们可以使用命令“kill -l”或“man 7 signal”来查询,命令如下:

[root@VM_0_12_centos ~]# kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL       5) SIGTRAP
 6) SIGABRT      7) SIGBUS       8) SIGFPE       9) SIGKILL     10) SIGUSR1
11) SIGSEGV     12) SIGUSR2     13) SIGPIPE     14) SIGALRM     15) SIGTERM
16) SIGSTKFLT   17) SIGCHLD     18) SIGCONT     19) SIGSTOP     20) SIGTSTP
21) SIGTTIN     22) SIGTTOU     23) SIGURG      24) SIGXCPU     25) SIGXFSZ
26) SIGVTALRM   27) SIGPROF     28) SIGWINCH    29) SIGIO       30) SIGPWR
31) SIGSYS      34) SIGRTMIN    35) SIGRTMIN+1  36) SIGRTMIN+2  37) SIGRTMIN+3
38) SIGRTMIN+4  39) SIGRTMIN+5  40) SIGRTMIN+6  41) SIGRTMIN+7  42) SIGRTMIN+8
43) SIGRTMIN+9  44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13
48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12
53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9  56) SIGRTMAX-8  57) SIGRTMAX-7
58) SIGRTMAX-6  59) SIGRTMAX-5  60) SIGRTMAX-4  61) SIGRTMAX-3  62) SIGRTMAX-2

这里我们介绍一下主要的信号,如表所示:

信号代号信号名称说明
1SIGHUP该信号让进程立即关闭,然后重新读取配置文件之后重启。
2SIGINT程序终止信号,用于终止前台进程。相当于输出ctrl+c快捷键
8SIGFPE在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
9SIGKILL用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。一般用于强制终止进程。
14SIGALRM时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号
15SIGTERM正常结束进程的信号,kill命令的默认信号。有时如果进程已经发生问题,这个信号是无法正常终止进程的,我们才会尝试SIGKILL信号,也就是信号9。
18SIGCONT该信号可以让暂停的进程恢复执行,本信号不能被阻断。
19SIGSTOP该信号可以暂停前台进程,相当于输入ctrl+z快捷键。本信号不能被阻断。

① kill

kill命令用来删除执行中的程序或工作。kill可将指定的信息送至程序。预设的信息为SIGTERM(15),可将指定程序终止。若仍无法终止该程序,可使用SIGKILL(9)信息尝试强制删除程序。程序或工作的编号可利用ps指令或job指令查看。

语法

kill(选项)(参数)

选项

-a:当处理当前进程时,不限制命令名和进程号的对应关系;
-l <信息编号>:若不加<信息编号>选项,则-l参数会列出全部的信息名称;
-p:指定kill 命令只打印相关进程的进程号,而不发送任何信号;
-s <信息名称或编号>:指定要送出的信息;
-u:指定用户。

参数

进程或作业识别号:指定要删除的进程或作业。

实例:使用“-1”信号,让进程重启

[root@localhost ~]# kill -1 2246
#使用“-1(数字一)”信号,让httpd的主进程重启动。

实例:使用“-19”信号,让进程暂停。

[root@localhost ~]# vi test.sh
#使用vi命令编辑一个文件,不要退出

② killall

killall命令使用进程的名称来杀死进程,使用此指令可以杀死一组同名进程。

我们可以使用kill命令杀死指定进程PID的进程,如果要找到我们需要杀死的进程,我们还需要在之前使用ps等命令再配合grep来查找进程,而killall把这两个过程合二为一,是一个很好用的命令。

语法

killall [选项][信号] 进程名

选项

-e:对长名称进行精确匹配;
-I: 忽略进程名的大小写
-p:杀死进程所属的进程组;
-i:交互式杀死进程,杀死进程前需要进行确认;
-l:打印所有已知信号列表;
-q:如果没有进程被杀死。则不输出任何信息;
-r:使用正规表达式匹配要杀死的进程名称;
-s:用指定的进程号代替默认信号“SIGTERM”;
-u:杀死指定用户的进程。

参数

进程名称:指定要杀死的进程名称。

实例

杀死所有同名进程

killall vi

[root@localhost ~]# killall -i sshd
#交互式杀死sshd进程
杀死 sshd(1733) ? (y/N) n
#这个进程是sshd的服务进程,如果杀死,所有的sshd连接都不能登录。
杀死 sshd(1735) ? (y/N) n
#这是我当前登录终端,不能杀死我自己吧!
杀死 sshd(1758) ? (y/N) y
#把另外一个sshd登录终端踢出。

③ pkill命令

pkill命令和killall命令非常类似,也是按照进程名来杀死进程。格式如下:

pkill [选项] [信号] 进程名

选项:
-t 终端号: 按照终端号踢出用户

[root@localhost ~]# pkill -9 -t pts/1
#强制杀死从pts/1虚拟终端登录的进程

【3】top命令监控服务器状态

top命令可以实时动态地查看系统的整体运行情况,是一个综合了多方信息监测系统性能和运行信息的实用工具。通过top命令所提供的互动式界面,用热键可以管理。

语法

top(选项)

选项

  • -b:以批处理模式操作;一般和-n选项合用,用于 把top命令重定向到文件中
  • -c:显示完整的治命令;
  • -n<次数>:循环显示的次数。通常和-b配合使用
  • -d:屏幕刷新间隔时间,也就是top命令每隔几秒刷新,默认是3秒
  • -I:忽略失效过程;
  • -s:保密模式,安全模式;避免在交互模式中出错
  • -S:累积模式;
  • -i<时间>:设置间隔时间;
  • -u<用户名>:指定用户名,只监听某个用户的进程
  • -p<进程号>:指定进程;只查看某个pid的进程

top交互命令

在top命令执行过程中可以使用的一些交互命令。这些命令都是单字母的,如果在命令行中使用了-s选项, 其中一些命令可能会被屏蔽。

  • ?或h:显示帮助画面,给出一些简短的命令总结说明;
  • k:按照pid号,给予某个进程一个信号。一般用于终止某个进程,信号9是强制终止的信号
  • i:忽略闲置和僵死进程,这是一个开关式命令;
  • q:退出程序;
  • r:按照pid号,给某个进程重设优先级(Nice)值
  • S:切换到累计模式;
  • s:改变两次刷新之间的延迟时间(单位为s),如果有小数,就换算成ms。输入0值则系统将不断刷新,默认值是3s;
  • f或者F:从当前显示中添加或者删除项目;
  • o或者O:改变显示项目的顺序;
  • l:切换显示平均负载和启动时间信息;
  • m:切换显示内存信息;
  • t:切换显示进程和CPU状态信息;
  • c:切换显示命令名称和完整命令行;
  • M:根据驻留内存大小进行排序;
  • P:根据CPU使用百分比大小进行排序,默认就是此项
  • N:根据PID大小排序;
  • T:按照CPU的累积运算时间进行排序,也就是TIME+项排序
  • w:将当前设置写入~/.toprc文件中。

实例一

top - 09:44:56 up 16 days, 21:23,  1 user,  load average: 9.59, 4.75, 1.92
Tasks: 145 total,   2 running, 143 sleeping,   0 stopped,   0 zombie
Cpu(s): 99.8%us,  0.1%sy,  0.0%ni,  0.2%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:   4147888k total,  2493092k used,  1654796k free,   158188k buffers
Swap:  5144568k total,       56k used,  5144512k free,  2013180k cached

实例说明

  • 09:44:56,[当前系统时间],
  • 16 days,[系统已经运行了16天],
  • 1 user,[1个用户当前登录],
  • load average: 9.59, 4.75, 1.92,系统在之前1分钟、5分钟、15分钟的平均负载
  • Tasks: 145 total,系统中的总进程数
    • 2 running:[正在运行的进程数],
    • 143 sleeping:[睡眠的进程数],
    • 0 stopped:[停止的进程数],
    • 0 zombie:僵尸进程,如果不是0则需要手工检查僵尸进程
  • Cpu(s): 99.8%us,用户模式(用户空间)占用的CPU百分比
    • 0.1%sy:系统模式(内核空间)占用CPU百分比
    • 0.0%ni:用户进程空间内改变过优先级的进程占用CPU百分比
    • 0.2%id:空闲CPU百分比
    • 0.0%wa:等待输入输出的进程的占用CPU百分比
    • 0.0%hi:硬中断请求服务占用的CPU百分比
    • 0.0%si:软中断请求服务占用的CPU百分比
    • 0.0%st:st(Steal time)虚拟时间百分比。就是当有虚拟机时,虚拟CPU等待实际CPU的时间百分比
  • Mem: 4147888k total:物理内存总量,单位为KB total= used+free
    • 2493092k used:使用的物理内存总量,
    • 1654796k free:空闲的物理内存总量
    • 158188k buffers:用作内核缓存的内存量,也就是作为缓冲的内存总量
  • Swap: 5144568k total:交换分区(虚拟内存)总大小
    • 56k used:已经使用的交换分区的大小
    • 5144512k free:空闲交换分区大小
    • 2013180k cached:作为缓存的交换分区大小

实例二

如下是top命令第二部分输出,主要是系统进程信息和PS命令输出类似。但是这里不能看到所有的进程信息,只能看到占比靠前的进程信息。

  PID USER      PR  NI  VIRT  RES  SHR S %CPU %MEM    TIME+  COMMAND
24773 root      20   0 1157m  37m 6468 T  0.8  3.7 405:40.56 YDService
25574 root      20   0  591m 6808 1468 S  0.4  0.7  77:31.67 barad_agent
 4362 root      20   0 2296m 167m 5768 S  0.2 16.9  12:15.87 java
22413 root      20   0 2296m 106m 1948 S  0.2 10.6 113:22.02 java
24789 root      20   0  636m  21m 9.8m S  0.2  2.2 100:29.52 YDEdr
    1 root      20   0 19368  320  140 S  0.0  0.0 146:43.78 init
  • PID:进程ID
  • USER:进程所属用户
  • PR:优先级,数值越小优先级越高
  • NI:优先级,数值越小优先级越高
  • VIRT:该进程使用的虚拟内存大小,单位KB
  • RES:该进程使用的物理内存大小,单位KB
  • SHR:共享内存大小,单位KB
  • S:进程状态
    • D:不可被唤醒的睡眠状态,通常用于I/O情况
    • R:该进程正在运行
    • S:该进程在睡眠状态,可被唤醒
    • T:停止状态,可能是在后台暂停或进程在除错状态
    • W:内存交互状态(从2.6内核开始无效)
    • X:死掉的进程(应该不会出现)
    • Z:僵尸进程。进程已经终止,但是部分程序还在内存当中。
    • <:高优先级(以下状态在BSD格式当中出现)
    • N:低优先级
    • L:被锁入内存
    • s:包含子进程
    • l:多线程(小写L)
    • +:位于后台
  • %CPU:该进程占用CPU的百分比
  • %MEM:该进程占用内存的百分比
  • TIME+:该进程总共占用的CPU的时间
  • COMMAND:进程的命令名

使用top命令查看某个进程:

# 14247是某个java进程的pid
top -p 14247

如果在操作终端执行top命令,并不能看到系统中所有的进程,默认看到的只是CPU占比靠前的进程。如果我们想要看到所有的进程可以把top命令的结果重定向到文件当中即可。不过top命令是持续运行的,这时就需要“-b”和“-n”选项了,具体命令如下:

[root@localhost ~]# top -b -n 1 > /root/top.log
#让top命令只执行一次,让后把结果保存到top.log文件中。这样就能看到所有的进程了

参考博文:Linux - 管道(|)和grep 命令

linux下查看进程参考博文ps 命令查看系统进程

主要功能: 1.读取配置文件程序 2.启动进程 3.监控进程,查看进程是否退出或者崩溃 4.若进程退出或者崩溃,重启程序。 5.支持sleep功能 6.进程若连续崩溃NUM_MAX次就进行相应的睡眠周期struct proc_struct proc: struct proc_ struct [mp: if(array) return 0 ∥切换到目录rse chdirldiri ifdp= opendir(dir}=NuLL}开日录/proc,矢败返回0,成功把描述指针返回给d return o 〃将φpro文件夹的描述符指针传递给re&epsilon;ddir,读取文件夹内容,循环赋值给结构体di while ((dirp= readdir(dp))= NULLY char data 301 ∥取文件名称赋值给数组daa(其中包含有进程的名称(pid sprintf(data, "s", dirp->d_name); ∥是否是由字符09组成的字符串,即得到所有进程的pid f((IsDigit(data)) prac =(struct proc_struct )4 malloc(sizeof(struct proc_struct) tmp proc: prac->pid =a: oi(dirp->d_name): It(proc tind( proc. array)) free( tmp); closedir(dp cturn proc_find 两个参数分别是两个进程描述的结构体指针 李比较两个进程pd是否相等 李*相等返回1,不相等返回0 幸率球事容球家草事家事球峰率享事球摩率球享享溶事*事卷寒球套事塞容寒/ int proc find( struct prcc_struct* src, struct proc- struct* dest) char buffer[40%6]. ps cmd[20] It fd. I sprintf(buffer, "ed/star", sre->pid); fd = open(butter, O_RDONLY) if(fd==-1) rerurn 0 memset(buffer, wO, sizeof(buffer)) len= read(fd, bufter, sizeof(bufter )-1) close(ld) if(l return 0: p= butter: p= strrchr(p, C) narq=strrchr(p, )) n=q-p-1 if (len >= sizeof, srt->name)) len= sizeof(src->name)-1 p+ l, len src->namelen]=0; =日 turn(strcmp( src->name, dest dest->name)==0)? 1: 0- 条善参数aay:让程结构体指针;参数sie进程列表数组aray的大小ie:配置文件路径 从配置文件得到指定的程序列表,将对应进程的信息填充到aray数组中 羋执行成功返回进程个数,执行失败返回0 int get_ proc( struct proc_struct array, int size, char file intnRet=o if(! array I‖(si 0)l‖fhle myprinttf"invalid parameterin retun o char line[4096]; FILE fp= fopen(file, T"); if(fp) printf("open file cs fail\n", file) return U memset(line, 0, 4095); while(fgets(lire, 4095, tp)&& nRet size) memcpy(void s)[(&arraylnRet )->cmdline), (void")line, strlen(line)-2 ) tmp= strrchr(line, / ) Lmp += I: memcpy((&array inRet))->name, tmp, strlen(tmp)- 2) nRet++ ); return(nReL); 康棒串串浓凉率旅浓串底率卖毒志着旅浓浓准溶房表 装 startProc *卷参数proc:要启动的进的结构体描述指针 启动程序 执行成功返回1,子进程退出 宗塞家康家家家家家家家家宋家家聚家苯家球察塞家塞家家容家塞家家家家室家家察家家家聚家聚寒撑家装家掌建察家家室事 int startProc (struct proc_ struct* proc, struct proc _struct*must_run_ proc int mIst_run_size static inti=d if( proc)return 0 if(strlen(proc->cmdline I<=0) return 0; int pid= forko: 〃进程内部返回值为0,返回给父进程的为自己的pid inta〓 if(pid pid= fork( ifpd≡0 execl(char")proc->cmdline,(char")prDc->name,NULL); ∥exit: It(o): sleep42片 waiL(NULL) sleep( I: if(i== must run size -1) if(check proc(&must run proc[i])==0) startProc( &mtust_run_proeli], must_run_prce, must_run_size); el if(i== must run size-11 i= else 1++ start Proc( &must_run_proclil, must_run_ proc, mustrun_ _size); !**幸幸串率幸米幸*家*幸毕零*幸幸半字幸字华米*幸半孝率非幸零幸学幸幸车 3a*8*daemon init 幸*启动配置文件当中的需要守护的程序 执行成功返回1,中途出错,返回-1 长界零家墨军零家零率家三哮零座零率零零容岸军零罕型率零零零零牢察座察零零零零季球军零容零 int moniter_ run(struct proc_struct"must_run_proc, int proc_ size) nti=0: for(i=0; i< must_run_size: i ++)监控程序是否正在运行 if(check_ proc(&(must un_ proc[il))<=o) ∥厘新片动程序 startProc(&' must run procli]), must run proc, proc size return I: 幸*事率事率率**率**字幸学摩*率*幸幸学幸半*率幸字****幸中*幸学幸 春*着*信号处理函数 exit_proc 翥安全结束监控的程序 4来没有返回值 告参毒萨响幸帝称昨嗜幸古称索点响卷南都南请南幸难布际本啪昨青市南动南香请非市赤南本 void exit_ proc(int ar InL I struct proc struct proc for(i=0; i< must run_ Size: i++) proc=&(must_run_proc[i]): kill(proc->pid, SIGTERM); exit flag=I exit(o): void exit_proc(int pid 要main L.获取程序列表2启动进程3.监控进程,若程序岀或崩溃,重新启动程序4.收到退 出信号,安全结束监控程序 成功返回1,失败返回0 零牢容容家容字家容容察*禁容容字哮零常字容容容家察容牢容零容容容容容牢字家客字容牢容零容*字容客字容容字容家容容字岩 static void run moniter( void data) 读取程序列表 must_ run _size get proc(must_run_proc, sIZE, data if(rmust run Sizc <=1) return o struct sigaction act, oldact act,sa handler= exit_proc act. sa flags =SA ONESHOT sigaction(SIGTERM, &act, NULL) sigaction(SIGINT, &act, NULL) sigaction(sIGHUP, &act, NULL); 检测并启动未启动的程序 moniter_ run(must run proc, must run slze) eturn null int creat and run moniter(char * file) 开线程: pthread_t moniter_ thread if(pthread_create(&moniter_thread, NULL, run_moniter, file)==0) printf("thread create Ok, check thread start \n") return printf( thread check create Errin"): return -I 零零零零享享事职增零半非寥零享半容零摩率率零享剩率容半半享零半率零半率零率辱寒零享 要 IsDigit 参茶爹数a字符串的地址 *判断字符串是否有纯数字字符组成 春客是纯数字返回1,不是纯数字返回0 喜非要串思率串串串串家串润串串串串串串毒毒喜串串最率毒串串踪串率串串非球毒串妆串串毒串串影零串串毒事串 static int IsDigit[char aD) Int size ∥得到当前字符串的长度 size= strlen(a: ∥若字符串长度为0,则直接返回0:即宇符串为空则返回0: if(size ==0) return 0; ∥循环遍历整个字符串 forli=0; i< Size; i++) ∥如果字符小于字符0,或者大于字符9,则返回0 if(ai]<ol ai>9) retum ∥走到这一步说明字符串由字符09组成,返回1 return l; 主进程源文件:man,c main.c #include"process, h Include <stdio. h> include <stdlib h> 甲 include< unistd. I> 甲 nclude< signal> 却 nclude <sys/ ypes,h include <sys/stat. h> 甲 include< tenth> int main(void) creat_and_run_moniter("proclistini") while(l) sleep(D) turn 以上内容是程序全部源文件
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

流烟默

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值