相信不少人都對Linux中top命令裏「load average」這一欄困惑過,到底什麼是Load,Load表明了什麼含義,Load高會有什麼後果?「%CPU」這一欄爲何會超過100%,它是如何計算的?
帶着這些問題,咱們經過一些測試,來探索下其中的不解之處。python
首先,咱們經過實驗來大概肯定其計算方式:
測試服務器:4核Xeon處理器
測試軟件:MySQL 5.1.40
服務器上除了mysql沒有運行其餘任何非系統自帶軟件。由於MySQL只能單線程運行單條SQL,因此能夠很好的經過增長查詢併發來控制使用的CPU核數。mysql
空載時,top的信息爲:linux
top – 14:51:47 up 35 days, 4:43, 1 user, load average: 0.00, 0.00, 0.00 Tasks: 76 total, 1 running, 75 sleeping, 0 stopped, 0 zombie Cpu(s): 0.0%us, 0.0%sy, 0.0%ni, 99.5%id, 0.1%wa, 0.2%hi, 0.2%si, 0.0%st
在數據庫中啓動一個大查詢:sql
top – 15:28:09 up 35 days, 5:19, 3 users, load average: 0.99, 0.92, 0.67 Tasks: 80 total, 1 running, 79 sleeping, 0 stopped, 0 zombie Cpu0 : 0.0%us, 0.0%sy, 0.0%ni, 96.3%id, 0.0%wa, 1.3%hi, 2.3%si, 0.0%st Cpu1 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu2 : 98.7%us, 1.3%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu3 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
同時能夠看到%CPU也是在100%數據庫
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 877 mysql 15 0 308m 137m 4644 S 99.9 6.8 15:13.28 mysqld
而後開啓第二個大查詢,不久就能夠看到top信息的變化,Load到了2: 數組
top – 15:36:44 up 35 days, 5:28, 3 users, load average: 1.99, 1.62, 1.08 Tasks: 80 total, 1 running, 79 sleeping, 0 stopped, 0 zombie Cpu0 : 0.0%us, 0.0%sy, 0.0%ni, 97.7%id, 0.0%wa, 1.0%hi, 1.3%si, 0.0%st Cpu1 : 99.0%us, 1.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu2 : 0.0%us, 0.0%sy, 0.0%ni,100.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Cpu3 : 99.0%us, 1.0%sy, 0.0%ni, 0.0%id, 0.0%wa, 0.0%hi, 0.0%si, 0.0%st
也能夠觀察到%CPU增長到了200%:服務器
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 877 mysql 15 0 312m 141m 4644 S 199.8 7.0 22:31.27 mysqld
由此能夠簡單的作出以下臨時結論:
1. %CPU是由每一個核的CPU佔用律之和算出來的。
2. load跟執行的任務數有關
不過要想準確的知道其含義,仍是必須從源碼入手。網絡
下載busybox的源碼,在procps目錄下有top.c的源碼,查看第293行附近(1.17.1版),能夠看到 併發
if (prev_hist_count) do { if (prev_hist[i].pid == pid) { cur->pcpu = cur->ticks - prev_hist[i].ticks; total_pcpu += cur->pcpu; break; } i = (i+1) % prev_hist_count; /* hist_iterations++; */ } while (i != last_i);
這就是計算%CPU的代碼,很明顯total_pcpu就是累加了每一個線程對每一個核的使用率,因此%CPU的最大值就是核數*100%。函數
而CPU利用率又是怎麼計算的呢,跟蹤代碼能夠發現,是從系統的/proc/stat這裏讀取的,這個文件的格式能夠參考:http://www.linuxhowtos.org/System/procstat.htm,下面是我筆記本上讀出來的內容。
plx@plinux-Laptop:~/busybox-1.17.1$ cat /proc/stat cpu 520529 3525 658608 3500749 210662 6650 29698 0 0 cpu0 249045 1936 466387 1624486 136381 308 17051 0 0 cpu1 271483 1588 192221 1876263 74281 6342 12646 0 0 intr 84067574 42497789 41743 0 0 0 0 0 0 1 57928 0 0 7175 0 0 0 477092 24693 0 5 0 183 0 20 0 0 0 12455 821851 745906 10192555 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 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 142313984 btime 1281403521 processes 6707 procs_running 2 procs_blocked 0 softirq 56932805 0 20168080 9440286 238191 821787 0 10621375 4052209 13257 11577620
cpuN的含義從左到右分別是:user、system、nice、idle、iowait、irq、softirq,具體含義能夠看文檔。
在下面幾行的含義是:
「intr」這行給出中斷的信息,第一個爲自系統啓動以來,發生的全部的中斷的次數;而後每一個數對應一個特定的中斷自系統啓動以來所發生的次數。
「ctxt」給出了自系統啓動以來CPU發生的上下文交換的次數。
「btime」給出了從系統啓動到如今爲止的時間,單位爲秒。
「processes (total_forks) 自系統啓動以來所建立的任務的個數目。
「procs_running」:當前運行隊列的任務的數目。
「procs_blocked」:當前被阻塞的任務的數目。
那麼CPU利用率能夠使用如下方法,先取兩個採樣點,而後計算其差值:
cpu usage=(idle2-idle1)/(cpu2-cpu1)*100 cpu usage=[(user_2 +sys_2+nice_2) - (user_1 + sys_1+nice_1)]/(total_2 - total_1)*100;
這是一段Bash代碼採集利用率的,摘自網絡:
#!/bin/sh ##echo user nice system idle iowait irq softirq CPULOG_1=$(cat /proc/stat | grep 'cpu ' | awk '{print $2" "$3" "$4" "$5" "$6" "$7" "$8}') SYS_IDLE_1=$(echo $CPULOG_1 | awk '{print $4}') Total_1=$(echo $CPULOG_1 | awk '{print $1+$2+$3+$4+$5+$6+$7}') sleep 5 CPULOG_2=$(cat /proc/stat | grep 'cpu ' | awk '{print $2" "$3" "$4" "$5" "$6" "$7" "$8}') SYS_IDLE_2=$(echo $CPULOG_2 | awk '{print $4}') Total_2=$(echo $CPULOG_2 | awk '{print $1+$2+$3+$4+$5+$6+$7}') SYS_IDLE=`expr $SYS_IDLE_2 - $SYS_IDLE_1` Total=`expr $Total_2 - $Total_1` SYS_USAGE=`expr $SYS_IDLE/$Total*100 |bc -l` SYS_Rate=`expr 100-$SYS_USAGE |bc -l` Disp_SYS_Rate=`expr "scale=3; $SYS_Rate/1" |bc` echo $Disp_SYS_Rate%
還有一段Perl的代碼,也是摘自網絡:
#!/usr/bin/perl use warnings; $SLEEPTIME=5; if (-e "/tmp/stat") { unlink "/tmp/stat"; } open (JIFF_TMP, ">>/tmp/stat") || die "Can't open /proc/stat file!\n"; open (JIFF, "/proc/stat") || die "Can't open /proc/stat file!\n"; @jiff_0=; print JIFF_TMP $jiff_0[0] ; close (JIFF); sleep $SLEEPTIME; open (JIFF, "/proc/stat") || die "Can't open /proc/stat file!\n"; @jiff_1=; print JIFF_TMP $jiff_1[0]; close (JIFF); close (JIFF_TMP); @USER=`awk '{print \$2}' "/tmp/stat"`; @NICE=`awk '{print \$3}' "/tmp/stat"`; @SYSTEM=`awk '{print \$4}' "/tmp/stat"`; @IDLE=`awk '{print \$5}' "/tmp/stat"`; @IOWAIT=`awk '{print \$6}' "/tmp/stat"`; @IRQ=`awk '{print \$7}' "/tmp/stat"`; @SOFTIRQ=`awk '{print \$8}' "/tmp/stat"`; $JIFF_0=$USER[0]+$NICE[0]+$SYSTEM[0]+$IDLE[0]+$IOWAIT[0]+$IRQ[0]+$SOFTIRQ[0]; $JIFF_1=$USER[1]+$NICE[1]+$SYSTEM[1]+$IDLE[1]+$IOWAIT[1]+$IRQ[1]+$SOFTIRQ[1]; $SYS_IDLE=($IDLE[0]-$IDLE[1]) / ($JIFF_0-$JIFF_1) * 100; $SYS_USAGE=100 - $SYS_IDLE; printf ("The CPU usage is %1.2f%%\n",$SYS_USAGE);
跟蹤busybox的代碼能夠知道,load是從/proc/loadavg中讀取的。
我本機的一次抓取內容以下:
plx@plinux-Laptop:~/busybox-1.17.1$ cat /proc/loadavg 0.64 0.81 0.86 3/364 6930
每一個值的含義依次爲:
lavg_1 (0.64) 1-分鐘平均負載
lavg_5 (0.81) 5-分鐘平均負載
lavg_15(0.86) 15-分鐘平均負載
nr_running (3) 在採樣時刻,運行隊列的任務的數目,與/proc/stat的procs_running表示相贊成思
nr_threads (364) 在採樣時刻,系統中活躍的任務的個數(不包括運行已經結束的任務)
last_pid(6930) 最大的pid值,包括輕量級進程,即線程。
假設當前有兩個CPU,則每一個CPU的當前任務數爲0.64/2=0.32
咱們能夠在linux內核中找到loadavg文件的源碼:
tatic int loadavg_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data) { int a, b, c; int len; # a = avenrun[0] + (FIXED_1/200); b = avenrun[1] + (FIXED_1/200); c = avenrun[2] + (FIXED_1/200); len = sprintf(page,"%d.%02d %d.%02d %d.%02d %ld/%d %d\n", LOAD_INT(a), LOAD_FRAC(a), LOAD_INT(b), LOAD_FRAC(b), LOAD_INT(c), LOAD_FRAC(c), nr_running(), nr_threads, last_pid); return proc_calc_metrics(page, start, off, count, eof, len); }
以及計算load的代碼:
#define FSHIFT 11 /* nr of bits of precision */ #define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point(定點) */ #define LOAD_FREQ (5*HZ) /* 5 sec intervals,每隔5秒計算一次平均負載值 */ #define CALC_LOAD(load, exp, n) \ load *= exp; \ load += n*(FIXED_1 - exp); \ load >>= FSHIFT; unsigned long avenrun[3]; EXPORT_SYMBOL(avenrun); /* * calc_load - given tick count, update the avenrun load estimates. * This is called while holding a write_lock on xtime_lock. */ static inline void calc_load(unsigned long ticks) { unsigned long active_tasks; /* fixed-point */ static int count = LOAD_FREQ; count -= ticks; if (count < 0) { count += LOAD_FREQ; active_tasks = count_active_tasks(); CALC_LOAD(avenrun[0], EXP_1, active_tasks); CALC_LOAD(avenrun[1], EXP_5, active_tasks); CALC_LOAD(avenrun[2], EXP_15, active_tasks); } }
看了大師的文章,理解了這些代碼。
因此能夠明白:Linux的系統負載指運行隊列的平均長度,也就是等待CPU的平均進程數。 Linux的系統負載指運行隊列的平均長度,也就是等待CPU的平均進程數。由於Linux內禁止浮點運算,所以系統的負載只能經過計算變化的次數這一修正值來計算。Linux內核定義一個長度爲3的雙字數組avenrun,雙字的低11位用於存放負載的小數部分,高21位用於存放整數部分。當進程所耗的 CPU時間片數超過CPU在5秒內可以提供的時間片數時,內核計算上述的三個負載。負載初始化爲0,假設最近一、五、15分鐘內的平均負載分別爲 load一、load5和load15,那麼下一個計算時刻到來時,內核經過下面的算式計算負載:
load1 -= load1 -* exp(-5 / 60) -+ n * (1 – exp(-5 / 60 )) load5 -= load5 -* exp(-5 / 300) + n * (1 – exp(-5 / 300)) load15 = load15 * exp(-5 / 900) + n * (1 – exp(-5 / 900))
其中,exp(x)爲e的x次冪,n爲當前運行隊列的長度。Linux內核認爲進程的生存時間服從參數爲1的指數分佈,指數分佈的機率密度爲:之內核計算負載load1爲例,設相鄰兩個計算時刻之間系統活動的進程集合爲S0。從1分鐘前到當前計算時刻這段時間裏面活動的load1個進程,設他們的集合是 S1,內核認爲的機率密度是:λe-λx,而在當前時刻活動的n個進程,設他們的集合是Sn內核認爲的機率密度是1-λe-λx。其中x = 5 / 60,由於相鄰兩個計算時刻之間進程所耗的CPU時間爲5秒,而考慮的時間段是1分鐘(60秒)。那麼能夠求出最近1分鐘系統運行隊列的長度:
load1 = |S1| -* λe-λx + |Sn| * (1-λe-λx) = load1 * λe-λx + n * (1-λe-λx)
其中λ = 1, x = 5 / 60, |S1|和|Sn|是集合元素的個數,這就是Linux內核源文件shed.c的函數calc_load()計算負載的數學依據。因此「Load值=CPU核數」,這是最理想的狀態,沒有任何競爭,一個任務分配一個核。因爲數據是每隔5秒鐘檢查一次活躍的進程數,而後根據這個數值算出來的。若是這個數除以CPU的核數,結果高於5的時候就代表系統在超負荷運轉了。