Jak ograniczyć całkowite zasoby (pamięć) procesu i jego potomków

16

Istnieje wiele pytań i odpowiedzi na temat ograniczania zasobów pojedynczego procesu, np. RLIMIT_AS może być wykorzystany do ograniczenia maksymalnej pamięci przydzielonej przez proces, który może być postrzegany jako VIRT top. Więcej na ten temat, np. Tutaj Czy istnieje sposób na ograniczenie ilości pamięci, którą dany proces może wykorzystać w Uniksie?

setrlimit(2) dokumentacja mówi:

Proces potomny utworzony za pomocą fork (2) dziedziczy limity zasobów rodzica. Limity zasobów są zachowane w execve (2).

Należy to rozumieć w następujący sposób:

Jeśli proces ma RLIMIT_AS wynoszący np. 2 GB, nie może przydzielić więcej pamięci niż 2 GB. Po odrodzeniu dziecka limit przestrzeni adresowej 2 GB zostanie przekazany dziecku, ale liczenie zaczyna się od 0. Dwa procesy łącznie mogą zająć do 4 GB pamięci.

Ale jaki byłby użyteczny sposób ograniczenia całkowitej sumy pamięci przydzielanej przez całe drzewo procesów?

jpe
źródło
7
Rzuciłbym okiem na grupy .
slm
@slm Dzięki! Wygląda na to, że warto spróbować cgroups. Jedynym działającym rozwiązaniem do tej pory (oprócz brzydkiego sposobu sumowania pamięci za pomocą PS i zabijania procesu nadrzędnego, jeśli jest powyżej limitu), który może działać, jest użycie jakiejś formy kontenera (LXC lub podobnych).
jpe
Tak - narzędzia, o których wiem, nie wykonują grup, tylko pojedyncze procesy, ale biorąc pod uwagę, jak grupy robocze działają dla technologii VM, takich jak LXC i Docker, spodziewałbym się, że zrobi to, co chcesz.
slm
1
Jeśli jest to Linux, umieść nadrzędny PID we własnej przestrzeni nazw i kontroluj w ten sposób wszystkie dzieci. Oto wstępna odpowiedź na tę koncepcję: unix.stackexchange.com/a/124194/52934
mikeserv

Odpowiedzi:

8

Nie jestem pewien, czy to odpowiada na twoje pytanie, ale znalazłem ten skrypt perla, który twierdzi, że robi dokładnie to, czego szukasz. Skrypt implementuje własny system egzekwowania limitów poprzez budzenie i sprawdzanie zużycia zasobów przez proces i jego dzieci. Wydaje się, że jest dobrze udokumentowany i wyjaśniony, a ostatnio został zaktualizowany.

Jak powiedział slm w swoim komentarzu, do tego można również użyć cgroups. Być może będziesz musiał zainstalować narzędzia do zarządzania grupami, zakładając, że jesteś w systemie Linux, którego powinieneś szukać libcgroups.

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

Upewnij się, że $USERjesteś użytkownikiem.

Użytkownik powinien wtedy mieć dostęp do ustawień pamięci grupy dyskusyjnej w /sys/fs/cgroup/memory/myGroup.

Następnie możesz ustawić limit na, powiedzmy 500 MB, wykonując następujące czynności:

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

Teraz uruchommy Vima:

cgexec -g memory:myGroup vim

Proces vim i wszystkie jego dzieci powinny być teraz ograniczone do użycia 500 MB pamięci RAM. Myślę jednak , że ten limit dotyczy tylko pamięci RAM, a nie wymiany. Gdy procesy osiągną limit, zaczną się zamieniać. Nie jestem pewien, czy dacie radę to obejść, nie mogę znaleźć sposobu ograniczenia użycia wymiany za pomocą cgroups.

arnefm
źródło
Proponowane rozwiązanie umożliwia ograniczenie rozmiaru zestawu rezydentnego drzewa procesów. Zachowanie wydaje się być inne niż RLIMIT_AS: możliwe jest malloc więcej pamięci niż jest to limit, jednak wydaje się, że nie można faktycznie użyć więcej.
jpe
Domyślnie limit pamięci cgroup dotyczy tylko (w przybliżeniu) wykorzystania fizycznej pamięci RAM. Dostępna jest opcja jądra (CONFIG_MEMCG_SWAP), aby umożliwić rozliczanie zamiany; zobacz dokumentację jądra, aby uzyskać szczegółowe informacje.
Søren Løvborg
1
On fedora,sudo yum install libcgroup-tools
jozxyqk
2

Stworzyłem skrypt, który to robi, używając cgmanager, który jest używany w Ubuntu. Jedną z zalet jest to, że nie wymaga to dostępu do konta root. Zauważ, że zarządzanie cgroup jest trochę specyficzne dla dystrybucji, więc nie wiem, czy to działa na dystrybucjach, które używają systemd zarządzania 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

użycie: zapisz jak limitmemna swojej ścieżce i uczyń go wykonywalnym. Następnie uruchom np limitmem 1G command.... To ogranicza faktycznie używaną pamięć. Jeśli zostanie to osiągnięte, zabójca OOM zabije proces lub jedno z jego dzieci, ale nie jest to przypadek, który nie ma nic wspólnego z tym poleceniem.

JanKanis
źródło
Dzięki za krótki cgmprzewodnik, przydał się
Witalij Isaev
1

/unix//a/536046/4319 :

W każdej dystrybucji opartej na systemie możesz także używać grup pośrednio przez systemd-run. Np. W przypadku ograniczenia pdftoppmdo 500 MB pamięci RAM użyj:

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

...

imz - Ivan Zakharyaschev
źródło