CPU资源调优
IRQ 叫中断号,操作系统之所以能够跟设备打交道,其实就是依赖于中断号实现,每个设备都有中断号。
在以前还在使用拨号上网的时代,那么时候经常使用计算机的人可能遇到的一个经典的案例就是,当你正在拨号上网的时候,同时再使用打印机,打印出来的文件乱码, 或者已经拨号连接上了, 这个时候使用打印机就会造成断网。这通常是由于硬件的中断号冲突导致的,解决方案是开机进入BIOS 手动调整其中一个设备的中断号。如今之所以几乎没有这样的故障出现,是因为所有硬件的中断号是动态分配的,不是在bios 里面写固定的。
对于CPU 而言,进程需要分配CPU 资源,设备也需要分配CPU 资源。
系统中有一个文件:
cat /etc/interrupts
可以查看到系统中所有的中断使用的情况。
第一列的数字是中断号,最后一列是设备,中间是每一个CPU 给设备分配的资源。
以网卡为例:
cat /proc/interrupts | grep ens
系统给ens160 这块网卡分配了5 个队列,其中4 个是发送接收队列,1 个是事件管理队列。
这个看起来有点复杂了, 换RHEL 6 的系统演示:
系统eth 0 这块网卡,中断号是19
cd /proc/irq
这个目录里面有很多中断号,每一个中断号对应一个设备。
刚刚看到的eth0 的中断号是19,进入到对应的目录里面来:
cd /proc/irq/19/
现在,这个文件内容最后有一个f,代表cpu mask,什么意思呢?
watch -n 1 -x cat /proc/interrupts | grep eth
现在有外部主机ping 这块网卡,之前有说道,第一列显示的是中断号,第二列、第三列、第四列分别显示的是CPU0、CPU1、CPU2 ... ,现在只有CPU 0那一列有数字,而且数字还在递增,说明eth 0 网卡接收到外部主机的ping 流量是CPU 0 在处理。
回到cpu mask,那个f,转换成十进制数,就是2^0=0,所以是CPU0,如果是CPU1,就是2^1,CPU2是2^2,CPU3是2^3,以此类推...
echo 00000002 > smp_affinity
现在再来观察:
是CPU1在处理。
同样地:
echo 00000004 > smp_affinity
echo 00000008 > smp_affinity
如果我想让CPU0和CPU1一起来处理eth0 网卡的流量,这样:
echo 00000003 > smp_affinity
在网卡流量较小的时候,仍然只会分配到一个CPU上工作,遇到并发流量时,才会分配到多个CPU处理。
那么,默认值的f,就表示分配到所有的cpu 处理,这个叫irq 的均衡。
除了irq 中断,还有cpu 中断,它决定了一个进程运行在哪一个cpu 上
默认情况下,一个进程也会均衡地运行在各个cpu 上,我们可以手动指定一个进程固定运行在某一个cpu 上。
ps -axo %cpu,%mem,%pid,comm,psr | more
psr 的参数是用来显示一个进程运行在哪个cpu 上。
下面做一个小实验:
cp -r /usr /etc /tmp &
watch -n 1 -x ps -axo %cpu,%mem,%pid,comm,psr | grep -w cp
一个程序繁忙的时候,它会运行在不同的cpu 上。
再来测试一个:
md5sum /dev/zero &
watch -n 1 -x ps -axo %cpu,%mem,%pid,comm,psr | grep -w md5sum
这个程序则只会在cpu2 上运行。
为什么有些程序会在不同的cpu 之间跑动呢?
一个cpu 同时只能处理一个程序,md5sum 本身就是一个程序, 而cp 则涉及循环,本质上是多个程序。
那么我们如何把那些不会分散在不同cpu上运行的程序分散,把分散在不同cpu上运行的程序固定呢?
taskset -p 11216
现在这个mask 值是f,表示这个进程可以在所有的cpu 上运行。
指定这个进程仅在cpu1上运行,那么mask 值应为2^1=2:
taskset -p 2 11216
指定这个进程仅在cpu1上运行,那么mask 值应为2^2=4:
taskset -p 4 11216
如果想要指定这个进程仅在cpu0 和cpu1 上运行:
taskset -p 3 11216
通过设置这个mask 值就可以根据自己的需要指定了。
那么这样调节有什么意义呢?
默认情况下,进程均衡运行在各个cpu 上,并且会在不同的cpu 上进行移动,优点是提升吞吐量,缺点是会导致cpu cache miss
为了提升进程的cache hit rate 缓存命中率,我们可以把进程绑定到指定的cpu 上运行。
接下来做一个实验:
mkdir /cpuset
系统中有一个特殊的文件系统叫cpuset:
cat /proc/filesystems
mount -t cpuset nodev /cpuset
cd /cpuset
这里面包含了所有的cpuset 资源,包括cpus、mems、tasks 等等...
可以理解为我们现在所在的工作目录就是根cpuset
可以看到vsftpd 这个进程在tasks 里面可以找到。
建子文件夹:
mkdir web
mkdir database
进入web 目录,里面已经有很多内容了,但是它没有资源,database 也一样
echo 1 > cpuset.cpus
把1 号处理器绑定给这个cpuset
因为这台机器不支持NUMA,所以只有一个memory zone
echo 0 > cpuset.mems
把0 号memory zone 绑定给这个cpuset
把任务附加给cpuset:
echo 1279 > tasks
此时,这个进程就会运行在这个cpuset 里面,它就得到了1号处理器和0号memory zone
可以用以下命令来验证:
watch -n 1 -x ps -axo %cpu,%mem,comm,pid,psr | grep vsftpd
但是,这里要注意,vsftpd 服务必须处于运行状态才可以看到效果,如果处于休眠状态则不生效。
如果没有手动挂载cpuset,默认挂载到/sys/fs/cgroup:
如果你不想用自定义的cpuset,不可以直接用rm -rf 这样的命令来删,这是内存中的数据,需要用rmdir 来删除:
cd /sys/fs/cgroup/cpuset
rmdir database
rmdir web
提示设备忙
我们把目录里面的资源解除再重试
这样就可以删除了。
如果要想让处于休眠状态的vsftpd服务绑定cpuset 生效,需要下面这样做:
cd /usr/local/bin
vim cpuset.sh
# chmod u+x cpuset.sh
# cd /etc/systemd/system/
# mkdir vsftpd.service.d
# cd vsftpd.service.d/
# vim 01-cpuset.conf
[Service]
ExecStartPost=/usr/local/bin/cpuset.sh
这行代码表示会在服务启动完成后执行这个脚本。
systemctl daemon-reload
systemctl restart vsftpd
验证结果:
ps axo %cpu,%mem,pid,comm,psr | grep ftp
这个参数,表示是否独占cpu 资源,默认是0,表示关闭,如果设为1,表示打开,那么其它cpuset 就不能设置与我相同的cpu 资源了,但这是对于子cpuset 而言,根cpuset 不会受影响。
那么如果我想查看某一个进程运行在哪个cpuset 上,下面这样操作:
cd /proc/1/cpuset
比如说上面这个ID 为1 的进程,其实也就是systemd 进程,运行在根cpuset 上。
vsftpd 运行在web cpuset 上。
持续更新中...