IRQ Balance steuert unter Linux die Verteilung von Hardware‑Interrupts auf CPU‑Kerne und entscheidet damit, ob Netzwerklast gleichmäßig läuft oder einzelne Kerne ausbremsen. Ich zeige dir, wie du irqbalance zielgerichtet einsetzt, wann ich auf manuelle IRQ‑Affinität umstelle und welche Einstellungen auf Servern mit hoher Netzwerklast wirklich zählen.
Zentrale Punkte
Bevor ich ins Detail gehe, fasse ich die wichtigsten Entscheidungen zusammen, die mir in Projekten mit hoher I/O‑Last zuverlässig geholfen haben. Ich halte die automatische Verteilung durch irqbalance als Startpunkt für sinnvoll, messe die Wirkung und passe selektiv an. Bei deterministischen Workloads pinne ich einzelne IRQs manuell auf bestimmte Kerne und schließe übrige CPUs von der automatischen Verteilung aus. NUMA‑Nähe berücksichtige ich früh, weil sie Latenz senkt und Durchsatz sichert. Mit einem klaren Monitoring erkenne ich Engpässe schneller und reguliere sie ohne unnötige Risiken.
Diese Liste zeigt dir, worauf ich bei der Konfiguration besonders achte:
- Automatik zuerst: irqbalance aktivieren, Wirkung messen
- Affinität gezielt: kritische IRQs pinnen, Jitter senken
- Banned CPUs: Kerne für App‑Threads freihalten
- NUMA beachten: IRQs nah am Speicherknoten halten
- Monitoring: /proc/interrupts und Latenzen prüfen
IRQ‑Grundlagen kurz erklärt
Ein Interrupt Request (IRQ) ist ein Signal, mit dem Hardware Arbeit an die CPU übergibt und damit einen laufenden Task unterbricht. Treffen zu viele dieser Signale auf denselben Kern, steigt dort die Auslastung und die Reaktionszeit wird zäh, während andere Kerne ungenutzt bleiben; genau das will ich mit Verteilung vermeiden. irqbalance verteilt diese IRQs dynamisch über mehrere Kerne und bewertet in Abständen den Systemzustand. Ich sehe mir dazu zuerst /proc/interrupts an und erkenne in den Spalten, wie viele IRQs pro CPU ankommen. Werden einzelne Spalten übermächtig, reguliere ich aktiv nach und reduziere so unnötige Hotspots.
Automatische Verteilung mit irqbalance
Ich starte auf modernen Distributionen mit dem Dienst irqbalance, der standardmäßig periodisch die IRQ‑Verteilung anpasst. Aktiv schalte ich ihn mit systemctl enable --now irqbalance und prüfe den Status, bevor ich tiefer eingreife; so nutze ich die vorhandene Automatik. Die Konfigurationsdateien liegen je nach System in /etc/sysconfig/irqbalance oder /etc/default/irqbalance, dort kann ich CPUs oder IRQs ausschließen. Besonders hilfreich ist die Variable IRQBALANCE_BANNED_CPUS als 64‑Bit‑Maske, um definierte Kerne für Applikationen zu reservieren. Wer tiefer in Praxisbeispiele einsteigen will, findet hier eine kompakte Einführung zur Netzwerk‑Performance, die ich in Workshops häufig referenziere und in Projekten übernehme.
Manuelle IRQ‑Affinität sicher umsetzen
Wenn Workloads sehr empfindlich auf Jitter reagieren oder bestimmte Kerne exklusiv für User‑Space‑Prozesse frei bleiben sollen, setze ich IRQ‑Affinität manuell. Ich schreibe dafür Bitmasken nach /proc/irq/IRQ‑NUMMER/smp_affinity und lege fest, auf welchen Kernen ein Interrupt laufen darf; das schafft planbares Verhalten. Vorab ermittle ich die relevanten IRQ‑Nummern mit grep in /proc/interrupts. Für Netzwerkgeräte pinne ich RX/TX‑Queues häufig auf Kerne, die nahe an den App‑Threads liegen, während ich andere Kerne freihalte. Gute Hintergründe zu diesem Vorgehen bündelt dieser kurze IRQ‑Affinity Guide, den ich regelmäßig als Startpunkt nutze.
Die folgende Tabelle zeigt gängige Bitmasken und was sie bedeuten. Ich nutze diese Beispiele, um Konfigurationen zügig und fehlerarm zu setzen und die Wirkung anschließend mit /proc/interrupts zu verifizieren.
| Ziel | Beispiel‑Maske (hex) | Kerne | Kommentar |
|---|---|---|---|
| Nur CPU0 | 0x1 | 0 | Einfacher Test, geringe Streuung |
| Nur CPU1 | 0x2 | 1 | Trennt IRQs von CPU0, senkt Interferenz |
| CPU0–CPU1 | 0x3 | 0–1 | Verteilt auf zwei Kerne, leichte Entlastung |
| CPU2–CPU3 | 0xC | 2–3 | Nützlich, wenn 0–1 für App‑Threads frei bleiben |
| CPU0–CPU3 | 0xF | 0–3 | Breite Streuung auf 4 Kerne, mischt Last |
Messung: /proc/interrupts richtig lesen
Ich öffne die Datei /proc/interrupts und sehe pro Zeile einen IRQ sowie pro Spalte die Zähler je CPU; das macht Ungleichgewichte sofort sichtbar. Wenn eine Spalte deutlich schneller wächst als andere, bündelt sich dort die Last. Dann prüfe ich, welcher Treiber beteiligt ist, und ob RSS/RPS bereits verteilt. Zusätzlich starte ich irqbalance temporär im Vordergrund mit Debug‑Ausgabe, um seine Entscheidungen zu verstehen und Fehleinschätzungen zu vermeiden. Nach jeder Änderung kontrolliere ich erneut die Zähler und messe Latenz unter Last, damit ich Effekte belegen und unnötige Risiken meiden kann.
CPU‑Isolierung und Banned‑Masken
Ich setze IRQBALANCE_BANNED_CPUS, um bestimmte Kerne konsequent von der automatischen Verteilung auszunehmen; so halte ich Ressourcen für App‑Threads frei. In neueren Setups nutze ich zusätzlich IRQBALANCE_BANNED_IRQS, wenn einzelne Geräte eigenständig auf einem Kern laufen sollen; das reduziert Störungen für sensible Workloads. In Low‑Latency‑Szenarien deaktiviere ich irqbalance gezielt und pinne IRQs statisch, damit keine Re‑Verteilung dazwischenfunkt. Wer die CPU‑Zuweisung von Interrupt‑Handling genauer verstehen will, findet nützliche Hintergründe zum Interrupt Handling auf Servern. Wichtig bleibt: erst messen, dann festlegen und die Wirkung wieder prüfen, um Überraschungen im Betrieb zu vermeiden.
NUMA‑Aspekte und Nähe
Auf NUMA‑Systemen achte ich darauf, IRQs möglichst auf Kerne jenes NUMA‑Knotens zu lenken, in dessen Speicher die betreffenden Daten liegen; das senkt Latenz und erhöht Durchsatz. Ich kombiniere das mit CPU‑Affinity für die Applikation, damit Threads und Interrupts lokal zueinander laufen. irqbalance kann auf NUMA gut arbeiten, doch ich justiere bei Bedarf mit Banned‑Masken nach. Kritisch ist, nicht über Knoten hinweg zu streuen, wenn Last ohnehin lokal gehalten werden kann. Wer diese Nähe wahrt, gewinnt konstante Antwortzeiten und schont wertvolle Cache-Ressourcen.
Netzwerk‑Intensiv: RSS, RPS/RFS und XPS
Bevor ich IRQ‑Masken fein abstimme, prüfe ich die NIC‑Features wie RSS sowie Kernel‑Mechanismen wie RPS/RFS und XPS; sie beeinflussen die Verteilung von Paketen stark. RSS verteilt Queue‑Interrupts bereits auf mehrere Kerne, während RPS/RFS die Verarbeitung im Kernel und XPS die Sendewege formen; das vermeidet unnötige Hotspots. Ich gleiche diese Mechanismen mit meiner IRQ‑Strategie ab, damit sie nicht gegeneinander arbeiten. Stimmen die Queues, die IRQ‑Affinitäten und die App‑Affinity zusammen, läuft Netzwerk‑I/O deutlich runder. Danach messe ich erneut unter realer Last, bevor ich weitere Schritte setze.
MSI‑X, Multi‑Queue und sauberes Queue‑Layout
Viele 10–100G‑NICs nutzen MSI‑X und stellen pro RX/TX‑Queue eigene Interrupt‑Vektoren bereit. Ich prüfe zuerst mit ethtool -l eth0 (Kanalanzahl) und /proc/interrupts, wie viele Queues wirklich aktiv sind und wie sie heißen (z. B. eth0‑TxRx‑0, eth0‑TxRx‑1). Ziel ist, die Zahl der Queues an die Zahl der genutzten Kerne pro NUMA‑Knoten anzupassen und diese deterministisch zu pinnen. Mit ethtool -L eth0 combined N stelle ich die Queue‑Anzahl ein; danach ordne ich die entstehenden IRQs per smp_affinity passenden Kernen zu. Ich achte darauf, RX/TX‑Paare derselben Queue auf denselben Kern oder zumindest denselben Socket zu legen, damit Cache‑Lokalität greift. Wichtig: Änderungen an Queue‑Zahl und Affinität prüfe ich direkt in /proc/interrupts und mit einem kurzen Lasttest (pps/Throughput), bevor ich weiter optimiere.
Interrupt‑Koaleszenz und NAPI‑Budget
Vor allem bei hohen Paket‑Raten beeinflussen Koaleszenz‑Werte die Effektivität meiner IRQ‑Strategie. Mit ethtool -c eth0 sehe ich, ob rx-usecs und rx-frames gesetzt sind. Mehr Koaleszenz reduziert die Anzahl der IRQs pro Sekunde und spart CPU, erhöht aber Latenz und Jitter. Ich justiere behutsam: kleine Schritte, jeweils messen (p95/p99‑Latenz und CPU‑Last). Auf Senderseite wirkt tx-usecs analog. Daneben skaliere ich das NAPI‑Verhalten über net.core.netdev_budget und net.core.netdev_budget_usecs, wenn NET_RX in SoftIRQs zu stauen beginnt. Wachsen die Drops in /proc/net/softnet_stat, erhöhe ich testweise das Budget oder verteile RX‑Queues konsequenter; wird die Systemlatenz zäh, drehe ich zurück. GRO/LRO und TSO/GSO berücksichtige ich im Zusammenspiel: Überschießende Aggregation senkt IRQ‑Last, kann aber Latenzspitzen erzeugen – ich gleiche sie mit dem Anwendungsprofil ab.
SoftIRQs transparent lesen
Neben HardIRQs entscheide ich über die Last in SoftIRQs. Mit cat /proc/softirqs beobachte ich NET_RX und NET_TX pro CPU; wenn einzelne Spalten dominieren, landet zu viel Arbeit dort in ksoftirqd‑Threads. Ein top -H zeigt mir schnell, welche ksoftirqd/N Kerne belasten. Ich messe tiefer mit perf top oder kurzen perf record Läufen, um Hotspots im Treiber oder der Stack‑Verarbeitung zu sehen. Werden ksoftirqd‑Threads aktiv (statt unmittelbarer Abarbeitung im IRQ‑Kontext), steigt die Latenz oft deutlich; ich reagiere mit besserer Queue‑Verteilung, größerem NAPI‑Budget oder gezieltem CPU‑Pinning der betroffenen ksoftirqd‑Threads via taskset -pc. Wichtig: Diese Anpassungen dokumentiere ich, weil sie subtil wirken und ich Rollbacks im Zweifel schnell brauche.
SMT/Hyper‑Threading und Topologie richtig nutzen
Mit aktivem SMT teile ich mir einen physischen Kern mit zwei logischen CPUs. Ich prüfe die Sibling‑Beziehungen über lscpu -e und /sys/devices/system/cpu/cpuX/topology/thread_siblings_list. Für latenzkritische Pfade vermeide ich, App‑Thread und zugehörigen IRQ auf denselben physischen Kern (verschiedene SMT‑Threads) zu legen; sie konkurrieren um Ausführungseinheiten und Caches. Ich bevorzuge Paare, in denen z. B. ein App‑Thread auf CPU2 und die dazugehörige RX‑Queue auf CPU3 (anderer physischer Kern, gleicher NUMA‑Knoten) läuft. Wenn SMT die Konstanz stört, plane ich stattdessen mit weniger, aber exklusiven physischen Kernen und spare mir unruhige Interferenzen.
Virtualisierung: KVM, vhost und SR‑IOV
In virtualisierten Umgebungen betrachte ich Host und Gast getrennt. Auf dem Host verteile ich physische NIC‑IRQs sauber über Kerne des passenden NUMA‑Knotens. Nutzt der Gast virtio‑net, entstehen zusätzliche IRQs für vhost‑Threads; ich erkenne sie in /proc/interrupts und pinne vhost‑Worker konsistent zu den Queues der physischen NIC. Auf Gastebene setze ich ebenfalls RSS/XPS und IRQ‑Affinitäten, sofern der virtio‑Treiber mehrere Queues bereitstellt. Bei SR‑IOV lohnt es sich, jedem Gast eine oder mehrere VFs mit eigenen MSI‑X‑Vektoren zuzuweisen und diese im Gast zu pinnen; die Isolation verbessert Latenz und Vorhersagbarkeit. Ich halte mich an ein klares Schema: vCPUs des Gasts auf dedizierte pCPUs, zugehörige IRQs auf nahe Kerne, und die Emulator‑/vhost‑Threads nicht mit rechenintensiven App‑Threads vermischen – so bleibt der Datenpfad planbar.
CPU‑Frequenz, C‑States und NOHZ‑Tuning
IRQ‑Latenzen leiden, wenn Kerne in tiefe C‑States abtauchen oder aggressiv takten. Für sensible Workloads setze ich den CPU‑Governor auf performance (cpupower frequency‑set -g performance) und reduziere tiefe C‑States über Boot‑ oder Treiber‑Optionen, um Aufwachzeiten zu begrenzen. Auf stark ausgelasteten Servern wirkt das oft positiver als jede Feinabstimmung bei Affinitäten. In sehr harten Latenz‑Profilen ergänze ich nohz_full= und rcu_nocbs= für isolierte Kerne, sodass Tick‑Timer und RCU‑Call‑Backs nicht querfeuern; die Housekeeping‑CPUs definiere ich bewusst getrennt. Diese Eingriffe teste ich jedoch separat, weil sie Seitenwirkungen auf Scheduling und Energieaufnahme haben können. Entscheidend bleibt: Messwerte vor und nach der Änderung sauber vergleichen, sonst tappe ich bei Optimierungen im Dunkeln.
Systemd, Cgroups und App‑Isolation
Neben IRQ‑Pinning trenne ich App‑Threads mit Cgroups und systemd‑Affinity. Über CPUAffinity= in Unit‑Dateien und die CPU‑Controller (cgroup v2) gebe ich Diensten feste Kerne. Damit verhindere ich, dass scheduler‑seitig Threads auf jene CPUs rutschen, die ich für IRQs vorgesehen habe. In Container‑Umgebungen setze ich cpuset.cpus und prüfe cpuset.cpus.effective, damit Ressourcen‑Versprechen wirklich greifen. Wichtig: IRQBALANCE_BANNED_CPUS steuert nur, wo irqbalance nicht verteilt; Kernel‑Threads wie ksoftirqd folgen weiterhin dem Scheduler. Für harte Isolation brauche ich also eine Kombination aus IRQ‑Affinitäten, CPU‑Affinity der Dienste und ggf. isolierten Kernen. So bleiben Datenpfad und Anwendung klar getrennt und die Last mischt sich nicht unkontrolliert.
Typische Fehler und Gegenmaßnahmen
Ich deaktiviere irqbalance niemals pauschal, ohne Lastbilder zu kennen; sonst klumpen IRQs schnell auf wenigen Kernen. Ebenso ungünstig ist es, alle Kerne für alle IRQs zu öffnen, obwohl sensible Threads exklusive Ressourcen brauchen. Ein weiterer Fehler: Änderungen nicht isoliert testen und Effekte nicht messen; so bleibt unklar, was tatsächlich hilft. Auch Hyper‑Threading‑Paare berücksichtige ich: App‑Thread und zugehöriger IRQ teilen sich besser nicht denselben physischen Kern. Ich dokumentiere jeden Schritt und mache Rollback‑Punkte, damit ich bei Problemen zügig zur letzten guten Konfiguration zurückkehre.
Praxis‑Checkliste für Server
Ich beginne stets mit einer Baseline: irqbalance aktiv, Systemlast erfassen, /proc/interrupts beobachten und Latenzen messen; erst danach fasse ich Einstellungen an. Im zweiten Schritt schließe ich mit IRQBALANCE_BANNED_CPUS jene Kerne aus, die für App‑Threads reserviert bleiben sollen; so verhindere ich unnötige IRQ‑Einstreuungen. Anschließend pinne ich kritische IRQs via smp_affinity auf wenige, gut gewählte Kerne und halte NUMA‑Nähe ein. Dann prüfe ich RSS/RPS/RFS und XPS sowie Offloading‑Optionen der NIC, um Arbeit sinnvoll zu verteilen. Abschließend teste ich unter Produktionslast, vergleiche Metriken und halte nur jene Änderungen, die nachweislich wirken.
Konfigurationsdateien und systemd‑Befehle
Ich aktiviere den Dienst mit systemctl enable --now irqbalance und prüfe mit systemctl status irqbalance die Laufzeit; so stelle ich den Dienst sicher bereit. In /etc/sysconfig/irqbalance oder /etc/default/irqbalance setze ich IRQBALANCE_BANNED_CPUS sowie optional IRQBALANCE_BANNED_IRQS. Änderungen übernehme ich mit systemctl restart irqbalance und beobachte parallel die Zähler in /proc/interrupts. Für Tests nutze ich den Foreground‑Modus von irqbalance, um Entscheidungen live zu verfolgen. Erst wenn ich das Verhalten verstehe, schreibe ich Anpassungen dauerhaft in die Konfiguration.
Wann ich irqbalance deaktiviere
In Echtzeit‑Setups oder bei extrem latenzsensitiven Anwendungen stoppe ich irqbalance und pinne IRQs statisch, damit keine Re‑Verteilung stört. Ich isoliere die Kerne für diese Workloads und lasse Traffic‑IRQs bewusst auf anderen Kernen laufen; so bleiben Applikations‑Threads planbar. Auch bei streng getrennten Tenant‑Umgebungen lohnt sich dieser Ansatz, weil ich Interferenzen zwischen VMs oder Containern reduziere. Treten Treiber auf, die mit der Automatik schlechter arbeiten, schließe ich deren IRQs via Banned‑Liste aus. Sobald die Lastmuster wieder variabler werden, aktiviere ich irqbalance erneut und überprüfe die Wirkung mit frischen Messwerten.
Kurz zusammengefasst
Ich starte mit irqbalance, messe die Wirkung und passe selektiv an, statt blind überall einzugreifen; so halte ich Systemsicht und Transparenz. Für sensible Workloads pinne ich passende IRQs, isoliere Kerne für Applikationen und respektiere NUMA‑Nähe. Mit Banned‑Masken steuere ich, wo irqbalance arbeiten darf, und verhindere ungewollte Verschiebungen. Ich prüfe regelmäßig /proc/interrupts, Latenz und Durchsatz, damit Änderungen belastbar belegt sind. Wer so vorgeht, schöpft das Potenzial von IRQ Balance aus und hält Server unter Netzwerklast spürbar reaktiv.


