Linux 内核需要对连接到计算机上的所有硬件设备进行管理,毫无疑问这是它的份内事。如果要管理这些设备,首先得和它们互相通信才行,一般有两种方案可实现这种功能:
第一种方案会让内核做不少的无用功,因为轮询总会周期性的重复执行,大量地耗用 CPU 时间,因此效率及其低下,所以一般都是采用第二种方案 。
对于中断,Linux也有自己的负载均衡策略,既可以调用Linux自己的优化模块irqbalance来实现自动的优化调节,也可以人为的将中断进行绑定的固定的物理CPU上。
(1)irqbalance简介与使用
irqbalance用于优化中断分配,它会自动收集系统数据以分析使用模式,并依据系统负载状况将工作状态置于 Performance mode 或 Power-save mode。处于Performance mode 时,irqbalance 会将中断尽可能均匀地分发给各个 CPU core,以充分利用 CPU 多核,提升性能。处于Power-save mode 时,irqbalance 会将中断集中分配给第一个 CPU,以保证其它空闲 CPU 的睡眠时间,降低能耗。
irqbalance这个进程默认是开机启用的,可以通过如下命令它的状态
# service irqbalance status
irqbalance (pid PID) is running…
对于人为将中断绑定到CPU上的情况时,为了消除irqbalance的自适应调节,需要将该进程关闭,可以用下面的命令关闭它:
# service irqbalance stop
Stopping irqbalance: [ OK ]
或者干脆取消开机启动:
# chkconfig irqbalance off
(2)irqbalance原理分析
irqbalance的意义在于SMP系统的中断优化,而SMP架构是一种CPU的分层拓扑,如下图所示
一个NUMA node包括一个或者多个Socket,以及与之相连的local memory。一个多核的Socket有多个Core。如果CPU支持超线程并开启,操作系统还会把这个Core看成 2个Logical Processor。因此irqbalance会需要考虑到各个CPU上的中断负载以及CPU所处的拓扑位置来进行中断优化调节。
irqbalance的源码下载可以到github网站https://github.com/Irqbalance;
个人对其源码的分析在https://github.com/lkn910907/Code-analyze/tree/master/irqbalance%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90,可便于新手了解。
//irqbalance.c |
int main( int argc, char ** argv) |
{ |
/* ... */ |
while (keep_going) { |
sleep_approx(SLEEP_INTERVAL); //#define SLEEP_INTERVAL 10 |
/* ... */ |
clear_work_stats(); |
parse_proc_interrupts(); |
parse_proc_stat(); |
/* ... */ |
calculate_placement(); |
activate_mappings(); |
/* ... */ |
} |
/* ... */ |
} |
从程序的主循环可以很清楚的看到它的逻辑,在退出之前每隔10秒它做了以下的几个事情:
1. 清除统计
2. 分析中断的情况
3. 分析中断的负载情况
4. 根据负载情况计算如何平衡中断
5. 实施中断亲和性变更
如果不了解中断亲和性可以下面找到详细说明 ~
在诊断模型下运行irqbalance可以给我们很多详细的信息:
#irqbalance –debug
可以详细看到irqbalcne对每个CPU的拓扑分层,以及每个拓扑域上的负载。
(2)自己设置中断亲和性
在 SMP 体系结构中,我们可以通过调用系统调用和一组相关的宏来设置 CPU 亲和力(CPU affinity),将一个或多个进程绑定到一个或多个处理器上运行。中断在这方面也毫不示弱,也具有相同的特性。中断亲和力是指将一个或多个中断源绑定到特定的 CPU 上运行。中断亲和力最初由 Ingo Molnar 设计并实现。
在 /proc/irq
目录中,对于已经注册中断处理程序的硬件设备,都会在该目录下存在一个以该中断号命名的目录 IRQ#
,IRQ#
目录下有一个 smp_affinity
文件(SMP 体系结构才有该文件),它是一个 CPU 的位掩码,可以用来设置该中断的亲和力, 默认值为 0xffffffff
,表明把中断发送到所有的 CPU 上去处理。如果中断控制器不支持 IRQ affinity
,不能改变此默认值,同时也不能关闭所有的 CPU 位掩码,即不能设置成 0x0
。
需要注意的是人为设置中断亲和性需要关闭irqbalance,消除其自适应中断调节
我们以网卡(eth1,中断号 44 )为例,在具有 8 个 CPU 的服务器上来设置网卡中断的亲和力(以下数据出自内核源码Documentation\IRQ-affinity.txt
):
[root@moon 44]# cat smp_affinity ffffffff [root@moon 44]# echo 0f > smp_affinity [root@moon 44]# cat smp_affinity 0000000f
当我们通过 echo 命令将 CPU 掩码写进 smp_affinity
文件时,此时的调用路线图为:write()
->sys_write()
->vfs_write()
->proc_file_write()
->irq_affinity_write_proc()
->set_affinity()
->set_ioapic_affinity()
->set_ioapic_affinity_irq()
->io_apic_write()
;其中在调用 set_ioapic_affinity_irq()
函数时,以中断号和 CPU 掩码作为参数,接着继续调用 io_apic_write()
,修改相应的中断重定向中的值,来完成中断亲和力的设置。
对于效果的检验,可以通过cat /proc/interrupts命令查看中断在各个CPU上的分布,如果是对特定中断处理,再加上grep过滤更方便查看。通过可以发现,一旦设置了亲和性,中断就只在特定的CPU上触发了。