Czy Linux zacznie zabijać moje procesy bez pytania, czy brakuje pamięci?

66

Uruchomiłem skrypt powłoki z poleceniami, aby uruchomić kilka programów intensywnie korzystających z pamięci (2-5 GB) jeden za drugim. Gdy wróciłem, aby sprawdzić postępy w skrypcie, z zaskoczeniem odkryłem, że niektóre z moich procesów przebiegały Killedzgodnie z raportem mojego terminalu. Kilka programów zakończyło się już sukcesywnie przed programami, które zostały później Killeduruchomione, ale wszystkie te programy później uległy awarii w wyniku segmentacji (która mogła, ale nie musi być spowodowana błędem w moim kodzie, czytaj dalej).

Przejrzałem historię użytkowania konkretnego klastra, z którego korzystałem, i zobaczyłem, że ktoś zaczął uruchamiać kilka procesów intensywnie wykorzystujących pamięć w tym samym czasie i w ten sposób wyczerpał rzeczywistą pamięć (a być może nawet przestrzeń wymiany) dostępną dla klastra. O ile wiem, te procesy wymagające dużej ilości pamięci zaczęły działać mniej więcej w tym samym czasie, gdy zaczęły się problemy z moimi programami.

Czy to możliwe, że Linux zabił moje programy, gdy zabrakło pamięci? Czy to możliwe, że błędy segmentacji, które dostałem później, były spowodowane brakiem pamięci dostępnej do uruchamiania moich programów (zamiast błędu w kodzie)?

Gwiazda neutronowa
źródło
2
Czy przydzielając pamięć, masz instrukcję, aby sprawdzić, czy pamięć została pomyślnie przydzielona? To powinno dać wskazówkę, czy w twoim kodzie jest błąd, czy też z powodu braku pamięci w systemie.
unxnut

Odpowiedzi:

72

To może.

Istnieją dwa różne warunki braku pamięci, które można napotkać w systemie Linux. Spotkanie zależy od wartości sysctl vm.overcommit_memory( /proc/sys/vm/overcommit_memory)

Wprowadzenie:
Jądro może wykonywać tak zwane „overcommit pamięci”. To wtedy jądro przydziela programom więcej pamięci, niż jest w rzeczywistości w systemie. Odbywa się to w nadziei, że programy faktycznie nie wykorzystają całej przydzielonej pamięci, ponieważ jest to dość częste zjawisko.

overcommit_memory = 2

Kiedy overcommit_memoryjest ustawiony na 2, jądro w ogóle nie wykonuje żadnego nadpisywania. Zamiast tego, gdy program ma przydzieloną pamięć, zagwarantowany jest dostęp do tej pamięci. Jeśli system nie ma wystarczającej ilości wolnej pamięci, aby zaspokoić żądanie alokacji, jądro po prostu zwróci błąd dla żądania. Program w pełni wdzięcznie poradzi sobie z sytuacją. Jeśli nie sprawdzi, czy alokacja się powiodła, gdy naprawdę się nie powiodła, aplikacja często napotyka awarię.

W przypadku segfault powinieneś znaleźć taką linię na wyjściu dmesg:

[1962.987529] myapp[3303]: segfault at 0 ip 00400559 sp 5bc7b1b0 error 6 in myapp[400000+1000]

Te at 0środki, które aplikacja próbowali uzyskać dostęp do niezainicjowanej wskaźnik, który może być wynikiem nieudanego wezwania alokacji pamięci (ale nie jest to jedyna droga).

overcommit_memory = 0 i 1

Gdy overcommit_memoryjest ustawiony na 0lub 1, funkcja overcommit jest włączona, a programy mogą przydzielać więcej pamięci, niż jest w rzeczywistości dostępne.

Jednak, gdy program chce użyć pamięci, która została przydzielona, ​​ale jądro stwierdza, że ​​tak naprawdę nie ma wystarczającej ilości pamięci, aby ją zaspokoić, musi odzyskać trochę pamięci. Najpierw próbuje wykonać różne zadania czyszczenia pamięci, takie jak opróżnianie pamięci podręcznej, ale jeśli to nie wystarczy, przerwie proces. To zakończenie jest wykonywane przez OOM-Killera. OOM-Killer patrzy na system, aby zobaczyć, które programy używają jakiej pamięci, jak długo działały, kto je uruchamia, oraz szereg innych czynników, aby ustalić, który z nich zostanie zabity.

Po tym, jak proces został zabity, używana pamięć zostaje zwolniona, a program, który właśnie spowodował stan braku pamięci, ma teraz potrzebną pamięć.

Jednak nawet w tym trybie programom nadal można odmówić żądania alokacji. Kiedy overcommit_memoryjest 0, jądro próbuje zgadnąć, kiedy powinien zacząć odrzucać żądania alokacji. Gdy jest ustawiony na 1, nie jestem pewien, jakiej determinacji używa, aby określić, kiedy powinien odrzucić żądanie, ale może odrzucić bardzo duże żądania.

Możesz sprawdzić, czy OOM-Killer jest zaangażowany, patrząc na wyniki dmesgi znajdując komunikaty, takie jak:

[11686.043641] Out of memory: Kill process 2603 (flasherav) score 761 or sacrifice child
[11686.043647] Killed process 2603 (flasherav) total-vm:1498536kB, anon-rss:721784kB, file-rss:4228kB
Patrick
źródło
Wygląda więc na to, że obie sytuacje mi się przytrafiły.
NeutronStar,
@Joshua Właśnie zaktualizowałem odpowiedź. Zapomniałem wspomnieć, że nadal możesz dostać błędy przydziału, gdy overcommit_memoryjest ustawiony na 0 lub 2.
Patrick
Wydaje mi się, że warto edytować link do oswajania zabójcy OOM w poście.
0xC0000022L
@ 0xC0000022L Dzięki, to dobry artykuł (choć trochę nieaktualny). Nie chciałem mówić nic o kontrolowaniu zabójcy OOM, ponieważ nie jest to częścią pytania (i nie jest to krótki temat), i mamy tutaj mnóstwo innych pytań na ten temat.
Patrick
1
@ mikeserv Nie mówię, że zachowanie zabójcy OOM nie ma nic wspólnego z kontrolowaniem go. Pytanie brzmiało, czy Linux zabije jego programy. Jak zapobiec temu linuxowi, najpierw trzeba ustalić, że tak naprawdę robi to Linux. A jeśli overcommit_memory=2zabójca OOM nie jest nawet włączony, więc kontrolowanie go nie ma znaczenia. Jednak gdy ustalimy, że jest to zabójca OOM, staje się on kolejnym tematem, który obejmuje wiele innych pytań i odpowiedzi tutaj.
Patrick
16

Prawda jest taka, że ​​bez względu na to, jak na to patrzysz - niezależnie od tego, czy proces został uduszony z powodu menedżera pamięci systemu, czy z innego powodu - nadal jest to błąd. Co się stało z tymi wszystkimi danymi, które właśnie przetwarzałeś w pamięci? Powinien był zostać uratowany.

Chociaż overcommit_memory=jest to najbardziej ogólny sposób konfigurowania zarządzania Linux OOM, można go również dostosowywać do poszczególnych procesów, takich jak:

echo [-+][n] >/proc/$pid/oom_adj

Zastosowanie -17powyższego spowoduje wykluczenie procesu z zarządzania brakiem pamięci. Prawdopodobnie ogólnie nie jest to świetny pomysł, ale jeśli polujesz na robaki, warto to zrobić - szczególnie jeśli chcesz wiedzieć, czy to OOM, czy kod. Dodatnie zwiększenie liczby zwiększy prawdopodobieństwo zabicia procesu w zdarzeniu OOM, co może pomóc w lepszym wzmocnieniu odporności kodu w sytuacjach o niskiej pamięci i zapewnić, że w razie potrzeby wyjdziesz z gracją.

Możesz sprawdzić bieżące ustawienia modułu obsługi OOM dla każdego procesu, na przykład:

cat /proc/$pid/oom_score 

W przeciwnym razie możesz popełnić samobójstwo:

sysctl vm.panic_on_oom=1
sysctl kernel.panic=X

Spowoduje to ponowne uruchomienie komputera w przypadku braku pamięci. Ustawiasz Xpowyższe na liczbę sekund, po których komputer ma się zatrzymać po panice jądra przed ponownym uruchomieniem. Zaszaleć.

A jeśli z jakiegoś powodu zdecydujesz, że ci się podoba, spraw, aby był trwały:

echo "vm.panic_on_oom=1" >> /etc/sysctl.conf
echo "kernel.panic=X" >> /etc/sysctl.conf
mikeserv
źródło
To wspólny klaster, którego używam, jestem pewien, że inni użytkownicy nie doceniliby jego ponownego uruchomienia bez ich zgody.
NeutronStar
3
@Joshua - Wątpię bardzo poważnie, że ktokolwiek by to chciał - nawet przeciwstawia się prawom robotyki Asimova. Z drugiej strony, jak już wspomniałem, możesz skonfigurować OOM na proces również w inny sposób. Oznacza to, że możesz osobiście segregować na podstawie własnych zestawów reguł dla każdego procesu. Takie rzeczy wydają się być szczególnie przydatne w scenariuszu klastrów współdzielonych.
mikeserv