In Linux, IRQ Balance gestisce la distribuzione degli interrupt hardware tra i core della CPU e determina quindi se il carico di rete viene distribuito in modo uniforme o se alcuni core vengono rallentati. Ti mostrerò come utilizzare irqbalance in modo mirato, quando passare all’affinità IRQ manuale e quali impostazioni utilizzare su server con elevato carico di rete che contano davvero.
Punti centrali
Prima di entrare nei dettagli, riassumo le decisioni più importanti che mi hanno aiutato in modo affidabile nei progetti con un carico I/O elevato. Ritengo che la distribuzione automatica tramite irqbalance sia un buon punto di partenza; ne misuro l’efficacia e apporto modifiche selettive. In presenza di carichi di lavoro deterministici, assegno manualmente singoli IRQ a determinati core ed escludo le restanti CPU dalla distribuzione automatica. Prendo in considerazione la vicinanza NUMA sin dalle prime fasi, poiché riduce la latenza e garantisce il throughput. Grazie a un monitoraggio chiaro, individuo più rapidamente i colli di bottiglia e li risolvo senza inutili I rischi.
Questo elenco ti mostra a cosa presto particolare attenzione durante la configurazione:
- Automatico Per prima cosa: attivare irqbalance, misurare l'effetto
- affinità in modo mirato: fissare gli IRQ critici, ridurre il jitter
- CPU vietate: Mantenere liberi i kernel per i thread delle app
- NUMA Nota: mantenere gli IRQ vicini al nodo di memoria
- Monitoraggio: Verificare /proc/interrupts e le latenze
Nozioni di base sugli IRQ spiegate in breve
Una richiesta di interruzione (IRQ) è un segnale con cui l’hardware trasferisce un’operazione alla CPU, interrompendo così un’attività in corso. Se un numero eccessivo di questi segnali raggiunge lo stesso core, il carico di lavoro su di esso aumenta e il tempo di risposta rallenta, mentre altri core rimangono inutilizzati; è proprio questo che voglio ottenere con Distribuzione evitare. irqbalance distribuisce dinamicamente questi IRQ su più core e valuta a intervalli regolari lo stato del sistema. Per prima cosa darò un’occhiata a /proc/interruzioni e osserva nelle colonne quanti IRQ arrivano per ogni CPU. Se alcune colonne diventano troppo piene, intervengo attivamente per regolare il carico e ridurre così gli IRQ superflui Hotspot.
Distribuzione automatica con irqbalance
Sulle distribuzioni moderne avvio il servizio irqbalance, che per impostazione predefinita regola periodicamente la distribuzione degli IRQ. Lo attivo con systemctl enable --now irqbalance e verifico lo stato prima di intervenire in modo più approfondito; in questo modo sfrutto ciò che è già disponibile Automatico. A seconda del sistema, i file di configurazione si trovano in /etc/sysconfig/irqbalance oppure /etc/default/irqbalance, lì posso escludere CPU o IRQ. Particolarmente utile è la variabile IRQBALANCE_BANNED_CPUS come maschera a 64 bit, per riservare core specifici alle applicazioni. Chi desidera approfondire gli esempi pratici, troverà qui una breve introduzione alla Prestazioni della rete, a cui faccio spesso riferimento nei workshop e che applico nei progetti.
Implementare in modo sicuro l'affinità IRQ manuale
Quando i carichi di lavoro sono molto sensibili al jitter o quando è necessario che determinati core rimangano liberi esclusivamente per i processi dello spazio utente, imposto manualmente l’affinità IRQ. A tal fine, scrivo le maschere di bit secondo /proc/irq/NUMERO IRQ/smp_affinity e stabilisco su quali core possa essere eseguito un interrupt; ciò garantisce una maggiore pianificabilità Condotta. Per prima cosa individuo i numeri IRQ rilevanti con grep in /proc/interruzioni. Per i dispositivi di rete, spesso assegno le code RX/TX a core vicini ai thread delle app, lasciando liberi gli altri core. Questo breve articolo fornisce una buona spiegazione di questo approccio Guida all’affinità IRQ, che utilizzo regolarmente come punto di partenza.
La tabella seguente mostra le maschere di bit più comuni e il loro significato. Utilizzo questi esempi per impostare le configurazioni in modo rapido e con il minor numero possibile di errori, per poi verificarne l'effetto con /proc/interruzioni a verificare.
| Obiettivo | Esempio di maschera (esadecimale) | nuclei | Commento |
|---|---|---|---|
| Solo CPU0 | 0x1 | 0 | Test semplice, basso dispersione |
| Solo CPU1 | 0x2 | 1 | Disconnette gli IRQ dalla CPU0, riduce Interferenza |
| CPU0–CPU1 | 0x3 | 0–1 | Distribuiti su due core, leggeri Sollievo |
| CPU2–CPU3 | 0xC | 2-3 | Utile se 0–1 per i thread delle app libero soggiorno |
| CPU0–CPU3 | 0xF | 0–3 | Ampia distribuzione su 4 nuclei, mista Carico |
Misurazione: leggere correttamente /proc/interrupts
Apro il file /proc/interruzioni e vedo un IRQ per ogni riga e, per ogni colonna, i contatori per ciascuna CPU; questo fa emergere immediatamente gli squilibri visibile. Se una colonna cresce in modo nettamente più rapido rispetto alle altre, il carico si concentra proprio lì. A quel punto verifico quale driver sia coinvolto e se RSS/RPS stiano già distribuendo il carico. Inoltre, avvio temporaneamente irqbalance in primo piano con l’output di debug, per comprenderne le decisioni ed evitare valutazioni errate. Dopo ogni modifica, ricontrollo i contatori e misuro la latenza sotto carico, in modo da poter documentare gli effetti ed evitare inutili I rischi può evitare.
Isolamento della CPU e maschere di esclusione
Ho impostato IRQBALANCE_BANNED_CPUS, per escludere sistematicamente determinati core dalla distribuzione automatica; in questo modo libero risorse per i thread delle app. Nelle configurazioni più recenti utilizzo inoltre IRQBALANCE_BANNED_IRQS, se si desidera che singoli dispositivi funzionino in modo autonomo su un kernel; ciò riduce le interferenze per i sistemi sensibili Carichi di lavoro. Negli scenari a bassa latenza, disattivo in modo mirato irqbalance e assegno gli IRQ in modo statico, in modo che nessuna ridistribuzione interferisca. Chi desidera comprendere più a fondo l’assegnazione da parte della CPU della gestione degli interrupt, troverà utili approfondimenti su Gestione degli interrupt sui server. È importante ricordare: prima misurare, poi definire e verificare nuovamente l'effetto, per evitare sorprese nel Operazione da evitare.
Aspetti NUMA e vicinanza
Sui sistemi NUMA, cerco di indirizzare gli IRQ, per quanto possibile, ai core di quel nodo NUMA in cui si trovano i dati in questione; ciò riduce la latenza e aumenta Produttività. Lo abbino all’affinità della CPU per l’applicazione, in modo che i thread e gli interrupt vengano eseguiti localmente tra loro. irqbalance funziona bene su NUMA, ma se necessario effettuo ulteriori regolazioni con le maschere di esclusione. È fondamentale non distribuire il carico su più nodi se è comunque possibile mantenerlo a livello locale. Chi mantiene questa vicinanza ottiene tempi di risposta costanti e preserva preziose Cache-Risorse.
Corso intensivo sulle reti: RSS, RPS/RFS e XPS
Prima di ottimizzare le maschere IRQ, verifico le funzionalità della scheda di rete (NIC), come l’RSS, e i meccanismi del kernel, quali RPS/RFS e XPS; questi influenzano notevolmente la distribuzione dei pacchetti. L’RSS distribuisce già gli interrupt delle code su più core, mentre l’RPS/RFS modella l’elaborazione nel kernel e l’XPS definisce i percorsi di trasmissione; ciò evita inutili Hotspot. Coordino questi meccanismi con la mia strategia IRQ, in modo che non entrino in conflitto tra loro. Se le code, le affinità IRQ e l’affinità delle app sono ben sincronizzate, l’I/O di rete funziona in modo decisamente più fluido. Successivamente, effettuo una nuova misurazione sotto carico reale, prima di procedere con ulteriori Passi metto.
MSI-X, Multi-Queue e struttura ordinata delle code
Molte schede di rete da 10–100G utilizzano MSI-X e mettono a disposizione vettori di interrupt dedicati per ogni coda RX/TX. Per prima cosa verifico con ethtool -l eth0 (numero di canali) e /proc/interruzioni, quante code sono effettivamente attive e come si chiamano (ad esempio,. eth0-TxRx-0, eth0-TxRx-1). L'obiettivo è quello di adeguare il numero delle code al numero di core utilizzati per ogni nodo NUMA e di fissarle in modo deterministico. Con ethtool -L eth0 combined N impostare il numero di code; successivamente ordino gli IRQ generati tramite smp_affinity ai core appropriati. Mi assicuro che le coppie RX/TX della stessa coda vengano assegnate allo stesso core o almeno allo stesso socket, in modo che Località della cache . Importante: le modifiche al numero di code e all’affinità le controllo direttamente in /proc/interruzioni e con un breve test di carico (pps/Throughput), prima di procedere con l'ottimizzazione.
Coalescenza degli interrupt e budget NAPI
Soprattutto in presenza di elevate frequenze di pacchetti, i valori di coalescenza influenzano l'efficacia della mia strategia IRQ. Con ethtool -c eth0 vedo se rx-usecs e rx-frames sono impostati. Una maggiore coalescenza riduce il numero di IRQ al secondo e alleggerisce il carico della CPU, ma aumenta la latenza e il jitter. Regolo con cautela: piccoli passi, misurando ogni volta (latenza p95/p99 e carico della CPU). Dal lato del mittente si nota tx-usecs analogico. Inoltre, regolo il comportamento del NAPI tramite net.core.netdev_budget e net.core.netdev_budget_usecs, quando NET_RX cominciano ad accumularsi negli SoftIRQ. Se i drop aumentano in /proc/net/softnet_stat, aumento il budget a titolo di prova oppure distribuisco le code RX in modo più coerente; se la latenza del sistema diventa eccessiva, riduco nuovamente i valori. Prendo in considerazione GRO/LRO e TSO/GSO in modo integrato: un’aggregazione eccessiva riduce il carico IRQ, ma può generare picchi di latenza – li bilancio con il profilo dell’applicazione.
Lettura trasparente degli SoftIRQ
Oltre agli HardIRQ, decido io il carico degli SoftIRQ. Con cat /proc/softirqs Osservo NET_RX e NET_TX per CPU; se alcune colonne prevalgono, in quei thread ksoftirqd finisce troppo lavoro. Un top -H fammi vedere subito quali ksoftirqd/N I noccioli appesantiscono. Misuro più in profondità con perf top o brevi record perf Eseguire per individuare eventuali punti critici nel driver o nell’elaborazione dello stack. Quando i thread ksoftirqd diventano attivi (anziché l'elaborazione immediata nel contesto IRQ), la latenza spesso aumenta in modo significativo; io reagisco con una migliore distribuzione delle code, un budget NAPI più ampio o un pinning mirato della CPU dei thread ksoftirqd interessati tramite taskset -pc. Importante: documento queste modifiche perché hanno un effetto impercettibile e, in caso di dubbio, ho bisogno di poter tornare rapidamente indietro.
Utilizzare correttamente SMT/Hyper-Threading e la topologia
Con SMT attivo, condivido un core fisico con due CPU logiche. Verifico le relazioni di parità tramite lscpu -e e /sys/devices/system/cpu/cpuX/topology/thread_siblings_list. Per i percorsi in cui la latenza è critica, evito di collocare il thread dell’app e l’IRQ associato sullo stesso core fisico (thread SMT diversi); essi entrano in competizione per le unità di esecuzione e le cache. Preferisco coppie in cui, ad esempio, un thread dell’app venga eseguito su CPU2 e la relativa coda RX su CPU3 (core fisico diverso, stesso nodo NUMA). Se l’SMT compromette la costanza, opto invece per un numero minore di core fisici, ma esclusivi, evitando così situazioni instabili Interferenze.
Virtualizzazione: KVM, vhost e SR-IOV
Negli ambienti virtualizzati considero separatamente l’host e il guest. Sull’host distribuisco in modo ordinato gli IRQ delle schede di rete fisiche tra i core del nodo NUMA corrispondente. Se il guest utilizza virtio-net, vengono generati IRQ aggiuntivi per i thread vhost; li riconosco in /proc/interruzioni e assegno in modo coerente i worker vhost alle code delle schede di rete fisiche. A livello di guest, imposto anche le affinità RSS/XPS e IRQ, a condizione che il driver virtio metta a disposizione più code. Con SR-IOV, vale la pena assegnare a ogni guest una o più VF con vettori MSI-X dedicati e fissarle all’interno del guest; l’isolamento migliora la latenza e la prevedibilità. Mi attengo a uno schema chiaro: vCPU dell’ospite su pCPU dedicate, IRQ associate su core vicini e non mescolare i thread dell’emulatore/vhost con thread di applicazioni ad alta intensità di calcolo – in questo modo il percorso dei dati rimane pianificabile.
Frequenza della CPU, stati C e ottimizzazione NOHZ
Le latenze IRQ ne risentono quando i core entrano in stati C profondi o funzionano a frequenze elevate. Per i carichi di lavoro sensibili, imposto il governor della CPU su performance (cpupower frequency‑set -g performance) e riduco gli stati C profondi tramite opzioni di avvio o dei driver, per limitare i tempi di risveglio. Su server sottoposti a carico elevato, questo approccio spesso dà risultati migliori rispetto a qualsiasi regolazione fine delle affinità. In contesti caratterizzati da latenze molto elevate, aggiungo nohz_full= e rcu_nocbs= per i core isolati, in modo che il tick-timer e i callback RCU non interferiscano tra loro; definisco volutamente separatamente le CPU di housekeeping. Tuttavia, testo questi interventi separatamente, poiché possono avere effetti collaterali sulla schedulazione e sul consumo energetico. L'aspetto fondamentale rimane: confrontare accuratamente i valori misurati prima e dopo la modifica, altrimenti mi ritrovo a brancolare nel buio Ottimizzazioni al buio.
Systemd, Cgroups e isolamento delle applicazioni
Oltre al pinning degli IRQ, isolo i thread delle app utilizzando i Cgroup e l’affinità di systemd. Tramite CPUAffinity= Nei file Unit e nei controller della CPU (cgroup v2) assegno core fissi ai servizi. In questo modo impedisco che, a livello di scheduler, i thread vengano spostati sulle CPU che ho destinato agli IRQ. Negli ambienti container impiego cpuset.cpus e controllare cpuset.cpus.effective, affinché gli impegni in materia di risorse abbiano davvero effetto. Importante: IRQBALANCE_BANNED_CPUS controlla solo dove irqbalance non distribuisce; i thread del kernel come ksoftirqd continuano a seguire lo scheduler. Per un isolamento rigido ho quindi bisogno di una combinazione di affinità IRQ, affinità CPU dei servizi e, se necessario, kernel isolati. In questo modo il percorso dei dati e l’applicazione rimangono chiaramente separati e il Carico non si mescola in modo incontrollato.
Errori tipici e misure correttive
Non disattivo mai irqbalance in modo indiscriminato senza conoscere i profili di carico; altrimenti gli IRQ si concentrano rapidamente su pochi core. Altrettanto svantaggioso è aprire tutti i core a tutti gli IRQ, nonostante i thread sensibili siano esclusivi Risorse necessario. Un altro errore: non testare le modifiche in modo isolato e non misurarne gli effetti; in questo modo non è chiaro cosa sia effettivamente utile. Prendo in considerazione anche le coppie di Hyper-Threading: è preferibile che il thread dell’app e l’IRQ associato non condividano lo stesso core fisico. Documento ogni passaggio e creo punti di rollback, in modo da poter tornare rapidamente all’ultima buono Torna alla configurazione.
Lista di controllo pratica per i server
Comincio sempre con una linea di base: irqbalance attivo, rilevamento del carico di sistema, monitoraggio di /proc/interrupts e misurazione delle latenze; solo dopo intervengo sulle impostazioni. Nella seconda fase concludo con IRQBALANCE_BANNED_CPUS seleziono quei core che devono rimanere riservati agli App Thread; in questo modo evito interferenze IRQ superflue. Successivamente, collego gli IRQ critici tramite smp_affinity su pochi core ben scelti, mantenendo la vicinanza NUMA. Successivamente verifico RSS/RPS/RFS e XPS, nonché le opzioni di offloading della scheda di rete (NIC), per distribuire il carico di lavoro in modo ottimale. Infine, eseguo dei test sotto carico di produzione, confronto le metriche e mantengo solo quelle modifiche che si dimostrano lavoro.
File di configurazione e comandi systemd
Attivo il servizio con systemctl enable --now irqbalance e verifica con systemctl status irqbalance la durata; ecco come lo imposto Servizio sicuramente pronto. In /etc/sysconfig/irqbalance oppure /etc/default/irqbalance metto IRQBALANCE_BANNED_CPUS e, facoltativamente, IRQBALANCE_BANNED_IRQS. Applicherò le modifiche con systemctl restart irqbalance e, parallelamente, osservo i contatori in /proc/interruzioni. Per i test utilizzo la modalità “Foreground” di irqbalance, in modo da poter seguire in tempo reale le decisioni prese. Solo dopo aver compreso il comportamento, inserisco le modifiche in modo permanente nel Configurazione.
Quando disattivo irqbalance
Nelle configurazioni in tempo reale o nelle applicazioni estremamente sensibili alla latenza, interrompo irqbalance e assegno gli IRQ in modo statico, in modo che nessuna ridistribuzione possa interferire. Isolo i core per questi carichi di lavoro e faccio in modo che gli IRQ di traffico vengano eseguiti intenzionalmente su altri core; in questo modo i thread delle applicazioni pianificabile. Questo approccio è utile anche in ambienti tenant rigorosamente separati, poiché riduce le interferenze tra macchine virtuali o container. Se si presentano driver che funzionano male con la modalità automatica, ne escludo gli IRQ tramite una lista di esclusione. Non appena i modelli di carico tornano ad essere più variabili, riattivo irqbalance e ne verifico l’effetto con nuovi Valori misurati.
Riassumendo brevemente
Comincio con irqbalance, ne valuto l'effetto e apporto modifiche mirate, invece di intervenire alla cieca ovunque; in questo modo mantengo una visione d'insieme del sistema e Trasparenza. Per i carichi di lavoro sensibili, assegno gli IRQ appropriati, isolo i kernel per le applicazioni e rispetto la vicinanza NUMA. Con le maschere di esclusione controllo dove irqbalance può operare e impedisco spostamenti indesiderati. Verifico regolarmente /proc/interruzioni, latenza e throughput, in modo che le modifiche siano documentate in modo attendibile. Chi procede in questo modo sfrutta appieno il potenziale di IRQ Balance e mantiene i server sotto carico di rete in modo tangibile reattivo.


