Disattiva il killer OOM di Linux di default?


37

Il killer OOM su Linux causa ogni tanto il caos con varie applicazioni e sembra che non sia stato fatto molto sul lato dello sviluppo del kernel per migliorarlo. Non sarebbe meglio, come best practice quando si configura un nuovo server , invertire il default sulla memoria in eccesso, ovvero disattivarlo ( vm.overcommit_memory=2) a meno che non si sappia che lo si desidera per il proprio uso particolare? E quali sarebbero questi casi d'uso in cui sai di voler impegnare eccessivamente?

Come bonus, dato che il comportamento in caso di vm.overcommit_memory=2dipende vm.overcommit_ratioe cambia spazio, quale sarebbe una buona regola empirica per il dimensionamento degli ultimi due in modo che l'intera configurazione continui a funzionare ragionevolmente?

Risposte:


63

Un'analogia interessante (da http://lwn.net/Articles/104179/ ):

Una compagnia aerea ha scoperto che era più economico far volare i suoi aerei con meno carburante a bordo. Gli aerei sarebbero più leggeri e consumerebbero meno carburante, risparmiando denaro. In rare occasioni, tuttavia, la quantità di carburante era insufficiente e l'aereo si schiantava. Questo problema è stato risolto dagli ingegneri dell'azienda attraverso lo sviluppo di uno speciale meccanismo OOF (esaurimento carburante). In casi di emergenza un passeggero è stato selezionato e lanciato fuori dall'aereo. (Se necessario, la procedura è stata ripetuta.) È stato sviluppato un ampio corpus di teoria e molte pubblicazioni sono state dedicate al problema di selezionare correttamente la vittima da espellere. La vittima dovrebbe essere scelta a caso? O si dovrebbe scegliere la persona più pesante? O il più vecchio? I passeggeri devono pagare per non essere espulsi, in modo che la vittima sia la più povera a bordo? E se per esempio fosse stata scelta la persona più pesante, dovrebbe esserci un'eccezione speciale nel caso in cui fosse il pilota? I passeggeri di prima classe dovrebbero essere esonerati? Ora che esisteva il meccanismo OOF, sarebbe stato attivato di tanto in tanto, ed espulso i passeggeri anche quando non c'era carenza di carburante. Gli ingegneri stanno ancora studiando esattamente come questo malfunzionamento è causato.


11
Mi è piaciuto molto, grazie per averlo scoperto.
Nick Bolton,

32

Il killer OOM provoca il caos solo se hai sovraccaricato il tuo sistema. Dai abbastanza swap e non eseguire applicazioni che improvvisamente decidono di consumare enormi quantità di RAM e non avrai problemi.

Per rispondere in modo specifico alle tue domande:

  • Non credo sia una buona idea disattivare il sovraccarico nel caso generale; pochissime applicazioni sono state scritte per gestire correttamente brk(2) (e i wrapper che la usano, come malloc(3)) restituendo un errore. Quando l'ho sperimentato nel mio lavoro precedente, mi è sembrato più una seccatura ottenere tutto ciò che è in grado di gestire gli errori di memoria insufficiente di quanto non fosse solo per affrontare le conseguenze di una OOM (che, nel nostro caso, era molto peggio che dover riavviare il servizio occasionale se si verificava una OOM - abbiamo dovuto riavviare un intero cluster, perché GFS è un mucchio di feci fumante).
  • Si desidera eseguire il commit eccessivo per qualsiasi processo che esegue il commit eccessivo della memoria. I due colpevoli più comuni qui sono Apache e JVM, ma molte app lo fanno in misura maggiore o minore. Essi pensano che potrebbero avere bisogno di un sacco di memoria ad un certo punto, in futuro, in modo da afferrare una grossa fetta destra fuori. Su un sistema abilitato per i sovraccarichi, il kernel fa "meh, qualunque cosa, vieni a darmi fastidio quando vuoi davvero scrivere su quelle pagine" e non succede nulla di brutto. Su un sistema di overcommit-off, il kernel dice "no, non puoi avere tanta memoria, se ti capita di scrivere su tutto ad un certo punto in futuro sono disossato, quindi nessuna memoria per te!" e l'allocazione fallisce. Dal nullalà fuori va "oh, OK, posso avere questa piccola quantità di segmento di dati di processo?", quindi il processo (a) si chiude con un errore di memoria esaurita o (b) non controlla il codice di ritorno da malloc, pensa che vada bene e scriva in una posizione di memoria non valida, causando un segfault. Per fortuna, JVM fa tutto il suo prealloc all'avvio (quindi la tua JVM si avvia o muore immediatamente, cosa che di solito noti), ma Apache fa cose stravaganti con ogni nuovo figlio, che può avere effetti entusiasmanti nella produzione (irripetibile "non gestire le connessioni "tipi di eccitazione).
  • Non vorrei impostare il mio overcommit_ratio su un valore superiore al valore predefinito del 50%. Ancora una volta, dai miei test, anche se configurarlo su 80 o 90 potrebbe sembrare un'idea interessante, il kernel richiede grossi blocchi di memoria in momenti scomodi e un sistema a pieno carico con un alto rapporto di sovraccarico probabilmente avrà memoria di riserva insufficiente quando il kernel ne ha bisogno (portando a paura, pestilenza e oops). Quindi giocare con overcommit introduce una nuova, ancora più divertente modalità di errore - piuttosto che riavviare qualsiasi processo OOMed quando si esaurisce la memoria, ora la macchina si arresta in modo anomalo, portando a un'interruzione di tutto sulla macchina. ECCEZIONALE!
  • Lo spazio di swap in un sistema senza sovraccarico dipende dalla quantità di memoria richiesta ma non utilizzata delle applicazioni, oltre a un margine di sicurezza salutare. Elaborare ciò che è necessario in un caso specifico è lasciato come esercizio per il lettore.

Fondamentalmente, la mia esperienza è che disattivare l'overcommit è un bell'esperimento che raramente funziona altrettanto bene in pratica come sembra in teoria. Ciò corrisponde perfettamente alle mie esperienze con altri parametri sintonizzabili nel kernel: gli sviluppatori del kernel Linux sono quasi sempre più intelligenti di te e le impostazioni predefinite funzionano meglio per la stragrande maggioranza dei casi. Lasciali soli e vai a cercare quale processo ha la perdita e risolvilo.


2
Non voglio che il mio processo di backup venga ucciso perché qualcuno sta eseguendo il mio server web. Le eccezioni vanno bene, ma il valore predefinito dovrebbe essere sicurezza e coerenza. Ottimizzazioni come OOM dovrebbero essere attivate manualmente IMHO. È come programmare, programmare in modo pulito e quindi ottimizzare. Il commit eccessivo è una funzionalità interessante, ma non dovrebbe essere l'impostazione predefinita.
Aki,

1
Se non si desidera che il processo di backup venga interrotto perché qualcuno sta eseguendo il Web server, non configurare il server Web in modo tale che un DoS possa causare il sovraccarico delle risorse del sistema.
Womble

Ho 8 GB di RAM e solo l'esecuzione di Firefox e una macchina virtuale a volte fa sì che il killer OOM uccida la VM. Compilando Unreal Engine 4, ogni invocazione di clang richiede 1 ~ 1,5 GB di memoria e, di nuovo, il killer OOM ne uccide uno ogni tanto. Ora in genere sto bene, senza l'assassino di OOM probabilmente sarebbero segfault comunque. È solo che ogni volta che il killer OOM vuole uccidere un processo, il mio sistema si blocca per 10 minuti prima che il cattivo processo venga effettivamente ucciso. Bug forse? Più probabilmente. Lo voglio? Sicuramente no. Ed è per questo che potresti voler disabilitare il killer OOM.
Shahbaz,

1
Se stai facendo tutto ciò su una scatola hai bisogno di più RAM, e disabilitare l'overcommit non farà che peggiorare le cose.
Ben Lutgens,

6

Hmm, non sono del tutto convinto dagli argomenti a favore del sovraccarico e dell'assassino di OOM ... Quando il womble scrive,

"Il killer OOM provoca il caos solo se hai sovraccaricato il tuo sistema. Dagli abbastanza scambio e non eseguire applicazioni che improvvisamente decidono di consumare enormi quantità di RAM, e non avrai problemi."

Si tratta di descrivere uno scenario ambientale in cui overcommit e OOM killer non vengono applicati o non "agiscono" realmente (se tutte le applicazioni allocassero la memoria in base alle necessità e ci fosse memoria virtuale sufficiente da allocare, le scritture di memoria seguiranno da vicino le allocazioni di memoria senza errori, quindi non potremmo davvero parlare di un sistema ipercompresso anche se fosse abilitata una strategia di sovraccarico). Si tratta di un'ammissione implicita che il sovraccarico e il killer OOM funzionano meglio quando non è necessario il loro intervento, che è in qualche modo condiviso dalla maggior parte dei sostenitori di questa strategia, per quanto ne so (e ammetto che non posso dire molto ...). Inoltre, facendo riferimento ad applicazioni con comportamenti specifici durante la preallocazione della memoria, mi viene da pensare che una gestione specifica possa essere regolata a livello di distribuzione, anziché avere un valore predefinito,

Per quanto riguarda la JVM, beh, è ​​una macchina virtuale, in una certa misura ha bisogno di allocare tutte le risorse di cui ha bisogno all'avvio, in modo da poter creare il suo ambiente "falso" per le sue applicazioni e mantenere la sua risorsa disponibile separata dall'host ambiente, per quanto possibile. Pertanto, potrebbe essere preferibile che fallisca all'avvio, invece che dopo un po 'come conseguenza di una condizione OOM "esterna" (causata da overcommit / OOM killer / qualunque cosa), o comunque soffrire per tale condizione che interferisce con la propria strategie interne di gestione OOM (in generale, una VM dovrebbe ottenere tutte le risorse necessarie dall'inizio e il sistema host dovrebbe "ignorarle" fino alla fine, allo stesso modo in cui qualsiasi quantità di RAM fisica condivisa con una scheda grafica non è mai - e non può essere - toccato dal sistema operativo).

A proposito di Apache, dubito che avere l'intero server occasionalmente ucciso e riavviato sia meglio che lasciare che un singolo figlio, insieme a una singola connessione, fallisca dal suo inizio (= il bambino / la connessione) (come se fosse un'istanza completamente nuova di la JVM creata dopo un'altra istanza eseguita per un po '). Immagino che la migliore 'soluzione' potrebbe dipendere da un contesto specifico. Ad esempio, considerando un servizio di e-commerce, potrebbe essere di gran lunga preferibile avere, a volte, alcune connessioni al carrello della spesa che falliscono casualmente invece di perdere l'intero servizio, con il rischio, ad esempio, di interrompere una finalizzazione dell'ordine in corso, oppure (forse peggio) un processo di pagamento, con tutte le conseguenze del caso (forse innocuo, ma forse dannoso - e di sicuro, in caso di problemi,

Allo stesso modo, su una workstation il processo che consuma la maggior parte delle risorse, e quindi la coda per essere una prima scelta per il killer OOM, potrebbe essere un'applicazione ad alta intensità di memoria, come un transcoder video o un software di rendering, probabilmente l'unica applicazione l'utente vuole non essere toccato. Queste considerazioni mi suggeriscono che la politica di default del killer OOM è troppo aggressiva. Utilizza un approccio "peggio adattamento" che è in qualche modo simile a quello di alcuni filesystem (l'OOMK prova e libera quanta più memoria possibile, riducendo il numero di sottoprocessi uccisi, al fine di prevenire qualsiasi ulteriore intervento in breve tempo, come inoltre un fs può allocare più spazio su disco rispetto a quello effettivamente necessario per un determinato file, per evitare ulteriori allocazioni se il file cresce e quindi prevenire la frammentazione, in una certa misura).

Tuttavia, penso che una politica opposta, come un approccio "best fit", potrebbe essere preferibile, in modo da liberare la memoria esatta necessaria ad un certo punto e non preoccuparsi di processi "grandi", che potrebbero essere sprecati memoria, ma potrebbe anche non esserlo, e il kernel non può saperlo (hmm, posso immaginare che tenere traccia del conteggio degli accessi alle pagine e del tempo potrebbe suggerire se un processo sta allocando memoria non ha più bisogno, quindi per indovinare se un processo sta sprecando memoria o semplicemente usando molto, ma i ritardi di accesso dovrebbero essere ponderati sui cicli della CPU per distinguere uno spreco di memoria da un'applicazione intensiva di memoria e CPU, ma, sebbene potenzialmente inaccurato, tale euristica potrebbe avere un sovraccarico eccessivo).

Inoltre, potrebbe non essere vero che uccidere il minor numero possibile di processi sia sempre una buona scelta. Ad esempio, in un ambiente desktop (pensiamo a un nettop o un netbook con risorse limitate, per esempio) un utente potrebbe eseguire un browser con diverse schede (quindi, consumando memoria - supponiamo che questa sia la prima scelta per OOMK) , oltre ad alcune altre applicazioni (un elaboratore di testi con dati non salvati, un client di posta, un lettore di pdf, un lettore multimediale, ...), oltre a qualche demone (di sistema), oltre ad alcune istanze di file manager. Ora, si verifica un errore OOM e OOMK sceglie di uccidere il browser mentre l'utente sta facendo qualcosa di ritenuto "importante" sulla rete ... l'utente sarebbe deluso. D'altra parte, chiudendo i pochi file manager "

Ad ogni modo, penso che l'utente dovrebbe essere abilitato a prendere una decisione da solo su cosa fare. In un sistema desktop (= interattivo), che dovrebbe essere relativamente abbastanza facile da fare, a condizione che siano riservate risorse sufficienti per chiedere all'utente di chiudere qualsiasi applicazione (ma anche chiudere alcune schede potrebbe essere sufficiente) e gestire la sua scelta (un'opzione potrebbe consiste nella creazione di un file di scambio aggiuntivo, se lo spazio è sufficiente). Per i servizi (e in generale), prenderei in considerazione anche altri due possibili miglioramenti: uno sta registrando gli intervalli killer OOM, nonché i processi che avviano / eseguono il fork degli errori in modo tale che l'errore possa essere facilmente debug (ad esempio, un'API potrebbe informare il processo che emette la creazione del nuovo processo o il biforcazione - quindi un server come Apache, con una patch adeguata, potrebbe fornire una registrazione migliore per alcuni errori); ciò potrebbe essere fatto in modo indipendente dal sovraccarico / OOMK in corso; in secondo luogo, ma non per importanza, potrebbe essere istituito un meccanismo per mettere a punto l'algoritmo OOMK - so che è possibile, in una certa misura, definire una politica specifica su un processo per processo, ma mirerei a meccanismo di configurazione "centralizzato", basato su uno o più elenchi di nomi di applicazioni (o ID) per identificare i processi pertinenti e dare loro un certo grado di importanza (secondo gli attributi elencati); tale meccanismo dovrebbe (o almeno potrebbe) essere anche stratificato, in modo tale che ci possa essere un elenco definito dall'utente di livello superiore, un elenco definito dal sistema (distribuzione) e voci definite dall'applicazione (livello inferiore) (quindi , ad esempio, un gestore di file DE potrebbe incaricare OOMK di eliminare in modo sicuro qualsiasi istanza,

Inoltre, potrebbe essere fornita un'API per consentire alle applicazioni di aumentare o ridurre il loro livello di 'importanza' in fase di esecuzione (rispetto agli scopi di gestione della memoria e indipendentemente dalla priorità di esecuzione), in modo che, ad esempio, un elaboratore di testi possa iniziare con una "importanza" bassa, ma aumentala man mano che alcuni dati vengono conservati prima di scaricare in un file o viene eseguita un'operazione di scrittura e riduci nuovamente l'importanza una volta terminata tale operazione (analogamente, un gestore di file potrebbe cambiare livello quando è passato da legare i file alla gestione dei dati e viceversa, invece di utilizzare processi separati, e Apache potrebbe dare diversi livelli di importanza ai diversi figli, o cambiare uno stato figlio secondo una politica decisa da amministratori di sistema ed esposta attraverso Apache - o qualsiasi altro tipo di server - impostazioni). Ovviamente, una simile API potrebbe e sarebbe abusata / abusata, ma penso che sia una preoccupazione minore rispetto al fatto che il kernel uccida arbitrariamente i processi per liberare memoria senza alcuna informazione rilevante su ciò che sta accadendo sul sistema (e il consumo di memoria / il tempo di creazione o lo stesso sono "abbastanza pertinente o" convalidante "per me) - solo gli utenti, gli amministratori e gli autori di programmi possono davvero determinare se un processo è" ancora necessario "per qualche motivo, qual è il motivo e / o se l'applicazione si trova in uno stato leader alla perdita di dati o altri danni / problemi se uccisi; tuttavia, si potrebbe ancora ipotizzare, ad esempio la ricerca di risorse di un certo tipo (descrittori di file, socket di rete, ecc.) acquisite da un processo e con operazioni in sospeso potrebbero dire se un processo dovrebbe trovarsi in uno stato "superiore" rispetto a quello impostato,

Oppure, evita semplicemente il commit eccessivo e lascia che il kernel faccia esattamente ciò che deve fare un kernel, allocando risorse (ma non salvandole arbitrariamente come fa il killer OOM), pianificando processi, prevenendo fame e deadlock (o salvando da esse), assicurando la piena anticipazione e separazione degli spazi di memoria e così via ...

Spenderei anche qualche parola in più sugli approcci di overcommit. Da altre discussioni ho avuto l'idea che una delle principali preoccupazioni sul sovraccarico (sia come motivo per volerlo sia come fonte di possibili problemi) consiste nella gestione delle forcelle: onestamente, non so esattamente come la copia- la strategia di scrittura è implementata, ma penso che qualsiasi politica aggressiva (o ottimistica) potrebbe essere mitigata da una strategia di localizzazione simile allo scambio. Cioè, invece di limitarsi a clonare (e ad adattare) una code page di processo biforcuta e strutture di pianificazione, alcune altre pagine di dati potrebbero essere copiate prima di una scrittura effettiva, scegliendo tra quelle pagine a cui il processo parent ha avuto accesso per scrivere più frequentemente (ovvero, utilizzando un contatore per le operazioni di scrittura).

Tutto, ovviamente, IMHO.


5
"Inoltre, potrebbe essere fornita un'API per consentire alle applicazioni di aumentare o ridurre il loro livello di" importanza "in fase di esecuzione". L'importanza è /proc/$PID/oom_adj.
Vi.

1
Per quanto riguarda la JVM, esiste un gotcha che ti fa desiderare un sovraccarico di memoria in alcuni casi: nel caso in cui desideri creare un'altra JVM dalla tua JVM originale, chiamerà fork (). Una chiamata fork assegnerà tutta la memoria del processo originale (prima), fino a quando non avvierà davvero il processo. Quindi supponiamo che tu abbia una JVM da 4 GB e desideri creare una nuova JVM da 512 KB, a meno che tu non abbia un overcommit, avrai bisogno di 8 GB di memoria per farlo ...
alci,

4
@Vi. Sembra ora/proc/$PID/oom_score_adj
m3nda

1

Se la tua memoria viene esaurita in modo esauriente dai processi nella misura in cui ciò può potenzialmente minacciare la stabilità del sistema, allora il killer OOM viene visualizzato. OOM Killer ha il compito di uccidere i processi fino a quando non viene liberata memoria sufficiente per il buon funzionamento del resto del processo. OOM Killer deve selezionare il processo "migliore" per uccidere. "Migliore" qui si riferisce a quel processo che libererà la massima memoria dall'uccisione ed è anche meno importante per il sistema. L'obiettivo primario è quello di uccidere il minor numero di processi che minimizza il danno fatto e allo stesso tempo massimizzare la quantità di memoria liberata. Per facilitare ciò, il kernel mantiene oom_score per ciascuno dei processi. Puoi vedere oom_score di ciascuno dei processi nel filesystem / proc nella directory pid

# cat /proc/10292/oom_score

Maggiore è il valore di oom_score di qualsiasi processo, maggiore è la probabilità di essere ucciso dall'OOM Killer in una situazione di memoria insufficiente.

Ringraziamento: - Il kernel Linux sta avviando il killer OOM

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.