一、cpu绑定(CPU Affinity)
1、属性
什么是CPU Affinity? Affinity是进程的一个属性,这个属性指明了进程调度器能够把这个进程调度到哪些CPU上。
在Linux中,我们可以利用CPU affinity把一个或多个进程绑定到一个或多个CPU上,CPUAffinity分为2种,softaffinity和hardaffinity。softaffinity仅是一个建议,如果不可避免,调度器还是会把进程调度到其它的CPU上。hardaffinity是调度器必须遵守的规则。
为什么要进行CPU 的绑定:
1):增加CPU缓存率
CPU之间是不共享缓存的,如果进程频繁的在各个CPU间进行切换,需要不断的使旧CPU的cache失效。如果进程只在某个CPU上执行,则不会出现失效的情况。
2)增加CPU缓存的命中率
在多个线程操作的是相同的数据的情况下,如果把这些线程调度到一个处理器上,大大的增加了CPU缓存的命中率。但是可能会导致并发性能的降低。如果这些线程是串行的,则没有这个影响。
3)适合time-sensitive
应用在real-time或time-sensitive应用中,我们可以把系统进程绑定到某些CPU上,把应用进程绑定到剩余的CPU上。典型的设置是,把应用绑定到某个CPU上,把其它所有的进程绑定到其它的CPU上。
2、示例
[root@compute ~]# ps -e |grep kvm //我这里列出了一下kvm的进程号
953 ? 00:00:00 kvm-irqfd-clean
6852 ? 00:01:55 qemu-kvm
6859 ? 00:00:00 kvm-pit/6852
7820 ? 00:00:53 qemu-kvm
7827 ? 00:00:00 kvm-pit/7820
18943 ? 00:44:56 qemu-kvm
18953 ? 00:00:00 kvm-pit/18943
[root@compute ~]# taskset -h //这里是关于taskset命令的用法
Usage: taskset [options] [mask | cpu-list] [pid|cmd [args...]]
Options:
-a, --all-tasks operate on all the tasks (threads) for a given pid
-p, --pid operate on existing given pid
-c, --cpu-list display and specify cpus in list format
-h, --help display this help
-V, --version output version information
The default behavior is to run a new command:
taskset 03 sshd -b 1024
You can retrieve the mask of an existing task:
taskset -p 700
Or set it:
taskset -p 03 700
List format uses a comma-separated list instead of a mask:
taskset -pc 0,3,7-11 700
Ranges in list format can take a stride argument:
e.g. 0-31:2 is equivalent to mask 0x55555555
For more information see taskset(1).
[root@compute ~]# cat /proc/7827/status //查看上面查询的PID进程信息
Name: kvm-pit/7820
State: S (sleeping)
Tgid: 7827
Ngid: 0
Pid: 7827
PPid: 2
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 64
Groups:
Threads: 1
SigQ: 0/192346
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: ffffffffffffffff
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Seccomp: 0
Cpus_allowed: ffff
Cpus_allowed_list: 0-15 //这里表明该进程实在0-15号CPU上
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 158
nonvoluntary_ctxt_switches: 0
[root@compute ~]# taskset -cp 6-8 7827 //将7827进程绑定到6-8号CPU上。
pid 7827's current affinity list: 0-15
pid 7827's new affinity list: 6-8
[root@compute ~]# cat /proc/7827/status
Name: kvm-pit/7820
State: S (sleeping)
Tgid: 7827
Ngid: 0
Pid: 7827
PPid: 2
TracerPid: 0
Uid: 0 0 0 0
Gid: 0 0 0 0
FDSize: 64
Groups:
Threads: 1
SigQ: 0/192346
SigPnd: 0000000000000000
ShdPnd: 0000000000000000
SigBlk: 0000000000000000
SigIgn: ffffffffffffffff
SigCgt: 0000000000000000
CapInh: 0000000000000000
CapPrm: 0000001fffffffff
CapEff: 0000001fffffffff
CapBnd: 0000001fffffffff
Seccomp: 0
Cpus_allowed: ffff
Cpus_allowed_list: 6-8 //已更改
Mems_allowed: 00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000000,00000001
Mems_allowed_list: 0
voluntary_ctxt_switches: 158
nonvoluntary_ctxt_switches: 0
一、I/O 调度算法调优
1、介绍
Linux I/O调度器(Linux I/O Scheduler)Linux内核中的一个组成部分,它介于通用块层和块设备驱动程序之间,用户可以通过调整这个调度器来优化系统性能。
2、I/O调度的4种算法
1)CFQ(完全公平调度器。进程平均使用IO带宽)
CFQ均匀地分布对I/O带宽的访问,避免进程被饿死并实现较低的延迟,是deadline和as调度器的折中,CFQ对于多媒体应用(video,audio)和桌面系统是最好的选择.CFQ赋予I/O请求一个优先级,而I/O优先级请求独立于进程优先级,高优先级的进程的读写不能自动地继承高的I/O优先级。
原理:CFQ为每个进程/线程,单独创建一个队列来管理该进程所产生的请求,也就是说每个进程一个队列,各队列之间的调度使用时间片来调度,以此来保证每个进程都能被很好的分配到I/O带宽.I/O调度器每次执行一个进程的4次请求。
2)NOOP(电梯式调度程序,通常用于内存存储的设备)
在Linux2.4或更早的版本的调度程序,那时只有这一种I/O调度算法,NOOP实现了一个简单的FIFO队列,它像电梯的工作主法一样对I/O请求进行组织,当有一个新的请求到来时,它将请求合并到最近的请求之后,以此来保证请求同一介质。
NOOP倾向饿死读而利于写,NOOP对于闪存设备,RAM,嵌入式系统是最好的选择。
电梯算法饿死读请求的解释:
因为写请求比读请求更容易,写请求通过文件系统cache,不需要等一次写完成,就可以开始下一次写操作,写请求通过合并,堆积到I/O队列中,读请求需要等到它前面所有的读操作完成,才能进行下一次读操作,在读操作之间有几毫秒时间,而写请求在这之间就到来,饿死了后面的读请求。
3)Deadline(针对延迟的调度器,每一个 I/O,都有一个最晚执行时间。)
通过时间以及硬盘区域进行分类,这个分类和合并要求类似于noop的调度程序,Deadline确保了在一个截止时间内服务请求,这个截止时间是可调整的,而默认读期限短于写期限,这样就防止了写操作因为不能被读取而饿死的现象,Deadline对数据库环境(ORACLE RAC,MYSQL等)是最好的选择。
4)Anticipatory(AS)(启发式调度,类似 Deadline 算法,但是引入预测机制提高性能。)
本质上与Deadline一样,但在最后一次读操作后,要等待6ms,才能继续进行对其它I/O请求进行调度,可以从应用程序中预订一个新的读请求,改进读操作的执行,但以一些写操作为代价,它会在每个6ms中插入新的I/O操作,而会将一些小写入流合并成一个大写入流,用写入延时换取最大的写入吞吐量,AS适合于写入较多的环境,比如文件服务器,AS对数据库环境表现很差。
2、修改centos 7.X的I/O调度算法
[root@controller ~]# cat /sys/block/sda/queue/scheduler //查看当前系统的 I/O调度算法
[noop] deadline cfq
[root@controller ~]# dmesg | grep -i scheduler //查看当前系统支持的IO调度算法
[ 1.007667] io scheduler noop registered
[ 1.007671] io scheduler deadline registered (default)
[ 1.007694] io scheduler cfq registered
[root@controller ~]# echo cfq > /sys/block/sda/queue/scheduler //临时修改I/O调度方法:
[root@controller ~]# cat /sys/block/sda/queue/scheduler
noop deadline [cfq]
[root@controller ~]# grubby --update-kernel=ALL --args="elevator=deadline" //永久的更改I/O调度方法:
[root@controller ~]# reboot
[root@controller ~]# cat /sys/block/sda/queue/scheduler
noop [deadline] cfq
修改配置文件配置I/O的方法:
[root@controller ~]# vi /etc/default/grub
在 “GRUB_CMDLINE_LINUX=” 后面添加elevator=cfq
例如:
GRUB_CMDLINE_LINUX="crashkernel=auto rhgb quiet elevator=noop numa=off"
[root@controller ~]# grub2-mkconfig -o /boot/grub2/grub.cfg //重新编译配置文件,BIOS-Based。
[root@controller ~]# grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg //UEFI-Based。
[root@controller ~]# reboot 重启后生效