前言
- 在工作中当我们使用了性能较差的 CPU 时,总会发现其软中断很容易偏高。
这是因为当软中断事件的频率过高时,由于 CPU 使用率偏高而导致内核线程的软中断处理不及时,从而引发网络收发延迟、调度缓慢等性能问题。其实,软中断 CPU 使用率过高也是一种最常见的性能问题,因此才需要我们做一些调优的方法,以便更合理的使用 CPU 来进行工作。 - 声明:以下所有展示的图片,都是经过性能调优后给出的(Linux 默认软中断只绑定到 CPU0 处理)。
一、什么是中断?
- 在解决软中断偏高的问题之前,我们先来简单的了解一下中断的概念的吧。顾名思义,“中断”就是字面意思,是指 CPU 在执行某一项工作时突然被其他事情打断而被迫处理优先级更高的事务。比如,当你在愉快的享受假期时突然被领导强制要求回公司加班赶项目,而你此时不得不做出停止休假并赶回去加班的决定,这个过程就叫做中断。
- Linux 中断是一种异步的事件处理机制,用来提高系统的并发处理能力。中断事件的发生会触发执行中断处理程序,而中断处理程序被分为上半部和下半部两个部分:
1、上半部对应硬中断,用来快速处理中断
2、下半部对应软中断,用来异步处理上半部未完成的工作 - Linux 软中断包括网络收发、定时、调度、RCU 锁等各种类型,可以通过查看 /proc/softirqs 观察软中断的运行情况。
- Linux 默认每个 CPU 都对应一条软中断内核进程,进程名是:ksoftirqd/n(n 等于 cpu 编号。例如:ksoftirqd/0, ksoftirqd/1, …)。
- ksofttrip 是一直循环运行的,只要系统发生了软中断,哪个 CPU 空闲了它就会去获取来处理。
二、如何查看系统运行以来的累积中断次数?
-
软中断 CPU 使用率(softirq)升高是一种很常见的性能问题。虽然软中断的类型很多,但多数情况下网络收发产生的软中断才是性能的瓶颈。那么,怎样查看 CPU 的中断次数呢?
-
方法一:
可通过 top 命令查看软中断的计数(按下1键可以显示每个 CPU 核心的详细信息):# top -d1
-
方法二:
可通过 /proc/softirqs 观察软中断的运行情况:# watch -d cat /proc/softirqs /* 定期查看中断次数变化 */
-
通过上边的 /proc/softirqs 内容变化情况来看:
发现 TIMER(定时中断)、NET_RX(网络接收)、SCHED(内核调度)、RCU(RCU 锁) 等这几个软中断都在不断的变化。
其中,NET_RX 网络数据包接收软中断的变化速率是最快的,而其他几种类型的软中断有一定的变化是正常的,因为它们是保证 Linux 调度、时钟和临界区保护等正常工作所必需的。 -
方法三:
可通过 /proc/interrupts 观察硬中断的运行情况:# cat /proc/interrupts
-
其中,可以看出
信号量:45 ethernet
网卡硬中断主要集中在 CPU3,结合 top 命令来看 CPU3 的软中断还是挺高的,为什么会这样呢?这是因为当网卡收到数据包时会产生中断,通知内核有新数据包,然后内核调用中断处理程序进行响应,把数据包从网卡缓存拷贝到内存。
三、CPU软中断性能调优
- Linux 默认情况下,所有网卡的软中断都是由 CPU0 来处理的,当你发现
ksoftrip/0 总是占比很高,说明可能是网卡问题了
。 - 通过以上方法分析软中断的产生原因,会发现确实是网络接收的软中断过高导致的 CPU 性能下降,那么如何调优呢?
- 想要优化,先确认你的网卡是否为多通道队列的,如何确认网卡是否为多队列的呢? 查看网卡通道队列的数量方式:
# cat /proc/interrupts | grep ethernet | wc -l
注意:当队列数量 > 1,才说明你的网卡是支持多通道队列的
方法1:smp irq affinity 技术
-
smp irq affinity 技术,也称为信号量分布技术,就是把特定信号量的处理放到固定的 CPU 上,每个网卡的通道队列都有一个自己的信号量(
如下图的 ethernet 网卡,对应了信号量45
)。查看网卡通道队列的信号量方法:# cat /proc/interrupts | grep ethernet
注意: 不同的 Linux 平台网卡名称不一定相同。比如我的网卡名是 ethernet,而有的平台又是 eth0,或者名命为其他的
。如果是实在不知自己的网卡名称,直接通过 cat /proc/interrupts 查看通道队列的信号量亦可。 -
当我们知道了网卡信号量"num"后,就可以开始对信号量进行绑定了,在 /proc/irq/num/ 下面有两个文件分别是 smp_affinity 和 smp_affinity_list。 smp_affinity 是通过 bitmask 算法绑定 CPU 的,smp_affinity_list 则是通过数字指定 CPU 编号的。
均衡配置: 将网络收发数据导致过高的软中断均摊到各个 CPU 来承担,以达到 CPU 整体性能提升的目的。使用以下任意一种绑定方法均可(以4核CPU为例)
:1. smp_affinity:
# echo f > /proc/irq/45/smp_affinity
2. smp_affinity_list:
# echo 0-3 > /proc/irq/45/smp_affinity_list
备注:以上命令的作用是,将中断号45的设备中断处理(包括软中断和硬中断)均摊绑定到各个 CPU 上。
方法2:RPS/RFS 技术
-
RPS 全称是 Receive Packet Steering,这是 Google 工程师 Tom Herbert (therbert@google.com) 提交的内核补丁,在 2.6.35 进入 Linux 内核。这个 patch 采用软件模拟的方式,实现了多队列网卡所提供的功能,分散了在多 CPU 系统上数据接收时的负载,把软中断分到各个 CPU 处理,而不需要硬件支持,大大提高了网络性能。
-
RFS 全称是 Receive Flow Steering,这也是 Tom 提交的内核补丁,它是用来配合 RPS 补丁使用的,是 RPS 补丁的扩展补丁,它把接收的数据包送达应用所在的 CPU 上,提高 cache 的命中率。
-
通俗点来说,单个网卡通道队列的软中断会平均到所有 CPU 上,中断会发生在处理数据包的那个 CPU 上,从而节省了 cpu cache。
-
那么如何配置 RPS/RFS 呢?
(以4核CPU为例)
:1. 配置 RPS,如果 CPU 核数是 4 个,可以设置成 f:
# echo f > /sys/class/net/eth0/queues/rx-0/rps_cpus
2. 配置内核参数 rps_sock_flow_entries(官方文档推荐设置:N=32768):
# echo 32768 > /proc/sys/net/core/rps_sock_flow_entries
3. 配置 rps_flow_cnt,单队列网卡可设置成 rps_sock_flow_entries(N=32768):
# echo 32768 > /sys/class/net/eth0/queues/rx-0/rps_flow_cnt
-
特别说明:
eth0:网卡设备(可能多个)
rx-0:接收队列(可能多个)
N:如果是多队列网卡,那么就按照队列数量设置成: N = rps_sock_flow_entries / 接收队列的数量
接收队列数量查看:cat /proc/interrupts | grep ethernet | wc -l
配置后效果展示,每个CPU都能接收软中断:
四、经验总结
-
如果是多队列网卡性能调优,优先推荐使用方法1。
-
如果是单网卡队列性能调优,优先推荐使用方法2(因为 RPS/RFS 能模拟多网卡工作)。
-
特别声明,如果方法1和方法2都不能达到理想效果,可以尝试将网卡软中断单独绑定到固定的 CPU 上处理,因为这样可以提高 cpu cache 的命中率,从而达到提升 CPU 整体性能的目的。例如,
将中断号45的中断处理单独绑定到 CPU3
:# echo 3 > /proc/irq/45/smp_affinity_list