¿Cómo detengo rápidamente un proceso que está causando problemas (debido al exceso de asignación de memoria)?

19

Todos lo hemos experimentado: se le pide a algún programa que haga algo que requiera una gran cantidad de memoria. Intenta de forma diligente asignar toda esta memoria, y el sistema comienza a sacudirse de inmediato, intercambiando interminablemente y volviéndose lento o no responde.

Recientemente experimenté esto en mi computadora portátil Ubuntu debido a un script de Matlab que intentaba asignar una matriz ridículamente enorme. Después de ~ 5 + minutos de paliza, pude Ctrl-F1 a una consola y maté a Matlab. Preferiría tener algunas teclas de acceso rápido que me hubieran dado el control del sistema inmediatamente y me hubieran permitido matar el proceso ofensivo; o, tal vez, simplemente rechazar silenciosamente asignar un búfer tan grande.

  1. ¿Cuál es la forma más rápida de recuperar el control de un sistema Linux que ha dejado de responder o extremadamente lento debido al intercambio excesivo?

  2. ¿Existe una forma efectiva de evitar que ocurra tal intercambio en primer lugar, por ejemplo, limitando la cantidad de memoria que un proceso puede intentar asignar?

nibot
fuente

Respuestas:

12

Presione Alt-SysRq-F para matar el proceso usando la mayor cantidad de memoria:

  • La clave SysRq generalmente se asigna a la clave Imprimir.
  • Si está utilizando un escritorio gráfico, es posible que deba presionar Ctrl-Alt-SysRq-F en caso de que presione Alt-SysRq desencadene otra acción (por ejemplo, un programa de instantáneas).
  • Si está utilizando una computadora portátil, es posible que también deba presionar una tecla de función.
  • Para obtener más información, lea el artículo de Wikipedia .
broma
fuente
5

He creado un script para este propósito: https://github.com/tobixen/thrash-protect

He tenido este script ejecutándose en servidores de producción, estaciones de trabajo y computadoras portátiles con buen éxito. Esta secuencia de comandos no mata los procesos, pero los suspende temporalmente: he tenido varias situaciones más tarde en las que estoy bastante seguro de que perdí el control debido a la paliza si no fuera por esta secuencia de comandos simple. En el "peor" caso, el proceso ofensivo se ralentizará mucho y, al final, el núcleo (OOM) lo matará, en el "mejor" caso el proceso ofensivo se completará ... en cualquier caso, el servidor o la estación de trabajo seguirá siendo relativamente receptivo para que sea fácil investigar la situación.

Por supuesto, "comprar más memoria" o "no usar intercambio" son dos respuestas alternativas más tradicionales a la pregunta "¿cómo evitar la paliza?", Pero en general tienden a no funcionar tan bien (instalar más memoria puede no sea trivial, un proceso deshonesto puede consumir toda la memoria, sin importar cuánto se haya instalado, y uno puede tener problemas de agitación incluso sin intercambio cuando no hay suficiente memoria para almacenar en búfer / almacenamiento en caché). Recomiendo thrash-protect además de mucho espacio de intercambio.

tobixen
fuente
Acerca de deshabilitar el intercambio, de acuerdo con unix.stackexchange.com/a/24646/9108 , podría no ser la mejor opción.
sashoalm
De hecho, alguien comentó lo mismo sobre mí, así que modifiqué el documento de protección contra golpes en ese momento.
tobixen
4
  1. ¿Cuál es la forma más rápida de recuperar el control de un sistema Linux que ha dejado de responder o extremadamente lento debido al intercambio excesivo?

Ya respondí anteriormente con Alt-SysRq-F

  1. ¿Existe una forma efectiva de evitar que ocurra tal intercambio en primer lugar, por ejemplo, limitando la cantidad de memoria que un proceso puede intentar asignar?

Estoy respondiendo esta segunda parte. Sí, ulimitaún funciona lo suficientemente bien como para limitar un solo proceso. Usted puede:

  • establecer un límite suave para un proceso que sabe que probablemente se saldrá de control
  • establezca un límite estricto para todos los procesos si desea un seguro adicional

Además, como se menciona brevemente:

Puede usar CGroups para limitar el uso de recursos y prevenir tales problemas

De hecho, los cgroups ofrecen un control más avanzado, pero actualmente son más complicados de configurar en mi opinión.

Ulimit de la vieja escuela

Una vez fuera

Aquí hay un ejemplo simple:

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

Eso:

  • Establece un límite suave de uso de memoria total de 1GB (ulimit asume el límite en la unidad de KB)
  • Ejecuta una llamada de función bash recursiva r2(){ r2 $@$@;};r2 r2que masticará exponencialmente la CPU y la RAM al duplicarse infinitamente mientras solicita memoria de pila.

Como puede ver, se detuvo al intentar solicitar más de 1 GB.

Tenga en cuenta que -vfunciona con asignación de memoria virtual (total, es decir, físico + intercambio).

Protección permanente

Para limitar la asignación de memoria virtual, ases el equivalente de -vpara limits.conf.

Hago lo siguiente para protegerme contra cualquier proceso de mala conducta:

  • Establezca un límite de espacio de direcciones rígidas para todos los procesos.
  • address space limit = <physical memory> - 256MB.
  • Por lo tanto, ningún proceso único con uso codicioso de memoria o un bucle activo y pérdida de memoria puede consumir TODA la memoria física.
  • El espacio libre de 256 MB está ahí para el procesamiento esencial con ssh o una consola.

Un trazador de líneas:

$ 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"

Para validar, esto da como resultado lo siguiente (por ejemplo, en un sistema de 16 GB):

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

Notas:

  • Solo mitiga contra un solo proceso que se va por la borda con el uso de memoria.
  • No evitará una carga de trabajo multiproceso con una fuerte presión de memoria que provoque golpes (entonces cgroups es la respuesta).
  • No use la rssopción en limits.conf. No es respetado por los núcleos más nuevos.
  • Es conservador
    • En teoría, un proceso puede solicitar especulativamente una gran cantidad de memoria, pero solo usa activamente un subconjunto (un conjunto de trabajo más pequeño / uso de memoria residente).
    • El límite duro anterior hará que dichos procesos se cancelen (incluso si de otro modo hubieran funcionado bien dado que Linux permite que el espacio de direcciones de la memoria virtual se sobrecompita).

Grupos CG más nuevos

Ofrece más control, pero actualmente es más complejo de usar:

  • Mejora en la oferta de ulimit.
    • memory.max_usage_in_bytes puede contabilizar y limitar la memoria física por separado.
    • Mientras que ulimit -my / o rssen el limits.confque estaba destinado a ofrecer una funcionalidad similar, pero que no funciona desde el núcleo de Linux 2.4.30!
  • Necesita habilitar algunas banderas del núcleo cgroup en gestor de arranque: cgroup_enable=memory swapaccount=1.
    • Esto no sucedió por defecto con Ubuntu 16.04.
    • Probablemente debido a algunas implicaciones de rendimiento de la sobrecarga contable adicional.
  • El material cgroup / systemd es relativamente nuevo y está cambiando bastante, por lo que el flujo ascendente implica que los proveedores de distribución de Linux aún no lo han hecho fácil de usar. Entre 14.04LTS y 16.04LTS, las herramientas de espacio de usuario para usar cgroups han cambiado.
    • cgm ahora parece ser la herramienta de espacio de usuario oficialmente compatible.
    • Los archivos de la unidad systemd todavía no parecen tener valores predeterminados predefinidos de "proveedor / distribución" para priorizar servicios importantes como ssh.

Por ejemplo, para verificar la configuración actual:

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

Por ejemplo, para limitar la memoria de un solo proceso:

$ 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

Para verlo en acción masticando RAM como un proceso en segundo plano y luego asesinado:

$ 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'

Tenga en cuenta el crecimiento exponencial (potencia de 2) en las solicitudes de memoria.

En el futuro, esperemos ver "distro / vendors" preconfigurar las prioridades y límites de cgroup (a través de unidades systemd) para cosas importantes como SSH y la pila gráfica, de modo que nunca pierdan memoria.

JPvRiel
fuente
2

Puede presionar Ctrl- zpara suspender el programa. Luego puede hacer kill %1(o lo que sea el número de trabajo o puede usar el PID).

Puede usar el ulimitcomando para intentar limitar la cantidad de memoria disponible para un proceso.

Pausado hasta nuevo aviso.
fuente
Ctrl-Z es bueno, pero generalmente estoy ejecutando una GUI de Matlab y he perdido la noción del terminal de control, así que no tengo una manera fácil de emitir la tecla Ctrl-Z. ¡Sería bueno si la GUI tuviera una tecla de acceso rápido para enviar SIGSTOP a cualquier aplicación que tenga foco!
nibot
Puede ejecutar lo kill -STOP <pid>que hará lo mismo que Ctrl-Z.
hlovdal
Sí, pero el problema es que, en tal situación, el sistema no responde tan bien que lleva mucho tiempo (o para siempre) llegar a un símbolo del sistema.
nibot
1

Puede usar CGroups para limitar el uso de recursos y prevenir tales problemas: https://en.wikipedia.org/wiki/Cgroups

1kenthomas
fuente
Incluya la información esencial en su respuesta y use el enlace solo para atribución y lectura adicional. Ese enlace describe qué es CGroups, pero en el enlace no es obvio cómo usarlo para resolver el problema. ¿Puedes ampliar tu respuesta para describir la solución a la pregunta? Gracias.
fixer1234
0

¡Sería bueno si la GUI tuviera una tecla de acceso rápido para enviar SIGSTOP a cualquier aplicación que tenga foco!

Siempre existe el xkillcomando clásico (de xorg-x11-apps-7.4-14.fc14.src.rpm en mi sistema). Supongo que no debería ser demasiado difícil hacer un clon que envíe SIGSTOP en lugar de matar la ventana de destino.

hlovdal
fuente
¿Cómo puedo hacer que xkill se inicie rápidamente al presionar alguna combinación de teclas?
nibot
No estoy seguro. Supongo que tanto Gnome como KDE tienen alguna funcionalidad global de acceso directo que se puede usar para iniciar programas.
hlovdal