Soluzione 1:
Tuttavia, un approccio "a martello" sarebbe l'aggiornamento a un sistema operativo a 64 bit (questo è 32 bit) perché il layout delle zone è fatto in modo diverso.
OK, quindi qui cercherò di rispondere al motivo per cui hai sperimentato un OOM qui. Ci sono una serie di fattori in gioco qui.
- La dimensione dell'ordine della richiesta e il modo in cui il kernel tratta determinate dimensioni dell'ordine.
- La zona selezionata.
- Le filigrane utilizzate da questa zona.
- Frammentazione nella zona.
Se guardi l'OOM stesso, c'è chiaramente molta memoria libera disponibile ma è stato invocato OOM-killer? Perché?
La dimensione dell'ordine della richiesta e il modo in cui il kernel tratta determinate dimensioni dell'ordine
Il kernel alloca la memoria in ordine. Un "ordine" è una regione di RAM contigua che deve essere soddisfatta affinché la richiesta funzioni. Gli ordini sono ordinati per ordini di grandezza (da qui il nome order) utilizzando l'algoritmo 2^(ORDER + 12)
. Quindi, l'ordine 0 è 4096, l'ordine 1 è 8192, l'ordine 2 è 16384 e così via.
Il kernel ha un valore hardcoded di quello che è considerato un 'ordine elevato' (> PAGE_ALLOC_COSTLY_ORDER
). Questo è l'ordine 4 e superiore (64kb o superiore è un ordine elevato).
Gli ordini elevati vengono soddisfatti per le allocazioni di pagine in modo diverso dagli ordini bassi. Un'allocazione di ordine elevato se non riesce a catturare la memoria, sui kernel moderni lo farà.
- Prova a eseguire in memoria la routine di compattazione per deframmentare la memoria.
- Mai chiama OOM-killer per soddisfare la richiesta.
La dimensione del tuo ordine è elencata qui
Dec 27 09:19:05 2013 kernel: : [277622.359064] squid invoked oom-killer: gfp_mask=0x42d0, order=3, oom_score_adj=0
L'ordine 3 è la più alta delle richieste di ordine basso e (come vedi) invoca OOM-killer nel tentativo di soddisfarla.
Si noti che la maggior parte delle allocazioni dello spazio utente non utilizza richieste di ordine elevato. In genere è il kernel che richiede regioni di memoria contigue. Un'eccezione potrebbe essere quando lo spazio utente utilizza le pagine enormi, ma non è questo il caso.
Nel tuo caso l'allocazione dell'ordine 3 viene chiamata dal kernel che desidera accodare un pacchetto nello stack di rete, richiedendo un'allocazione di 32kb per farlo.
La zona selezionata.
Il kernel divide le tue regioni di memoria in zone. Questo taglio viene eseguito perché su x86 alcune regioni di memoria sono indirizzabili solo da un determinato hardware. Ad esempio, l'hardware precedente potrebbe essere in grado di indirizzare la memoria solo nella zona "DMA". Quando vogliamo allocare della memoria, prima viene scelta una zona e solo la memoria libera da questa zona viene presa in considerazione quando si prende una decisione di allocazione.
Anche se non sono completamente a conoscenza dell'algoritmo di selezione della zona, il tipico caso d'uso non è mai quello di allocare da DMA, ma di solito selezionare la zona indirizzabile più bassa che potrebbe soddisfare la richiesta.
Molte informazioni sulla zona vengono sputate durante OOM che possono anche essere raccolte da /proc/zoneinfo
.
Dec 27 09:19:05 2013 kernel: : [277622.359382] DMA free:2332kB min:36kB low:44kB high:52kB active_anon:0kB inactive_anon:0kB active_file:0kB inactive_file:0kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:15968kB managed:6960kB mlocked:0kB dirty:0kB writeback:0kB mapped:0kB shmem:0kB slab_reclaimable:8kB slab_unreclaimable:288kB kernel_stack:0kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359393] Normal free:114488kB min:3044kB low:3804kB high:4564kB active_anon:0kB inactive_anon:0kB active_file:252kB inactive_file:256kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:894968kB managed:587540kB mlocked:0kB dirty:0kB writeback:0kB mapped:4kB shmem:0kB slab_reclaimable:117712kB slab_unreclaimable:138616kB kernel_stack:11976kB pagetables:0kB unstable:0kB bounce:0kB free_cma:0kB writeback_tmp:0kB pages_scanned:982 all_unreclaimable? yes
Dec 27 09:19:05 2013 kernel: : [277622.359404] HighMem free:27530668kB min:512kB low:48272kB high:96036kB active_anon:2634060kB inactive_anon:217596kB active_file:4688452kB inactive_file:1294168kB unevictable:0kB isolated(anon):0kB isolated(file):0kB present:36828872kB managed:36828872kB mlocked:0kB dirty:0kB writeback:0kB mapped:183132kB shmem:39400kB slab_reclaimable:0kB slab_unreclaimable:0kB kernel_stack:0kB pagetables:430856kB unstable:0kB bounce:367564104kB free_cma:0kB writeback_tmp:0kB pages_scanned:0 all_unreclaimable? no
Le zone che hai, DMA, Normal e HighMem indicano una piattaforma a 32 bit, perché la zona HighMem è inesistente a 64 bit. Anche su sistemi a 64 bit Normal è mappato a 4 GB e oltre mentre a 32 bit mappa fino a 896 Mb (sebbene, nel tuo caso il kernel riporti solo la gestione di una porzione più piccola di questa:- managed:587540kB
.)
È possibile stabilire da dove provenga questa allocazione guardando di nuovo la prima riga, gfp_mask=0x42d0
ci dice che tipo di allocazione è stata fatta. L'ultimo byte (0) ci dice che si tratta di un'allocazione dalla zona normale. I significati gfp si trovano in include/linux/gfp.h.
Le filigrane utilizzate da questa zona.
Quando la memoria è bassa, le azioni per recuperarla sono specificate dalla filigrana. Vengono visualizzati qui:min:3044kB low:3804kB high:4564kB
. Se la memoria libera raggiunge "bassa", lo scambio avverrà fino a quando non superiamo la soglia "alta". Se la memoria raggiunge 'min', dobbiamo uccidere cose per liberare memoria tramite l'OOM-killer.
Frammentazione nella zona.
Per vedere se una richiesta per uno specifico ordine di memoria può essere soddisfatta, il kernel tiene conto di quante pagine libere e disponibili di ciascun ordine. Questo è leggibile in /proc/buddyinfo
. I rapporti OOM-killer sputano anche le informazioni sull'amico come si vede qui:
Normal: 5360*4kB (UEM) 3667*8kB (UEM) 3964*16kB (UEMR) 13*32kB (MR) 0*64kB 1*128kB (R) 1*256kB (R) 0*512kB 0*1024kB 0*2048kB 0*4096kB = 115000kB
Perché un'allocazione di memoria sia soddisfatta deve avere memoria libera disponibile nella dimensione dell'ordine richiesta o un'allocazione maggiore. Avere un sacco di dati liberi negli ordini bassi e nessuno negli ordini superiori significa che la tua memoria è frammentata. Se ottieni un'allocazione di ordine molto elevata è possibile (anche con molta memoria libera) che non sia soddisfatta a causa della mancanza di pagine di ordine elevato disponibili. Il kernel può deframmentare la memoria (questo si chiama compattazione della memoria) spostando molte pagine di basso ordine in modo che non lascino spazi vuoti nello spazio della ram indirizzabile.
È stato invocato OOM-killer? Perché?
Quindi, se teniamo conto di queste cose, possiamo dire quanto segue;
- È stata tentata un'allocazione contigua di 32 kB. Dalla zona normale.
- C'era abbastanza memoria libera nella zona selezionata.
- C'era memoria disponibile di ordine 3, 5 e 6
13*32kB (MR) 1*128kB (R) 1*256kB (R)
Quindi, se ci fosse memoria libera, altri ordini potrebbero soddisfare la richiesta. cosa è successo?
Bene, c'è di più nell'allocazione da un ordine oltre al semplice controllo della quantità di memoria libera disponibile per quell'ordine o superiore. Il kernel sottrae effettivamente la memoria da tutti gli ordini inferiori dalla linea libera totale e quindi esegue il controllo minimo della filigrana su ciò che rimane.
Quello che succede nel tuo caso è controllare la nostra memoria libera per quella zona che dobbiamo fare.
115000 - (5360*4) - (3667*8) - (3964*16) = 800
Questa quantità di memoria libera viene confrontata con min
filigrana, che è 3044. Quindi, tecnicamente parlando, non hai memoria libera per eseguire l'allocazione richiesta. Ed è per questo che hai invocato OOM-killer.
Riparazione
Ci sono due correzioni. L'aggiornamento a 64 bit modifica il partizionamento della tua zona in modo tale che "Normale" sia da 4 GB a 36 GB, quindi non finirai per "predefinire" l'allocazione di memoria in una zona che può essere così fortemente frammentata. Non è che hai più memoria indirizzabile che risolve questo problema (perché stai già utilizzando PAE), ma semplicemente che la zona da cui selezioni ha più memoria indirizzabile.
Il secondo modo (che non ho mai testato) è provare a fare in modo che il kernel compatti la memoria in modo più aggressivo.
Se modifichi il valore di vm.extfrag_threshold
da 500 a 100, è più probabile che compatti la memoria nel tentativo di onorare un'allocazione di ordine elevato. Anche se non ho mai modificato questo valore prima d'ora, dipenderà anche dall'indice di frammentazione disponibile in /sys/kernel/debug/extfrag/extfrag_index
. Al momento non ho una scatola con un kernel abbastanza nuovo per vedere cosa mostra di offrire più di questo.
In alternativa puoi eseguire una sorta di cron job (questo è orribilmente, orribilmente brutto) per compattare manualmente la memoria da solo scrivendo in /proc/sys/vm/compact_memory
.
In tutta onestà, però, non penso che ci sia davvero un modo per mettere a punto il sistema per evitare questo problema:è la natura dell'allocatore di memoria a funzionare in questo modo. Cambiare l'architettura della piattaforma che utilizzi è probabilmente l'unica soluzione sostanzialmente risolvibile.
Soluzione 2:
Fin dall'inizio:dovresti davvero scegli un sistema operativo a 64 bit. Hai una buona ragione per rimanere a 32 bit qui?
È difficile diagnosticare questo problema senza dare un'occhiata più da vicino al sistema, preferibilmente nel momento in cui fallisce, quindi il mio post (rapido) è più o meno genericamente rivolto ai problemi di memoria sui sistemi a 32 bit. Ho già detto che il passaggio a 64 bit farebbe sparire tutto?
Il tuo problema è triplice.
Prima di tutto, anche su un kernel PAE, lo spazio degli indirizzi per processo è limitato a 4GiB[1]. Ciò significa che la tua istanza squid non sarà mai in grado di consumare più di 4GiB di RAM per processo. Non ho molta familiarità con squid, ma se questo è il tuo server proxy principale, potrebbe comunque non essere sufficiente.
In secondo luogo, su un sistema a 32 bit con grandi quantità di RAM, molta memoria in quella che viene chiamata "ZONE_NORMAL" viene utilizzata per memorizzare le strutture di dati necessarie per utilizzare la memoria in ZONE_HIGHMEM. Queste strutture di dati non possono essere spostate in ZONE_HIGHMEM da sole, perché la memoria che il kernel utilizza per i propri scopi deve essere sempre in ZONE_NORMAL (cioè nel primo 1GiB-ish). Più memoria hai in ZONE_HIGHMEM (molta, nel tuo caso), più questo diventa un problema, perché il kernel ha quindi bisogno di sempre più memoria da ZONE_NORMAL per gestire ZONE_HIGHMEM. Man mano che la quantità di memoria libera in ZONE_NORMAL si esaurisce, il tuo sistema potrebbe non riuscire in alcune attività, perché ZONE_NORMAL è dove molto di cose accade su un sistema a 32 bit. Tutte le operazioni di memoria relative al kernel, ad esempio;)
Terzo, anche se è rimasta della memoria in ZONE_NORMAL (non ho esaminato i tuoi log in dettaglio), alcune operazioni di memoria richiederanno memoria non frammentata. Ad esempio, se tutta la tua memoria è frammentata in pezzi molto piccoli, alcune operazioni che richiedono più di questo, falliranno. [3] Un breve sguardo ai tuoi log mostra una quantità abbastanza significativa di frammentazione in ZONE_DMA e ZONE_NORMAL.
Modifica:la risposta di Mlfe sopra ha un'eccellente spiegazione di come funziona in dettaglio.
Di nuovo:su un sistema a 64 bit, tutta la memoria è in ZONE_NORMAL. Non esiste una zona HIGHMEM sui sistemi a 64 bit. Problema risolto.
Modifica:potresti dare un'occhiata qui [4] per vedere se puoi dire a oom-killer di lasciare in pace i tuoi processi importanti. Questo non risolverà tutto (se non del tutto), ma potrebbe valere la pena provare.
[1] http://en.wikipedia.org/wiki/Physical_address_extension#Design
[2] http://www.redhat.com/archives/rhelv5-list/2008-September/msg00237.html e https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/5/html /Tuning_and_Optimizing_Red_Hat_Enterprise_Linux_for_Oracle_9i_and_10g_Databases/sect-Oracle_9i_and_10g_Tuning_Guide-Hardware_Architectures_and_Linux_Kernels-a32_bit_Architecture_and_the_hugemem_Kernel.html
[3] http://bl0rg.krunch.be/oom-frag.html
[4] http://lwn.net/Articles/317814/
Soluzione 3:
@MIfe ha già fornito un eccellente resoconto su come vengono gestite le allocazioni di memoria nel kernel e ti ha anche fornito una soluzione adeguata come il passaggio a un sistema operativo a 64 bit e un brutto hack come la compattazione manuale della memoria tramite /proc/sys/vm/compact_memory
in cron
.
I miei 2 centesimi sarebbero un'altra soluzione alternativa che potrebbe aiutarti:
Ho notato che hai tcp_tso_segment
nel backtrace del kernel, così facendo:
# ethtool -K ethX tso off gso off lro off
può diminuire la pressione su mm
costringendolo a utilizzare ordini inferiori.
PS . l'elenco di tutti gli offload può essere ottenuto tramite # ethtool -k ethX
Soluzione 4:
Il panico è dovuto al fatto che sysctl "vm.panic_on_oom =1" è impostato:l'idea è che il riavvio del sistema lo riporti a uno stato sano. Puoi cambiarlo in sysctl.conf.
Proprio in cima leggiamo calamaro invocato oom killer. Potresti controllare la configurazione di squid e il suo utilizzo massimo di memoria (o semplicemente passare a un sistema operativo a 64 bit).
/proc/meminfo mostra la zona di memoria elevata in uso, quindi stai eseguendo un kernel a 32 bit con 36 GB di memoria. Puoi anche vedere che nella zona normale, per soddisfare la richiesta di memoria di squid, il kernel ha scansionato 982 pagine senza successo:
pages_scanned:982 all_unreclaimable? yes