• linux包之procps之ps与top


    概述  阅读man ps页,与man top页,最权威与标准,也清楚

    概念

    英语

    命令

    ps

    top

    slabtop

    pidof

    =========================================

    有时候系统管理员可能只关心现在系统中运行着哪些程序,而不想知道有哪些进程在运行。由于一个应用程序可能需要启动多个进程。所以在同等情况下,进程的数量要比程序多的多。使用ps可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等。

    ps中的session leader引发的搜索与思考

    在第 1 节 “信号的基本概念”
    中我说过“Shell可以同时运行一个前台进程和任意多个后台进程”其实是不全面的,现在我们来研究更复杂的情况。事实上,Shell分前后台来控制的不是进程而是作业(Job)或者进程组(Process Group)。一个前台作业可以由多个进程组成,一个后台作业也可以由多个进程组成,Shell可以同时运行一个前台作业和任意多个后台作业,这称为作业控制(Job Control)。例如用以下命令启动5个进程:
    $ proc1 | proc2 &
    $ proc3 | proc4 | proc5
    其中proc1和proc2属于同一个后台进程组,proc3、proc4、proc5属于同一个前台进程组,Shell进程本身属于一个单独的进程组。这些进程组的控制终端相同,它们属于同一个Session。当用户在控制终端输入特殊的控制键(例如Ctrl-C) 时,内核会发送相应的信号(例如SIGINT)给前台进程组的所有进程。各进程、进程组、Session的关系如下图所示

    http://blog.chinaunix.net/uid-25681671-id-3201927.html  Linux下PS命令详解


    http://blog.csdn.net/jibcy/article/details/7368611    Session与进程组
    http://blog.csdn.net/jibcy/article/details/7357301    unix 进程组 会话期 作业控制
    在UNIX系统中,作业控制允许在一个终端上启动多个作业(进程组),控制哪一个作业可以存取该终端,以及哪些作业在后台运行。
    为了支持作业控制,引入了进程组,会话期,控制终端等概念,还需要内核以一定的信号支持。
    一·进程组。
    每一个进程除了有一个进程PID之外,还属于一个进程组,用进程组ID表示。返回当前进程组ID的系统调用为:
    二·对话期(session)
    对话期是一个或多个进程组的集合,对话期可以有一个控制终端。
    三。前台进程组,后台进程组
    一个对话期的几个进程组可以被分成一个前台进程组以及一个或几个后台进程组。
    如果一个对话期有一个控制终端,那么它有一个前台进程组,其他进程组为后台进程组。
    无论何时键入中断键(Ctrl-C)或者退出键(Ctrl-),就会造成中断信号SIGINT或者退出信号SITQUIT送至前台进程组中的所有进程。
    只有前台进程组中的进程可以接受终端输入,如果后台进程组的进程试图读终端,那么内核会发送一个特定的信号SIGTTIN给后台作业,这通常会停止(挂起)次后台作业。当用将次后台进程转为前台进程后(移入前台进程组),会发送一个SIGCONT信号给该进程,使该进程继续运行。

    -A 显示所有进程(等价于-e)(utility)
    -a 显示一个终端的所有进程,除了会话引线
    ps c 列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。

    最常用的方法是ps -aux,然后再利用一个管道符号导向到grep去查找特定的进程,然后再对特定的进程进行操作。

    ps aux的显示来观察auditd的启动优先级
    root 3318 0.0 0.0 12516 764 ? S<sl Mar25 0:00 auditd
    S<sl S表示进程正在睡眠,<表示具有较高的优先级,s表示多进程,l表示多线程

    ps和top的区别
    ps看到的是命令执行瞬间的进程信息,而top可以持续的监视
    ps只是查看进程,而top还可以监视系统性能,如平均负载,cpu和内存的消耗
    另外top还可以操作进程,如改变优先级(命令r)和关闭进程(命令k)

    linux上进程有5种状态:
    1. 运行(正在运行或在运行队列中等待)
    2. 中断(休眠中, 受阻, 在等待某个条件的形成或接受到信号)
    3. 不可中断(收到信号不唤醒和不可运行, 进程必须等待直到有中断发生)
    4. 僵死(进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放)
    5. 停止(进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行)

    1)ps a 显示现行终端机下的所有程序,包括其他用户的程序。
    2)ps -A 显示所有程序。
    3)ps c 列出程序时,显示每个程序真正的指令名称,而不包含路径,参数或常驻服务的标示。
    4)ps -e 此参数的效果和指定"A"参数相同。
    5)ps e 列出程序时,显示每个程序所使用的环境变量。
    6)ps f 用ASCII字符显示树状结构,表达程序间的相互关系。
    7)ps -H 显示树状结构,表示程序间的相互关系。
    8)ps -N 显示所有的程序,除了执行ps指令终端机下的程序之外。
    9)ps s 采用程序信号的格式显示程序状况。
    10)ps S 列出程序时,包括已中断的子程序资料。
    11)ps -t <终端机编号>  指定终端机编号,并列出属于该终端机的程序的状况。
    12)ps u   以用户为主的格式来显示程序状况。
    13)ps x   显示所有程序,不以终端机来区分。
    14)ps -l 較長,較詳細的顯示該PID的信息

    解释

    [root@CentOS5 ~]# ps aux|grep D

    # ps -lA |more
    相關信息的意義:
    F 進程的標誌(flag),4表示用戶為超級用戶
    S 進程的狀態(stat),各STAT的意義見下文
    C CPU使用資源的百分比
    PRI priority(優先級)的縮寫,
    NI Nice值,
    ADDR 核心功能,指出該進程在內存的那一部分,如果是運行的進程,一般都是“-”
    SZ 用掉的內存的大小
    WCHAN 當前進程是否正在運行,若為“-”表示正在運行

    NI 进程的NICE值,数值大,表示较少占用CPU时间;
    TIME 进程使用的总cpu时间
    VSZ 进程所使用的虚存的大小(Virtual Size)
    RSS 进程使用的驻留集大小或者是实际内存的大小,Kbytes字节

    # ps aux |more
    USER 进程的属主;
    PID 进程的ID;
    PPID 父进程;
    %CPU 进程占用的CPU百分比;
    %MEM 占用内存的百分比;
    NI 进程的NICE值,数值大,表示较少占用CPU时间;
    VSZ 該进程使用的虚拟內存量(KB);
    RSS 該進程占用的固定內存量(KB)(驻留中页的数量);
    TTY 該進程在那個終端上運行(登陸者的終端位置),若與終端無關,則顯示(?)。若為pts/0等,則表示由網絡連接主機進程
    WCHAN 當前進程是否正在進行,若為-表示正在進行;
    START 該進程被觸發启动时间;
    TIME 該进程實際使用CPU運行的时间;
    COMMAND 命令的名称和参数;

    STAT 进程的状态:进程状态使用字符表示的(STAT的状态码)

    R 运行 Runnable (on run queue) 正在运行或在运行队列中等待。
    S 睡眠 Sleeping 休眠中, 受阻, 在等待某个条件的形成或接受到信号。
    I 空闲 Idle
    Z 僵死 Zombie(a defunct process) 进程已终止, 但进程描述符存在, 直到父进程调用wait4()系统调用后释放。
    D 不可中断 Uninterruptible sleep (ususally IO) 收到信号不唤醒和不可运行, 进程必须等待直到有中断发生。
    D 无法中断的休眠状态(通常 IO 的进程);
    T 终止 Terminate 进程收到SIGSTOP, SIGSTP, SIGTIN, SIGTOU信号后停止运行运行。
    P 等待交换页
    W 无驻留页 has no resident pages 没有足够的记忆体分页可分配。
    X 死掉的进程
    < 高优先级进程 高优先序的进程
    N 低优先级进程 低优先序的进程
    L 内存锁页 Lock 有记忆体分页分配并缩在记忆体内
    s 进程的领导者(在它之下有子进程);
    l 多进程的(使用 CLONE_THREAD, 类似 NPTL pthreads)
    + 位于后台的进程组

    ==================================================

    TIME  --  CPU Time
    Total CPU time the task has used since it started.  
    When ’Cumulative mode’ is On, each process is listed with the cpu time that it and its dead children  has  used.  
    You toggle ’Cumulative mode’ with ’S’, which is a command-line option and an interactive command.  See the ’S’ interactive command for additional information regarding this mode.

    TIME+  --  CPU Time, hundredths
    The same as ’TIME’, but reflecting more granularity through hundredths of a second.

    granularity    粒度
    hundredths of a second    百分之一秒

    ================================================

    样例:
    查看当前系统进程的uid,pid,stat,pri, 以uid号排序.
    ps -eo pid,stat,pri,uid --sort uid
    查看当前系统进程的user,pid,stat,rss,args, 以rss排序.
    ps -eo user,pid,stat,rss,args --sort rss

    可以用下面命令查看进程状态
    ps -aux
    列出类似程序树的程序显示(显示进程下有哪些子进程)
    ps -axjf
    找出与 cron 与 syslog 这两个服务有关的 PID 号码
    ps aux | egrep '(cron|syslog)'
    也可以这样使用ps格式输出来查看进程状态:
    ps -eo user,stat..,cmd

    查看当前系统进程的uid,pid,stat,pri, 以uid号排序.
    ps -eo pid,stat,pri,uid --sort uid
    查看当前系统进程的user,pid,stat,rss,args, 以rss排序.
    ps -eo user,pid,stat,rss,args --sort rss

    在Linux下,还有一种方法检查某个进程是否存在,利用/proc文件系统. /proc/pid/stat里面有进程的状态,进程可执行文件名等.如果该文件不存在了,那进程肯定退出了.如果存在,可以检查状态和文件名是否正确.效率可能比PS还是高一些,因为/proc是虚拟文件系统,存在与内存中.
    如何利用/proc文件系统
    cat /proc/pid/status
    这里pid是你的进程ID,看看输出结果,有一栏是State

    - When the process that dies is the session leader of a session that is
    attached to a terminal device, SIGHUP is sent to all processes in
    the foreground process group of that terminal device.
    - When the death of a process causes a process group to become orphaned,
    and one or more processes in the orphaned group are stopped, then
    SIGHUP and SIGCONT are sent to all members of the orphaned
    group. (An orphaned process group is one where no process in the group
    has a parent which is part of the same session, but not the same process
    group.)

    ref: http://www.unixguide.net/unix/programming/1.15.shtml
    《UNIX环境高级编程》第十章说:“如果对话期首进程(session leader)终止,则也产生SIGHUP。此信号被发送给该对话期前台进程组中的每一个进程。”
    对这个不少人可能有疑问,而实际上,上文应该少说了一个条件:即session leader必须还有一个控制终端(CTTY)。

    [root@109-com1 ~]# ps  -eLo pid,lwp,pcpu | grep 11167

    -e与-A是一样的
    [root@250-shiyan ~]# ps -e >pse
    [root@250-shiyan ~]# ps -A >psa
    [root@250-shiyan ~]# diff pse psa
    75c75
    <  7761 pts/1    00:00:00 ps
    ---
    >  7762 pts/1    00:00:00 ps
    本终端的进程除外
    [root@250-shiyan ~]# ps -N
    [root@250-shiyan ~]# ps T
      PID TTY      STAT   TIME COMMAND
     7657 pts/1    Ss     0:00 -bash
     7784 pts/1    R+     0:00 ps T

    [root@250-shiyan ~]# ps a
      PID TTY      STAT   TIME COMMAND
     1143 tty1     Ss+    0:00 /sbin/mingetty /dev/tty1
     1145 tty2     Ss+    0:00 /sbin/mingetty /dev/tty2
     1147 tty3     Ss+    0:00 /sbin/mingetty /dev/tty3
     1149 tty4     Ss+    0:00 /sbin/mingetty /dev/tty4
     1151 tty5     Ss+    0:00 /sbin/mingetty /dev/tty5
     1153 tty6     Ss+    0:00 /sbin/mingetty /dev/tty6
     3636 pts/0    Ss+    0:00 -bash
     7657 pts/1    Ss     0:00 -bash
     8372 pts/1    R+     0:00 ps a
    [root@250-shiyan ~]# ps u
    USER       PID %CPU %MEM    VSZ   RSS TTY      STAT START   TIME COMMAND
    root      1143  0.0  0.1   4064   572 tty1     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty1
    root      1145  0.0  0.1   4064   572 tty2     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty2
    root      1147  0.0  0.1   4064   568 tty3     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty3
    root      1149  0.0  0.1   4064   568 tty4     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty4
    root      1151  0.0  0.1   4064   572 tty5     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty5
    root      1153  0.0  0.1   4064   572 tty6     Ss+  Jan13   0:00 /sbin/mingetty /dev/tty6
    root      3636  0.0  0.9 111176  4912 pts/0    Ss+  Mar12   0:00 -bash
    root      7657  0.0  0.9 111176  4812 pts/1    Ss   Mar16   0:00 -bash
    root      8375  0.0  0.2 110236  1168 pts/1    R+   10:20   0:00 ps u
    [root@250-shiyan ~]# ps v
      PID TTY      STAT   TIME  MAJFL   TRS   DRS   RSS %MEM COMMAND
     1143 tty1     Ss+    0:00      0    11  4052   572  0.1 /sbin/mingetty /dev/tty1
     1145 tty2     Ss+    0:00      0    11  4052   572  0.1 /sbin/mingetty /dev/tty2
     1147 tty3     Ss+    0:00      0    11  4052   568  0.1 /sbin/mingetty /dev/tty3
     1149 tty4     Ss+    0:00      0    11  4052   568  0.1 /sbin/mingetty /dev/tty4
     1151 tty5     Ss+    0:00      0    11  4052   572  0.1 /sbin/mingetty /dev/tty5
     1153 tty6     Ss+    0:00      0    11  4052   572  0.1 /sbin/mingetty /dev/tty6
     3636 pts/0    Ss+    0:00      0   845 110330 4912  0.9 -bash
     7657 pts/1    Ss     0:00      0   845 110330 4812  0.9 -bash
     8379 pts/1    R+     0:00      0    76 108051 1036  0.2 ps v
    [root@250-shiyan ~]# ps -o rss,pid,vsz,cmd
      RSS   PID    VSZ CMD
     4812  7657 111176 -bash
     1036  8370 108128 ps -o rss,pid,vsz,cmd
    [root@84-monitor monitor]# ps -O sid,vsz
      PID   SID    VSZ S TTY          TIME COMMAND
     7684 12642 108096 R pts/3    00:00:00 ps -O sid,vsz
    12642 12642 108432 S pts/3    00:00:00 -bash
    [root@84-monitor monitor]# ps  -ww -f 13691
    UID        PID  PPID  C STIME TTY      STAT   TIME CMD
    root     13691     1  0 Feb26 ?        Sl    54:36 /usr/local/jdk1.6//bin/java -Djava.util.logging.config.file=/var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41/endorsed -classpath /var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41/bin/bootstrap.jar -Dcatalina.base=/var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41 -Dcatalina.home=/var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41 -Djava.io.tmpdir=/var/www/html/apache-tomcat-6.0.41/apache-tomcat-6.0.41/temp org.apache.catalina.startup.Bootstrap start
    [root@84-monitor monitor]# ps -fp 16213,8761
    UID        PID  PPID  C STIME TTY          TIME CMD
    root      8761  8760  0 Feb03 ?        00:00:00 /usr/sbin/postdrop -r
    apache   16213 27245  0 Mar15 ?        00:00:00 /usr/sbin/httpd
    [root@84-monitor monitor]# ps -fp "4083 7277" -fp 16213,8761
    UID        PID  PPID  C STIME TTY          TIME CMD
    postfix   4083  1251  0 13:43 ?        00:00:00 pickup -l -t fifo -u
    root      7277 11415  0 Feb03 ?        00:00:00 CROND
    root      8761  8760  0 Feb03 ?        00:00:00 /usr/sbin/postdrop -r
    apache   16213 27245  0 Mar15 ?        00:00:00 /usr/sbin/httpd
    [root@84-monitor monitor]# ps X
      PID   STACKP      ESP      EIP TMOUT ALARM STAT TTY        TIME COMMAND
     1282 82161fb0 82161e88 0683b4c0     -     - Ss+  tty1       0:00 /sbin/mingetty /dev/tty1
     1284 a21c4fb0 a21c4e88 4f87b4c0     -     - Ss+  tty2       0:00 /sbin/mingetty /dev/tty2
     1286 653d3a10 653d38e8 f4d6b4c0     -     - Ss+  tty3       0:00 /sbin/mingetty /dev/tty3
     1289 22ec1380 22ec1258 3b3334c0     -     - Ss+  tty4       0:00 /sbin/mingetty /dev/tty4
     1293 a024df80 a024de58 f77eb4c0     -     - Ss+  tty5       0:00 /sbin/mingetty /dev/tty5
    [root@84-monitor monitor]# ps j
     PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
        1  1282  1282  1282 tty1      1282 Ss+      0   0:00 /sbin/mingetty /dev/tty1
        1  1284  1284  1284 tty2      1284 Ss+      0   0:00 /sbin/mingetty /dev/tty2
        1  1286  1286  1286 tty3      1286 Ss+      0   0:00 /sbin/mingetty /dev/tty3
        1  1289  1289  1289 tty4      1289 Ss+      0   0:00 /sbin/mingetty /dev/tty4
        1  1293  1293  1293 tty5      1293 Ss+      0   0:00 /sbin/mingetty /dev/tty5
    [root@84-monitor monitor]# ps s
      UID   PID          PENDING          BLOCKED          IGNORED           CAUGHT STAT TTY        TIME COMMAND
        0  1282 0000000000000000 0000000000000000 0000000000000000 0000000000000000 Ss+  tty1       0:00 /sbin/mingetty /dev/tty1
        0  1284 0000000000000000 0000000000000000 0000000000000000 0000000000000000 Ss+  tty2       0:00 /sbin/mingetty /dev/tty2
        0  1286 0000000000000000 0000000000000000 0000000000000000 0000000000000000 Ss+  tty3       0:00 /sbin/mingetty /dev/tty3
    [root@84-monitor monitor]# ps -yl
    S   UID   PID  PPID  C PRI  NI   RSS    SZ WCHAN  TTY          TIME CMD
    R     0  9619 12642  0  80   0   936 27023 -      pts/3    00:00:00 ps
    S     0 12642 12638  0  80   0  2000 27108 wait   pts/3    00:00:00 bash

    ===============================================

    15:50:33 7 ~:#man top

    top详解

    top界面介绍

    摘要区域  summary area

    任务区域  task area

    主界面按数字1如果有多个cpu核心,逐个显示

    主界面按字母l第一行开关,t接下来两行开关,m内存两行开关

    ALTERNATE-DISPLAY Mode  轮流显示模式  通过A(shift+a)同时显示4个字段组,分别是1-def,2-job,3-mem,4-usr。用a或w来进行选择,选中之后,-/+进行关闭或打开,可独立关闭某一个字段组

    full-screen mode  全屏模式  整个屏幕代表单个窗口,但是可以更改成4个不同的字段组filed groups,通过G(shift+g)交互式命令来选择1,2,3,4。

    FIELDS / Columns

    f或o交互命令,’f’ (Fields select) or ´o’ (Order fields)

    top命令
    字段解释:
      PID:进程的ID
      USER:进程所有者
      PR:进程的优先级别,越小越优先被执行
      NInice:值
      VIRT:进程占用的虚拟内存
      RES:进程占用的物理内存
      SHR:进程使用的共享内存
      S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
      %CPU:进程占用CPU的使用率
      %MEM:进程使用的物理内存和总内存的百分比
      TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
      COMMAND:进程启动命令名称
      
    子命令:
      P:按%CPU使用率排行
      T:按TIME+排行
      M:按%MEM排行
    字段:
       f 选择字段  选择显示列:执行top命令后,按 f 键,再按某一列的代表字母,即可选中或取消显示;
       o 排序字段  列显示位置调整:执行top命令后,按 o 键,选择要调整位置的列(如K:CUP Usageage),按动一下大写K则显示位置往上调整,按动一下小写K则显示位置往下调整。
       F或者O 列排序   执行top命令后,按 shift + f(小写)或o(小写),二者等同。进入选择排序列页面,再按要排序的列的代表字母即可;
       排序,默认是按cpu排序,>左移一列排序,shift+r反向,<右移一列排序。
    
    只显示asterisk及其线程的top显示。
    top -H -p `pidof asterisk`
    centos7-top
    %MEM  --  Memory Usage (RES)
               A task's currently used share of available physical memory.
    CODE  --  Code Size (KiB)
               The amount of physical memory devoted to executable code, also known as  the  Text  Resident  Set
               size or TRS.
    DATA  --  Data + Stack Size (KiB)
               The amount of physical memory devoted to other than executable code, also known as the Data Resi‐
               dent Set size or DRS.
    VIRT  --  Virtual Memory Size (KiB)
               The  total  amount  of  virtual  memory  used by the task.  It includes all code, data and shared
               libraries plus pages that have been swapped out and pages that have been mapped but not used.
    SWAP  --  Swapped Size (KiB)
               The non-resident portion of a task's address space.
    RES  --  Resident Memory Size (KiB)
               The non-swapped physical memory a task is using.
    SHR  --  Shared Memory Size (KiB)
               The amount of shared memory available to a task, not all of which is typically resident.  It sim‐
               ply reflects memory that could be potentially shared with other processes.
    
    centos6-top
    %MEM  --  Memory usage (RES)
              A task’s currently used share of available physical memory.
    CODE  --  Code size (kb)
              The amount of physical memory devoted to executable code, also known as the  ’text  resi-
              dent set’ size or TRS.
    DATA  --  Data+Stack size (kb)
              The  amount  of  physical memory devoted to other than executable code, also known as the
              ’data resident set’ size or DRS.
    VIRT  --  Virtual Image (kb)
              The  total  amount  of  virtual  memory used by the task.  It includes all code, data and
              shared libraries plus pages that have been swapped out. (Note: you can define  the  STAT-
              SIZE=1 environment variable and the VIRT will be calculated from the /proc/#/state VmSize
              field.)
    
              VIRT = SWAP + RES.
    SWAP  --  Swapped size (kb)
              The swapped out portion of a task’s total virtual memory image.
    RES  --  Resident size (kb)
              The non-swapped physical memory a task has used.
    
              RES = CODE + DATA.
    SHR  --  Shared Mem size (kb)
              The amount of shared memory used by a task.  It simply  reflects  memory  that  could  be
              potentially shared with other processes.

    ====================================================

    [root@cu-dbs-154 ~]# rpm -qf /usr/bin/slabtop
    procps-3.2.8-36.el6.x86_64

    slabtop

           default sort criteria is to sort by the number of objects ("o").
           The sort criteria can also be changed while slabtop is running by pressing the associated character.
           a:     sort by number of active objects
           b:     sort by objects per slab
           c:     sort by cache size
           l:     sort by number of slabs
           v      sort by number of active slabs
           n:     sort by name
           o:     sort by number of objects
           p:     sort by pages per slab
           s:     sort by object size
           u:     sort by cache utilization

  • 相关阅读:
    手把手教会你如何通过C#创建Windows Service
    推荐几款软件界面模型设计工具
    visual studio 2010小技巧
    C# 枚举在属性中运用
    C# Stream 和 byte[] 之间的转换
    推荐一款DataGridView的打印解决方案
    VB提高专辑VB编写自定义类(下)
    vb 怎么把长整型转字符串
    Android NAND: nand_dev_load_disk_state, restore failed: size required (3546398242485400641) exceeds device limit (6920
    VB中各种类型的转换
  • 原文地址:https://www.cnblogs.com/createyuan/p/3733540.html
Copyright © 2020-2023  润新知