衡量Linux CPU使用的指标
需要关注以下地方:
第一段需要关注的值:
·使用率
·在用户空间所消耗的时间百分比
·在系统空间锁消耗的时间百分比
·消耗在IO等待上的时间
如果一个主机上有大量的cpu消耗在IO等待上,那么说明IO活动非常频繁,而IO子系统性能非常差。
因此我们看到wite的时间居高不下时,说明IO活动非常频繁的,IO子系统非常差
但如果wite的时间不是特别离谱,一般而言问题都不大
第二段需要关注的值:
·空闲时间
·平均负载,CPU等待运行活动队列中等待运行的进程的个数
·可运行的进程数,处于等待状态的进程数,这个数值一般不能超出所有cpu物理核心的10倍,如果超出则意味着CPU性能差
·阻塞进程,一般引起阻塞是由IO调用引起的,IO调用长时间不能得到满足则会引起阻塞,而处于非可中断的状态
·上下文切换的个数
·中断的个数,一旦有IO发生,这个对应的设备则会发起中断请求,中断了CPU正在进行的进程而转而中断处理程序
在任何CPU上如果中断量很大的话也意味着cpu被打断的次数频繁,所以衡量一个cpu是否被频繁打断的话,不仅要衡量context switch还要衡量interrupts
绑定CPU
如果我们期望某一颗cpu上只运行某一进程
以nginx为例:
我们有2个4核的cpu,8个核心,我们可以实现2个核心运行内核线程以及nginx的master进程,而其他6个核心运行worker线程
实现绑定:
在启动系统时向内核传递参数,手动隔离专用的物理核心,在隔离完成之后手动启动起来了,并且手动去分配这被隔离的6个核心
隔离物理核心并不意味着没有切换,因为有些不是切换而是中断处理,隔离了其他进程但不能隔离这个CPU中断的能力
因为我们有8颗物理核心,当任何一个进程的中断,有可能会被8颗中的某1颗所处理,所以可能包括当前已经隔离出的CPU,也就是说只是隔离的进程而没有隔离中断,而我们为了做到绝不处理任何额外的程序,还需要隔离中断
可以明确定义某个中断号的中断只关联到某特定的CPU上,而不中断到已经被隔离出的CPU
总结如下:
在启动系统时向内核传递参数,手动隔离专用的物理核心,在隔离完成之后手动启动起来了,并且手动去分配这被隔离的6个核心
还要将隔离出的CPU从中断处理中隔离出来,绑定进程到CPU上
如何实现在启动系统时隔离CPU并且在启动之后使用taskset绑定其他进程
在/etc/grub.conf 向内核中传递参数:
isolcpus=cpu number,..cpu number
将隔离出的CPU,后面跟上隔离出来的号码比如8颗CPU,将0和1正常使用,将2到7都隔离出来,于是
isolcpus=2,3,4,..cpu number
这样以来 这些CPU将不再处理了
启动完成后手动将几个worker进程绑定在taskset上,明确说明启动几个线程,完后将从隔离的cpu上脱离出来即可
脱离中断处理程序
明确定义每个中断,只能够运行在哪个CPU上,用来实现将一个irq(中断请求处理),只关联到负责运行在其他cpu上的应用程序,我们被称为cpumask
[[email protected]_node1 ~]# ls /proc/irq/
0 10 12 14 2 25 4 6 8 default_smp_affinity
1 11 13 15 24 3 5 7 9
查看默认是实现的关联性
[[email protected]_node1 ~]#cat /proc/irq/default_smp_affinity
3
查看第1号中断下所关联的cpu
[[email protected]_node1 ~]# cat /proc/irq/1/smp_affinity
3
这是在第3课cpu上运行的,我们继续看
[[email protected]_node1 ~]# cat /proc/irq/10/smp_affinity
3
[[email protected]_node1 ~]# cat /proc/irq/11/smp_affinity
1
默认情况下可能都会关联到同一CPU上去,而默认我们给的是mask
而这些mask完全可以使用cpu的号码或响应mask值响应定义的,而我们记得这些中断的处理我们必须手动指定专门隔离出来之外的cpu上
而我们指定的话直接使用echo就可以了(比如我们指定0号和1号cpu)
[[email protected]_node1 ~]# echo cpu_mask > /proc/irq/<irq_num>/smp_affinity
这些操作必须手动进行
实现调度器的定义
对于一个系统而言最核心的资源是CPU和内存,因此我们完全可以将一个主机上的所有可能cpu并将其归类到根上,而后将其分资源组,可能每个组里分配不均,资源组织有CPU是不能运行进程的,所以我们还要对其划分内存资源
但是注意的是内存是无法分段的,因为内存只有一段
如果真的要跨段访问的话,首先要通知给自己的内存控制器,自己的内存控制器发现不是自己本段内存的,于是向对方内存控制器发送请求,所以我们的周期需要在6次之内才能完成,于是跨段内存访问在numa架构上性能比较差的,所以在很多时候,内核默认策略是每隔1秒钟的时候就重新均衡一次,也就是说在numa结构上一个进程有可能随时被调度到其他CPU上去,那也就意味着跨段内存访问也很常见,为了提高numa架构的性能,应该启用cpu清元性,尽可能不做均衡,除非在不均衡情况非常糟糕的情况下
如果资源组有numa这种机制的话,说明每个物理cpu本地都对应都一段物理内存,一般比较常见的是有两个cpu,每个CPU有N个核心的这种方式
linux如果使用资源组的机制,那么我们需要使用cpusets这种虚拟文件系统来完成了
简单来讲,我们需要分为2个步骤:
1、划分资源组,并且将资源组内哪些资源划分进来
2、将某个进程绑定到组内,就能实现将实现只能在这个资源组内,尽可能实现本地资源的本地性
RHEL6 自带就有其机制,可以自动完成资源组的划分和归派
手动划分资源组:
[[email protected] ~]# mkdir /cgroup
编辑fstab,加入以下参数
cpuset /cgroup cpuset defaults 0 0
挂载虚拟文件系统
[[email protected] ~]# mount -a
进入cgroup
[[email protected] ~]# cd /cgroup/
[[email protected] cgroup]# ls
cgroup.event_control memory_spread_page
cgroup.procs memory_spread_slab
cpu_exclusive mems
cpus notify_on_release
mem_exclusive release_agent
mem_hardwall sched_load_balance
memory_migrate sched_relax_domain_level
memory_pressure tasks
memory_pressure_enabled
里面会自动生成很多文件,其中有一个为cpus,为归类到根组中的cpu有哪些
[[email protected] cgroup]# cat cpus
0-3
很显然的看到,我们的所有cpu都归类到根组中
查看自动归类到根组中的内存资源有哪些
[[email protected] cgroup]# cat mems
0
很显然,我们不是非一致内存访问结构,所有只有第0段
查看关联到此组的所有进程PID
[[email protected] cgroup]# cat tasks
1
2
3
###略###
我们在这个根组下创建的任何子目录都是一级组,我们来测试一下:
[[email protected] cgroup]# mkdir group{1,2}
[[email protected] cgroup]# ls
cgroup.event_control memory_pressure_enabled
cgroup.procs memory_spread_page
cpu_exclusive memory_spread_slab
cpus mems
group1 notify_on_release
group2 release_agent
mem_exclusive sched_load_balance
mem_hardwall sched_relax_domain_level
memory_migrate tasks
memory_pressure
[[email protected] cgroup]# ls group1/
cgroup.event_control memory_spread_page
cgroup.procs memory_spread_slab
cpu_exclusive mems
cpus notify_on_release
mem_exclusive sched_load_balance
mem_hardwall sched_relax_domain_level
memory_migrate tasks
memory_pressure
查看cpus,发现是空的
[[email protected] cgroup]# cat group1/cpus
[[email protected] cgroup]#
将其归类
将某个group归类到某个cpu中
[[email protected] cgroup]# cd group1/
[[email protected] group1]# echo 0 > cpus
[[email protected] group1]# cat cpus
0
再归类到mems中
[[email protected] group1]# echo 0 > mems
[[email protected] group1]# cat mems
0
然后切换至group2中去 见其归类
[[email protected] group1]# cd ../group2/
[[email protected] group2]# echo 1 > cpus
[[email protected] group2]# echo 0 > mems
#因为只有第0段内存
由此,这两个组分好了,那么查看组2内的任务tasks
[[email protected] group2]# cat tasks
[[email protected] group2]#
发现文件是空的,因为里面没有任何任务在执行,因此有些时候可以自己实现将某个任务只能运行在某个资源组上
那么我们来查看以下根组的tasks,随便找一个PID,将此PID值能运行在组1上
[[email protected] group2]# more ../tasks
比如将进程httpd只运行在组1上
[[email protected] group2]# ps -aux | grep httpd
Warning: bad syntax, perhaps a bogus ‘-‘? See /usr/share/doc/procps-3.2.8/FAQ
apache 2760 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2762 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2765 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2766 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2767 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2768 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2771 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
apache 2772 0.0 0.5 480180 9836 ? S Sep21 0:00 /usr/sbin/httpd
root 20120 0.0 1.0 480044 20200 ? Ss Sep19 0:08 /usr/sbin/httpd
root 20736 0.0 0.0 103256 804 pts/0 R+ 16:12 0:00 grep httpd
将2760的进程号定义到组0上去
[[email protected] cgroup]# echo 2760 > group1/tasks
[[email protected] cgroup]# cat ./group1/tasks
2760
那么这个进程只能在组1内,也就是0号CPU运行
[[email protected] cgroup]# ps axo comm,pid,psr | grep httpd
httpd 2760 0
httpd 2762 2
httpd 2765 3
httpd 2766 1
httpd 2767 0
httpd 2768 3
httpd 21124 0
[[email protected] cgroup]# ps axo comm,pid,psr | grep httpd
httpd 2760 0
httpd 2762 2
httpd 2765 3
httpd 2766 1
httpd 2767 0
[[email protected] cgroup]# ps axo comm,pid,psr | grep httpd
httpd 2760 0
httpd 2762 2
httpd 2765 3
httpd 2766 1
一个任务只能关联到一个组中,要在一级组上就不能在根组上,因为不能同时调度
那么我们讲进程号2762 绑定到组2的task中,再来观察根组的task
[[email protected] cgroup]# grep 2762 tasks
2762
将其绑定到group2,那么之前的group中的tasks内的进程号则会消失
[[email protected] cgroup]# echo 2762 > group2/tasks
[[email protected] cgroup]# grep 2762 tasks
[[email protected] cgroup]# grep 2762 group2/tasks
2762
所以说一个任务只能绑定在一个组内
控制numa策略
在numa结构上,如果每次都使用手动操作可能是一件非常痛苦的事情,好在numa本身可以实现cpu管理的
numactl使用指定的调度或内存放置策略运行进程,使得可以自动关联一个进程到某个numa资源组中
numactl的使用
在numa结构中,如果频繁手动挂载文件系统,会很麻烦,好在umactl提供了numactl命令手动绑定一个进程到某个资源组上的,一个numa节点包含numa内的所有cpu和本地内存
numactl --show 查看当前情况
numactl --hardware 显示系统中可用的节点清单
numactl --membind 只从指定节点分配内存,明确说明进程绑定到哪一段内存中
numactl --cpunodebind 绑定在多个cpu节点上,每个本地的资源组,有的时候我们允许它在两个节点上使用,这时候可以使用这个参数
numactl --preferred 更加期望运行在哪个节点上
CPU亲和性管理进程 numad
使用numa命令管理节点后 numad自动启动为守护进程,并且自动监控保证某些进程只能运行在特定上,numad可对基准性能有50%的提高,要想实现目的,numad会周期性访问/proc/文件系统中的信息,而借此信息实现资源绑定
所以将numad服务启动起来,在numa结构上会提高性能
而numa中常用的另外一个命令叫做numastat,主要显示进程运行状态
默认跟踪分类
numa_hit
numa_miss
第一次在此节点运行,之后依然在此节点运行被称为命中,如果第一次在此节点运行,往后被负载均衡到其他节点上运行,意味着所有本地资源无法命中
hit越高,说明提升的性能越好
numa_foreign
等等。。。。。不再一一介绍
总结
CPU进程优化思路:
#一定做到CPU切换次数较低,尽可能做到上下文切换次数较低,这样额外的系统性能开销就会减小,那么降低的方法无非是将进程绑定在cpu上
常用的方法:
1.taskset 简单绑定,cpu的亲缘性
2.使用cpuset,cpu内存资源组的概念来实现基于虚拟文件系统的cpu亲缘性绑定
3.在numa体系上使用numa的控制功能
以上都为CPU的亲缘性,只不过使用的手段和途径不同
一个最有效的隔离方式是实现开机之后将cpu直接隔离出来,因为绑定之后这个cpu还处于工作状态,它还是需要进行切换的,为了避免切换,可以使用内核参数
通过内核参数:
isolcpus = #
其次将中断处理从隔离出来的CPU上玻璃掉,使用taskset绑定进程至其专用的cpu
再一就是定义进程的优先级别
关注磁盘IO活动情况查看:
常用工具
iostat
dstat
[[email protected] ~]# iostat -x /dev/xvdj 1 5
Linux 2.6.32-431.11.2.el6.x86_64 (ip-172-31-21-32) 09/28/14 _x86_64_ (4 CPU)
avg-cpu: %user %nice %system %iowait %steal %idle
12.76 0.00 2.85 0.07 0.03 84.30
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
xvdj 0.00 69.14 0.00 64.07 0.00 1065.66 16.63 0.51 7.91 0.43 2.74
avg-cpu: %user %nice %system %iowait %steal %idle
6.53 0.00 2.01 0.00 0.00 91.46
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
xvdj 0.00 0.00 0.00 48.00 0.00 384.00 8.00 0.12 2.60 0.31 1.50
avg-cpu: %user %nice %system %iowait %steal %idle
3.52 0.00 1.01 0.00 0.00 95.48
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
xvdj 0.00 0.00 0.00 24.00 0.00 192.00 8.00 0.07 2.83 0.33 0.80
avg-cpu: %user %nice %system %iowait %steal %idle
5.99 0.00 1.75 0.25 0.00 92.02
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
xvdj 0.00 0.00 0.00 33.00 0.00 264.00 8.00 0.15 4.64 0.24 0.80
avg-cpu: %user %nice %system %iowait %steal %idle
8.29 0.00 6.28 0.00 0.00 85.43
Device: rrqm/s wrqm/s r/s w/s rsec/s wsec/s avgrq-sz avgqu-sz await svctm %util
xvdj 0.00 0.00 0.00 25.00 0.00 200.00 8.00 0.07 2.76 0.36 0.90
对于繁忙的服务器来讲,读写应该是很大的
如果我们发现%util始终居高不下,意味着IO性能低下
使用dstat
[[email protected] group1]# dstat
Terminal width too small, trimming output.
----total-cpu-usage---- -dsk/total- -net/total->
usr sys idl wai hiq siq| read writ| recv send>
0 0 100 0 0 0| 838B 10k| 0 0 >
0 0 100 0 0 0| 0 0 | 652B 1577B>
0 0 100 0 0 0| 0 984k| 17k 12k>
0 0 100 0 0 0| 0 0 |3165B 2248B>
0 0 99 0 0 0| 0 104k|2908B 3347B>
0 0 100 0 0 0| 0 0 | 732B 615B>
0 0 100 0 0 0| 0 0 |1552B 2159B>
因此我们总结出
iostat 通常使用 -x参数
iostat -x /dev/xxxx
dstat 通常使用 -d -r参数
dstat -d -r
还可以使用dstat --top-io 来查看哪个进程最占用IO
[[email protected] group1]# dstat -d -r --top-io
-dsk/total- --io/total- ----most-expensive----
read writ| read writ| i/o process
838B 10k|0.04 0.79 |init 8038B 1160B
0 0 | 0 0 |zabbix_serv1041B 556B
0 0 | 0 0 |zabbix_serv 634B 201B
0 0 | 0 0 |sshd: [email protected] 144B 196B
0 176k| 0 28.0 |mongod 0 8601B
0 96k| 0 8.00 |mongod 0 8192B
0 0 | 0 0 |zabbix_serv2494B 1196B
通过这种方式来观测哪个进程最消耗IO
以上,为linux常用系统评估的一些方法,感谢各位看官