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命令之firewalld的用法

《Linux命令之firewalld的用法》:本文主要介绍Linux命令之firewalld的用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux命令之firewalld1、程序包2、启动firewalld3、配置文件4、firewalld规则定义的九大

Linux之计划任务和调度命令at/cron详解

《Linux之计划任务和调度命令at/cron详解》:本文主要介绍Linux之计划任务和调度命令at/cron的使用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录linux计划任务和调度命令at/cron一、计划任务二、命令{at}介绍三、命令语法及功能 :at

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

Linux内核参数配置与验证详细指南

《Linux内核参数配置与验证详细指南》在Linux系统运维和性能优化中,内核参数(sysctl)的配置至关重要,本文主要来聊聊如何配置与验证这些Linux内核参数,希望对大家有一定的帮助... 目录1. 引言2. 内核参数的作用3. 如何设置内核参数3.1 临时设置(重启失效)3.2 永久设置(重启仍生效

kali linux 无法登录root的问题及解决方法

《kalilinux无法登录root的问题及解决方法》:本文主要介绍kalilinux无法登录root的问题及解决方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,... 目录kali linux 无法登录root1、问题描述1.1、本地登录root1.2、ssh远程登录root2、

Linux ls命令操作详解

《Linuxls命令操作详解》通过ls命令,我们可以查看指定目录下的文件和子目录,并结合不同的选项获取详细的文件信息,如权限、大小、修改时间等,:本文主要介绍Linuxls命令详解,需要的朋友可... 目录1. 命令简介2. 命令的基本语法和用法2.1 语法格式2.2 使用示例2.2.1 列出当前目录下的文

Linux中的计划任务(crontab)使用方式

《Linux中的计划任务(crontab)使用方式》:本文主要介绍Linux中的计划任务(crontab)使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、前言1、linux的起源与发展2、什么是计划任务(crontab)二、crontab基础1、cro

Linux换行符的使用方法详解

《Linux换行符的使用方法详解》本文介绍了Linux中常用的换行符LF及其在文件中的表示,展示了如何使用sed命令替换换行符,并列举了与换行符处理相关的Linux命令,通过代码讲解的非常详细,需要的... 目录简介检测文件中的换行符使用 cat -A 查看换行符使用 od -c 检查字符换行符格式转换将

Linux系统配置NAT网络模式的详细步骤(附图文)

《Linux系统配置NAT网络模式的详细步骤(附图文)》本文详细指导如何在VMware环境下配置NAT网络模式,包括设置主机和虚拟机的IP地址、网关,以及针对Linux和Windows系统的具体步骤,... 目录一、配置NAT网络模式二、设置虚拟机交换机网关2.1 打开虚拟机2.2 管理员授权2.3 设置子

Linux系统中卸载与安装JDK的详细教程

《Linux系统中卸载与安装JDK的详细教程》本文详细介绍了如何在Linux系统中通过Xshell和Xftp工具连接与传输文件,然后进行JDK的安装与卸载,安装步骤包括连接Linux、传输JDK安装包... 目录1、卸载1.1 linux删除自带的JDK1.2 Linux上卸载自己安装的JDK2、安装2.1