mirror of
https://github.com/beyondx/Notes.git
synced 2026-11-06 07:17:25 +08:00
add lots file of APUE
This commit is contained in:
@@ -81,5 +81,6 @@ mendeley — a papers eg. pdf word organizer
|
||||
dia — a 'viso' like flow-chart drawing software.
|
||||
fcitx — a input method
|
||||
evince — a simple pdf viewer
|
||||
iftop --- interface top
|
||||
|
||||
###################################################################
|
||||
|
||||
191
Zim/Linux/Load和CPU利用率是如何算出来的.txt
Normal file
191
Zim/Linux/Load和CPU利用率是如何算出来的.txt
Normal file
@@ -0,0 +1,191 @@
|
||||
Content-Type: text/x-zim-wiki
|
||||
Wiki-Format: zim 0.4
|
||||
Creation-Date: 2012-10-23T10:30:02+08:00
|
||||
|
||||
====== Load和CPU利用率是如何算出来的 ======
|
||||
Created Tuesday 23 October 2012
|
||||
|
||||
本文内容遵从CC版权协议, 可以随意转载, 但必须以超链接形式标明文章原始出处和作者信息及版权声明
|
||||
网址: http://www.penglixun.com/tech/system/how_to_calc_load_cpu.html
|
||||
|
||||
相信很多人都对Linux中top命令里“load average”这一栏困惑过,到底什么是Load,Load代表了什么含义,Load高会有什么后果?“%CPU”这一栏为什么会超过100%,它是如何计算的?带着这些问题,我们通过一些测试,来探索下其中的不解之处。
|
||||
|
||||
首先,我们通过实验来大概确定其计算方式:
|
||||
测试服务器:4核Xeon处理器
|
||||
测试软件:MySQL 5.1.40
|
||||
服务器上除了MySQL没有运行其他任何非系统自带软件。因为__MySQL只能单线程运行单条SQL__,所以可以很好的通过**增加查询并发**来控制使用的CPU核数。
|
||||
|
||||
空载时,top的信息为:
|
||||
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
|
||||
|
||||
在数据库中启动一个大查询:
|
||||
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 //__同一个PID下有两个线程,每个线程处理一条SQL语句__。
|
||||
|
||||
由此可以简单的做出如下临时结论:
|
||||
1. %CPU是由__每个核的CPU占用率之和__算出来的。
|
||||
2. load跟执行的任务数有关
|
||||
|
||||
不过要想准确的知道其含义,还是必须从源码入手。
|
||||
|
||||
===== CPU利用率的计算方法 =====
|
||||
下载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%
|
||||
|
||||
===== Load的计算方法 =====
|
||||
跟踪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内禁止浮点运算,因此系统的负载只能通过计算变化的次数这一修正值来计算。Linux内核定义一个长度为3的双字数组avenrun,双字的低11位用于存放负载的小数部分,高21位用于存放整数部分。当进程所耗的 CPU时间片数超过CPU在5秒内能够提供的时间片数时,内核计算上述的三个负载。负载初始化为0,假设最近1、5、15分钟内的平均负载分别为 load1、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的时候就表明系统在超负荷运转了。
|
||||
Reference in New Issue
Block a user