CPU 負載¶
Linux 透過 /proc/stat 和 /proc/uptime 匯出各種資訊,使用者空間工具(如 top(1))使用這些資訊來計算系統在特定狀態下花費的平均時間,例如
$ iostat
Linux 2.6.18.3-exp (linmac) 02/20/2007
avg-cpu: %user %nice %system %iowait %steal %idle
10.01 0.00 2.92 5.44 0.00 81.63
...
這裡系統認為在預設取樣週期內,系統花費了 10.01% 的時間在使用者空間工作,2.92% 的時間在核心中,並且總體上 81.63% 的時間處於空閒狀態。
在大多數情況下,/proc/stat 資訊非常接近現實,但是由於核心收集此資料的方式/時間的性質,有時根本無法信任它。
那麼這些資訊是如何收集的呢?每當定時器中斷髮出訊號時,核心會檢視此時正在執行的任務型別,並增加與此任務型別/狀態相對應的計數器。問題在於,系統可能在兩次定時器中斷之間在各種狀態之間切換了多次,但僅針對最後一種狀態增加計數器。
例子¶
如果我們想象一個系統,其中一個任務以以下方式定期消耗週期
time line between two timer interrupts
|--------------------------------------|
^ ^
|_ something begins working |
|_ something goes to sleep
(only to be awaken quite soon)
在上述情況下,根據 /proc/stat,系統將 0% 負載(因為定時器中斷將始終在系統執行空閒處理程式時發生),但實際上負載接近 99%。
可以想象更多的情況,核心的這種行為將導致 /proc/stat 中的資訊非常不穩定
/* gcc -o hog smallhog.c */
#include <time.h>
#include <limits.h>
#include <signal.h>
#include <sys/time.h>
#define HIST 10
static volatile sig_atomic_t stop;
static void sighandler(int signr)
{
(void) signr;
stop = 1;
}
static unsigned long hog (unsigned long niters)
{
stop = 0;
while (!stop && --niters);
return niters;
}
int main (void)
{
int i;
struct itimerval it = {
.it_interval = { .tv_sec = 0, .tv_usec = 1 },
.it_value = { .tv_sec = 0, .tv_usec = 1 } };
sigset_t set;
unsigned long v[HIST];
double tmp = 0.0;
unsigned long n;
signal(SIGALRM, &sighandler);
setitimer(ITIMER_REAL, &it, NULL);
hog (ULONG_MAX);
for (i = 0; i < HIST; ++i) v[i] = ULONG_MAX - hog(ULONG_MAX);
for (i = 0; i < HIST; ++i) tmp += v[i];
tmp /= HIST;
n = tmp - (tmp / 3.0);
sigemptyset(&set);
sigaddset(&set, SIGALRM);
for (;;) {
hog(n);
sigwait(&set, &i);
}
return 0;
}
參考¶
感謝¶
Con Kolivas, Pavel Machek