linux top命令分析 +部分内核源码
■软中断处理时间( SoftIrq time) 系统处理软中断中断所花费的时间。 ■丢失时间( Steal time) 被强制等待( involuntary wait)虚拟CPU的时间,此时 hypervisor在为另一个虚 拟处理器服务 下面是我们在top命令看到的CPU山用率信息及各项值含义。 pu(s):0.2%us,0.2%S;0.0%ni199.2%id,0.5%Wa,0.0%hi,0.0%s, 0.0%st us: User time sy: System time ni: nice time id idle time wa: Waiting time hi: Hard irg time si: Softing time st: Steal time 1.2CPU占用率计算 Linux cpu占用率计算,都是根据/proc/stat文件内容计算而来,卜面是stat 文件内容样例,内核版本不同,会稍有不同,但内容基本一致 root @linux driver -# cat/proc/stat cpu66173346850392523305557354883514244158490 cpu06422864125723579891795042731239744200 cpul308241011961858264165790266600 cpu216759822615133658340033288291663105630 cpu31216641761072465846219478291178040 intr6116016235870936293002030100040525716100000000000 00000000000000000000000000000000000000000000 000020000000000000001115034600000002935310000000 00000000000000000000000000000000000000000000 00000000000000000007806901000000040000000000000 00000000000000000000000000000000000000000000 00000 ctxt1887995917 btime1244449131 processes 1938183 procs running 1 procs blocked O CPU信息,cpu为总的信息,cpu0...cpun为各个具体CPU信息 cpu66173346850392523305557354883514244158490 上面共有8个值(单位: ticks),分别为 User time, 661733 Nice time, 468 System time, 503925 Id| e time,233055573 Waiting time, 548835 Hard Irg time, 14244 SoftirQ time, 15849 Steal time, 0 cPU占用率计算公式如下: CPU H]=user +system+nice +idle+iowait +irg+softirq+ Stl %us=( User time+ Nice time)/CPU时间*100% Sy=(System time Hard Irq time SoftIrQ time)/CPU Hf H*100% %d=( Idle time)/CpU时间*100% %n=( Nice time)CpU时间*100% %Wa= Waiting time)/CPU时间*100% %hi=( Hard Irg time)CPU时间*100% %s=( SoftiRQ time)CPU时间*100% st=( Steal time)CPU时间*100% 2CPU占用率内核实现 下面以RHEL6内核源码版本2.6.32-220.,e6×86_64为例,来介绍内核源码实现。 /proc/stat文件的创建由函数 proc_stat_init()实现,在文件fs/proC/ stat,c中,在内核 初始化时调用。/proc/stat文件相关函数时间均在 stat c文件中 对/proc/stat文件的读写方法为proc_stat_ operations 00152: static const struct file_operations proc_ _stat_operations =f 00153 open stat open 00154 read seq_ read 00155 lseek seq Iseek 00156:. release single_release, 00157:}; 00158 打廾文件函数 stat open(),函数首先申请大小为sze的内存,来存放临时数据 (也是我们看到的stat里的最终数据)。 00128: static int stat open(struct inode *inode, struct file file) 00129 00130 unsigned size=4096*(1 + num_possible_cpus(/32) 00131 char * buf; 00132 struct seq_file *m 00133 int res 00134 00135 /*don t ask for more than the kmalloc( max size, currently 128KB*/ 00136:if(sze>128*1024) 00137: size=128米1024 00138 buf kmalloc(size, GFP_KERNEL); 00139 if(! buf) 00140 return - ENOMEM 00141 00142: res single_open (file, show_stat, NULL) 00143:f(!res){ 00144 m= file->private data 00145 m->buf buf: 00146 m->SIze= sIzer 00147 s else 00148 kfree (buf) 00149: return res 00150:3? end stat open? 00151 /proc/stat文件的数据山 show state)函数填充注意42行 for_each_possible_cpu() 循环,是累加计算所有CPU的数据,如我们前面的示例看到的/proc/stat文件中第一行 值。 cp66173346850392523305557354883514244158490 00025: static int show stat(struct seq_file* p, void*v) 00026:{ 00027:nti,j 00028: unsigned long jif 00029: cputime64 t user, nice, system, idle, iowait irg, softing steal 00030: cputime64 t guest: 00031:U64sum=0 00032: u64 sum_softing=0; 00033: unsigned int per_softirq_sums[NR_SOFTIRQS]=10S 00034: struct timespec boottime 00035 00036: user nice= system= idle= nowait 00037: q=softirq steal cputime64_zero 00038: guest =cputime64_zero: 00039: getboottime(&boottime) 00040: if boottime tv_secr 00041 00042 for each _possible cput 00043: user= cputime64_add(user, kstat_cpu(i). cpustat. user) 00044 nice= cputime64_add(nice, kstat_ cpu(i). cpustat. nice); 00045 system= cputime64_ add (system, kstat_cpu(i). cpustat. system) 00046: idle= cputime64_add (idle, kstat_cpu(). cpustat. idle 00047 idle= cputime64-_add(idle, arch_idle_time(; 00048 nowait= cputime64_add(iowait, kstat cpu(i).cpustat. iowa 00049 irq= cputime64_add (irg, kstat_cpu(). cpustat. irg) 00050 softirq= cputime64_add (softirq kstat_cpu(i). cpustat. softing) 00051: steal= cputime 64_add(steal, kstat_cpu(i). cpustat. steal) 00052: guest= cputime 64_add (guest, kstat_cpu (i). cpustat. guest) 00053: sum + kstat cpu irgs sum( 00054 sum + arch_irq stat cpu 00055 00056 forG=0;j< NR_-SOFTIRQS; j++)t 00057 unsigned int softirq_stat=kstat_softirqs_cpu (, i 00058: 00059 per_softing_sums[]+=softirq_stat 00060: sum_softing + softirq_stat 00061 00062 00063: sum + arch irg stat o: 00064 00065: seq_printf(p, cpu %llu%llu %llu %llu %alu %ollu %alu %llu %olu\n 00066 (unsigned long long)cputime64_to_ clock_t(user) 00067 (unsigned long longcputime64 to clock t(nice) 00068 (unsigned long long)cputime64-to_ clock_t(system), 00069: (unsigned longlong)cputime64_to clock_t(idle) 00070 (unsigned long long)cputime64to_clock_t(iowait 00071: (unsigned long long)cputime64_ to clock_t(irq 00072: (unsigned long long)cputime64_to_clock_t(softing), 00073 (unsigned long long)cputime64_to__clock_t(steal), 00074; (unsigned long long)cputime64_to_clock_t(guest)) 计算总的CPU各个值user、nice、 system、idle、 nowait、irq、 softirq、stea后, 就分别计算各个CPU的使用情况(75~100行) 00075: for_ each_online cpu(i 00076 00077: / Copy values here to work around gcc-2.95.3, gcc-2.96*/ 00078 user= kstat cpu( cpustat. user 00079 nice= kstat cpu(. cpustat. nice: 00080 system = kstat_cpu(. cpustat. system 00081: idle= kstat_cpu(cpustat. idle 00082: idle cputime64_add (idle, arch_idle_time(d) 00083 nowait kstat cpu(. cpustat. nowait 00084: irg kstat cpu(. cpustat. irg 00085 softing= kstat cpu( cpustat. softing: 00086 steal kstat cpu( cpustat. steal 00087 guest kstat cpu(. cpustat. guest: 00088 00089 sCpu%d llu %llu %llu %lulu %llu %ollu %llu /llu\ t q_printf(p 00090: 00091: (unsigned long long)cputime64 to clock t(user) 00092 (unsigned long long)cputime64to_ clock_t(nice) 00093: (unsigned long long)cputime64 to clock_t(system) 00094: (unsigned longlong)cputime64_to_clock_t(idle 00095: (unsigned long long)cputime64_to_clock_t(iowait), 00096; (unsigned long long)cputime64-to_clock_t(irg), 00097 (unsigned long long)cputime64_to_clock_t(softirq), 00098: (unsigned long long)cputime64_to_clock_t(steal), 00099: (unsigned long long)cputime64_to_clock_t(guest) 00100:} 104行计算所有CPU上中断次数,104~105行计算CPU上每个中断向量的 中断次数。注意:/proc/stat文件中,将所有可能的 NR IRQS个中断向量计数 都记录下来,但我们的机器上通过只是用少量的中断向量,这就是看到/proc/stat 文什中,int一行后面很多值为0的原因 show_stat()函数最后获取进程切换次数 next、内核启动的时间btme 所有创建的进程 processes、正在运行进程的数量 procs_ running、阻塞的进程数 量 procs blocked和所有io等待的进程数量 00101: seq_printf(p, "intr %ollu"(unsigned long long)sum) 00102: 00103 x sum again it could be updated? 00104: for_ each_ irq_nrg 00105 seq_printf(p, "%u", kstat_irqs()) 00106 00107 seq_printf 00108 Inctxt %ollu\n 00109 btime olu\n 00110: processes lu\n 00111: procs_running %lu\n 00112 procs blocked %lu\n 00113: nr context switches o, 00114 (unsigned long)jif, 00115: total forks 00116 nr running 00117 nr_nowait) 00118: 00119: seq_printf(p, "softirq %llu"(unsigned long long)sum_softirq) 00120: 00121: for(i=0;i< NR_SOFTIRQS; i++ 00122 seq_printf(p, %u,per_softirq_sums[in) 00123: seq_printf(p, n; 00124: 00125: return 0: 00126:1? end show_ stat? 00127 3 Linux cPu占用率精确性分析 在使用类似top命令,观察系统及各进程CPU占用率时,可以指定刷新时间间隔, 以及时刷新和实吋观察CPU占用率。 top命令默认情况下,是每3秒刷新一次。也可以通过top-d来 指定刷新频率,如top-d0.1或top-d0.01等。top执行时,也可以按“s”键,修改 时间间隔。 我们可以将CPU占用率刷新问隔设置很低,如0.01秒。但过低的刷新频率是否能 够更准确观察到C門U占用※?Linux系统提供的CPU占用礻信息是否足够精硝? 根据前面分析,我们已知 Linux是根据/proc/stat文件的内容来计算CPU占用率,也 就是精确度和/proc/stat提供的数据精确度有关。那么 (1)/poc/stat文件中的内容单位是什么? (2)多久会刷新/po/stat中的数据? Cp92604160589490320280700 cp08004733677236580300 3.1/proc/tat中的数据单位精度 /proc/stat中CPU数据信息,单位是 ticks。内核中有个全局变量jfes,米记录系 统启动以来,经历的tcks数量。 cp173020036639630000 ticks(滴答)就是系统吋钟中断的吋间间隔,该值与内核中HZ值有关,即 ticks L/HZ。Hz值的大小,在内核编译时可配置的。某台机器上是RHEL6.1内核,配置的 Hz值为1000 /root(a ssd boot/# uname-a Linux ssd2.6.32-131.0.15.el6x8664# SMP Tue May1015.:42:40EDT2011x8664x8664x8664 GNulinux /root(assd boot/ i cat config-26.32-131015.el6x86 64 grep CONFIG HZ CONFiG HZ 100 is not set CONFiG HZ 250 is not set 开 CONFIG HZ300 Is not set CONTIG 11Z 1000=y CONFIG HZ=1000 /rootassd boot/# Hz的值,就是每秒的时钟中断数量。可以观察/proc/ interrupts中时钟中断值变化, 来计算Hz的值。当HZ的值为1000时, ticks的单位即为1/1000秒,即1ms Every 5.0s: cat/proc/interrupts grep LOc Tue may l515:54:222012 LOC 1021246 30859928013 10995 37126 95699 11592852399641 55290l 6392358053 20580 17037 49626 1004223 48133 Local timer interrupts 32CPU利用率统计信息更新 在时钟中断程序中,更新CPU利用信息,即每个tcks更新一次。 include/inux/ kernel stat h中,有相应函数接口,专门用来更新CPU利用率信息。如 account user time()是更新用户态CPU信息。 00111:/* 00112: Lock/unlock the current runqueue- to extract task statistics 00113 00114: extern unsigned long long task delta exec(struct task struct 00115 00116: extern void account user time(struct task struct*, cputime t, cputime t 00117: extern void account system time(struct task struct int, cputime t,
暂无评论