轉載 - Linux 多核下綁定硬件中斷到不同 CPU(IRQ Affinity)
作者
digoal
日期
2016-11-20
標簽
Linux , IRQ , 中斷 , CPU親和 , 綁定中斷處理CPU
背景
原文
http://www.vpsee.com/2010/07/load-balancing-with-irq-smp-affinity/
原文
硬件中斷發生頻繁,是件很消耗 CPU 資源的事情,在多核 CPU 條件下如果有辦法把大量硬件中斷分配給不同的 CPU (core) 處理顯然能很好的平衡性能。現在的服務器上動不動就是多 CPU 多核、多網卡、多硬盤,如果能讓網卡中斷獨占1個 CPU (core)、磁盤 IO 中斷獨占1個 CPU 的話將會大大減輕單一 CPU 的負擔、提高整體處理效率。VPSee 前天收到一位網友的郵件提到了 SMP IRQ Affinity,引發了今天的話題:D,以下操作在 SUN FIre X2100 M2 服務器+ 64位版本 CentOS 5.5 + Linux 2.6.18-194.3.1.el5 上執行。
什么是中斷
中文教材上對 “中斷” 的定義太生硬了,簡單的說就是,每個硬件設備(如:硬盤、網卡等)都需要和 CPU 有某種形式的通信以便 CPU 及時知道發生了什么事情,這樣 CPU 可能就會放下手中的事情去處理應急事件,硬件設備主動打擾 CPU 的現象就可稱為硬件中斷,就像你正在工作的時候受到 QQ 干擾一樣,一次 QQ 搖頭就可以被稱為中斷。
中斷是一種比較好的 CPU 和硬件溝通的方式,還有一種方式叫做輪詢(polling),就是讓 CPU 定時對硬件狀態進行查詢然后做相應處理,就好像你每隔5分鐘去檢查一下 QQ 看看有沒有人找你一樣,這種方式是不是很浪費你(CPU)的時間?所以中斷是硬件主動的方式,比輪詢(CPU 主動)更有效一些。
好了,這里又有了一個問題,每個硬件設備都中斷,那么如何區分不同硬件呢?不同設備同時中斷如何知道哪個中斷是來自硬盤、哪個來自網卡呢?這個很容易,不是每個 QQ 號碼都不相同嗎?同樣的,系統上的每個硬件設備都會被分配一個 IRQ 號,通過這個唯一的 IRQ 號就能區別張三和李四了。
在計算機里,中斷是一種電信號,由硬件產生,并直接送到中斷控制器(如 8259A)上,然后再由中斷控制器向 CPU 發送信號,CPU 檢測到該信號后,就中斷當前的工作轉而去處理中斷。然后,處理器會通知操作系統已經產生中斷,這樣操作系統就會對這個中斷進行適當的處理。現在來看一下中斷控制器,常見的中斷控制器有兩種:可編程中斷控制器 8259A 和高級可編程中斷控制器(APIC),中斷控制器應該在大學的硬件接口和計算機體系結構的相關課程中都學過。傳統的 8259A 只適合單 CPU 的情況,現在都是多 CPU 多核的 SMP 體系,所以為了充分利用 SMP 體系結構、把中斷傳遞給系統上的每個 CPU 以便更好實現并行和提高性能,Intel 引入了高級可編程中斷控制器(APIC)。
光有高級可編程中斷控制器的硬件支持還不夠,Linux 內核還必須能利用到這些硬件特質,所以只有 kernel 2.4 以后的版本才支持把不同的硬件中斷請求(IRQs)分配到特定的 CPU 上,這個綁定技術被稱為 SMP IRQ Affinity. 更多介紹請參看 Linux 內核源代碼自帶的文檔:linux-2.6.31.8/Documentation/IRQ-affinity.txt
如何綁定中斷處理CPU
先看看系統上的中斷是怎么分配在 CPU 上的,很顯然 CPU0 上處理的中斷多一些:
# cat /proc/interrupts CPU0 CPU1 0: 918926335 0 IO-APIC-edge timer 1: 2 0 IO-APIC-edge i8042 8: 0 0 IO-APIC-edge rtc 9: 0 0 IO-APIC-level acpi 12: 4 0 IO-APIC-edge i8042 14: 8248017 0 IO-APIC-edge ide0 50: 194 0 IO-APIC-level ohci_hcd:usb2 58: 31673 0 IO-APIC-level sata_nv 90: 1070374 0 PCI-MSI eth0
233: 10 0 IO-APIC-level ehci_hcd:usb1
NMI: 5077 2032
LOC: 918809969 918809894
ERR: 0
MIS: 0
即能看到中斷號,也能看到每個CPU的中斷處理次數。
為了不讓 CPU0 很累怎么把部分中斷轉移到 CPU1 上呢?或者說如何把 eth0 網卡的中斷轉到 CPU1 上呢?先查看一下 IRQ 90 中斷的 smp affinity,看看當前中斷是怎么分配在不同 CPU 上的(ffffffff 意味著分配在所有可用 CPU 上):
# cat /proc/irq/90/smp_affinity
7fffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff,ffffffff
在進一步動手之前我們需要先停掉 IRQ 自動調節的服務進程,這樣才能手動綁定 IRQ 到不同 CPU,否則自己手動綁定做的更改將會被自動調節進程給覆蓋掉。如果想修改 IRQ 90 的中斷處理,綁定到第2個 CPU(CPU1):
# /etc/init.d/irqbalance stop # echo "2" > /proc/irq/90/smp_affinity
(上面的 echo “2” 是怎么來的?為什么是 ”2“?請參考本文末尾:計算 SMP IRQ Affinity)過段時間在看 /proc/interrupts,是不是 90:eth0 在 CPU1 上的中斷增加了(145)、在 CPU0 上的中斷沒變?不斷打印 /proc/interrupts 就會發現 eth0 在 CPU0 上的中斷數始終保持不變,而在 CPU1 上的中斷數是持續增加的,這正是我們想要的結果:
# cat /proc/interrupts CPU0 CPU1 0: 922506515 0 IO-APIC-edge timer 1: 2 0 IO-APIC-edge i8042 8: 0 0 IO-APIC-edge rtc 9: 0 0 IO-APIC-level acpi 12: 4 0 IO-APIC-edge i8042 14: 8280147 0 IO-APIC-edge ide0 50: 194 0 IO-APIC-level ohci_hcd:usb2 58: 31907 0 IO-APIC-level sata_nv 90: 1073399 145 PCI-MSI eth0
233: 10 0 IO-APIC-level ehci_hcd:usb1
NMI: 5093 2043
LOC: 922389696 922389621
ERR: 0
MIS: 0
有什么用
在網絡非常 heavy 的情況下,對于文件服務器、高流量 Web 服務器這樣的應用來說,把不同的網卡 IRQ 均衡綁定到不同的 CPU 上將會減輕某個 CPU 的負擔,提高多個 CPU 整體處理中斷的能力;對于數據庫服務器這樣的應用來說,把磁盤控制器綁到一個 CPU、把網卡綁定到另一個 CPU 將會提高數據庫的響應時間、優化性能。合理的根據自己的生產環境和應用的特點來平衡 IRQ 中斷有助于提高系統的整體吞吐能力和性能。
VPSee 經常收到網友來信問到如何優化 Linux、優化 VPS、這個問題不太好回答,要記住的是性能優化是一個過程而不是結果,不是看了些文檔改了改參數就叫優化了,后面還需要大量的測試、監測以及持續的觀察和改進。
其他
把irqbalance 停掉會不會有其他問題出玩?
不會有什么嚴重問題,但是沒有 irqbalance 也沒有合理的做手動 irq 綁定的話會有性能問題。手動 irq 只推薦給很 heavy、很特殊的情況,比如帶多網卡多硬盤的網絡存儲服務器,一般機器一般應用還是用 irqbalance 省心。
事實上以前 Linux 是不推薦使用 irqbalance 的,原因在于當時的 irqbalance 實現很簡單,沒有什么優化作用,后來的 irqbalance (cache topology + power aware) 有了很大改進,在多核上表現良好。
計算 SMP IRQ Affinity
“echo 2 > /proc/irq/90/smp_affinity” 中的 ”2“ 是怎么來的,這其實是個二進制數字,代表 00000010,00000001 代表 CPU0 的話,00000010 就代表 CPU1, “echo 2 > /proc/irq/90/smp_affinity” 的意思就是說把 90 中斷綁定到 00000010(CPU1)上。所以各個 CPU 用二進制和十六進制表示就是:
Binary Hex CPU 0 00000001 1 CPU 1 00000010 2 CPU 2 00000100 4 CPU 3 00001000 8
如果我想把 IRQ 綁定到 CPU2 上就是 00000100=4:
# echo "4" > /proc/irq/90/smp_affinity
如果我想把 IRQ 同時平衡到 CPU0 和 CPU2 上就是 00000001+00000100=00000101=5
# echo "5" > /proc/irq/90/smp_affinity
需要注意的是,在手動綁定 IRQ 到 CPU 之前需要先停掉 irqbalance 這個服務,irqbalance 是個服務進程、是用來自動綁定和平衡 IRQ 的:
# /etc/init.d/irqbalance stop
還有一個限制就是,IO-APIC 有兩種工作模式:
logic 和 physical,
在 logic 模式下 IO-APIC 可以同時分布同一種 IO 中斷到8顆 CPU (core) 上(受到 bitmask 寄存器的限制,因為 bitmask 只有8位長。);
在 physical 模式下不能同時分布同一中斷到不同 CPU 上,比如,不能讓 eth0 中斷同時由 CPU0 和 CPU1 處理,這個時候只能定位 eth0 到 CPU0、eth1 到 CPU1,也就是說 eth0 中斷不能像 logic 模式那樣可以同時由多個 CPU 處理。
下面給大家提供一個計算小腳本值提供中斷在單CPU上
#!/bin/bash
#Author Jiaion MSN:Jiaion@msn.com
[ $# -ne 1 ] && echo ‘$1 is Cpu core number’ && exit 1 CCN=$1
echo “Print eth0 affinity”
for((i=0; i<${CCN}; i++))
do
echo ==============================
echo "Cpu Core $i is affinity"
((affinity=(1<<i)))
echo "obase=16;${affinity}" | bc
echo ==============================
done