Cómo limitar los recursos totales (memoria) de un proceso y sus elementos secundarios

16

Hay muchas preguntas y respuestas sobre la limitación de los recursos de un solo proceso, por ejemplo, RLIMIT_AS se puede utilizar para restringir la memoria máxima asignada por un proceso que puede verse como VIRT top. Más sobre el tema, por ejemplo, aquí. ¿Hay alguna manera de limitar la cantidad de memoria que un proceso en particular puede usar en Unix?

setrlimit(2) la documentación dice:

Un proceso hijo creado a través de fork (2) hereda los límites de recursos de su padre. Los límites de recursos se conservan en execve (2).

Debe entenderse de la siguiente manera:

Si un proceso tiene un RLIMIT_AS de, por ejemplo, 2GB, entonces no puede asignar más memoria que 2GB. Cuando genera un hijo, el límite de espacio de direcciones de 2 GB se transferirá al hijo, pero el recuento comienza desde 0. Los 2 procesos juntos pueden ocupar hasta 4 GB de memoria.

Pero, ¿cuál sería la forma útil de restringir la suma total de memoria asignada por un árbol completo de procesos?

jpe
fuente
77
Echaría un vistazo a cgroups .
slm
@slm ¡Gracias! Parece que cgroups es algo para probar. La única solución que funciona hasta ahora (además de una forma fea de sumar memoria usando PS y eliminar el proceso padre si está por encima del límite) que podría funcionar es usar alguna forma de contenedor (lxc o similares).
jpe
Sí, las herramientas que conozco no hacen un grupo, solo procesos individuales, sino que dado cómo funcionan los cgroups para tecnologías VM como LXC y Docker, esperaría que haga lo que desea.
slm
1
Si se trata de Linux, coloque el PID primario en su propio espacio de nombres y así lo controle a él y a todos sus hijos. Aquí hay una respuesta introductoria a ese concepto: unix.stackexchange.com/a/124194/52934
mikeserv

Respuestas:

8

No estoy seguro de si esto responde a su pregunta, pero encontré este script de Perl que dice hacer exactamente lo que está buscando. El script implementa su propio sistema para hacer cumplir los límites al despertar y verificar el uso de recursos del proceso y sus elementos secundarios. Parece estar bien documentado y explicado, y se ha actualizado recientemente.

Como slm dijo en su comentario, cgroups también se puede usar para esto. Es posible que deba instalar las utilidades para administrar cgroups, suponiendo que esté en Linux que debería buscar libcgroups.

sudo cgcreate -t $USER:$USER -a $USER:$USER -g memory:myGroup

Asegúrese de que $USERsea ​​su usuario.

Su usuario debería tener acceso a la configuración de memoria de cgroup en /sys/fs/cgroup/memory/myGroup.

Luego puede establecer el límite en, digamos 500 MB, haciendo esto:

echo 500000000 > /sys/fs/cgroup/memory/myGroup/memory.limit_in_bytes

Ahora ejecutemos Vim:

cgexec -g memory:myGroup vim

El proceso vim y todos sus elementos secundarios ahora deberían limitarse al uso de 500 MB de RAM. Sin embargo , creo que este límite solo se aplica a la RAM y no al intercambio. Una vez que los procesos alcanzan el límite, comenzarán a intercambiarse. No estoy seguro de si puede evitar esto, no puedo encontrar una manera de limitar el uso de intercambio utilizando cgroups.

arnefm
fuente
La solución propuesta permite limitar el tamaño del conjunto residente de un árbol de procesos. El comportamiento parece ser diferente de RLIMIT_AS: es posible asignar más memoria del límite, sin embargo, parece que no es posible usar más.
jpe
De manera predeterminada, el límite de memoria de cgroup se aplica solo (aproximadamente) al uso de RAM física. Hay una opción de kernel (CONFIG_MEMCG_SWAP) para habilitar la contabilidad de intercambio; ver los documentos del kernel para más detalles.
Søren Løvborg
1
En fedora,sudo yum install libcgroup-tools
jozxyqk
2

Creé un script que hace esto, usando cgmanager que se usa en Ubuntu. Una ventaja es que esto no requiere acceso de root. Tenga en cuenta que cgroup management es un poco específico de la distribución, por lo que no sé si esto funciona en distribuciones que usan systemd cgroup management.

#!/bin/sh

set -eu

if [ "$#" -lt 2 ]
then
    echo Usage: `basename $0` "<limit> <command>..."
    exit 1
fi

limit="$1"
shift
cgname="limitmem_$$"
echo "limiting memory to $limit (cgroup $cgname) for command $@"

cgm create memory "$cgname" >/dev/null
cgm setvalue memory "$cgname" memory.limit_in_bytes "$limit" >/dev/null
# try also limiting swap usage, but this fails if the system has no swap
cgm setvalue memory "$cgname" memsw.limit_in_bytes "$limit" >/dev/null 2>&1 || true

# spawn subshell to run in the cgroup
set +e
(
set -e
cgm movepid memory "$cgname" `sh -c 'echo $PPID'` > /dev/null
exec "$@"
)
# grab exit code
exitcode=`echo $?`

set -e

echo -n "peak memory used: "
cgm getvalue memory "$cgname" memory.max_usage_in_bytes | tail -1 | cut -f2 -d\"

cgm remove memory "$cgname" >/dev/null
exit $exitcode

uso: guardar como limitmemen su ruta y hacerlo ejecutable. Luego corre eg limitmem 1G command.... Esto limita la memoria realmente utilizada. Si se alcanza eso, el asesino de OOM matará el proceso o uno de sus hijos, pero no algo aleatorio que no tenga nada que ver con este comando.

JanKanis
fuente
Gracias por la breve cgmguía, fue útil
Vitaly Isaev
1

/unix//a/536046/4319 :

En cualquier distribución basada en systemd, también puede usar cgroups indirectamente a través de systemd-run. Por ejemplo, para su caso de limitación pdftoppma 500M de RAM, use:

systemd-run --scope -p MemoryLimit=500M pdftoppm

...

imz - Ivan Zakharyaschev
fuente