硬件中斷髮生頻繁,是件很消耗 CPU 資源的事情,在多核 CPU 條件下若是有辦法把大量硬件中斷分配給不一樣的 CPU (core) 處理顯然能很好的平衡性能。如今的服務器上動不動就是多 CPU 多核、多網卡、多硬盤,若是能讓網卡中斷獨佔1個 CPU (core)、磁盤 IO 中斷獨佔1個 CPU 的話將會大大減輕單一 CPU 的負擔、提升總體處理效率。我前天收到一位網友的郵件提到了 SMP IRQ Affinity,引起了今天的話題。如下操做在 SUN FIre X2100 M2 服務器+ 64位版本 CentOS 5.5 + Linux 2.6.18-194.3.1.el5 上執行。linux
什麼是中斷算法
中文教材上對 「中斷」 的定義太生硬了,簡單的說就是,每一個硬件設備(如:硬盤、網卡等)都須要和 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網絡
如何使用oracle
先看看系統上的中斷是怎麼分配在 CPU 上的,很顯然 CPU0 上處理的中斷多一些:ide
# 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
爲了避免讓 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
過段時間在看 /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 中斷有助於提升系統的總體吞吐能力和性能。
本人常常收到網友來信問到如何優化 Linux、優化 VPS、這個問題不太好回答,要記住的是性能優化是一個過程而不是結果,不是看了些文檔改了改參數就叫優化了,後面還須要大量的測試、監測以及持續的觀察和改進。
綁定進程到不一樣CPU
介紹了在 Linux 多核下如何綁定硬件中斷到不一樣 CPU,其實也能夠用相似的作法把進程手動分配到特定的 CPU 上,平時在 Linux 上運行的各類進程都是由 Linux 內核統一分配和管理的,由進程調度算法來決定哪一個進程能夠開始使用 CPU、哪一個進程須要睡眠或等待、哪一個進程運行在哪一個 CPU 上等。若是你對操做系統的內核和進程調度程序感興趣的話,不妨看看那本經典的 Operating Systems Design and Implementation(Linus Torvalds 就是看了這本書受到啓發寫出了 Linux),從簡單的 Minix 入手,hack 內核是件頗有意思的事情,本人之前修改過 Minix 內核的進程調度,學到了內核方面的不少東西。另外推薦一本課外讀物:Just for Fun,Linus Torvalds 寫的一本自傳。
Linux 給咱們提供了方便的工具用來手動分配進程到不一樣的 CPU 上(CPU Affinity),這樣咱們能夠按照服務器和應用的特性來安排特定的進程到特定的 CPU 上,好比 Oracle 要消耗大量 CPU 和 I/O 資源,若是咱們能分配 oracle 進程到某個或多個 CPU 上並由這些 CPU 專門處理 Oracle 的話會毫無疑問的提升應用程序的響應和性能。還有一些特殊狀況是必須綁定應用程序到某個 CPU 上的,好比某個軟件的受權是單 CPU 的,若是想運行在多 CPU 機器上的話就必須限制這個軟件到某一個 CPU 上。
安裝 schedutils
在 CentOS/Fedora 下安裝 schedutils:
# yum install schedutils
在 Debian/Ubuntu 下安裝 schedutils:
# apt-get install schedutils
若是正在使用 CentOS/Fedora/Debian/Ubuntu 的最新版本的話,schedutils/util-linux 這個軟件包可能已經裝上了。
計算 CPU Affinity 和計算 SMP IRQ Affinity 差很少:
0x00000001 (CPU0)
0x00000002 (CPU1)
0x00000003 (CPU0+CPU1)
0x00000004 (CPU2)
...
使用 schedutils
若是想設置進程號(PID)爲 12212 的進程到 CPU0 上的話:
# taskset 0x00000001 -p 12212