Jak mogę ograniczyć ilość pamięci dostępnej dla procesu?

11

Pracuję nad programem; czasami kończy się to przydzielaniem bardzo dużej ilości pamięci (>> 10G na maszynie z 8G pamięci fizycznej), co powoduje, że system przestaje odpowiadać. Chcę ograniczyć ilość pamięci, którą proces może przydzielić. Zwykle robiłbym to:

ulimit -m 4000000 ; ./myprogram

... co powinno zabić mój program, jeśli spróbuje użyć więcej niż 4 GB pamięci.

W OS X El Capitan wydaje się, że nie ma to żadnego wpływu; nawet ulimit -m 1(ograniczenie wszystkich programów do zaledwie 1kB pamięci!) jest nieskuteczne.

Jak ustawić górną granicę pamięci dostępnej dla określonego procesu?

CPCALLEN
źródło
Kiedy mówisz Zwykły sposób bym to zrobił , co masz na myśli? Mówiąc dokładniej, kiedy to zwykle robisz? A kiedy to robisz, masz na myśli w Mac OS X El Capitan czy w innym środowisku? Czy możesz podać przykład, kiedy z powodzeniem go wykorzystałeś? Zasadniczo próbuję tylko wyjaśnić, czy ten proces działa dla Ciebie normalnie, ale po prostu nie działa dla tego konkretnego programu? Czy to w ogóle nie działa, ale uważasz, że powinno?
Monomeeth
1
Przez „zwykły sposób, w jaki to zrobiłbym”, mam na myśli sposób, w jaki zrobiłbym to na innych smakach Uniksa. Zauważam, że właśnie odkryłem, że ulimit -mnie działa już w systemie Linux (> 2.4.30), ale ulimit -vnadal działa zgodnie z oczekiwaniami. (Podobnie jak ulimit -m, ulimit -vrównież wydaje się nie mieć wpływu na OS X.)
cpcallen
Brzmi to tak, jakbyś miał przeciek pamięci w pisanym programie i musisz lepiej wyrzucać śmieci. Czy czytałeś już na temat zarządzania pamięcią w go?
Todd Dabney
1
Nie, nie wyciek pamięci - po prostu wyszukiwanie wykresu potencjalnie bardzo dużej przestrzeni stanów. Mógłbym dodać kod, aby przerwać wyszukiwanie, jeśli różne struktury wewnętrzne stają się zbyt duże, ale spodziewałem się, że będę w stanie zrobić to samo (zapobiegać zaklinowaniu się maszyny przez duże dane wejściowe) za pomocą jednowarstwowej powłoki.
cpcallen

Odpowiedzi:

1

Istnieją dwa podejścia do ograniczenia użycia pamięci: ex post facto i zapobiegawcze. To znaczy, możesz spróbować zabić swój program po tym, jak stał się zbyt duży, lub możesz tak zaprogramować, aby nie był zbyt duży.

Jeśli nalegasz na faktyczne podejście ex post, możesz użyć następującego skryptu Bash. Ten skrypt najpierw wyszukuje ilość pamięci (zgodnie z definicją „rezydentnego rozmiaru zestawu”), której używa proces z pid procesid, odfiltrowuje wszystkie dane nienumeryczne za pomocą grep i zapisuje tę ilość jako zmienną n. Skrypt sprawdza następnie, czy n jest większe od podanego x. Jeśli tak, proces z procid pid zostaje zabity.

Proszę zanotować:

  1. Musisz zastąpić <pid>identyfikatorem procesu swojego programu.
  2. Musisz zamienić <x>na rss = "resident set size" (tzn. Rzeczywisty rozmiar pamięci), którego program nie powinien przekraczać.

n=$(ps -<pid> -o rss | grep '[0-9]') if [ $n -gt <x> ]; then kill -9 <pid>; fi

Jeśli chcesz, aby działało to co sekundę, po prostu umieść go w pętli i każ mu czekać y sekund po każdej iteracji. Możesz również napisać podobne polecenie za pomocą top. Twoim punktem wyjścia byłoby top -l 1|grep "<pid>"|awk '{print $10}'.

@ kenorb za odpowiedź pomógł mi z moim skrypcie


Chociaż uważam, że to odpowiada na pytanie, na dłuższą metę uważam, że lepszym rozwiązaniem programistycznym jest podejście zapobiegawcze z wykorzystaniem ręcznego przydzielania pamięci.

Po pierwsze, czy jesteś pewien, że użycie pamięci jest naprawdę problemem? GO dokumentacja stanowi:

Alokator pamięci Go rezerwuje duży region pamięci wirtualnej jako arenę alokacji. Ta pamięć wirtualna jest lokalna dla określonego procesu Go; rezerwacja nie pozbawia innych procesów pamięci.

Jeśli nadal uważasz, że masz problem, zachęcam do ręcznego zarządzania pamięcią, tak jak dzieje się to w języku programowania C. Ponieważ go jest napisane w C, podejrzewałem, że będą sposoby na zarządzanie pamięcią C / alokacje, i rzeczywiście są. Zobacz to repozytorium github, które:

pozwala na ręczne zarządzanie pamięcią za pomocą standardowego alokatora C dla twojego systemu. Jest to cienkie opakowanie na malloc, calloc i wolne od. Zobacz man malloc, aby uzyskać szczegółowe informacje na temat tych funkcji w systemie. Ta biblioteka używa cgo.

Przypadek użycia podano jako:

Dlaczego miałbyś tego chcieć?

Gdy program powoduje presję pamięci lub w systemie kończy się pamięć, pomocne może być ręczne kontrolowanie przydziału i zwolnienia pamięci. Go może pomóc kontrolować przydziały, ale nie można jawnie cofnąć przydziału niepotrzebnych danych.

To wydaje się lepszym rozwiązaniem długoterminowym.

Jeśli chcesz dowiedzieć się więcej o C (w tym o zarządzaniu pamięcią), język programowania C jest standardowym źródłem informacji.

Evan Rosica
źródło
Jestem pewien, że użycie pamięci jest naprawdę problemem. Gdy program nieoczekiwanie urósł do rozmiaru> 2x fizycznej pamięci, maszyna prawie całkowicie nie reagowała z powodu przerzucania stron. Minęło około godziny od momentu, gdy kliknąłem ^ C, a kiedy macOS wznowił reagowanie na kliknięcia myszą; w międzyczasie jedynym dowodem na to, że nie został zamrożony, był cichy hałas twardego dysku, który szybko mijał.
cpcallen
Jest to prawdopodobnie rozsądne rozwiązanie w praktyce, ale w przeciwieństwie do ulimit w systemach operacyjnych UNIX (innych niż Darwin), zależy to od możliwości przydzielenia wystarczającej ilości pamięci w odpowiednim czasie, aby pomyślnie wykonać polecenie kill. Naprawdę wolałbym mieć ograniczenia wielkości procesu narzucane przez jądro.
cpcallen
@cpcallen podczas niektórych poszukiwań wydaje się, że „parametr -m dla ulimit nie ma wpływu na systemy Linux z wersjami jądra nowszymi niż 2.4.30”. Wygląda na to, że OSX również zauważył tę zmianę. Wypróbuj opcję -v, aby ograniczyć przestrzeń adresową
Evan Rosica