irqbalance详解(其一)
irqbalance 是什么?項目主頁上有以下描述:
Irqbalance is a daemon to help balance the cpu load generated by interrupts across all of a systems cpus.它避免了單 cpu 負(fù)載過重情況的出現(xiàn)。用法如下:
root@a7661ef9b2f8 test]# irqbalance -h irqbalance: option requires an argument -- 'h' irqbalance [--oneshot | -o] [--debug | -d] [--foreground | -f] [--hintpolicy= | -h [exact|subset|ignore]] [--banscript= | -b <script>][--powerthresh= | -p <off> | <n>] [--banirq= | -i <n>] [--policyscript= | -l <script>] [--pid= | -s <file>] [--deepestcache= | -c <n>] # 查看當(dāng)前運行情況 service irqbalance status# 終止服務(wù) service irqbalance stop首先有一些前置知識需要說明,這涉及到 irqbalance cputree 的分層。
前置知識
中斷
每個硬件設(shè)備都需要和 CPU 有某種形式的通信以便 CPU 及時知道發(fā)生了什么,這樣 CPU 可能就會放下手中的事情去處理應(yīng)急事件,硬件設(shè)備主動打擾 CPU 的現(xiàn)象就可稱為硬件中斷。就像正在一心一意的寫代碼時,突然釘釘“噔噔”地響起來,這時我們就知道有事情需要處理,這里的“噔噔”聲就可以理解成一次中斷。
CPU 和硬件溝通的方式中,還有一種叫做輪詢(polling),就是讓 CPU 定時對硬件狀態(tài)進行查詢?nèi)缓笞鱿鄳?yīng)處理,這比較浪費 CPU,屬于一種硬件被動的方式。相比下來,硬件主動的方式(中斷)更有效一些。
那每個硬件設(shè)備都有中斷,很簡單啊,給它們分個唯一的號碼,也就是 irq 號,在 /proc/interrupts 文件中的第一列可以看到所有的irq。
只有 kernel 2.4 以后的版本才支持的把不同的硬件中斷請求(IRQs)分配到特定的 CPU 上的綁定技術(shù)被稱為 SMP IRQ Affinity,這個后面還會詳細(xì)說。
NUMA架構(gòu)
簡要介紹一下 NUMA 架構(gòu)。
NUMA 架構(gòu)出現(xiàn)前,CPU 頻率一路歡脫越來越高,直至碰到物理極限的天花板,后轉(zhuǎn)向核數(shù)越來越多的方向發(fā)展。
NUMA 架構(gòu)中,內(nèi)存訪問有遠(yuǎn)近之分,只有當(dāng) CPU 訪問自身直接 attach 內(nèi)存對應(yīng)的物理地址時,才會有較短的響應(yīng)時間(Local Access)。而如果需要訪問其他 CPU attach 的內(nèi)存的數(shù)據(jù)時,就需要通過 inter-connect 通道訪問,響應(yīng)時間就相比之前變慢了(Remote Access),NUMA(Non-Uniform Memory Access)就此得名。 --- 引自 http://cenalulu.github.io/lin...
圖畫下來大概是下面這個樣子:
numactl --hardware 命令可以查看的那個機器的 numa 拓?fù)?#xff0c;比如這臺機器:
[root@d2b9eb755bb1 ~]# numactl --hardware available: 2 nodes (0-1) node 0 cpus: 0 1 2 3 4 5 6 7 8 9 10 11 24 25 26 27 28 29 30 31 32 33 34 35 node 0 size: 130946 MB node 0 free: 9892 MB node 1 cpus: 12 13 14 15 16 17 18 19 20 21 22 23 36 37 38 39 40 41 42 43 44 45 46 47 node 1 size: 131072 MB node 1 free: 35969 MB node distances: node 0 10: 10 211: 21 10或者用這個腳本也行:
[root@d2b9eb755bb1 ~]# for i in `ls /sys/devices/system/node | grep node`;do echo -ne "$i\t";cat /sys/devices/system/node/$i/cpulist;done node0 0-11,24-35 node1 12-23,36-47或者用 lscpu 這個命令,
[root@d2b9eb755bb1 ~]# lscpu Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 48 On-line CPU(s) list: 0-47 Thread(s) per core: 2 Core(s) per socket: 12 Socket(s): 2 NUMA node(s): 2 Vendor ID: GenuineIntel CPU family: 6 Model: 79 Stepping: 1 CPU MHz: 2197.264 BogoMIPS: 4401.60 Virtualization: VT-x L1d cache: 32K L1i cache: 32K L2 cache: 256K L3 cache: 30720K NUMA node0 CPU(s): 0-11,24-35 NUMA node1 CPU(s): 12-23,36-47CPU 相關(guān)
cpu cache 結(jié)構(gòu)圖如下:
從硬件的角度,上圖的 L1 和 L2 Cache 都被兩個 HT 共享,且在同一個物理 Core。而 L3 Cache 則在物理 CPU 里,被多個 Core 來共享。 而從 OS 內(nèi)核角度,每個 HT 都是一個邏輯 CPU。
以 cpu0 為例,如下:
點到為止,想了解更多可以翻翻以前的課本。更多 cpu 信息可以從 /proc/cpuinfo 文件中獲取到。
irq 親緣綁定
下面基于實踐簡單說下這個事情。
/proc/interrupts 文件中可以看到各個 cpu 上的中斷情況。
/proc/irq/#/smp_affinity_list 可以查看指定中斷當(dāng)前綁定的 CPU,當(dāng)然也 可以看 smp_affinity 這個文件,它是一個16進制bitmask,以逗號分隔,比如 0000,00000020表示該 irq 分給了 CPU5。
所以,通過如下腳本獲得各網(wǎng)卡中斷的當(dāng)前 cpu 的整體情況(平時只對網(wǎng)卡中斷感興趣):
cat /proc/interrupts | grep eth0- | cut -d: -f1 | while read i; do echo -ne irq":$i\t bind_cpu: "; cat /proc/irq/$i/smp_affinity_list; done | sort -n -t' ' -k3效果大約是這樣的:
irq:113 bind_cpu: 0 irq:117 bind_cpu: 1 irq:136 bind_cpu: 2 irq:109 bind_cpu: 3 irq:137 bind_cpu: 4 irq:106 bind_cpu: 5 irq:112 bind_cpu: 6 irq:111 bind_cpu: 7 irq:115 bind_cpu: 8 irq:149 bind_cpu: 8 irq:152 bind_cpu: 8 irq:133 bind_cpu: 9 irq:110 bind_cpu: 10 irq:114 bind_cpu: 11 irq:130 bind_cpu: 24 irq:148 bind_cpu: 24 irq:131 bind_cpu: 25 irq:139 bind_cpu: 26 irq:118 bind_cpu: 27 irq:132 bind_cpu: 27 irq:123 bind_cpu: 28 irq:128 bind_cpu: 28 irq:134 bind_cpu: 28 irq:142 bind_cpu: 28 irq:150 bind_cpu: 28 irq:135 bind_cpu: 29 irq:108 bind_cpu: 30 irq:116 bind_cpu: 31 irq:119 bind_cpu: 32 irq:124 bind_cpu: 32 irq:126 bind_cpu: 32 irq:127 bind_cpu: 32 irq:138 bind_cpu: 32 irq:151 bind_cpu: 32 irq:107 bind_cpu: 33 irq:121 bind_cpu: 34 irq:140 bind_cpu: 34 irq:120 bind_cpu: 35 irq:122 bind_cpu: 35 irq:125 bind_cpu: 35 irq:129 bind_cpu: 35 irq:141 bind_cpu: 35 irq:143 bind_cpu: 35 irq:144 bind_cpu: 35 irq:145 bind_cpu: 35 irq:146 bind_cpu: 35 irq:147 bind_cpu: 35 irq:153 bind_cpu: 35可以看到,我這臺機器有一半cpu 是空閑的,已經(jīng)綁定的 cpu 綁定的 irq 也不太均衡。
假如要更改的話,可以有如下類似的操作 echo 3 > /proc/irq/24/smp_affinity。
這個也是后面 irqbalance 用來調(diào)整中斷的方法。
irqbalance 代碼分析
下面以 v1.07 為例來進行分析。
irqbalance 中把中斷分成了 8 種 class, 4 種 type。
/** IRQ Classes*/ #define IRQ_OTHER 0 #define IRQ_LEGACY 1 #define IRQ_SCSI 2 #define IRQ_VIDEO 3 #define IRQ_ETH 4 #define IRQ_GBETH 5 #define IRQ_10GBETH 6 #define IRQ_VIRT_EVENT 7/** IRQ Types*/ #define IRQ_TYPE_LEGACY 0 #define IRQ_TYPE_MSI 1 #define IRQ_TYPE_MSIX 2 #define IRQ_TYPE_VIRT_EVENT 3為啥是 8 種 class 呢?這個是依據(jù) pci 設(shè)備初始化時注冊的類型,可以通過以下腳本來查看
[root@d2b9eb755bb1 ~]# for i in `ls /sys/bus/pci/devices/*/class`;do echo $(( `cat $i` >> 16));done | sort -nu | wc -l 8以上的 class 對應(yīng) IRQ Classes 使用如下的數(shù)組:
static short class_codes[MAX_CLASS] = {IRQ_OTHER,IRQ_SCSI,IRQ_ETH,IRQ_VIDEO,IRQ_OTHER,IRQ_OTHER,IRQ_LEGACY,IRQ_OTHER,IRQ_OTHER,IRQ_LEGACY,IRQ_OTHER,IRQ_OTHER,IRQ_LEGACY,IRQ_ETH,IRQ_SCSI,IRQ_OTHER,IRQ_OTHER,IRQ_OTHER, };MAX_CLASS = 0x12 即 18。
不同 class 的中斷平衡的時候作用域不同,有的在PACKAGE,有的在CACHE,有的在CORE。這個關(guān)系對應(yīng)依靠以下數(shù)組進行轉(zhuǎn)換:
irqbalance 會根據(jù)cpu的結(jié)構(gòu)由上到下建立了一個樹形結(jié)構(gòu),最頂層是 numa_nodes,向下以此為 CPU packages、Cache domains以及CPU cores,自頂向下。
irqbalance 的主函數(shù)很簡單,10s 一個周期,做以下事情:
【1】清除上次統(tǒng)計結(jié)果
【2】分析中斷情況
【3】分析中斷的負(fù)載情況
【4】計算如何平衡中斷
【5】實施上面指定的方案
中斷最終是運行在某一個cpu上的,所以有的中斷雖然分配在cache、package層次上,但是最終還是在cpu上運行,所有每個cpu執(zhí)行中斷數(shù)大概等于所有父節(jié)點的中斷數(shù)一級一級平均下來。然后用該cpu的負(fù)載除以該cpu平均處理的中斷數(shù),得到單位中斷所占用的負(fù)載,那么每個中斷的負(fù)載就等于該中斷在單位時間內(nèi)新增的個數(shù)乘以單位中斷所占用的負(fù)載。那問題來了,如何計算負(fù)載的呢?
答案是通過/proc/stat 文件的 irq + softirq 獲得的,以 cpu0 為例,一個可能的數(shù)據(jù)如下:
以上的數(shù)組表示從系統(tǒng)啟動開始累計到當(dāng)前時刻的 jiffies數(shù)(jiffies 是內(nèi)核中的一個全局變量,用來記錄自系統(tǒng)啟動一來產(chǎn)生的節(jié)拍數(shù),在linux中,一個節(jié)拍大致可理解為操作系統(tǒng)進程調(diào)度的最小時間片,不同linux內(nèi)核可能值有不同,通常在1ms到10ms之間)。
以上各字段的含義如下表:
| 200118431 | user | 處于用戶態(tài)的運行時間,不包含 nice值為負(fù)進程。 |
| 1258 | nice | nice值為負(fù)的進程所占用的CPU時間 |
| 112897097 | system | 處于核心態(tài)的運行時間 |
| 1062445972 | idle | 除IO等待時間以外的其它等待時間 |
| 321829 | iowait | IO等待時間(since 2.5.41) |
| 0 | irq | 硬中斷時間 |
| 1048436 | softirq | 軟中斷時間 |
| 0 | steal | - |
| 0 | guest | - |
| 0 | guest_nice | - |
具體可以看 /proc 目錄詳解。
所以,cpu->last_load = (irq_load + softirq_load)。
每個CORE的負(fù)載是附在上面的中斷的負(fù)載的總和,
每個DOMAIN是包含的CORE的總和,
每個PACKAGE包含的DOMAIN的總和,就像樹層次一樣的計算。
關(guān)于如何平衡上面得到的 load 值呢?下一篇再做講解。
To be continued...
總結(jié)
以上是生活随笔為你收集整理的irqbalance详解(其一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu 20.04配置 bond
- 下一篇: 【dubbo源码解析】 --- dub