在Linux的網絡調優方面,如果你發現網絡流量上不去,那么有一個方面需要去查一下:網卡處理網絡請求的中斷是否被綁定到單個CPU(或者說跟處理其它中斷的是同一個CPU)。
先說一下背景
網卡與操作系統的交互一般有兩種方式,
- 一種是中斷(IRQ,網卡在收到了網絡信號之后,主動發送中斷到CPU,而CPU將會立即停下手邊的活以便對這個中斷信號進行分析),
- 另一種叫DMA(Direct Memory Access, 也就是允許硬件在無CPU干預的情況下將數據緩存在指定的內存空間內,在CPU合適的時候才處理)
——記得在10多年前開始玩電腦的時候就知道DMA是效率比較高的方式了。但是,這么多年過去了,在網卡方面,大部分還是在用IRQ方式(據說DMA技術僅僅被應用在少數高端網卡上; 另一個說法是:DMA方式會使外部設備的控制器獨占PCI總線,從而CPU無法與外部設備進行交互,這對通用型操作系統Linux來說,是很難接收的,所以DMA方式在Linux內核里使用得很少)。
但是(再來一個但是),在現在的對稱多核處理器(SMP)上,一塊網卡的IRQ還是只有一個CPU來響應,其它CPU無法參與,如果這個CPU還要忙其它的中斷(其它網卡或者其它使用中斷的外設(比如磁盤)),那么就會形成瓶頸。
問題判定
網上不少講這個問題的文章都是直接讓查詢IRQ跟CPU的綁定情況,甚至直接修改。但我們應該先判斷我們的系統是不是受這個問題影響,然后再來看怎么解決。
首先,讓你的網絡跑滿(比如對於MySQL/MongoDB服務,可以通過客戶端發起密集的讀操作; 或者執行一個i大文件傳送任務)
第一個要查明的是:是不是某個CPU在一直忙着處理IRQ?
這個問題我們可以從 mpstat -P ALL 1
的輸出中查明:里面的 %irq
一列即說明了CPU忙於處理中斷的時間占比
18:20:33 CPU %user %nice %sys %iowait %irq %soft %steal %idle intr/s
18:20:33 all 0,23 0,00 0,08 0,11 6,41 0,02 0,00 93,16 2149,29
18:20:33 0 0,25 0,00 0,12 0,07 0,01 0,05 0,00 99,49 127,08
18:20:33 1 0,14 0,00 0,03 0,04 0,00 0,00 0,00 99,78 0,00
18:20:33 2 0,23 0,00 0,02 0,03 0,00 0,00 0,00 99,72 0,02
18:20:33 3 0,28 0,00 0,15 0,28 25,63 0,03 0,00 73,64 2022,19
上面的例子中,第四個CPU有25.63%時間在忙於處理中斷(這個數值還不算高,如果高達80%(而同時其它CPU這個數值很低)以上就說明有問題了),后面那個 intr/s 也說明了CPU每秒處理的中斷數(從上面的數據也可以看出,其它幾個CPU都不怎么處理中斷)。
然后我們就要接着查另外一個問題:這個忙於處理中斷的CPU都在處理哪個(些)中斷?
cat /proc/interrupts
CPU0 CPU1 CPU2 CPU3
0: 245 0 0 7134094 IO-APIC-edge timer
8: 0 0 49 0 IO-APIC-edge rtc
9: 0 0 0 0 IO-APIC-level acpi
66: 67 0 0 0 IO-APIC-level ehci_hcd:usb2
74: 902214 0 0 0 PCI-MSI eth0
169: 0 0 79 0 IO-APIC-level ehci_hcd:usb1
177: 0 0 0 7170885 IO-APIC-level ata_piix, b4xxp
185: 0 0 0 59375 IO-APIC-level ata_piix
NMI: 0 0 0 0
LOC: 7104234 7104239 7104243 7104218
ERR: 0
MIS: 0
這里記錄的是自啟動以來,每個CPU處理各類中斷的數量(第一列是中斷號,最后一列是對應的設備名)[詳細說明: E.2.10 /proc/interrupts - Deployment Guide - RedHat Enterprise Linux 6 ),從上面可以看到: eth0
所出發的中斷全部都是 CPU0
在處理,而CPU0所處理的中斷請求中,主要是eth0和LOC中斷。(有時我們會看到幾個CPU對同一個中斷類型所處理的的請求數相差無幾(比如上面的LOC一行),這並不一定是說多個CPU會輪流處理同一個中斷,而是因為這里記錄的是“自啟動以來”的統計,中間可能因為irq balancer重新分配過處理中斷的CPU——當然,也可能是誰手工調節過)。
解決問題
首先說明幾點:
- 首先應該根據上面的診斷方法查明當前系統是不是受這個原因影響,如果不是,那么就沒有必要往下看了;
- 現在的多數Linux系統中已經有了IRQ Balance這個服務(服務程序一般是
/usr/sbin/irqbalance
),它可以自動調節分配各個中斷與CPU的綁定關系,以避免所有中斷的處理都集中在少數幾個CPU上; - 在某些情況下,這個IRQ Balance反而會導致問題,會出現 irqbalance 這個進程反而自身占用了較高的CPU(當然也就影響了業務系統的性能)[參考: mongodb性能問題及原理分析 ]
下面來說手工將中斷限定到少數幾個CPU的方法。
首先當然要查明,該網卡的中斷當前是否已經限定到某些CPU了?具體是哪些CPU?
根據上面 /proc/interrupts
的內容我們可以看到 eth0 的中斷號是74,然后我們來看看該中斷號的CPU綁定情況(或者說叫親和性 affinity)
$ sudo cat /proc/irq/74/smp_affinity
ffffff
這個輸出是一個16進制的數值,0xffffff = ‘0b111111111111111111111111’
,這就意味着這里有24個CPU,所有位都為1表示所有CPU都可以被該中斷干擾。
另一個例子:
$ sudo cat /proc/irq/67/smp_affinity
00000001
這個例子說明,只有CPU0處理編號為67的中斷。
修改配置的方法:
我們可以用 echo 2 > /proc/irq/74/smp_affinity
的方法來修改這個設置(設置為2表示將該中斷綁定到CPU1上,0x2 = 0b10,而第一個CPU為CPU0)