Nie jestem programistą jądra, ale spędziłem lata na filozofowaniu tego problemu, ponieważ wiele razy wpadłem na to. Właściwie wymyśliłem metaforę dla całej sytuacji, więc powiem ci to. Zakładam w mojej historii, że rzeczy takie jak „zamiana” nie istnieją. W dzisiejszych czasach zamiana nie ma większego sensu z 32 GB pamięci RAM.
Wyobraź sobie swoją okolicę, w której woda jest podłączona do każdego budynku za pomocą rur, a miasta muszą zarządzać wydajnością. Załóżmy, że produkujesz tylko 100 jednostek wody na sekundę (a cała niewykorzystana pojemność marnuje się, ponieważ nie masz zbiorników zbiornikowych). Każdy dom (dom = mała aplikacja, terminal, widget zegara itp.) Wymaga jednej 1 jednostki wody na sekundę. To wszystko jest miłe i dobre, ponieważ twoja populacja wynosi 90, więc każdy dostaje wystarczającą ilość wody.
Teraz burmistrz (= ty) decyduje, że chcesz otworzyć dużą restaurację (= przeglądarkę). Ta restauracja pomieści wielu kucharzy (= karty przeglądarki). Każdy kucharz potrzebuje 1 jednostki wody na sekundę. Zaczynasz od 10 kucharzy, więc całkowite zużycie wody w całym sąsiedztwie wynosi 100 jednostek wody, co wciąż jest dobre.
Teraz zaczyna się zabawa: zatrudniasz innego kucharza w swojej restauracji, co daje całkowite zapotrzebowanie na wodę 101, czego oczywiście nie masz. Musisz coś zrobić.
Zarządzanie wodą (= jądro) ma 3 opcje.
1. Pierwsza opcja to po prostu odłączyć usługę dla domów, które ostatnio nie korzystały z wody. Jest to w porządku, ale jeśli odłączony dom chce ponownie skorzystać z wody, będzie musiał ponownie przejść długą procedurę rejestracji. Kierownictwo może odłączyć wiele domów, aby zwolnić więcej zasobów wodnych. W rzeczywistości rozłączą wszystkie domy, które ostatnio nie używały wody, dzięki czemu zawsze będzie dostępna pewna ilość darmowej wody.
Chociaż twoje miasto nadal funkcjonuje, wadą jest to, że postęp zatrzymuje się. Większość czasu spędzasz na oczekiwaniu na zarządzanie zasobami wodnymi w celu przywrócenia usługi.
To właśnie robi jądro ze stronami z kopiami plików. Jeśli uruchomisz duży plik wykonywalny (np. Chrome), jego plik zostanie skopiowany do pamięci. Gdy zabraknie pamięci lub jeśli są części, do których ostatnio nie uzyskano dostępu, jądro może je upuścić, ponieważ i tak może je ponownie załadować z dysku. Jeśli zostanie to zrobione nadmiernie, spowoduje to zatrzymanie pulpitu, ponieważ wszystko będzie tylko czekało na We / Wy dysku. Zauważ, że jądro upuści również wiele ostatnio używanych stron, kiedy zaczniesz robić dużo IO. Dlatego po skopiowaniu kilku dużych plików, takich jak obrazy DVD, potrzeba wielu lat, aby przejść do aplikacji działającej w tle.
Jest to dla mnie najbardziej denerwujące zachowanie, ponieważ nienawidzę czkawek i nie masz nad tym kontroli. Byłoby miło móc to wyłączyć. Myślę o czymś podobnym do
sed -i 's/may_unmap = 1/may_unmap = (vm_swappiness >= 0)/' mm/vmscan.c
a następnie możesz ustawić vm_swappiness na -1, aby to wyłączyć. To działało całkiem dobrze w moich małych testach, ale niestety nie jestem programistą jądra, więc nie wysłałem go nikomu (i oczywiście powyższa mała modyfikacja nie jest kompletna).
2)Kierownictwo mogłoby odrzucić prośbę nowego kucharza o wodę. To początkowo brzmi jak dobry pomysł. Istnieją jednak dwie wady. Po pierwsze, są firmy, które wymagają dużej ilości abonamentów wodnych, nawet jeśli ich nie używają. Jednym z możliwych powodów jest uniknięcie narzutów związanych z zarządzaniem zasobami wodnymi, ilekroć potrzebują dodatkowej wody. Zużycie wody rośnie i maleje w zależności od pory dnia. Np. W przypadku restauracji firma potrzebuje znacznie więcej wody w południe w porównaniu do północy. Żądają więc wszelkiej możliwej wody, której mogą użyć, ale marnuje przydziały wody o północy. Problem polega na tym, że nie wszystkie firmy mogą prawidłowo przewidzieć szczytowe zużycie, dlatego żądają znacznie więcej w nadziei, że nigdy nie będą musiały się martwić o dodatkowe.
Oto, co robi maszyna wirtualna Java: przy uruchamianiu przydziela dużą ilość pamięci, a następnie działa z tego. Domyślnie jądro przydziela pamięć tylko wtedy, gdy aplikacja Java faktycznie zacznie z niej korzystać. Jeśli jednak wyłączysz opcję overcommit, jądro potraktuje rezerwację poważnie. Pozwoli to na pomyślne zakończenie alokacji, jeśli faktycznie ma na to odpowiednie zasoby.
Istnieje jednak jeszcze jeden poważniejszy problem z tym podejściem. Powiedzmy, że jedna firma zaczyna żądać jednej jednostki wody każdego dnia (zamiast w krokach co 10). W końcu osiągniesz stan, w którym masz 0 wolnych jednostek. Teraz ta firma nie będzie mogła przydzielić więcej. W porządku, i tak zależy na dużych firmach. Problem polega jednak na tym, że małe domy również nie będą mogły poprosić o więcej wody! Nie będziesz w stanie zbudować małych publicznych łazienek, aby poradzić sobie z nagłym napływem turystów. W pobliskim lesie nie będzie można zapewnić awaryjnej wody do pożaru.
W kategoriach komputerowych: W sytuacjach bardzo niskiej pamięci bez nadmiernego zaangażowania nie będziesz mógł otworzyć nowego Xtermu, nie będziesz mógł ssh na swoim komputerze, nie będziesz mógł otworzyć nowej karty, aby wyszukać możliwe poprawki Innymi słowy, wyłączenie overcommit powoduje, że pulpit jest bezużyteczny, gdy brakuje mu pamięci.
3. Oto ciekawy sposób rozwiązania problemu, gdy firma zaczyna zużywać zbyt dużo wody. Gospodarka wodna wysadza to w powietrze! Dosłownie: trafia na stronę restauracji, wrzuca do niej dynamity i czeka, aż wybuchnie. Spowoduje to natychmiastowe zmniejszenie zapotrzebowania miasta na wodę, dzięki czemu nowi ludzie będą mogli się wprowadzać, tworzyć publiczne łazienki itp. Ty, jako burmistrz, możesz odbudować restaurację w nadziei, że tym razem zużyje mniej wody. Na przykład powiesz ludziom, aby nie chodzili do restauracji, jeśli w środku jest już zbyt wiele osób (np. Otworzysz mniej kart przeglądarki).
To właśnie robi jądro, gdy zabraknie mu wszystkich opcji i potrzebuje pamięci: wywołuje zabójcę OOM. Wybiera dużą aplikację (opartą na wielu heurystykach) i zabija ją, zwalniając sporo pamięci, ale utrzymując responsywny pulpit. W rzeczywistości jądro Androida robi to jeszcze bardziej agresywnie: zabija ostatnio używaną aplikację, gdy pamięć jest niska (w porównaniu do podstawowego jądra, które robi to tylko w ostateczności). Nazywa się to Viking Killer w Androidzie.
Myślę, że jest to jedno z najprostszych rozwiązań problemu: to nie tak, że masz więcej opcji niż to, więc dlaczego nie przejść przez to wcześniej niż później, prawda? Problem polega na tym, że jądro czasami wykonuje sporo pracy, aby uniknąć wywoływania zabójcy OOM. Właśnie dlatego widzisz, że twój pulpit jest bardzo wolny i jądro nic z tym nie robi. Ale na szczęście istnieje możliwość samodzielnego wywołania zabójcy OOM! Najpierw upewnij się, że magiczny klawisz sysrq jest włączony (np. echo 1 | sudo tee
/proc/sys/kernel/sysrq
), A następnie za każdym razem, gdy czujesz, że w jądrze zaczyna brakować pamięci, po prostu naciśnij Alt + SysRQ, Alt + f.
OK, więc to wszystko jest miłe, ale chcesz to wypróbować? Niski poziom pamięci jest bardzo prosty do odtworzenia. Mam do tego bardzo prostą aplikację. Będziesz musiał uruchomić go dwa razy. Pierwsze uruchomienie określi, ile masz wolnej pamięci RAM, drugie uruchomienie spowoduje powstanie niskiej ilości pamięci. Zauważ, że ta metoda zakłada, że masz zamianę wyłączoną (np. Wykonaj a sudo swapoff -a
). Kod i sposób użycia są następujące:
// gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
int main(int argc, char** argv)
{
int limit = 123456789;
if (argc >= 2) {
limit = atoi(argv[1]);
}
setbuf(stdout, NULL);
for (int i = 1; i <= limit; i++) {
memset(malloc(1 << 20), 1, 1 << 20);
printf("\rAllocated %5d MiB.", i);
}
sleep(10000);
return 0;
}
A oto jak go wykorzystujesz:
$ gcc -std=c99 -Wall -Wextra -Werror -g -o eatmem eatmem.c
$ ./eatmem
Allocated 31118 MiB.Killed
$ ./eatmem 31110
Allocated 31110 MiB.Killed
Pierwsze wywołanie wykryło, że mamy 31 118 MB wolnej pamięci RAM. Powiedziałem więc aplikacji, aby przydzieliła 31 110 MiB RAM, aby jądro jej nie zabiło, ale pochłonęło prawie całą moją pamięć. Mój system zamarł: nawet wskaźnik myszy nie drgnął. Nacisnąłem Alt + SysRQ, Alt + f i to zabiło mój proces jedzenia i system został przywrócony.
Mimo że omawialiśmy nasze opcje, co robimy w sytuacji braku pamięci, najlepszym podejściem (podobnie jak w każdej innej niebezpiecznej sytuacji) jest unikanie tego w pierwszej kolejności. Istnieje wiele sposobów, aby to zrobić. Jednym z powszechnych sposobów, w jaki widziałem, jest umieszczanie źle działających aplikacji (takich jak przeglądarki) w innych kontenerach niż reszta systemu. W takim przypadku przeglądarka nie będzie miała wpływu na pulpit. Ale sama prewencja nie wchodzi w zakres pytania, więc nie będę o tym pisać.
TL; DR: Chociaż obecnie nie ma możliwości pełnego uniknięcia stronicowania, można ograniczyć całkowite zatrzymanie systemu, wyłączając funkcję overcommit. Ale twój system będzie nadal bezużyteczny w sytuacji niskiego poziomu pamięci, ale w inny sposób. Niezależnie od powyższego, w sytuacji braku pamięci naciśnij Alt + SysRQ, Alt + f, aby zabić duży proces wybrany przez jądro. Twój system powinien przywrócić swoją responsywność po kilku sekundach. Zakłada się, że magiczny klucz sysrq jest włączony (domyślnie nie jest).