Como limitar o total de recursos (memória) de um processo e seus filhos

16

Existem muitas perguntas e respostas sobre como restringir os recursos de um único processo, por exemplo, o RLIMIT_AS pode ser usado para restringir a memória máxima alocada por um processo que pode ser visto como VIRT de maneira semelhante top. Mais sobre o tópico, por exemplo, aqui. Existe uma maneira de limitar a quantidade de memória que um processo específico pode usar no Unix?

setrlimit(2) a documentação diz:

Um processo filho criado via fork (2) herda os limites de recursos de seus pais. Os limites de recursos são preservados no execve (2).

Deve ser entendido da seguinte maneira:

Se um processo tiver um RLIMIT_AS de, por exemplo, 2 GB, ele não poderá alocar mais memória que 2 GB. Quando gera um filho, o limite de espaço de endereço de 2 GB é passado para o filho, mas a contagem começa em 0. Os dois processos juntos podem levar até 4 GB de memória.

Mas qual seria a maneira útil de restringir a soma total de memória alocada por toda uma árvore de processos?

jpe
fonte
7
Eu daria uma olhada no cgroups .
Slm
@slm Obrigado! Soa como cgroups é algo para tentar. A única solução que funciona até agora (além de uma maneira feia de somar memória usando o PS e eliminar o processo pai se estiver acima do limite) que pode funcionar é usar alguma forma de contêiner (lxc ou similares).
JPE
Sim - as ferramentas que eu conheço não fazem um grupo, apenas processos únicos, mas, considerando como os cgroups funcionam para tecnologias de VM como LXC e Docker, eu esperaria que ele fizesse o que você deseja.
slm
1
Se for Linux, coloque o PID pai em seu próprio espaço de nome e controle-o e todos os seus filhos dessa maneira. Aqui está uma resposta de introdução a esse conceito: unix.stackexchange.com/a/124194/52934
mikeserv

Respostas:

8

Não tenho certeza se isso responde à sua pergunta, mas encontrei este script perl que afirma fazer exatamente o que você está procurando. O script implementa seu próprio sistema para impor os limites, despertando e verificando o uso de recursos do processo e de seus filhos. Parece estar bem documentado e explicado, e foi atualizado recentemente.

Como slm disse em seu comentário, o cgroups também pode ser usado para isso. Pode ser necessário instalar os utilitários para gerenciar cgroups, supondo que você esteja no Linux que deve procurar libcgroups.

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

Verifique se $USERé o seu usuário.

Seu usuário deve ter acesso às configurações de memória do cgroup em /sys/fs/cgroup/memory/myGroup.

Você pode definir o limite para, digamos, 500 MB, fazendo o seguinte:

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

Agora vamos executar o Vim:

cgexec -g memory:myGroup vim

O processo vim e todos os seus filhos agora devem ser limitados ao uso de 500 MB de RAM. No entanto , acho que esse limite se aplica apenas à RAM e não à troca. Quando os processos atingirem o limite, eles começarão a trocar. Não tenho certeza se você pode contornar isso, não consigo encontrar uma maneira de limitar o uso de swap usando cgroups.

arnefm
fonte
A solução proposta permite limitar o tamanho do conjunto residente de uma árvore de processos. O comportamento parece ser diferente de RLIMIT_AS: é possível armazenar mais memória do que o limite, no entanto, parece não ser possível realmente usar mais.
JPE
Por padrão, o limite de memória do cgroup se aplica somente ao (aproximadamente) uso físico da RAM. Existe uma opção do kernel (CONFIG_MEMCG_SWAP) para ativar a contabilidade de troca; veja a documentação do kernel para detalhes.
Søren Løvborg
1
No fedora,sudo yum install libcgroup-tools
jozxyqk 10/03/2015
2

Eu criei um script que faz isso, usando o cgmanager, que é usado no Ubuntu. Uma vantagem é que isso não requer acesso root. Observe que o gerenciamento do cgroup é um pouco específico para a distribuição, portanto não sei se isso funciona em distribuições que usam o gerenciamento systemd cgroup.

#!/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: salve como limitmemno seu caminho e torne-o executável. Em seguida, execute, por exemplo limitmem 1G command.... Isso limita a memória realmente usada. Se isso for alcançado, o assassino da OOM matará o processo ou um de seus filhos, mas não algo aleatório que não tenha nada a ver com esse comando.

JanKanis
fonte
Obrigado pelo cgmguia breve , foi útil #
Vitaly Isaev
1

/unix//a/536046/4319 :

Em qualquer distribuição baseada em systemd, você também pode usar os cgroups indiretamente através do systemd-run. Por exemplo, para o seu caso de limitar pdftoppma 500M de RAM, use:

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

...

imz - Ivan Zakharyaschev
fonte