Linux環境下進程的CPU占用率


阿里雲服務器網站:https://promotion.aliyun.com/ntms/yunparter/invite.html?userCode=qqwovx6h

文字來源:http://www.samirchen.com/linux-cpu-performance/

1、Linux 環境下查看 CPU 信息

通過 cat /proc/cpuinfo 命令,可以查看 CPU 相關的信息:

[root@rh~]$cat/proc/cpuinfo
processor:0
vendor_id:GenuineIntel
cpufamily:6
model:44
modelname:Intel(R)Xeon(R)CPUE5620@2.40GHz
stepping:2
cpuMHz:1596.000
cachesize:12288KB
physicalid:0
siblings:8
coreid:0
cpucores:4
apicid:0
initialapicid:0
fpu:yes
fpu_exception:yes
cpuidlevel:11
wp:yes
flags:fpuvmedepsetscmsrpaemcecx8apicsepmtrrpgemcacmovpatpse36clflushdtsacpimmxfxsrssesse2sshttmpbesyscallnxpdpe1gbrdtscplmconstant_tscarch_perfmonpebsbtsrep_goodxtopologynonstop_tscaperfmperfpnipclmulqdqdtes64monitords_cplvmxsmxesttm2ssse3cx16xtprpdcmpciddcasse4_1sse4_2popcntaeslahf_lmaratepbdtstpr_shadowvnmiflexpriorityeptvpid
bogomips:4800.15
clflushsize:64
cache_alignment:64
addresssizes:40bitsphysical,48bitsvirtual
powermanagement:
......

 

在查看到的相關信息中,通常有些信息比較讓人迷惑,這里列出一些解釋:

  • physical id: 指的是物理封裝的處理器的 id。
  • cpu cores: 位於相同物理封裝的處理器中的內核數量。
  • core id: 每個內核的 id。
  • siblings: 位於相同物理封裝的處理器中的邏輯處理器的數量。
  • processor: 邏輯處理器的 id。

我們通常可以用下面這些命令獲得這些參數的信息:

[root@rh ~]$ cat /proc/cpuinfo | grep "physical id"| sort|uniq
physical id :0
physical id :1
[root@rh ~]$ cat /proc/cpuinfo | grep "cpu cores"| sort|uniq
cpu cores :4
[root@rh ~]# cat /proc/cpuinfo | grep "core id" | sort|uniq
core id :0
core id :1
core id :10
core id :9
[root@rh ~]$ cat /proc/cpuinfo | grep "siblings"| sort|uniq
siblings :8
[root@rh ~]$ cat /proc/cpuinfo | grep "processor"| sort|uniq
processor :0
processor :1
processor :10
processor :11
processor :12
processor :13
processor :14
processor :15
processor :2
processor :3
processor :4
processor :5
processor :6
processor :7
processor :8
processor :9

通過上面的結果,可以看出這台機器:

  • 1)有 2 個物理封裝的處理器(physical id 有 2 個);
  • 2)每個物理封裝的處理器有 4 個內核(cpu cores 為 4);
  • 3)每個物理封裝的處理器有 8 個邏輯處理器(siblings 為 8),可見台機器的處理器開啟了超線程技術,每個內核(core)被划分為了 2 個邏輯處理器(processor);
  • 4)總共有 16 個邏輯處理器(processor 有 16 個);

超線程技術:超線程技術就是利用特殊的硬件指令,把兩個邏輯內核模擬成兩個物理芯片,讓單個處理器都能使用線程級並行計算,進而兼容多線程操作系統和軟件,減少了CPU的閑置時間,提高的CPU的運行效率。

可以使用 mpstat 命令或 sar 命令來查看。 具體使用可以通過 man mpstat/sar 來查看。

下面實例數據是內核 2.6.24-24 版本以上的:

[root@rh ~]$ cat /proc/stat
cpu 22344724045041824108021655975341258620900
cpu0 17625111934142575516534590721678000
cpu1 124129139234258609121139271569700
cpu2 14953055861825310182230738785100
cpu3 40880138057258734793404191586200
cpu4 13417171611232582975610429221715500
cpu5 23935533183725605479369202609500
cpu6 193200319132256197282060183069000
cpu7 184300324300256161424170203308100
cpu8 319400661718252319111474156766100
cpu9 80798715203525829339237191630800
cpu10 42472414005225869331168181505900
cpu11 16305440147325567904207143467400
cpu12 8227015781525853353319101480800
cpu13 1889446479709253542796532615788500
cpu14 70003020476025769743159162105900
cpu15 4567114089925855452138161553600
intr 114673438499700110001000001302607002620870712704000<...省略若干數據...>
ctxt 89793364
btime 1366591448
processes 27283
procs_running 1
procs_blocked 0
softirq 1262462448063122856507893291074176388225020046121395355817613064075931

第一行的數據表示的是 CPU 總的使用情況。我們來解釋一下這行數據各數值的含義:

  • 1)這些數值的單位都是 jiffies,jiffies 是內核中的一個全局變量,用來記錄系統啟動以來產生的節拍數,在 Linux 中,一個節拍大致可以理解為操作系統進程調度的最小時間片,不同的 Linux 系統內核這個值可能不同,通常在 1ms 到 10ms 之間。
  • 2)cpu 223447 240 4504182 410802165 59753 412 586209 0 0
    • user(223447) 從系統啟動開始累積到當前時刻,處於用戶態的運行時間,不包含 nice 值為負的進程。
    • nice(240) 從系統啟動開始累積到當前時刻,nice 值為負的進程所占用的 CPU 時間。
    • system(4504182) 從系統啟動開始累積到當前時刻,處於核心態的運行時間。
    • idle(410802165) 從系統啟動開始累積到當前時刻,除 IO 等待時間以外的其他等待時間。
    • iowait(59753) 從系統啟動開始累積到當前時刻,IO 等待時間。(since 2.5.41)
    • irq(412) 從系統啟動開始累積到當前時刻,硬中斷時間。(since 2.6.0-test4)
    • softirq(586209) 從系統啟動開始累積到當前時刻,軟中斷時間。(since 2.6.0-test4)
    • stealstolen(0) Which is the time spent in other operating systems when running in a virtualized environment.(since 2.6.11)
    • guest(0) Which is the time spent running a virtual CPU for guest operating systems under the control of the Linux kernel.(since 2.6.24)

從以上信息我們可以得到總的 CPU 活動時間為:

totalCPUTime = user + nice + system + idle + iowait + irq + softirq + stealstolen + guest

2.2.1、存儲進程信息的文件目錄

Linux 系統貫徹“一切都是文件”的思想,所有的進程的運行狀態也都可以通過讀取文件來獲取。 /proc 文件系統是一個偽文件系統,它只存在內存當中,而不占用外存空間。它以文件系統的方式為內核與進程提供通信的接口。用戶和應用程序可以通過 /proc 得到系統的信息,並可以改變內核的某些參數。

/proc/[PID]/ 目錄下的各個文件記錄着 這個進程的各項運行指標。 是進程號。

2.2.2、查看進程運行的詳細信息

通過查看 /proc/[PID]/stat 文件,可以進程運行的詳細信息,其中就包括 CPU 占用信息。 比如:

[root@rh ~]$ cat /proc/1/stat
1(init) S 0110-1420275230262635222948351651023463188016200101198205443841844674407370955161511000004096536962595184467440737095516150004003400

 

/proc/[PID]/stat 文件信息解釋

看到上面這些信息,肯定會很迷惑,不知道每個字段都是什么意思。

1)我們可以通過 man 5 proc 命令查看文檔,找到 /proc/[pid]/stat 節點,就可以看到各字段的意思了。如:

/proc/[pid]/stat
Status information about the process.Thisis used by ps(1).Itisdefinedin/usr/src/linux/fs/proc/array.c.
The fields,in order,with their proper scanf(3) format specifiers, are:
pid %d The process ID.
comm %s The filename of the executable,in parentheses.Thisis visible whether ornot the executable is swapped out.
state %c One character from the string"RSDZTW"where R is running, S is sleeping in an interruptible wait, D is waiting in uninterruptible disk sleep, Z is zombie, T is traced or stopped (on a signal),and W is paging.
ppid %d The PID of the parent.
......

2)具體解釋,一個示例:

  • pid=6873 進程(包括輕量級進程,即線程)號
    comm=a.out 應用程序或命令的名字。
    task_state=R 任務的狀態,R:runnign, S:sleeping (TASK_INTERRUPTIBLE), D:disk sleep (TASK_UNINTERRUPTIBLE), T: stopped, T:tracing stop, Z:zombie, X:dead。
    ppid=6723 父進程ID。
    pgid=6873 線程組號。
    sid=6723 該任務所在的會話組 ID。
    tty_nr=34819(pts/3) 該任務的 tty 終端的設備號,INT(34817/256)= 主設備號,(34817-主設備號)= 次設備號。
    tty_pgrp=6873 終端的進程組號,當前運行在該任務所在終端的前台任務(包括 shell 應用程序)的 PID。
    task->flags=8388608 進程標志位,查看該任務的特性。
    min_flt=77 該任務不需要從硬盤拷數據而發生的缺頁(次缺頁)的次數。
    cmin_flt=0 累計的該任務的所有的 waited-for 進程曾經發生的次缺頁的次數目。
    maj_flt=0 該任務需要從硬盤拷數據而發生的缺頁(主缺頁)的次數。
    cmaj_flt=0 累計的該任務的所有的 waited-for 進程曾經發生的主缺頁的次數目。
    utime=1587 該任務在用戶態運行的時間,單位為 jiffies。
    stime=1 該任務在核心態運行的時間,單位為 jiffies。
    cutime=0 累計的該任務的所有的 waited-for 進程曾經在用戶態運行的時間,單位為 jiffies。
    cstime=0 累計的該任務的所有的 waited-for 進程曾經在核心態運行的時間,單位為 jiffies。
    priority=25 任務的動態優先級。
    nice=0 任務的靜態優先級。
    num_threads=3 該任務所在的線程組里線程的個數。
    it_real_value=0 由於計時間隔導致的下一個 SIGALRM 發送進程的時延,以 jiffy 為單位。
    start_time=5882654 該任務啟動的時間,單位為 jiffies。
    vsize=1409024(page) 該任務的虛擬地址空間大小。
    rss=56(page) 該任務當前駐留物理地址空間的大小;Number of pages the process has in real memory,minu 3 for administrative purpose. 這些頁可能用於代碼,數據和棧。
    rlim=4294967295(bytes) 該任務能駐留物理地址空間的最大值。
    start_code=134512640 該任務在虛擬地址空間的代碼段的起始地址。
    end_code=134513720 該任務在虛擬地址空間的代碼段的結束地址。
    start_stack=3215579040 該任務在虛擬地址空間的棧的結束地址。
    kstkesp=0 esp(32 位堆棧指針) 的當前值, 與在進程的內核堆棧頁得到的一致。
    kstkeip=2097798 指向將要執行的指令的指針, EIP(32 位指令指針)的當前值。
    pendingsig=0 待處理信號的位圖,記錄發送給進程的普通信號。
    block_sig=0 阻塞信號的位圖。
    sigign=0 忽略的信號的位圖。
    sigcatch=082985 被俘獲的信號的位圖。
    wchan=0 如果該進程是睡眠狀態,該值給出調度的調用點。
    nswap 被 swapped 的頁數,當前沒用。
    cnswap 所有子進程被 swapped 的頁數的和,當前沒用。
    exit_signal=17 該進程結束時,向父進程所發送的信號。
    task_cpu(task)=0 運行在哪個 CPU 上。
    task_rt_priority=0 實時進程的相對優先級別。
    task_policy=0 進程的調度策略,0=非實時進程,1=FIFO實時進程;2=RR實時進程

2.2.3、關於進程占用 CPU 的相關信息

在上述的時間中,這些信息會在計算 CPU 占用率時用到:

  • pid 進程號。
  • utime 該任務在用戶態運行的時間,單位為 jiffies。
  • stime 該任務在核心態運行的時間,單位為 jiffies。
  • cutime 累計的該任務的所有的 waited-for 進程曾經在用戶態運行的時間,單位為 jiffies。
  • cstime 累計的該任務的所有的 waited-for 進程曾經在核心態運行的時間,單位為 jiffies。

該進程的 CPU 占用時間(該值包括其所有線程的 CPU 時間):

processCPUTime = utime + stime + cutime + cstime

該文件包含了某一輕量級進程(lwp,即通常所說的線程)所有的活動信息,該文件中的所有值都是從系統啟動開始累計到當前時刻。該文件的內容格式以及各字段的含義與 /proc/[PID]/stat 文件類似。該文件中的 tid 字段表示的是輕量級線程號。

該線程的 CPU 占用時間:

threadCPUTime = utime + stime

2.4.1、基本思想

首先,通過讀取 /proc/stat 文件獲取總的 CPU 時間,讀取 /proc/[PID]/stat 獲取進程 CPU 時間,讀取 /proc/[PID]/task/[TID]/stat 獲取線程 CPU 時間。然后,采樣兩個足夠短的時間間隔的 CPU 快照與進程或線程快照來計算其 CPU 使用率。

2.4.2、計算總的 CPU 使用率 totalCPUUse

1)采樣兩個足夠短的時間間隔的 CPU 快照,即讀取 /proc/stat 文件,獲取兩個時間點的下列數據:

  • CPUT1 (user1, nice1, system1, idle1, iowait1, irq1, softirq1, stealstolen1, guest1);
  • CPUT2 (user2, nice2, system2, idle2, iowait2, irq2, softirq2, stealstolen2, guest2);

2)計算總的 CPU 時間 totalCPUTime:

  • CPUTime1 = user1 + nice1 + system1 + idle1 + iowait1 + irq1 + softirq1 + stealstolen1 + guest1;
  • CPUTime2 = user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + stealstolen2 + guest2;

totalCPUTime = CPUTime2 – CPUTime1;

3)計算 CPU 空閑時間 idleCPUTime:

idleCPUTime = idle2 – idle1;

4)計算總的 CPU 使用率 totalCPUUse:

totalCPUUse = (totalCPUTime – idleCPUTime) / totalCPUTime;

2.4.3、計算某一進程的 CPU 使用率 processCPUUse

1)采樣兩個足夠短的時間間隔的 CPU 快照和對應的進程快照,即讀取 /proc/stat 文件,獲取兩個時間點的下列數據:

  • CPUT1 (user1, nice1, system1, idle1, iowait1, irq1, softirq1, stealstolen1, guest1);
  • CPUT2 (user2, nice2, system2, idle2, iowait2, irq2, softirq2, stealstolen2, guest2);

讀取 /proc/[PID]/stat 文件,獲取兩個時間點的下列數據:

  • ProcessT1 (utime1, stime1, cutime1, cstime1);
  • ProcessT2 (utime2, stime2, cutime2, cstime2);

2)計算總的 CPU 時間 totalCPUTime 和進程時間 processTime:

  • CPUTime1 = user1 + nice1 + system1 + idle1 + iowait1 + irq1 + softirq1 + stealstolen1 + guest1;
  • CPUTime2 = user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + stealstolen2 + guest2;

totalCPUTime = CPUTime2 – CPUTime1;

  • processTime1 = utime1 + stime1 + cutime1 + cstime1;
  • processTime2 = utime2 + stime2 + cutime1 + cstime2;

processTime = processTime2 – processTime1;

3)計算該進程的 CPU 使用率 processCPUUse:

processCPUUse = processTime / totalCPUTime;

2.4.4、計算某一線程的 CPU 使用率 threadCPUUse

1)采樣兩個足夠短的時間間隔的 CPU 快照和對應的線程快照,即讀取 /proc/stat 文件,獲取兩個時間點的下列數據:

  • CPUT1 (user1, nice1, system1, idle1, iowait1, irq1, softirq1, stealstolen1, guest1);
  • CPUT2 (user2, nice2, system2, idle2, iowait2, irq2, softirq2, stealstolen2, guest2);

讀取 /proc/[PID]/task/[TID]/stat 文件,獲取兩個時間點的下列數據:

  • threadT1 (utime1, stime1);
  • threadT2 (utime2, stime2);

2)計算總的 CPU 時間 totalCPUTime 和線程時間 threadTime:

  • CPUTime1 = user1 + nice1 + system1 + idle1 + iowait1 + irq1 + softirq1 + stealstolen1 + guest1;
  • CPUTime2 = user2 + nice2 + system2 + idle2 + iowait2 + irq2 + softirq2 + stealstolen2 + guest2;

totalCPUTime = CPUTime2 – CPUTime1;

  • threadTime1 = utime1 + stime1;
  • threadTime2 = utime2 + stime2;

threadTime = threadTime2 – threadTime1;

3)計算該線程的 CPU 使用率 threadCPUUse:

threadCPUUse = threadTime / totalCPUTime;

2.5.1、基本思想

首先,通過讀取 /proc/stat 文件獲取總的 CPU 時間,讀取 /proc/[PID]/stat 獲取進程 CPU 時間,讀取 /proc/[PID]/task/[TID]/stat 獲取線程 CPU 時間,讀取 /proc/cpuinfo 獲取 CPU 個數。

在多核情況下計算進程或線程的 CPU 使用率,用上面的方式得到的通常是相對於 CPU 所有核的總共時間的占用率,而我們通常習慣得到進程或線程對某一個單核的占用率。所以我們可以按上面的方式計算得到 CPU 占用率,然后把結果乘上 CPU 的核數,即可得到進程或線程相對於一個單核的占用率。

2.5.2、計算總的 CPU 使用率

同 2.4.2。

2.5.3、計算某一進程的 CPU 使用率 mProcessCPUUse

1)同 2.4.3 計算某一進程的 CPU 使用率 processCPUUse;

2)讀取 /proc/cpuinfo 文件獲取邏輯 CPU(processor) 的個數(參見 1.1): processorNum

3)多核情況下該進程的 CPU 使用率 mProcessCPUUse:

mProcessCPUUse = processCPUUse * processorNum;

2.5.4、計算某一線程的 CPU 使用率 mThreadCPUUse

1)同 2.4.4 計算某一線程的 CPU 使用率 threadCPUUse;

2)讀取 /proc/cpuinfo 文件獲取邏輯 CPU(processor) 的個數(參見 1.1): processorNum

3)多核情況下該線程的 CPU 使用率 mThreadCPUUse:

mThreadCPUUse = threadCPUUse * processorNum;

  • 1)不同內核版本 /proc/stat 文件格式不一致。/proc/stat 文件中第一行是總的 CPU 使用情況。
    • 各個內核版本都有的 4 個字段:user, nice, system, idle;
    • 2.5.41 版本新增字段:iowait;
    • 2.6.0-test4 版本新增字段:irq, softirq;
    • 2.6.11 版本新增字段:stealstolen;
    • 2.6.24 版本新增字段:guest;
  • 2)/proc/[PID]/task 目錄是 Linux 2.6.0-test6 之后才有的功能。
  • 3)關於 CPU 使用率為負的情況,解決方案是如果出現負值,連續采樣計算 CPU 使用率直到為非負。
  • 4)有些線程生命周期較短,可能在采樣期間就已經死掉了。
#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>
#include<time.h>
typedefstruct procstat {
char processorName[20];
unsignedint user;
unsignedint nice;
unsignedint system;
unsignedint idle;
unsignedint iowait;
unsignedint irq;
unsignedint softirq;
unsignedint stealstolen;
unsignedint guest;
}Procstat;
Procstat getCPUStatus(){
// Get "/proc/stat" info.
FILE* inputFile = NULL;
chdir("/proc");
inputFile = fopen("stat","r");
if(!inputFile){
perror("error: Can not open file.\n");
}
char buff[1024];
fgets(buff,sizeof(buff), inputFile);// Read 1 line.
printf(buff);
Procstat ps;
sscanf(buff,"%s %u %u %u %u %u %u %u %u %u", ps.processorName,&ps.user,&ps.nice,&ps.system,&ps.idle,&ps.iowait,&ps.irq,&ps.softirq,&ps.stealstolen,&ps.guest);// Scan from "buff".
printf("user: %u\n", ps.user);
fclose(inputFile);
return ps;
}
float calculateCPUUse(Procstat ps1,Procstat ps2){
unsignedint totalCPUTime =(ps2.user + ps2.nice + ps2.system + ps2.idle + ps2.iowait + ps2.irq + ps2.softirq + ps2.stealstolen + ps2.guest)-(ps1.user + ps1.nice + ps1.system + ps1.idle + ps1.iowait + ps1.irq + ps1.softirq + ps1.stealstolen + ps1.guest);
unsignedint idleCPUTime = ps2.idle - ps1.idle;
floatCPUUse=((float) totalCPUTime -(float) idleCPUTime)/(float) totalCPUTime;
printf("totalCPUTime: %u\nidleCPUTime: %u\n", totalCPUTime, idleCPUTime);
returnCPUUse;
}
int main(int argc,char* argv[]){
printf("Test CPU\n");
// Get processor num.
int processorNum = sysconf(_SC_NPROCESSORS_CONF);// "unistd.h" is required.
printf("Processors: %d\n", processorNum);
// Test
Procstat ps1, ps2;
int i =0;
for(i =0; i <=100000; i++){
srand((unsigned) time(NULL));
int m = rand()%100000;
int n =1+ rand()%100000;
int k = m / n;
if(i ==10){
ps1 = getCPUStatus();
}
if(i ==10000){
ps2 = getCPUStatus();
}
}
floatCPUUse= calculateCPUUse(ps1, ps2);
printf("CPUUse: %f\n",CPUUse);
return0;
}

幾個常用參數:

  • a: 與終端無關的所有進程。
  • u: 有效用戶(effective user)的相關進程。
  • x: 通常與 a 參數因使用,可列出較完整的信息。
  • -e: 選擇所有進程。
  • -L: 顯示線程,一般是 LWP 或 NLWP 列。
  • -o: 用戶自定義顯示選項。

示例1)列出所有當前所有正在內存中的進程

[root@rh ~]$ ps aux
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
root 10.00.0193561536?SsApr220:01/sbin/init
root 20.00.000? S Apr220:00[kthreadd]
root 30.00.000? S Apr220:00[migration/0]
root 40.00.000? S Apr220:00[ksoftirqd/0]
root 50.00.000? S Apr220:00[migration/0]
root 60.00.000? S Apr220:00[watchdog/0]
root 70.00.000? S Apr220:00[migration/1]
root 80.00.000? S Apr220:00[migration/1]
......

示例2)列出進程號為 13560 這個進程的所有線程及 CPU 占用率

[root@rh ~]$ ps -eLo pid,lwp,pcpu | grep 13560
135601356049.5

幾個常用的參數:

  • -d: 后面接秒數,就是整個進程畫面更新的頻率。默認是 5 秒。
  • -b: 以批處理的方式執行 top,還有更多的參數可用。通常會搭配數據流重導向,將批處理的結果輸出為文件。
  • -n: 與 -b 搭配,意義是,需要進行幾次 top 的輸出結果。
  • -p: 指定某個 PID 來進行觀察監測。
  • 在 top 執行過程中可以使用的按鍵命令:
  • ?: 顯示在 top 中可以輸入的按鍵命令。
  • P: 按照 CPU 的使用資源排序顯示。
  • M: 按內存(Memory)的使用資源排序顯示。
  • N: 按 PID 來排序。
  • T: 按該進程使用的 CPU 時間積累(TIME+)排序。
  • k: 給某個 PID 一個信號(signal)。
  • r: 給某個 PID 重新確定一個值。
  • 1: 顯示所有 CPU 占用信息。

示例1)將 top 命令執行兩次,然后將結果輸出到 /top_result.data

[root@rh ~]$ top -b -n 2&gt;/top_result.data

示例2)監測進程 13620

[root@rh ~]$ top -d 2-p 13620
top -16:27:35 up 4 days,7:43,2 users, load average:0.35,0.47,0.44
Tasks:1 total,1 running,0 sleeping,0 stopped,0 zombie
Cpu(s):0.1%us,3.1%sy,0.0%ni,96.5%id,0.0%wa,0.0%hi,0.3%si,0.0%st
Mem:16320632k total,1790796k used,14529836k free,233168k buffers
Swap:8232952k total,0k used,8232952k free,941540k cached
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
13620 test1370 20011060944760 R 53.40.00:04.78 netperf

http://www.blogjava.net/fjzag/articles/317773.html

http://www.brokestream.com/procstat.html

http://blog.csdn.net/zg_hover/article/details/4356210

《鳥哥Linux私房菜》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM