转自:http://blog.csdn.net/whrszzc/article/details/50533866

版权声明:本文为博主原创文章,未经博主允许不得转载。

深入代码详谈irqbalance

之前在工作中简单研究了一下irqbalance,主要为了解决当时网卡性能问题,现在简单分享一点心得,希望能对大家有一丝帮助,也欢迎大家一起讨论。

总结的时候做了一个ppt,感兴趣的同学可以瞅瞅
http://download.csdn.net/detail/whrszzc/9413678

本例是采用的1.0.6版本的irqbalance,代码可以在下面网址获取:
https://github.com/Irqbalance/irqbalance

话说在前面,由于本人很讨厌直接贴上代码的不负责行为,这里虽然是深入代码详谈,但以总结心得为主,代码只给出个流程。

首先,借用网上的找来的一段介绍,稍微了解下irqbalance的功能:
irqbalance用于优化中断分配,它会自动收集系统数据以分析使用模式,并依据系统负载状况将工作状态置于 Performance mode 或 Power-save mode。
处于Performance mode 时,irqbalance 会将中断尽可能均匀地分发给各个 CPU core,以充分利用 CPU 多核,提升性能。
处于Power-save mode 时,irqbalance 会将中断集中分配给第一个 CPU,以保证其它空闲 CPU 的睡眠时间,降低能耗。(暂不讨论这种模式)
简单来说,Irqbalance的主要功能是优化中断分配,收集系统数据并分析,通过修改中断对于cpu的亲和性来尽量让中断合理的分配到各个cpu,以充分利用多核cpu,提升性能。

分析代码之前,首先来了解两个概念,numa架构和smp_affinity。
简单画个图来解释下numa:

NUMA模式是一种分布式存储器访问方式,处理器可以同时访问不同的存储器地址,大幅度提高并行性。
NUMA模式下,处理器被划分成多个”节点”(node), 每个节点被分配有的本地存储器空间。
所有节点中的处理器都可以访问全部的系统物理存储器,但是访问本节点内的存储器所需要的时间,比访问某些远程节点内的存储器所花的时间要少得多。
irqbalance就是根据这种架构来分配中断的。主要的原因是避免终端在节点中迁移产生过多的代价。

smp_affinity是用来设置中断亲缘的CPU的mask码,简单来说就是在cpu上分配中断。
SMP affinity is controlled by manipulating files in the /proc/irq/ directory.
In /proc/irq/ are directories that correspond to the IRQs present on your
system (not all IRQs may be available). In each of these directories is
the “smp_affinity” file, and this is where we will work our magic.

好的,废话不多说,下面在代码层面介绍irqbalance,具体的流程会在最后贴出。

首先看一下irqbalance用到的数据结构是什么样的:

简单说就是根据cpu的结构由上到下建立了一个树形结构,当然,为了平衡终端,每个节点还会挂接本节点分配的中断。

树形结构建立好之后,自然是开始分配中断。irqbalance中把中断分成了八种类型:

#define IRQ_OTHER       0
#define IRQ_LEGACY 1
#define IRQ_SCSI 2
#define IRQ_TIMER 3
#define IRQ_ETH 4
#define IRQ_GBETH 5
#define IRQ_10GBETH 6
#define IRQ_VIRT_EVENT 7

依据就是pci设备初始化时注册的类型:/sys/bus/pci/devices/0000:00:01.0/class
每种中断类型又分别对应一种分配方式,分配方式一共有四种:

BALANCE_PACKAGE
BALANCE_CACHE
BALANCE_NONE
BALANCE_CORE

代表中断的分配范围,不急,接着看一下具体的分配方式:
首先是中断在numa_node中分配,有两种情况:
/sys/bus/pci/devices/0000:00:01.0/numa_node中指定了非-1的numa_node,则把中断分配到对应的numa;如果是-1的话,则根据中断数平均的分到两个numa
分配好numa_node之后开始在整个树中进行分配,分配哪一个层次的原则是

BALANCE_NONE分配在numa_node层
BALANCE_PACKAGE分配在package层
BALANCE_CACHE分配在cache层
BALANCE_CORE分配在core层

决定出那一层之后,最后就是在每个层次中分配节点,原则是分配在负载最小的子节点,如果负载相同则分配在中断种类最少的节点

那么问题又来了,负载是个什么概念呢?
每个节点有各自的负载,自下而上进行计算。
处于最底层的每个逻辑cpu的负载的计算方法是:
在/proc/stat获取每个cpu的信息如下
cpu0 2383 0 298701 468097 158010 572 121175 0 0 0
取第6、7项,分别代表从系统启动开始累计到当前时刻,硬中断、软中断时间(单位是jiffies),然后将累加的值转换成纳秒单位,转换方法是:和*1*10^9/HZ。
了解了逻辑cpu的负载的计算方法不难得到负载所表示的意义:单位时间(10s)内,cpu处理软中断加上硬中断的时间的和

逻辑cpu这一层的负载计算完成之后,要开始计算上层节点的负载情况,计算方法是父节点负载等于各孩子节点负载的和的平均值,自下向上进行运算,如下图所示

应该很容易理解吧

到此为止,我们已经得到了各个节点的负载情况,那么下一步是做什么呢?irqbalance的最终目的在于平衡中断,现在环境已经搭建好了,就差平衡中断了。但是,平衡之前还有一件事情要做,就是计算每个中断的负载。中断的负载不同于前面说的负载,运算比较复杂,等于本层次单位中断的负载情况再乘以每个中断新增个数,中断的负载也是自下向上进行运算,

有点晕?
详细解释一下:中断最终是运行在某一个cpu上的,所以有的中断虽然分配在cache、package层次上,但是最终还是在cpu上运行,所有每个cpu执行中断数大概等于所有父节点的中断数一级一级平均下来。然后用该cpu的负载除以该cpu平均处理的中断数,得到单位中断所占用的负载,那么每个中断的负载就等于该中断在单位时间内新增的个数乘以单位中断所占用的负载

计算方法稍微说明一下:
首先是各节点的平均中断数的计算,每个节点的中断数等于父节点的中断数除以该节点的个数再加上该节点的中断数,注意:这里说的中断数不是中断的种数,是所有中断的新增的个数的和
然后用每个节点的负载除以该节点的平均处理的中断数,得到该节点单位中断所占用的负载
最后针对每一个中断,用该中断在单位时间内(10s)新增的个数乘以单位中断所占用的负载,得到每个中断自己的负载情况。附上图示:

前方高能!
最后,也就是到了最终的临门一脚,开始分配中断。平衡算法如下:
得到每个节点的负载以及每个中断的负载之后,就需要找到负载较高的节点,把该节点的中断从节点中移动到其他的节点来平衡每个cpu的中断。简单来说,是统计每一个层次所有节点的负载的离散状态,找出偏差比较高的节点,把一个或多个中断从本节点剔除,重新分配到该层次负载较小的节点,来达到平衡的目的

取cpu层次的来解释一下,其他层次类似:
经过前面的计算已经得到了每个cpu的负载,也就是得到了一些样本数据,接下来计算负载的平均值和标准差(用于描述数据的离散情况)
接下来是找出负载异常的样本数据,方法找到负载数据与平均值的差大于标准差的样本,有一个前提是该样本所包含的中断种数需要多于1种,然后把该样本中的中断按照中断的负载情况由大到小进行排序,依次从该节点移除,直到该节点的负载情况小于等于平均值为止
最后就是把剔除的中断重新进行分配,分配的时候是选取负载最小的节点进行分配

平衡算法我个人认为是irqbalance中最核心的一个部分,也是最容易出问题的部分。为什么呢?放在最后再说。。

先整理一下irqbalance的流程:
初始化的过程只是建立链表的过程,暂不描述,只考虑正常运行状态时的流程
-处理间隔是10s
-清除所有中断的负载值
-/proc/interrupts读取中断,并记录中断数
-/proc/stat读取每个cpu的负载,并依次计算每个层次每个节点的负载以及每个中断的负载
-通过平衡算法找出需要重新分配的中断
-把需要重新分配的中断加入到新的节点中
-配置smp_affinity使处理生效

至于最后的smp_affinity是如何设置的在此不再赘述,不懂的可以稍微了解一下,比较简单。
irqbalance支持用户配置每个中断的分配情况,设置在/proc/irq/#irq/affinity_hint中,irqbalance有三种模式处理这个配置
EXACT模式下用户设置的cpu掩码强制生效
SUBSET模式下,会尽量把中断分配到用户指定的cpu上,最终生效的是用户设置的掩码和中断所属节点的掩码的交集
IGNORE模式下,不考虑用户的配置

最后总结一下irqbalance:
irqbalance比较适合中断种类非常多,单一中断数量并不是很多的情况,可以很均衡的分配中断
如果遇到中断种类过少或者是某一个中断数量过大,会导致中断不停的在cpu之间迁移,每10s迁移一次,会降低系统性能,并且会导致过多的中断偶尔同时集中同一个cpu上(原因有二,一是平衡中断时优先转移的是负载较大的中断;二是没有计算平衡之后的负载情况)
irqbalance的计算是建立在假设每种中断的处理时间大概相等的情况下,实际的真实状态可能并非如此
irqbalance对于中断的迁移只能在规定的作用域之内进行迁移,特别的,对于numa来说,一旦大部分中断被分配到了同一个numa上,则不论如何平衡,都不会使中断迁移到另一个numa的cpu上

最后的最后,既然说要深入代码详谈,就稍微贴一段代码的流程吧

流程:
build_object_tree ---建立cpu/cache/package的二叉树,并打印。读取pci硬件注册的中断,并建立数据链
force_rebalance_irq ---把所有irq加到rebalance_irq_list链表中
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断不处理
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,每层次负载等于子节点负载总和除以子结点个数
---输出“-----------------------------------------”
sleep_approx(SLEEP_INTERVAL) ---等待10秒
clear_work_stats ---清除中断的负载值,不同于之前记录的中断个数
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断不处理
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,每层次负载等于子节点负载总和除以子结点个数
calculate_placement ---先把rebalance_irq_list中的中断移动到numa节点,然后从numa节点开始由上而下分发中断,依据是中断的level和子节点的负载情况,优先选择负载小的子节点,如果相同则选择中断个数少的子节点
activate_mappings ---配置smp_affinity,exact模式下,直接使用affinity_hint下发,SUBSET模式下,使用affinity_hint和中断所属节点的cpu mask的交集,其他模式使用irq所属节点的cpu mask
dump_tree ---把中断分布情况打印出来,cycle_count++
while (keep_going) ---第一个循环
sleep_approx(SLEEP_INTERVAL) ---等待10秒
clear_work_stats ---清除中断的负载值,不同于之前记录的中断个数
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断加入到new_irq_list中并置need_rescan标记
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,和各层次中断的负载。方法在上面
if (need_rescan) ---由于有新增中断,需要重新建立表,need_rescan置0
---输出“Rescanning cpu topology”
reset_counts ---清零中断的计数
clear_work_stats ---清零中断的负载
free_object_tree ---清除所有的二叉树和中断数据链
build_object_tree ---重新建立cpu/cache/package的二叉树,并打印。读取pci硬件注册的中断,并建立数据链,此处会把新增中断new_irq_list加入到中断链中
force_rebalance_irq ---把所有irq加到rebalance_irq_list链表中
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断加入到new_irq_list中并置need_rescan标记
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,每层次负载等于子节点负载总和除以子结点个数
sleep_approx(SLEEP_INTERVAL) ---等待10秒---主要为了统计计数
clear_work_stats ---清零中断的负载
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断加入到new_irq_list中并置need_rescan标记
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,每层次负载等于子节点负载总和除以子结点个数
---cycle_count置0
calculate_placement ---先把rebalance_irq_list中的中断移动到numa节点,然后从numa节点开始由上而下分发中断
activate_mappings ---配置smp_affinity
dump_tree ---把中断分布情况打印出来,cycle_count++
while (keep_going) ---正常循环
sleep_approx(SLEEP_INTERVAL) ---等待10秒
clear_work_stats ---清除中断的负载值,不同于之前记录的中断个数
parse_proc_interrupts ---在/proc/interrupts读取中断,并记录中断个数,新中断加入到new_irq_list中并置need_rescan标记
parse_proc_stat ---在/proc/stat读取每个cpu的中断负载,由下向上计算各层次负载,和各层次中断的负载。方法在上面
update_migration_status ---计算各节点的标准差和平均值,把负载大于平均值的节点中的中断,按照负载从小到大的形式加入到rebalance_irq_list,直到负载小于平均值或者中断数为1
calculate_placement ---先把rebalance_irq_list中的中断移动到numa节点,然后从numa节点开始由上而下分发中断
activate_mappings ---配置smp_affinity
dump_tree ---把中断分布情况打印出来,cycle_count++
free_object_tree ---清除数据

深入代码详谈irqbalance【转】的更多相关文章

  1. Android 设计模式实战之关于封装计费代码库的策略模式详谈

    写在之前 这周生活上出现了很多的不如意,从周一开始就觉得哪里出现了问题,然后就是各种烦躁的情绪,后来事情还真是如预感的那样发生了,很是心痛,但也无可奈何,希望大家都好好珍惜自己身边的人:友人,亲人,家 ...

  2. JavaScript RegExp 基础详谈

    前言: 正则对于一个码农来说是最基础的了,而且在博客园中,发表关于讲解正则表达式的技术文章,更是数不胜数,各有各的优点,但是就是这种很基础的东西,如果我们不去真正仔细研究.学习.掌握,而是抱着需要的时 ...

  3. OO之美4(好代码与坏代码)

    前言:写代码不仅仅要做到能与机器交流,更要做到能和人交流 编码规范:编码规范就是最佳实践,是前辈在编码这件事上的积累和总结,是智慧的延续和工业的实践,如下: ⑴命名规范 ⑵避免行数过多的方法 ⑶代码缩 ...

  4. JavaScript正则详谈

    JavaScript RegExp 基础详谈   前言: 正则对于一个码农来说是最基础的了,而且在博客园中,发表关于讲解正则表达式的技术文章,更是数不胜数,各有各的优点,但是就是这种很基础的东西,如果 ...

  5. 详谈Javascript类与继承

    本文将从以下几方面介绍类与继承 类的声明与实例化 如何实现继承 继承的几种方式 类的声明与实例化 类的声明一般有两种方式 //类的声明 var Animal = function () { this. ...

  6. MFC自动生成代码详解(一)

    首先声明这篇博客是给MFC刚刚上路的coder准备的,老鸟们就自觉无视我吧! 大家有没有感觉,创建MFC工程时他总会生成一大堆文件一大堆代码.虽然给我们带来了便利,但是调试的时候碰到这些代码总是畏首畏 ...

  7. 用zrender实现工作流图形化设计(附范例代码)

    公司研发的管理系统有工作流图形化设计和查看功能,这个功能的开发历史比较久远.在那个暗无天日的年月里,IE几乎一统江湖,所以顺理成章地采用了当时红极一时的VML技术. 后来的事情大家都知道了,IE开始走 ...

  8. JVM 详谈

    JVM 详谈 本来这次应该讲讲ORM 的几个框架,但是笔者还没有完全总结出来,所以这里先插入一次学习JVM的心得.作为一个Java程序员,如果不了解JVM的工作原理,就很难从底层去把 握Java语言和 ...

  9. JavaScript RegExp 正则表达式基础详谈

    前言: 正则对于一个码农来说是最基础的了,而且在博客园中,发表关于讲解正则表达式的技术文章,更是数不胜数,各有各的优点,但是就是这种很基础的东西,如果我们不去真正仔细研究.学习.掌握,而是抱着需要的时 ...

随机推荐

  1. ATM-db-dnhandler

    import os,jsonfrom conf import settings def select(name): user_path = os.path.join(settings.BASE_DB, ...

  2. formpanel布局的学习

    FormPanel有两种布局:form和column,form是纵向布局,column为横向布局.默认为后者.使用layout属性定义布局类型.对于一个复杂的布局表单,最重要的是正确分割,分割结果直接 ...

  3. vue.js 发布后路径引用问题

    在发布到iis目录下时候,如果放在网站的根目录下的时候,是不会有什么问题的 但是一旦放在了非根目录的其他文件夹里面,这时候index.html里引用的js和css文件路径都会找不到 错误如下 打开in ...

  4. day 52 Django基础一之web框架的本质

    Django基础一之web框架的本质   django第一天 本节目录 一 web框架的本质及自定义web框架 二 模板渲染JinJa2 三 MVC和MTV框架 四 Django的下载安装 五 基于D ...

  5. TCP/IP网络编程之优雅地断开套接字

    基于TCP套接字的半关闭 Linux的close函数和Windows的closesocket函数意味着完全断开连接,完全断开连接不仅指无法传输数据,而且也不能接收数据.因此,在某些情况下,通信一方调用 ...

  6. mysql练习题练习

    1.数据库是按照原文制作的,表格结构一样具体存储的数据有些差异 原文地址:MySQL练习题 原答案地址:MySQL练习题参考答案 2.查询“生物”课程比“物理”课程成绩高的所有学生的学号: selec ...

  7. 【Remove Duplicates from Sorted Array】cpp

    题目: https://leetcode.com/problems/remove-duplicates-from-sorted-array/ Given a sorted array, remove ...

  8. MYSQL学习心得(转)

    适合有SQL SERVER或ORACLE基础的人看,有对比,学习更有效果 转自:http://www.cnblogs.com/lyhabc/ 我的MYSQL学习心得(一) 简单语法 我的MYSQL学习 ...

  9. java线程安全问题原因及解决办法

    1.为什么会出现线程安全问题 计算机系统资源分配的单位为进程,同一个进程中允许多个线程并发执行,并且多个线程会共享进程范围内的资源:例如内存地址.当多个线程并发访问同一个内存地址并且内存地址保存的值是 ...

  10. P3078 [USACO13MAR]扑克牌型Poker Hands

    题目描述 Bessie and her friends are playing a unique version of poker involving a deck with N (1 <= N ...