Linux 性能调优之 CPU 亲和性配置
写在前面
- 考试整理相关笔记
- 博文内容涉及 Linux 中CPU 亲和性配置简单介绍
- 为什么需要配置CPU亲和性?
taskset
,systemd
,cgroup V1、V2 版本 cpuset
三种不同方式配置 Demo- 理解不足小伙伴帮忙指正 :) 生活加油
不必太纠结于当下,也不必太忧虑未来,当你经历过一些事情的时候,眼前的风景已经和从前不一样了。——村上春树
CPU 亲和性配置
在大多数情况下,操作系统内核会自动决定将进程调度到哪个 CPU
或内核上运行,并且会多次对进程进行 CPU 调度
。这意味着一个进程可能会在不同的 CPU 或内核上交替运行,并且可以利用系统中所有可用的 CPU 资源。通常情况下,这种自动调度对系统运行是没有问题的。
为什么需要配置亲和性?
然而,在某些情况下,我们可能希望限制特定进程仅在特定的CPU或内核上运行(也称为CPU绑定或CPU亲和性)
, 多方面考虑:
性能优化:
减少上下文切换
:当进程被限制在特定的CPU上运行时,操作系统会减少将其从一个CPU迁移到另一个CPU的可能性,从而减少了上下文切换的开销。上下文切换涉及保存和恢复进程的CPU状态,这是一个相对昂贵的操作。
缓存局部性
:如果进程频繁
访问内存中的某些区域,将其绑定到某个CPU可以确保这些区域的数据和指令更可能驻留在该CPU的缓存中
,从而提高了缓存命中率
,降低了访问延迟
。
资源管理:
负载均衡
:管理员可以根据系统的工作负载和性能特性,将进程分配到不同的CPU上,以实现更均衡的负载分布。
资源隔离
:通过限制进程在特定的CPU上运行,可以确保它们不会与其他高优先级或关键进程争夺资源,从而实现资源隔离。
硬件亲和性:
NUMA(非统一内存访问)优化
:在多处理器系统中,内存访问延迟可能因CPU和内存之间的物理距离而异。通过将进程绑定到靠近其所需数据的CPU上,可以减少内存访问延迟。
硬件特性利用
:某些CPU可能具有特定的硬件特性或加速功能(如特定的指令集或硬件加速器),将进程绑定到这些CPU上可以确保它们能够充分利用这些特性。
故障隔离:
提高系统可用性
:如果一个CPU发生故障,绑定在该CPU上的进程将受到影响。通过将进程分散到多个CPU上,可以降低单一故障点对系统的影响。
安全性:
限制进程访问敏感资源
:通过限制进程在特定的CPU上运行,可以限制它们访问某些敏感硬件资源或执行某些敏感操作的能力。
调试和诊断:
简化问题跟踪
:当进程在特定的CPU上运行时,与该CPU相关的性能指标(如缓存命中率、中断频率等)可能更容易跟踪和分析。
特定工作负载需求:
实时性要求
:对于需要高实时性的应用程序(如音频处理、视频流等),将其绑定到特定的CPU可以确保它们获得足够的CPU资源,并减少由于其他进程的干扰而导致的延迟。
高性能计算
:在科学计算和数据分析等应用中,将进程绑定到特定的CPU可以确保它们获得稳定的性能,并减少由于系统负载变化而导致的性能波动。
通过限制进程运行在特定的 CPU 或内核上,我们可以更好地控制系统资源的使用方式
,以满足特定应用或任务的需求。这可以通过使用适当的系统工具或编程接口来实现,如
- 任务集调度器
taskset
命令 - 在
systemd
的unit
文件中,CPUAffinity=
指令 - 使用
cgroup
的cpuset
控制器进行 CPU 亲和性限制
taskset
taskset
是一个在 Linux 系统中用于设置或检索进程 CPU 亲和性(affinity)
的命令行工具。通过 taskset
,你可以控制进程应该在哪些 CPU 核心或哪些 CPU 集合上运行。这对于性能调优和故障隔离特别有用。
实验环境
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/demogroup]
└─$hostnamectl
Static hostname: liruilongs.github.io
Pretty hostname: VM-16-15-centos
Icon name: computer-vm
Chassis: vm 🖴
Machine ID: 613a0293049a4b07973b2510b3318f11
Boot ID: 013acbf6ee7b4ab4a8c124e6a3361457
Virtualization: kvm
Operating System: CentOS Stream 9
CPE OS Name: cpe:/o:centos:centos:9
Kernel: Linux 5.14.0-437.el9.x86_64
Architecture: x86-64
Hardware Vendor: Tencent Cloud
Hardware Model: CVM
Firmware Version: seabios-1.9.1-qemu-project.org
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/demogroup]
└─$
查看帮助文档了解命令使用
┌──[root@liruilongs.github.io]-[~]
└─$man taskset
查询已运行进程的 CPU 亲和性,3960506 为 进程 ID:
┌──[root@liruilongs.github.io]-[~]
└─$taskset -p 3960506
pid 3960506's current affinity mask: 3
CPU亲和性使用的是位掩码,采用16进制表示,这里的 3 意思,CPU 0和1
┌──[root@liruilongs.github.io]-[~]
└─$printf "%032x\n" $((2**0+2**1))
00000000000000000000000000000003
更改已运行进程的 CPU 亲和性
┌──[root@liruilongs.github.io]-[~]
└─$taskset -pc 0 3960506
pid 3960506's current affinity list: 0,1
pid 3960506's new affinity list: 0
┌──[root@liruilongs.github.io]-[~]
└─$
通过 /prod/{PID}/status
查看 CPU 亲和性
┌──[root@liruilongs.github.io]-[~]
└─$egrep Cpu /proc/3960506/status
Cpus_allowed: 1
Cpus_allowed_list: 0
通过 systemd 的 service unit 文件配置
systemd
提供了简单的方法可用实现 CPU
资源的亲和性限制。通过在服务的 unit
文件中[Service]
块中,添加CPUAffinity=
""即可。
[Service]
CPUAffinity=1-3
CPUAffinity=0-3
:允许进程在 CPU 核心 0、1、2 和 3 上运行。CPUAffinity=0,2,3
:允许进程在 CPU 核心 0、2 和 3 上运行,但不允许在核心 1 上运行
如果一个 unit 文件中有多行 CPUAffinity=
指令,systemd 确实会合并这些设置,但合并的方式是逻辑 OR
,而不是逻辑 AND
。这意味着只要在任何一行 CPUAffinity=
中列出的 CPU 核心,进程都有权限运行。
也可以使用 命令行的方式
┌──[root@liruilongs.github.io]-[~]
└─$systemctl set-property <service name> CPUAffinity=<value>
使用 cgroup
的 cpuset
进行 CPU 亲和性限制
这里需要注意 cgroup
版本不同,对应的限制方式也不同,在 v2 版本中不直接支持 cpuset
控制器。cpuset
控制器是 cgroup v1
中的一个功能,它允许管理员为 cgroup
中的进程分配特定的 CPU
核心和内存节点,在 cgroup v2
中,cpuset
功能被整合到了统一的资源管理中,并且不再提供单独的 cpuset
控制器。
Cgroup V1
从Linux 4.6
开始,cpuset
API已被标记为过时,并建议使用基于文件系统的统一cgroup v2 API
。在 cgroup v1 版本。可以通过下面的方式将多个CPU分配给进程任务
当前使用的实验环境
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$hostnamectl
Static hostname: vms99.liruilongs.github.io
Icon name: computer-vm
Chassis: vm
Machine ID: ea70bf6266cb413c84266d4153276342
Boot ID: 28c693c2b1fc4d45935d6daf6296cece
Virtualization: vmware
Operating System: Rocky Linux 8.9 (Green Obsidian)
CPE OS Name: cpe:/o:rocky:rocky:8:GA
Kernel: Linux 4.18.0-513.9.1.el8_9.x86_64
Architecture: x86-64
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$
确定 Cgroup 版本
┌──[root@vms99.liruilongs.github.io]-[~]
└─$mount -l | grep cgroup
tmpfs on /sys/fs/cgroup type tmpfs (ro,nosuid,nodev,noexec,seclabel,mode=755)
cgroup on /sys/fs/cgroup/systemd type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,xattr,release_agent=/usr/lib/systemd/systemd-cgroups-agent,name=systemd)
cgroup on /sys/fs/cgroup/freezer type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,freezer)
cgroup on /sys/fs/cgroup/cpuset type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpuset)
cgroup on /sys/fs/cgroup/pids type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,pids)
cgroup on /sys/fs/cgroup/cpu,cpuacct type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,cpu,cpuacct)
cgroup on /sys/fs/cgroup/hugetlb type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,hugetlb)
cgroup on /sys/fs/cgroup/perf_event type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,perf_event)
cgroup on /sys/fs/cgroup/devices type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,devices)
cgroup on /sys/fs/cgroup/net_cls,net_prio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,net_cls,net_prio)
cgroup on /sys/fs/cgroup/memory type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,memory)
cgroup on /sys/fs/cgroup/blkio type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,blkio)
cgroup on /sys/fs/cgroup/rdma type cgroup (rw,nosuid,nodev,noexec,relatime,seclabel,rdma)
Cgroup v1 版本 通过 cpuset
控制器将多核系统的每个 CPU
内核分配给进程任务。cpuset 在 /sys/fs/cgroup
目录下。
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$ls /sys/fs/cgroup/cpuset
cgroup.clone_children cpuset.mem_exclusive cpuset.mems
cgroup.procs cpuset.mem_hardwall cpuset.sched_load_balance
cgroup.sane_behavior cpuset.memory_migrate cpuset.sched_relax_domain_level
cpuset.cpu_exclusive cpuset.memory_pressure notify_on_release
cpuset.cpus cpuset.memory_pressure_enabled release_agent
cpuset.effective_cpus cpuset.memory_spread_page tasks
cpuset.effective_mems cpuset.memory_spread_slab
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$
可以手动读取和修改/sys/fs/cgroup下面的控制文件
cpuset.cpus
文件列出了 cgroup 中的进程可以使用的 CPU 核心,0-5 表示 CPU 核心 0 到 5 是可用的。cpuset.mems
文件列出了 cgroup 中的进程可以使用的内存节点,在没有启用 NUMA 的系统上,实际上只有一个内存区域,因此在 cpuset.mems 文件中通常只会看到 0。
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$cat cpuset.cpus
0-5
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$cat cpuset.mems
0
每个运行的进程在 /proc
目录下都会有一个 cpuset
文件,列出了该进程绑定的 cgroup
目录是什么,默认值是/(cgroup的根目录)
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$cat /proc/1/cpuset
/
通过 cpuset
控制器配置服务 CPU
亲和性,这里以 httpd 为 Demo
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$systemctl enable --now httpd
Created symlink /etc/systemd/system/multi-user.target.wants/httpd.service → /usr/lib/systemd/system/httpd.service.
查看当前的亲和性
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$pgrep httpd
37923
37924
37925
37926
37927
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$egrep Cpu /proc/3792?/status
/proc/37923/status:Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
/proc/37923/status:Cpus_allowed_list: 0-127
/proc/37924/status:Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
/proc/37924/status:Cpus_allowed_list: 0-127
/proc/37925/status:Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
/proc/37925/status:Cpus_allowed_list: 0-127
/proc/37926/status:Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
/proc/37926/status:Cpus_allowed_list: 0-127
/proc/37927/status:Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
/proc/37927/status:Cpus_allowed_list: 0-127
┌──[root@vms99.liruilongs.github.io]-[/sys/fs/cgroup/cpuset]
└─$
Cpus_allowed
字段显示了哪些 CPU 核心是允许该进程使用的
,它通常显示为一系列的 ffffffff(在这种情况下,每个 ffffffff 代表 32 个 CPU 核心的掩码,因为 ffffffff 在二进制中是 32 个 1)。由于您有四个 ffffffff,这通常意味着您的系统支持最多 128 个 CPU 核心(32 * 4 = 128),并且所有这些核心都允许该进程访问。
Cpus_allowed_list
字段提供了一个更人类可读的列表,显示了允许该进程访问的 CPU 核心的范围
。在这种情况下,0-127 表示进程可以在 0 到 127 的任何 CPU 核心上运行。
下面修改进程 40604
的亲和性
┌──[root@vms99.liruilongs.github.io]-[~]
└─$systemctl restart httpd
┌──[root@vms99.liruilongs.github.io]-[~]
└─$pgrep httpd
40604
40607
40608
40609
40610
确认当前亲和性
┌──[root@vms99.liruilongs.github.io]-[~]
└─$egrep Cpu /proc/40604/status
Cpus_allowed: ffffffff,ffffffff,ffffffff,ffffffff
Cpus_allowed_list: 0-127
创建一个 cgroup
┌──[root@vms99.liruilongs.github.io]-[~]
└─$mkdir -p /sys/fs/cgroup/cpuset/cpuset0
配置 cpuset
,这里配置 CPU 允许在 0,1
对应的 CPU 上运行
┌──[root@vms99.liruilongs.github.io]-[~]
└─$echo 0-1 > /sys/fs/cgroup/cpuset/cpuset0/cpuset.cpus
┌──[root@vms99.liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cpuset/cpuset0/cpuset.cpus
0-1
将进程添加到 cgroup
, 这里是 tasks 文件,和 Cgroup v2 版本不同
┌──[root@vms99.liruilongs.github.io]-[~]
└─$echo 40604 > /sys/fs/cgroup/cpuset/cpuset0/tasks
┌──[root@vms99.liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cpuset/cpuset0/tasks
40604
验证配置
┌──[root@vms99.liruilongs.github.io]-[~]
└─$cat /proc/40604/status | grep Cpu
Cpus_allowed: 00000000,00000000,00000000,00000003
Cpus_allowed_list: 0-1
┌──[root@vms99.liruilongs.github.io]-[~]
└─$printf "%032x\n" $((2**0+2**1))
00000000000000000000000000000003
Cgroup V2
实验环境
┌──[root@liruilongs.github.io]-[~]
└─$hostnamectl
Static hostname: liruilongs.github.io
Pretty hostname: VM-16-15-centos
Icon name: computer-vm
Chassis: vm 🖴
Machine ID: 613a0293049a4b07973b2510b3318f11
Boot ID: e548e77ef4f04d5f80c87ef18f6000c0
Virtualization: kvm
Operating System: CentOS Stream 9
CPE OS Name: cpe:/o:centos:centos:9
Kernel: Linux 5.14.0-437.el9.x86_64
Architecture: x86-64
Hardware Vendor: Tencent Cloud
Hardware Model: CVM
Firmware Version: seabios-1.9.1-qemu-project.org
┌──[root@liruilongs.github.io]-[~]
└─$
确认当前系统使用的 Cgroup
版本,当前版本为 cgroup v2
的版本。
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup]
└─$mount | grep cgroup
cgroup2 on /sys/fs/cgroup type cgroup2 (rw,nosuid,nodev,noexec,relatime,nsdelegate,memory_recursiveprot)
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup]
└─$which systemd-cgtop
/usr/bin/systemd-cgtop
在 cgroup v2 中,可以通过 cpuset.cpus.effective
和 cpuset.mems.effective
等只读文件来查看 cgroup 当前可以访问的 CPU 和内存资源,但这些文件是只读的
,不能直接修改它们来设置 CPU 核心或内存节点的分配
┌──[root@liruilongs.github.io]-[~]
└─$ls /sys/fs/cgroup/cpu*
/sys/fs/cgroup/cpuset.cpus.effective /sys/fs/cgroup/cpuset.mems.effective
/sys/fs/cgroup/cpuset.cpus.isolated /sys/fs/cgroup/cpu.stat
┌──[root@liruilongs.github.io]-[/sys/fs/cgroup/demogroup]
└─$ll /sys/fs/cgroup/cpuset.cpus.effective
-r--r--r-- 1 root root 0 May 4 16:48 /sys/fs/cgroup/cpuset.cpus.effective
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cpuset.cpus.effective
0-1
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cpuset.mems.effective
0
cgroup v2 中控制应用程序的 CPU 亲和性,需要启用特定的 CPU 控制器,并创建一个专用的控制组。建议在 /sys/fs/cgroup/
根控制组群中至少创建两级子控制组
验证 /sys/fs/cgroup/cgroup.controllers
文件中是否提供了 cpu
和 cpuset
控制器:
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cgroup.controllers
cpuset cpu io memory hugetlb pids rdma misc
为 /sys/fs/cgroup/
根控制组的直接子组
启用了 cpu
和 cpuset
控制器。子组 是可以指定进程的 Cgroup 层级,并根据标准对每个进程应用控制检查的地方,用户可以在任意级别读取 cgroup.subtree_control
文件的内容,以了解子组
中哪些控制器可用于启用。默认情况下,根控制组中的 /sys/fs/cgroup/cgroup.subtree_control
文件包含 memory
和 pids
控制器。
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cgroup.subtree_control
memory pids
┌──[root@liruilongs.github.io]-[~]
└─$echo "+cpu" >> /sys/fs/cgroup/cgroup.subtree_control
┌──[root@liruilongs.github.io]-[~]
└─$echo "+cpuset" >> /sys/fs/cgroup/cgroup.subtree_control
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/cgroup.subtree_control
cpuset cpu memory pids
┌──[root@liruilongs.github.io]-[~]
└─$
创建 /sys/fs/cgroup/Example/
目录,/sys/fs/cgroup/Example/
目录定义了一个子组。此外,上一步为这个子组启用了 cpu
和 cpuset
控制器。
┌──[root@liruilongs.github.io]-[~]
└─$mkdir /sys/fs/cgroup/Example/
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/Example/cgroup.controllers
cpuset cpu memory pids
创建 /sys/fs/cgroup/Example/
目录时,一些 cgroups-v2
接口文件以及 cpu
和 cpuset
特定于控制器的文件也会在目录中自动创建。/sys/fs/cgroup/Example/
目录还包含 memory
和 pids
控制器的特定于控制器的文件
┌──[root@liruilongs.github.io]-[~]
└─$ls /sys/fs/cgroup/Example/
cgroup.controllers cpuset.cpus.exclusive memory.oom.group
cgroup.events cpuset.cpus.exclusive.effective memory.peak
cgroup.freeze cpuset.cpus.partition memory.reclaim
cgroup.kill cpuset.mems memory.stat
cgroup.max.depth cpuset.mems.effective memory.swap.current
cgroup.max.descendants cpu.stat memory.swap.events
cgroup.procs cpu.weight memory.swap.high
cgroup.stat cpu.weight.nice memory.swap.max
cgroup.subtree_control memory.current memory.swap.peak
cgroup.threads memory.events memory.zswap.current
cgroup.type memory.events.local memory.zswap.max
cpu.idle memory.high pids.current
cpu.max memory.low pids.events
cpu.max.burst memory.max pids.max
cpuset.cpus memory.min pids.peak
cpuset.cpus.effective memory.numa_stat
┌──[root@liruilongs.github.io]-[~]
└─$
启用 /sys/fs/cgroup/Example/
中与 CPU 相关的控制器
,以获取仅与 CPU 相关的控制器:
──[root@liruilongs.github.io]-[~]
└─$echo "+cpu" >> /sys/fs/cgroup/Example/cgroup.subtree_control
┌──[root@liruilongs.github.io]-[~]
└─$echo "+cpuset" >> /sys/fs/cgroup/Example/cgroup.subtree_control
创建 /sys/fs/cgroup/Example/tasks/
目录,/sys/fs/cgroup/Example/tasks/
目录定义了一个子组,以及只与 cpu
和 cpuset
控制器相关的文件。
┌──[root@liruilongs.github.io]-[~]
└─$mkdir /sys/fs/cgroup/Example/tasks/
┌──[root@liruilongs.github.io]-[~]
└─$cat /sys/fs/cgroup/Example/tasks/cgroup.controllers
cpuset cpu
┌──[root@liruilongs.github.io]-[~]
└─$
配置 CPU 亲和性
┌──[root@liruilongs.github.io]-[~]
└─$echo "1" > /sys/fs/cgroup/Example/tasks/cpuset.cpus
┌──[root@liruilongs.github.io]-[~]
└─$
通过 httpd 的服务测试
┌──[root@liruilongs.github.io]-[~]
└─$systemctl enable --now httpd
┌──[root@liruilongs.github.io]-[~]
└─$pgrep httpd
879
1096
1098
1105
1106
11313
┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/1105/status | grep Cpu
Cpus_allowed: 3
Cpus_allowed_list: 0-1
将服务的 PID 添加到 Example/tasks
子组中:
┌──[root@liruilongs.github.io]-[~]
└─$echo "1105" > /sys/fs/cgroup/Example/tasks/cgroup.procs
验证配置
┌──[root@liruilongs.github.io]-[~]
└─$cat /proc/1105/status | grep Cpu
Cpus_allowed: 2
Cpus_allowed_list: 1
┌──[root@liruilongs.github.io]-[~]
└─$
┌──[root@vms99.liruilongs.github.io]-[~]
└─$printf "%032x\n" $((2**1))
00000000000000000000000000000002
博文部分内容参考
© 文中涉及参考链接内容版权归原作者所有,如有侵权请告知 :)
《 Red Hat Performance Tuning 442 》
© 2018-2024 liruilonger@gmail.com, All rights reserved. 保持署名-非商用-相同方式共享(CC BY-NC-SA 4.0)
- 点赞
- 收藏
- 关注作者
评论(0)