Skip to content

Situazione di Linux oom (kernel a 32 bit)

Questo tutorial è stato valutato dai nostri esperti, quindi garantiamo la veridicità del nostro post.

Soluzione:

Soluzione 1:

L'approccio "a mazza" sarebbe quello di passare a un sistema operativo a 64 bit (questo è a 32 bit) perché la disposizione delle zone è diversa.

OK, cercherò di rispondere al motivo per cui si è verificato un OOM. Ci sono diversi fattori in gioco.

  • 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 si guarda all'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 certe dimensioni dell'ordine

Il kernel alloca la memoria per ordine. Un "ordine" è una regione di RAM contigua che deve essere soddisfatta affinché la richiesta funzioni. Gli ordini sono disposti per ordini di grandezza (da cui il nome ordine) 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 codificato di ciò che viene considerato un "ordine elevato" (> PAGE_ALLOC_COSTLY_ORDER). Si tratta dell'ordine 4 e superiore (64kb o più sono un ordine elevato).

Gli ordini alti vengono soddisfatti per le allocazioni di pagine in modo diverso dagli ordini bassi. Un'allocazione di ordine alto, se non riesce a prendere la memoria, nei kernel moderni lo farà.

  • Provare a eseguire la routine di compattazione della memoria per deframmentare la memoria.
  • Mai chiamare OOM-killer per soddisfare la richiesta.

La dimensione dell'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 basso ordine e (come si vede) invoca OOM-killer nel tentativo di soddisfarla.

Si noti che la maggior parte delle allocazioni nello spazio utente non utilizza richieste di ordine alto. In genere è il kernel a richiedere regioni contigue di memoria. Un'eccezione può essere rappresentata da quando lo spazio utente utilizza hugepages, ma non è questo il caso.

Nel vostro caso l'allocazione di ordine 3 è chiamata dal kernel che vuole accodare un pacchetto nello stack di rete e richiede un'allocazione di 32kb per farlo.

La zona selezionata.

Il kernel divide le regioni di memoria in zone. Questa suddivisione avviene perché su x86 alcune regioni di memoria sono indirizzabili solo da un certo hardware. L'hardware più vecchio può essere in grado di indirizzare la memoria solo nella zona 'DMA', ad esempio. Quando si vuole allocare un po' di memoria, si sceglie prima una zona e si fa solo la memoria libera di questa zona viene presa in considerazione quando si decide di allocarla.

Anche se non sono completamente informato sull'algoritmo di selezione delle zone, il caso d'uso tipico non è mai quello di allocare da DMA, ma di selezionare la zona indirizzabile più bassa che può soddisfare la richiesta.

Durante l'OOM vengono fornite molte informazioni sulle zone, che possono essere ricavate anche 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 avete, DMA, Normal e HighMem, indicano una piattaforma a 32 bit, perché la zona HighMem è inesistente sui 64 bit. Inoltre, sui sistemi a 64 bit Normal è mappata fino a 4 GB e oltre, mentre sui 32 bit è mappata fino a 896 Mb (anche se, nel vostro caso, il kernel riporta di gestire solo una porzione minore di questa cifra:-). managed:587540kB.)

È possibile capire da dove proviene 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 di gfp si trovano in include/linux/gfp.h.

I watermark utilizzati da questa zona.

Quando la memoria è bassa, le azioni per recuperarla sono specificate dal watermark. Vengono visualizzate qui: min:3044kB low:3804kB high:4564kB. Se la memoria libera raggiunge il valore 'low', si verifica lo swapping finché non si supera la soglia 'high'. Se la memoria raggiunge 'min', è necessario uccidere qualcosa per liberare memoria tramite l'OOM-killer.

Frammentazione nella zona.

Per vedere se una richiesta di uno specifico ordine di memoria può essere soddisfatta, il kernel tiene conto di quante pagine libere sono disponibili per ogni ordine. Questo è leggibile in /proc/buddyinfo. I rapporti di OOM-killer forniscono anche le buddyinfo, 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

Affinché un'allocazione di memoria sia soddisfatta deve essere disponibile memoria libera nell'ordine di grandezza richiesto o in un'allocazione superiore. Avere molti dati liberi negli ordini bassi e nessuno in quelli alti significa che la memoria è frammentata. Se si ottiene un'allocazione di ordine molto alto, è possibile (anche con molta memoria libera) che non venga soddisfatta perché non ci sono pagine di ordine alto disponibili. Il kernel può deframmentare la memoria (si chiama compattazione della memoria) spostando molte pagine di ordine basso in modo che non lascino spazi vuoti nello spazio di memoria indirizzabile.

È stato invocato OOM-killer? Perché?

Quindi, se prendiamo in considerazione queste cose, possiamo dire quanto segue;

  • È stata tentata un'allocazione contigua di 32 kB. Dalla zona normale.
  • Nella zona selezionata c'era abbastanza memoria libera.
  • C'era memoria disponibile per gli ordini 3, 5 e 6. 13*32kB (MR) 1*128kB (R) 1*256kB (R)

Quindi, se c'è era memoria libera, altri ordini potrebbe soddisfare la richiesta. cosa è successo?

L'allocazione da un ordine non si limita a controllare la quantità di memoria libera disponibile per quell'ordine o superiore. Il kernel sottrae effettivamente la memoria di tutti gli ordini inferiori dalla linea libera totale e poi esegue il controllo della filigrana minima su ciò che rimane.

Quello che succede nel vostro caso è controllare la memoria libera per quella zona che dobbiamo fare.

115000 - (5360*4) - (3667*8) - (3964*16) = 800

Questa quantità di memoria libera viene controllata rispetto al valore min che è 3044. Quindi, tecnicamente parlando, non c'è più memoria libera per eseguire l'allocazione richiesta. Questo è il motivo per cui si è invocato OOM-killer.


Correzione

Ci sono due soluzioni. L'aggiornamento a 64 bit modifica il partizionamento della zona in modo che 'Normale' sia da 4 GB a 36 GB, così non si finisce per 'predefinirne' l'allocazione di memoria in una zona che può essere così pesantemente frammentata. Non è il fatto di avere più memoria indirizzabile che risolve il problema (perché state già usando PAE), ma solo il fatto che la zona che selezionate ha più memoria indirizzabile.

Il secondo modo (che non ho mai testato) è cercare di far sì che il kernel compatti la memoria in modo più aggressivo.

Se si cambia il valore di vm.extfrag_threshold da 500 a 100, è più probabile che compatti la memoria nel tentativo di onorare un'allocazione di alto ordine. Anche se non ho mai avuto a che fare con questo valore prima d'ora, dipende 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 per offrire più di questo.

In alternativa si potrebbe eseguire una sorta di cron job (questo è orribilmente, orribilmente brutto) per compattare manualmente la memoria scrivendo in /proc/sys/vm/compact_memory.

In tutta onestà, però, non credo che ci sia 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 utilizzata è probabilmente l'unica soluzione risolutiva.

Soluzione 2:

All'inizio: dovreste davvero scegliere un sistema operativo a 64 bit. Avete una buona ragione per rimanere a 32 bit?

È difficile diagnosticare questo problema senza esaminare più da vicino il sistema, preferibilmente nel momento in cui si guasta, quindi il mio (rapido) post è più o meno genericamente rivolto ai problemi di memoria sui sistemi a 32 bit. Ho già detto che passare a 64 bit farebbe sparire tutto questo?

Il problema è triplice.

Prima di tutto, anche su un kernel PAE, lo spazio di indirizzamento per processo è limitato a 4GiB.[1]. Questo significa che la vostra istanza di 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 vostro server proxy principale, potrebbe non essere comunque sufficiente.

In secondo luogo, su un sistema a 32 bit con grandi quantità di RAM, molta memoria in quella che viene chiamata 'ZONE_NORMAL' è usata per memorizzare strutture di dati che sono necessarie per usare la memoria in ZONE_HIGHMEM. Queste strutture dati non possono essere spostate in ZONE_HIGHMEM, perché la memoria che il kernel utilizza per i propri scopi deve sempre trovarsi in ZONE_NORMAL (cioè nei primi 1GiB circa). Più memoria si ha in ZONE_HIGHMEM (molta, nel vostro caso), più questo diventa un problema, perché il kernel ha bisogno di sempre più memoria da ZONE_NORMAL per gestire ZONE_HIGHMEM. Quando la quantità di memoria libera in ZONE_NORMAL si esaurisce, il sistema può fallire in alcuni compiti, perché ZONE_NORMAL è il luogo in cui si trova un file molto di cose che avvengono in un sistema a 32 bit. Tutte le operazioni di memoria relative al kernel, per esempio 😉

In terzo luogo, anche se è rimasta un po' di memoria in ZONE_NORMAL (non ho esaminato i vostri log in dettaglio), alcune operazioni di memoria richiederanno memoria non frammentata. Per esempio, se tutta la memoria è frammentata in pezzi molto piccoli, alcune operazioni che necessitano di una quantità maggiore di memoria falliranno. [3] Un breve sguardo ai log mostra una quantità abbastanza significativa di frammentazione in ZONE_DMA e ZONE_NORMAL.

Modifica: la risposta di Mlfe qui sopra contiene 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: si può dare un'occhiata qui [4] per vedere se è possibile dire a oom-killer di lasciare in pace i processi importanti. Questo non risolverà tutto (o quasi), ma potrebbe valere la pena di 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 articolo su come vengono gestite le allocazioni di memoria nel kernel e 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 via /proc/sys/vm/compact_memory in cron.

I miei 2 centesimi sarebbero un altro workaround che potrebbe aiutarvi:
Ho notato che hai tcp_tso_segment nel backtrace del kernel, quindi facendo:

# ethtool -K ethX tso off gso off lro off

può diminuire la pressione su mm costringendolo a usare ordini più bassi.

PS. l'elenco di tutti gli offload può essere ottenuto tramite # ethtool -k ethX


Soluzione 4:

Il panico è dovuto al fatto che il sysctl "vm.panic_on_oom = 1" è impostato -- l'idea è che il riavvio del sistema lo riporti a uno stato sano di mente. Si può cambiare in sysctl.conf.

In alto si legge che squid ha invocato oom killer. Si potrebbe 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 un'alta zona di memoria in uso, quindi si sta eseguendo un kernel a 32 bit con 36 GB di memoria. Si può 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

Recensioni e valutazioni degli articoli



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.