SMP affinity and interrupt handling in Linux

2023-12-29 11:40

本文主要是介绍SMP affinity and interrupt handling in Linux,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

2019独角兽企业重金招聘Python工程师标准>>> hot3.png

本文是一篇译文,原文的地址为:http://www.alexonlinux.com/smp-affinity-and-proper-interrupt-handling-in-linux


Introduction

硬件中断是非常昂贵的,因为它会消耗大量的CPU处理能力。硬件和软件工程师已经做了很多工作试图改变这种状况,但硬件中断仍然会消耗大量CPU。

硬件中断对桌面系统的影响并不明显,具体信息可以通过/proc/interrupts文件来查看。这个文件中记录了所有的硬件设备以及它们在每个CPU上收到的中断数目。如果是在一个常规的桌面系统上,将会看到系统中处理的中断数目是相对较小的。即使是每秒处理百万数据包的超强服务器,一秒内也只需处理数万个中断。即便如此,通过恰当的方式降低中断所带来的CPU开销仍将毫无疑问的帮助提升系统性能。


But really, what can we do about interrupts?

事实上,还有很多事情可以做。许多Linux的发行版中都包含了能够明显改善这种情况的修改。一种有效的技术就是NAPI,它显著的降低了中断的数目和开销,因此现代服务器无需再忍受1Gbps的以太网链路。NAPI现在已经成为了内核的一部分。其他的技术还有像中断合并等。

在本文中,我将把注意力放在一个最有力的中断处理优化技术上。


SMP affinity

术语“SMP亲和性” 或 “处理器亲和性”有着广泛的含义,需要首先来解释一下。“亲和性”是指在多处理器系统中,将特定的任务放到特定的处理器上执行。例如,当进程Y运行在处理器X上时,它们之间就具有了亲缘关系,处理器会将进程的部分内存内容缓存在cache中,因此频繁的将进程调度到不同的处理器上是一种低效的方法。

考虑到中断的情况,“SMP 亲和性”是指特定的中断由哪个处理器来处理。与进程的情况不同,将中断绑定到特定的CPU上将会引起性能下降。下面就来说说为什么会这样。中断处理程序通常都非常小,因此它所占用的内存也很小,也就是说将中断保持在同一个CPU上不会改善缓存命中的情况。相反,过多的中断会使一个核心过载,而其他的核心却处在相对空闲的状态。调度程序却对此一无所知,它会假设我们的中断处理核心与其他核心一样繁忙,由此带来的结果就是你可能会面临一个瓶颈,这是由于某些进程或线程运行在了只有90%可用处理能力的核心上所造成的。

事情还可能会变得更加糟糕,因为在默认情况下core0会被用来处理所有中断。在一个繁忙的系统上,所有的中断加起来可能会消耗掉core0 30%的处理能力。假设所有核心都具有相同的处理能力,我们会发现我们的软件系统只能有效地利用CPU全部处理能力的70%。


Who's responsible

APIC or Advanced Programmable Interrupt Controller 已经集成到所有现代x86体系架构的系统中 --- 包括 SP(single-processor)和MP。APIC负责递交中断,并决定中断的去向(到哪个核心)。

默认情况下,APIC将所有中断递交到core0,这就是为什么在主流现代Linux操作系统上/proc/interrupts文件的输出内容看起来如下的原因:

         CPU0     CPU1     CPU2     CPU30:   123357        0        0        0   IO-APIC-edge  timer8:        0        0        0        0   IO-APIC-edge  rtc11:        0        0        0        0  IO-APIC-level  acpi
169:        0        0        0        0  IO-APIC-level  uhci_hcd:usb1
177:        0        0        0        0  IO-APIC-level  qla2xxx
185:        0        0        0        0  IO-APIC-level  qla2xxx
193:    12252        0        0        0  IO-APIC-level  ioc0
209:        0        0        0        0  IO-APIC-level  uhci_hcd:usb2
217:      468        0        0        0  IO-APIC-level  eth0
225:      285        0        0        0  IO-APIC-level  eth1
NMI:      120       66       76       45
LOC:   123239   123220   123187   123065
ERR:        0
MIS:        0

看到了吗,CPU0处理了所有的硬件中断,这就是你将在一个错误配置了中断SMP亲和性的系统上看到的情形。


Simple solution for the problem

随着APIC的引入,这个问题已经有了多种解决方案。APIC包含了若干中断传递和指定模式,包括物理模式和逻辑模式,固定模式和低优先级模式,等等。一个重要的事实是它有能力将中断递送到任何一个核上,甚至在它们之间进行负载均衡。

APIC也有配置限制,它只能配置在前8个核心上。如果你有多于8个的核心,不要指望序号大于7的核心能够收到中断。

默认情况下,它的操作是physical/fixed的。也就是说,它会将特定的中断传递到特定的核心上(默认是core0),但你也可以方便的更改接收指定中断的核心号。

对于/proc/interrupts文件第一列中的每个IRQ号,在/proc/irq/目录下都有一个对应的子目录,其中包含了一个名为smp_affinity的文件,它可以用来修改处理这个中断的核心号。读这个文件可以获得一个由十六进制表示的掩码,其中的每一位代表一个核心。当特定位置位时,APIC会将这个中断传递到对应的核心。

让我们来看一个例子:

#
# cat /proc/interruptsCPU0     CPU1     CPU2     CPU30: 19599546        0        0        0   IO-APIC-edge  timer8:        0        0        0        0   IO-APIC-edge  rtc11:        0        0        0        0  IO-APIC-level  acpi
169:        0        0        0        0  IO-APIC-level  uhci_hcd:usb1
177:        0        0        0        0  IO-APIC-level  qla2xxx
185:        0        0        0        0  IO-APIC-level  qla2xxx
193:    95337        0        0        0  IO-APIC-level  ioc0
209:        0        0        0        0  IO-APIC-level  uhci_hcd:usb2
217:   100778        0        0        0  IO-APIC-level  eth0
225:    56651        0        0        0  IO-APIC-level  eth1
NMI:      466      393      422      372
LOC: 19600453 19600434 19600401 19600279
ERR:        0
MIS:        0
#
#
# echo "2" > /proc/irq/217/smp_affinity
# cat /proc/interruptsCPU0     CPU1     CPU2     CPU30: 19606722        0        0        0   IO-APIC-edge  timer8:        0        0        0        0   IO-APIC-edge  rtc11:        0        0        0        0  IO-APIC-level  acpi
169:        0        0        0        0  IO-APIC-level  uhci_hcd:usb1
177:        0        0        0        0  IO-APIC-level  qla2xxx
185:        0        0        0        0  IO-APIC-level  qla2xxx
193:    95349        0        0        0  IO-APIC-level  ioc0
209:        0        0        0        0  IO-APIC-level  uhci_hcd:usb2
217:   101027       49        0        0  IO-APIC-level  eth0
225:    56655        0        0        0  IO-APIC-level  eth1
NMI:      466      393      422      372
LOC: 19607629 19607610 19607577 19607455
ERR:        0
MIS:        0
#

可以看到,当我们输入了这个神奇的命令后,CPU1将代替CPU0,开始从eth0接收中断。用于完成这件事情的echo命令非常有趣,如果我们将“4”写入这个文件中,那么CPU2将代替CPU1来处理eth0的中断,就像之前已经提到的,它采用了位掩码的方式,每一位与一个单独的CPU关联。

那么写入“3”又会怎么样呢?理论上,这时APIC应该将中断传递到CPU0和CPU1上,不幸的是,这里的情况会更复杂一些。具体的行为依赖于APIC是否工作在 physical "destination mode" 以及 low priority "delivery mode"模式下。如果是的话,那么你不太可能看到CPU0处理所有中断,这是因为当APIC工作在 physical / low priority模式下的时候,会自动在前8个核心之间进行负载均衡。

因此,如果在你的系统上CPU0默认处理了全部的中断,这可能暗示了APIC的配置问题。


Ultimate solution

首先,不幸的是,别无选择,除非替换内核。配置APIC是内核的工作,如果我们想改变现状就只能在内核里修改它,那么唯一的问题就是,用什么来替换内核?

我在OpenSuSE 10.2(内核版本2.6.18)上进行了测试,使用OpenSuSE的默认内核配置(/proc/config.gz)安装2.6.24.3版本的内核(当时最新的版本)来修正这个问题。在这个内核上,事情看起来正确了:

# cat /proc/interruptsCPU0     CPU1     CPU2     CPU30:   728895   728796   728624   728895  IO-APIC-edge     timer8:        0        0        0        0  IO-APIC-edge     rtc11:        0        0        0        0  IO-APIC-fasteoi  acpi16:        0        0        0        0  IO-APIC-fasteoi  uhci_hcd:usb119:        0        0        0        0  IO-APIC-fasteoi  uhci_hcd:usb224:    14090    14090    14327    14056  IO-APIC-fasteoi  ioc049:        7        9        7        8  IO-APIC-fasteoi  qla2xxx50:        8       12       11       10  IO-APIC-fasteoi  qla2xxx77:     2849     2759     2841     2827  IO-APIC-fasteoi  eth078:    25072    25138    24996    24980  IO-APIC-fasteoi  eth1
NMI:        0        0        0        0
LOC:  2915270  2915256  2915228  2915092
ERR:        0

看起来不错不是吗?所有的核心都在处理中断,工作效率也因此得到了最大化。

还有一个内核的配置选项横在我们的道路上,一旦移除掉它就能够在所有晚于2.6.10版本的内核上获得类似的效果。这个选项就是CONFIG_HOTPLUG_CPU,它增加了对CPU热插拔的支持。关掉这个选项,并正确的配置APIC。

实际上这很好理解,APIC被告知使用哪个CPU来接收中断,你需要增加一段代码来告知APIC如何处理CPU的移除事件 --- 而CPU的移除事件正是CONFIG_HOTPLUG_CPU允许你做的事,我假定这个功能在较早时被内核遗忘了,并在2.6.24.3版本之后被添加了进来。


Conclusion

我们已经看到,通过修改内核的配置可以获得实实在在的改进。在一个繁忙的系统中,通过一个小小的改动就可以成倍的提升服务器的生产力。

希望你能够认同这篇文章的内容,并将这些技术用到实际中去。


转载于:https://my.oschina.net/u/2310891/blog/379247

这篇关于SMP affinity and interrupt handling in Linux的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/549330

相关文章

Linux join命令的使用及说明

《Linuxjoin命令的使用及说明》`join`命令用于在Linux中按字段将两个文件进行连接,类似于SQL的JOIN,它需要两个文件按用于匹配的字段排序,并且第一个文件的换行符必须是LF,`jo... 目录一. 基本语法二. 数据准备三. 指定文件的连接key四.-a输出指定文件的所有行五.-o指定输出

Linux jq命令的使用解读

《Linuxjq命令的使用解读》jq是一个强大的命令行工具,用于处理JSON数据,它可以用来查看、过滤、修改、格式化JSON数据,通过使用各种选项和过滤器,可以实现复杂的JSON处理任务... 目录一. 简介二. 选项2.1.2.2-c2.3-r2.4-R三. 字段提取3.1 普通字段3.2 数组字段四.

Linux kill正在执行的后台任务 kill进程组使用详解

《Linuxkill正在执行的后台任务kill进程组使用详解》文章介绍了两个脚本的功能和区别,以及执行这些脚本时遇到的进程管理问题,通过查看进程树、使用`kill`命令和`lsof`命令,分析了子... 目录零. 用到的命令一. 待执行的脚本二. 执行含子进程的脚本,并kill2.1 进程查看2.2 遇到的

Linux云服务器手动配置DNS的方法步骤

《Linux云服务器手动配置DNS的方法步骤》在Linux云服务器上手动配置DNS(域名系统)是确保服务器能够正常解析域名的重要步骤,以下是详细的配置方法,包括系统文件的修改和常见问题的解决方案,需要... 目录1. 为什么需要手动配置 DNS?2. 手动配置 DNS 的方法方法 1:修改 /etc/res

Linux创建服务使用systemctl管理详解

《Linux创建服务使用systemctl管理详解》文章指导在Linux中创建systemd服务,设置文件权限为所有者读写、其他只读,重新加载配置,启动服务并检查状态,确保服务正常运行,关键步骤包括权... 目录创建服务 /usr/lib/systemd/system/设置服务文件权限:所有者读写js,其他

Linux下利用select实现串口数据读取过程

《Linux下利用select实现串口数据读取过程》文章介绍Linux中使用select、poll或epoll实现串口数据读取,通过I/O多路复用机制在数据到达时触发读取,避免持续轮询,示例代码展示设... 目录示例代码(使用select实现)代码解释总结在 linux 系统里,我们可以借助 select、

Linux挂载linux/Windows共享目录实现方式

《Linux挂载linux/Windows共享目录实现方式》:本文主要介绍Linux挂载linux/Windows共享目录实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地... 目录文件共享协议linux环境作为服务端(NFS)在服务器端安装 NFS创建要共享的目录修改 NFS 配

linux系统中java的cacerts的优先级详解

《linux系统中java的cacerts的优先级详解》文章讲解了Java信任库(cacerts)的优先级与管理方式,指出JDK自带的cacerts默认优先级更高,系统级cacerts需手动同步或显式... 目录Java 默认使用哪个?如何检查当前使用的信任库?简要了解Java的信任库总结了解 Java 信

Linux命令rm如何删除名字以“-”开头的文件

《Linux命令rm如何删除名字以“-”开头的文件》Linux中,命令的解析机制非常灵活,它会根据命令的开头字符来判断是否需要执行命令选项,对于文件操作命令(如rm、ls等),系统默认会将命令开头的某... 目录先搞懂:为啥“-”开头的文件删不掉?两种超简单的删除方法(小白也能学会)方法1:用“--”分隔命

Linux五种IO模型的使用解读

《Linux五种IO模型的使用解读》文章系统解析了Linux的五种IO模型(阻塞、非阻塞、IO复用、信号驱动、异步),重点区分同步与异步IO的本质差异,强调同步由用户发起,异步由内核触发,通过对比各模... 目录1.IO模型简介2.五种IO模型2.1 IO模型分析方法2.2 阻塞IO2.3 非阻塞IO2.4