Come posso interrompere rapidamente un processo che sta causando il thrashing (a causa dell'eccessiva allocazione di memoria)?


19

L'abbiamo sperimentato tutti: ad alcuni programmi viene chiesto di fare qualcosa che richiede un'enorme quantità di memoria. Cerca diligentemente di allocare tutta questa memoria e il sistema inizia immediatamente a battere, scambiando continuamente e diventando lento o non reattivo.

Di recente l'ho sperimentato sul mio laptop Ubuntu a causa di uno script Matlab che cerca di allocare una matrice ridicolmente enorme. Dopo ~ 5 + minuti di thrashing, sono stato in grado di Ctrl-F1 su una console e uccidere Matlab. Preferirei di gran lunga avere un tasto di scelta rapida che mi avrebbe dato immediatamente il controllo del sistema e mi avrebbe permesso di interrompere il processo offensivo; o, forse, semplicemente rifiutare silenziosamente di allocare un buffer così grande.

  1. Qual è il modo più rapido per riprendere il controllo di un sistema Linux diventato non reattivo o estremamente lento a causa dell'eccessivo scambio?

  2. Esiste un modo efficace per impedire che tale scambio avvenga in primo luogo, ad esempio limitando la quantità di memoria che un processo può tentare di allocare?

Risposte:


12

Premi Alt-SysRq-F per terminare il processo usando la maggior quantità di memoria:

  • La chiave SysRq è in genere associata alla chiave Stampa.
  • Se si utilizza un desktop grafico, potrebbe essere necessario premere Ctrl-Alt-SysRq-F nel caso in cui premendo Alt-SysRq si inneschi un'altra azione (ad es. Programma di istantanee).
  • Se stai usando un laptop potresti dover premere anche un tasto funzione.
  • Per maggiori informazioni leggi l' articolo di Wikipedia .

5

Ho realizzato uno script per questo scopo: https://github.com/tobixen/thrash-protect

Ho avuto questo script in esecuzione su server di produzione, workstation e laptop con buon successo. Questo script non uccide i processi, ma li sospende temporaneamente: in seguito ho avuto diverse situazioni in cui sono abbastanza sicuro di aver perso il controllo a causa del thrash se non fosse stato per questo semplice script. Nel caso "peggiore" il processo offensivo verrà rallentato molto e alla fine verrà eliminato dal kernel (OOM), nel caso "migliore" il processo offensivo verrà effettivamente completato ... in ogni caso, il server o la workstation rimarrà relativamente reattivo in modo che sia facile indagare sulla situazione.

Naturalmente, "acquistare più memoria" o "non usare lo scambio" sono due risposte alternative e più tradizionali alla domanda "come evitare il thrashing?", Ma in generale tendono a non funzionare così bene (l'installazione di più memoria può non banale, un processo non autorizzato può consumare tutta la memoria, indipendentemente da quanto è stato installato, e si possono avere problemi di arresto anche senza scambio quando non c'è memoria sufficiente per il buffering / la memorizzazione nella cache). Consiglio vivamente la protezione da thrash e molto spazio di swap.


Informazioni sulla disabilitazione dello scambio, secondo unix.stackexchange.com/a/24646/9108 potrebbe non essere l'opzione migliore.
sashoalm,

In effetti, qualcuno ha commentato lo stesso su di me, quindi ho modificato il documento di protezione da thrash a quel punto.
Tobixen,

4
  1. Qual è il modo più rapido per riprendere il controllo di un sistema Linux diventato non reattivo o estremamente lento a causa dell'eccessivo scambio?

Ho già risposto sopra con Alt-SysRq-F

  1. Esiste un modo efficace per impedire che tale scambio avvenga in primo luogo, ad esempio limitando la quantità di memoria che un processo può tentare di allocare?

Sto rispondendo a questa seconda parte. Sì, ulimitfunziona ancora abbastanza bene per limitare un singolo processo. Puoi:

  • impostare un limite leggero per un processo che probabilmente andrà fuori controllo
  • impostare un limite rigido per tutti i processi se si desidera un'assicurazione aggiuntiva

Inoltre, come brevemente menzionato:

È possibile utilizzare CGroup per limitare l'utilizzo delle risorse e prevenire tali problemi

In effetti, i cgroups offrono un controllo più avanzato, ma attualmente sono più complicati da configurare secondo me.

Ulimit vecchia scuola

Una volta fuori

Ecco un semplice esempio:

$ bash
$ ulimit -S -v $((1*2**20))
$ r2(){r2 $@$@;};r2 r2
bash: xmalloc: .././subst.c:3550: cannot allocate 134217729 bytes (946343936 bytes allocated)

It:

  • Imposta un limite soft per l'utilizzo della memoria complessiva da 1 GB (ulimit assume il limite in unità kB)
  • Esegue una chiamata di funzione bash ricorsiva r2(){ r2 $@$@;};r2 r2che mastica esponenzialmente CPU e RAM raddoppiando infinitamente se stessa mentre richiede la memoria dello stack.

Come puoi vedere, si è arrestato quando si è cercato di richiedere più di 1 GB.

Nota, -vopera sull'allocazione di memoria virtuale (totale, cioè fisica + swap).

Protezione permanente

Per limitare l'allocazione di memoria virtuale, asè l'equivalente di -vper limits.conf.

Faccio quanto segue per proteggermi da ogni singolo processo di comportamento scorretto:

  • Impostare un limite di spazio per l'indirizzo fisso per tutti i processi.
  • address space limit = <physical memory> - 256MB.
  • Pertanto, nessun singolo processo con avido utilizzo della memoria o un loop attivo e una perdita di memoria può consumare TUTTA la memoria fisica.
  • Un headroom di 256 MB è lì per l'elaborazione essenziale con ssh o una console.

Una fodera:

$ sudo bash -c "echo -e \"*\thard\tas\t$(($(grep -E 'MemTotal' /proc/meminfo | grep -oP '(?<=\s)\d+(?=\skB$)') - 256*2**10))\" > /etc/security/limits.d/mem.conf"

Per convalidare, ciò si traduce in quanto segue (ad es. Su un sistema da 16 GB):

$ cat /etc/security/limits.d/mem.conf
*   hard    as      16135196
$ ulimit -H -v
161351960

Appunti:

  • Riduce solo un singolo processo che va in mare con l'uso della memoria.
  • Non impedirà un carico di lavoro multi-processo con una forte pressione della memoria che causa il thrashing (cgroups è quindi la risposta).
  • Non utilizzare l' rssopzione in limits.conf. Non è rispettato dai kernel più recenti.
  • È conservatore.
    • In teoria, un processo può richiedere speculativamente molta memoria ma utilizzare attivamente solo un sottoinsieme (set di lavoro più piccolo / uso della memoria residente).
    • Il suddetto limite rigido causerà l'interruzione di tali processi (anche se potrebbero altrimenti funzionare correttamente dato che Linux consente lo spazio di indirizzi della memoria virtuale per essere sovraccaricato).

CGgr più recenti

Offre un maggiore controllo, ma attualmente più complesso da usare:

  • Migliora l'offerta ulimit.
    • memory.max_usage_in_bytes può rendere conto e limitare la memoria fisica separatamente.
    • Considerando che ulimit -me / o rssin limits.confdoveva offrire funzionalità simili, ma non funziona dal kernel Linux 2.4.30!
  • Devi abilitare alcune bandiere kernel cgroup in bootloader: cgroup_enable=memory swapaccount=1.
    • Questo non è accaduto di default con Ubuntu 16.04.
    • Probabilmente a causa di alcune implicazioni prestazionali di spese generali di contabilità aggiuntive.
  • roba cgroup / systemd è relativamente nuova e sta cambiando un po ', quindi il flusso a monte implica che i fornitori di distribuzione Linux non hanno ancora reso facile da usare. Tra 14.04LTS e 16.04LTS, gli strumenti dello spazio utente per utilizzare i cgroup sono cambiati.
    • cgm ora sembra essere lo strumento userspace ufficialmente supportato.
    • i file di unità di sistema non sembrano ancora avere impostazioni predefinite "vendor / distro" per dare priorità a servizi importanti come ssh.

Ad esempio per verificare le impostazioni correnti:

$ echo $(($(cat /sys/fs/cgroup/memory/memory.max_usage_in_bytes) / 2**20)) MB
11389 MB
$ cat /sys/fs/cgroup/memory/memory.stat
...

Ad esempio per limitare la memoria di un singolo processo:

$ cgm create memory mem_1G
$ cgm setvalue memory mem_1G memory.limit_in_bytes $((1*2**30))
$ cgm setvalue memory mem_1G memory.memsw.limit_in_bytes $((1*2**30))
$ bash
$ cgm movepid memory mem_1G $$
$ r2(){ r2 $@$@;};r2 r2
Killed

Per vederlo in azione masticare la RAM come processo in background e poi essere ucciso:

$ bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2' & while [ -e /proc/$! ]; do ps -p $! -o pcpu,pmem,rss h; sleep 1; done
[1] 3201
 0.0  0.0  2876
 102  0.2 44056
 103  0.5 85024
 103  1.0 166944
 ...
98.9  5.6 920552
99.1  4.3 718196
[1]+  Killed                  bash -c 'cgm movepid memory mem_1G $$; r2(){ r2 $@$@;};r2 r2'

Nota la crescita esponenziale (potenza di 2) nelle richieste di memoria.

In futuro, speriamo di vedere "distro / vendor" preconfigurare priorità e limiti del cgroup (tramite unità systemd) per cose importanti come SSH e lo stack grafico, in modo tale da non perdere la memoria.


2

Potrebbe essere possibile premere Ctrl- zper sospendere il programma. Quindi puoi fare kill %1(o qualunque sia il numero del lavoro o puoi usare il PID).

È possibile utilizzare il ulimitcomando per provare a limitare la quantità di memoria disponibile a un processo.


Ctrl-Z è carino, ma di solito sto eseguendo una GUI di Matlab e ho perso la traccia del terminale di controllo, quindi non ho modo di emettere il tasto Ctrl-Z. Sarebbe bello se la GUI avesse un tasto di scelta rapida per inviare SIGSTOP a qualunque applicazione abbia lo stato attivo!
nibot

Puoi correre kill -STOP <pid>che farà la stessa cosa di Ctrl-Z.
hlovdal

Sì, ma l'intero problema è che, in una situazione del genere, il sistema è così non reattivo che ci vuole molto tempo (o per sempre) per arrivare al prompt dei comandi.
nibot

1

È possibile utilizzare i CGroup per limitare l'utilizzo delle risorse e prevenire tali problemi: https://en.wikipedia.org/wiki/Cgroups


Si prega di includere le informazioni essenziali nella risposta e utilizzare il collegamento solo per l'attribuzione e ulteriori letture. Tale collegamento descrive cosa sono i CGroup, ma dal collegamento non è ovvio come utilizzarlo effettivamente per risolvere il problema. Puoi espandere la tua risposta per descrivere la soluzione alla domanda? Grazie.
fixer1234

0

Sarebbe bello se la GUI avesse un tasto di scelta rapida per inviare SIGSTOP a qualunque applicazione abbia il focus!

C'è sempre il xkillcomando classico (da xorg-x11-apps-7.4-14.fc14.src.rpm sul mio sistema). Immagino che non dovrebbe essere troppo difficile creare un clone che invia SIGSTOP invece di uccidere la finestra di destinazione.


Come posso fare in modo che xkill si avvii rapidamente premendo una combinazione di tasti?
nibot

Non sono sicuro. Presumo che sia gnome che KDE abbiano alcune funzionalità di collegamento globali che possono essere usate per avviare programmi.
hlovdal
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.