Czy Linux może „zabraknąć pamięci RAM”?

20

Widziałem kilka postów w sieci ludzi najwyraźniej narzekających na hostowany VPS nieoczekiwanie zabijający procesy, ponieważ zużyli zbyt dużo pamięci RAM.

Jak to jest możliwe? Myślałem, że wszystkie współczesne systemy operacyjne zapewniają „nieskończoną pamięć RAM”, używając po prostu wymiany dysku na wszystko, co przechodzi przez fizyczną pamięć RAM. Czy to jest poprawne?

Co może się stać, jeśli proces zostanie „zabity z powodu małej pamięci RAM”?

lustro
źródło
12
Żaden system operacyjny nie ma nieskończonej pamięci RAM. Oprócz fizycznych układów pamięci RAM w komputerze, systemy operacyjne mogą - zwykle opcjonalnie - używać tak zwanego „pliku wymiany” znajdującego się na dysku. Gdy komputer potrzebuje więcej pamięci niż w pamięci RAM, zamienia niektóre elementy na plik wymiany. Ale gdy plik wymiany osiągnie swoją pojemność - albo dlatego, że ustawisz maksymalny rozmiar (typowy), albo dysk się zapełni - zabraknie pamięci wirtualnej.
John Dibling
@JohnDibling; więc czy jest jakiś powód, dla którego chciałoby się ograniczyć wielkość wymiany poza oszczędzaniem miejsca na dysku dla systemu plików? Innymi słowy, jeśli mam dysk o pojemności 20 GB i tylko 1 GB plików, czy jest jakiś powód, aby nie ustawiać mojego rozmiaru wymiany na 19 GB?
poniedziałek
1
Aby uprościć rzeczy, powiedziałbym, że dwa powody ograniczenia rozmiaru wymiany to: 1) zmniejszenie zużycia dysku i 2) zwiększenie wydajności. To ostatnie może być bardziej prawdziwe w systemie Windows niż / * NIX, ale z drugiej strony, jeśli używasz przestrzeni wymiany na dysku, wydajność spada. Dostęp do dysku jest albo wolniejszy niż pamięć RAM, albo znacznie wolniejszy niż pamięć RAM, w zależności od systemu.
John Dibling,
9
Zamiana nie jest RAM . en.wikipedia.org/wiki/Random-access_memory Ilość pamięci RAM w systemie to ilość pamięci RAM w systemie - okres. To nie jest dwuznaczny ani dynamiczny wolumin. Jest to absolutnie naprawione. „Pamięć” jest bardziej niejednoznaczną koncepcją, ale rozróżnienie między pamięcią RAM a innymi formami przechowywania jest, jak wskazuje terdon (+1), dość znaczące. Wymiana dysku nie może zastąpić wydajności pamięci RAM o wiele rzędów wielkości . System, który nadmiernie zależy od zamiany, jest co najwyżej tymczasowy i ogólnie: śmieci.
goldilocks,
1
Więc miejsce na dysku jest teraz nieskończone?
Kaz

Odpowiedzi:

41

Co może się stać, jeśli proces zostanie „zabity z powodu małej pamięci RAM”?

Czasami mówi się, że linux domyślnie nigdy nie odrzuca próśb o więcej pamięci z kodu aplikacji - np malloc(). 1 To nie jest prawdą; domyślnie używa heurystyki

Oczywiste przekroczenie przestrzeni adresowej jest odrzucane. Używany w typowym systemie. Zapewnia to, że poważna alokacja nie powiedzie się, jednocześnie umożliwiając nadmierne zaangażowanie, aby zmniejszyć użycie swapu.

From [linux_src]/Documentation/vm/overcommit-accounting(wszystkie cytaty pochodzą z drzewa 3.11). Dokładnie to, co liczy się jako „poważnie dziki przydział”, nie jest wyraźnie określone, więc musielibyśmy przejrzeć źródło, aby ustalić szczegóły. Możemy również użyć metody eksperymentalnej w przypisie 2 (poniżej), aby spróbować uzyskać odbicie heurystyki - na tej podstawie moja początkowa obserwacja empiryczna jest taka, że ​​w idealnych okolicznościach (== system jest bezczynny), jeśli nie „ Jeśli masz zamianę, będziesz mógł przeznaczyć około połowy pamięci RAM, a jeśli masz zamianę, dostaniesz około połowy pamięci RAM plus całą swoją wymianę. To mniej więcej na proces (należy jednak pamiętać, że limit ten jest dynamiczny i może ulec zmianie ze względu na stan, zob. Uwagi w przypisie 5).

Połowa pamięci RAM plus swap jest wyraźnie domyślną wartością dla pola „CommitLimit” w /proc/meminfo. Oto, co to znaczy - i zauważ, że tak naprawdę nie ma to nic wspólnego z omówionym limitem (z [src]/Documentation/filesystems/proc.txt):

CommitLimit: w oparciu o współczynnik nadmiaru („vm.overcommit_ratio”), jest to całkowita ilość pamięci obecnie dostępnej do przydzielenia w systemie. Limit ten jest przestrzegany tylko wtedy, gdy włączone jest ścisłe rozliczanie z nadmiernym zaangażowaniem (tryb 2 w „vm.overcommit_memory”). CommitLimit jest obliczany według następującego wzoru: CommitLimit = ('vm.overcommit_ratio' * Fizyczna pamięć RAM) + Zamień Na przykład w systemie z 1G fizycznej pamięci RAM i 7G swapu z 'vm.overcommit_ratio' wynoszącym 30 dałoby to CommitLimit 7,3G.

Cytowany poprzednio dokument księgowania nadmiarowego stwierdza, że ​​wartością domyślną vm.overcommit_ratiojest 50. Więc jeśli możesz sysctl vm.overcommit_memory=2, możesz dostosować vm.covercommit_ratio (with sysctl) i zobaczyć konsekwencje. 3 Tryb domyślny, gdy CommitLimitnie jest wymuszony i tylko „oczywiste nadmierne polecenia przestrzeni adresowej są odrzucane”, to kiedy vm.overcommit_memory=0.

Chociaż strategia domyślna ma heurystyczny limit na proces zapobiegający „poważnie dzikiej alokacji”, pozostawia system jako całość wolną od poważnie dzikiej alokacji. 4 Oznacza to, że w pewnym momencie może zabraknąć pamięci i musi ogłosić bankructwo w niektórych procesach za pośrednictwem zabójcy OOM .

Co zabija zabójca OOM? Niekoniecznie proces, który poprosił o pamięć, gdy jej nie było, ponieważ niekoniecznie jest to naprawdę winny proces, a co ważniejsze, niekoniecznie ten, który najszybciej usunie system z problemu, w którym się znajduje.

Jest to cytowane stąd, które prawdopodobnie przytacza źródło 2.6.x:

/*
 * oom_badness - calculate a numeric value for how bad this task has been
 *
 * The formula used is relatively simple and documented inline in the
 * function. The main rationale is that we want to select a good task
 * to kill when we run out of memory.
 *
 * Good in this context means that:
 * 1) we lose the minimum amount of work done
 * 2) we recover a large amount of memory
 * 3) we don't kill anything innocent of eating tons of memory
 * 4) we want to kill the minimum amount of processes (one)
 * 5) we try to kill the process the user expects us to kill, this
 *    algorithm has been meticulously tuned to meet the principle
 *    of least surprise ... (be careful when you change it)
 */

Co wydaje się być porządnym uzasadnieniem. Jednak bez uzyskania wiedzy sądowej numer 5 (który jest zbędny z numeru 1) wydaje się być trudny pod względem implementacji sprzedaży, a numer 3 jest zbędny z numeru 2. Dlatego warto rozważyć sprowadzenie tego do # 2/3 i # 4.

Przeszukałem najnowsze źródło (3.11) i zauważyłem, że ten komentarz zmienił się w międzyczasie:

/**
 * oom_badness - heuristic function to determine which candidate task to kill
 *
 * The heuristic for determining which task to kill is made to be as simple and
 * predictable as possible.  The goal is to return the highest value for the
 * task consuming the most memory to avoid subsequent oom failures.
 */

Jest to nieco bardziej wyraźnie na temat nr 2: „Celem jest [zabicie] zadania zajmującego najwięcej pamięci, aby uniknąć późniejszych awarii oom”, a przez domniemanie nr 4 ( „chcemy zabić minimalną liczbę procesów ( jeden ) ) .

Jeśli chcesz zobaczyć zabójcę OOM w akcji, zobacz przypis 5.


1 Złudzenie, którego Gilles na szczęście mnie pozbył, zobacz komentarze.


2 Oto prosty fragment C, który prosi o coraz większe fragmenty pamięci, aby określić, kiedy żądanie o więcej nie powiedzie się:

#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>

#define MB 1 << 20

int main (void) {
    uint64_t bytes = MB;
    void *p = malloc(bytes);
    while (p) {
        fprintf (stderr,
            "%lu kB allocated.\n",
            bytes / 1024
        );
        free(p);
        bytes += MB;
        p = malloc(bytes);
    }
    fprintf (stderr,
        "Failed at %lu kB.\n",
        bytes / 1024
    );
    return 0;
}            

Jeśli nie znasz C, możesz to skompilować gcc virtlimitcheck.c -o virtlimitcheck, a następnie uruchomić ./virtlimitcheck. Jest całkowicie nieszkodliwy, ponieważ proces nie zajmuje miejsca, o które prosi - tzn. Tak naprawdę nigdy nie wykorzystuje pamięci RAM.

W systemie 3.11 x86_64 z systemem 4 GB i 6 GB wymiany nie udało mi się przy ~ 7400000 kB; liczba się zmienia, więc być może stan jest czynnikiem. To przypadkowo blisko CommitLimitIN /proc/meminfo, ale poprzez modyfikację tego vm.overcommit_rationie robi żadnej różnicy. W 32-bitowym systemie ARM 3.6.11 z 448 MB i 64 MB wymiany, jednak nie udaje mi się przy ~ 230 MB. Jest to interesujące, ponieważ w pierwszym przypadku ilość ta jest prawie dwa razy większa niż ilość pamięci RAM, podczas gdy w drugim przypadku jest to około 1/4, co - silnie implikuje ilość wymiany. Zostało to potwierdzone przez wyłączenie swapu w pierwszym systemie, gdy próg awarii spadł do ~ 1,95 GB, co jest bardzo podobnym współczynnikiem do małego pudełka ARM.

Ale czy to naprawdę na proces? To wydaje się być. Krótki program poniżej prosi o zdefiniowaną przez użytkownika część pamięci, a jeśli się powiedzie, czeka na trafienie return - w ten sposób możesz wypróbować wiele instancji jednocześnie:

#include <stdio.h>
#include <stdlib.h>

#define MB 1 << 20

int main (int argc, const char *argv[]) {
    unsigned long int megabytes = strtoul(argv[1], NULL, 10);
    void *p = malloc(megabytes * MB);
    fprintf(stderr,"Allocating %lu MB...", megabytes);
    if (!p) fprintf(stderr,"fail.");
    else {
        fprintf(stderr,"success.");
        getchar();
        free(p);
    }
    return 0;
}

Uważaj jednak, że nie chodzi wyłącznie o ilość pamięci RAM i wymiany bez względu na użycie - uwagi na temat skutków stanu systemu znajdują się w przypisie 5.


3 CommitLimit odnosi się do ilości przestrzeni adresowej dozwolonej dla systemu, gdy vm.overcommit_memory = 2. Przypuszczalnie wtedy ilość, którą można przeznaczyć, powinna być równa minus to, co już zostało zatwierdzone, co jest najwyraźniej Committed_ASpolem.

Potencjalnie interesującym eksperymentem pokazującym to jest dodanie #include <unistd.h>na początku virtlimitcheck.c (patrz przypis 2) i fork()tuż przed while()pętlą. Nie gwarantuje się, że będzie działać zgodnie z opisem tutaj bez żmudnej synchronizacji, ale istnieje spora szansa, że ​​tak, YMMV:

> sysctl vm.overcommit_memory=2
vm.overcommit_memory = 2
> cat /proc/meminfo | grep Commit
CommitLimit:     9231660 kB
Committed_AS:    3141440 kB
> ./virtlimitcheck 2&> tmp.txt
> cat tmp.txt | grep Failed
Failed at 3051520 kB.
Failed at 6099968 kB.

Ma to sens - patrząc szczegółowo na tmp.txt możesz zobaczyć procesy naprzemiennie ich coraz większe alokacje (jest to łatwiejsze, jeśli wrzucisz pid do wyniku), dopóki jeden, najwyraźniej, nie stwierdzi wystarczająco, że drugi zawiedzie. Zwycięzca może następnie zgarnąć wszystko do CommitLimitminus Committed_AS.


4 Warto w tym miejscu wspomnieć, że jeśli jeszcze nie rozumiesz adresowania wirtualnego i stronicowania na żądanie, to, co sprawia, że ​​możliwe jest przejęcie zobowiązań, to przede wszystkim to, że to, co jądro alokuje do procesów użytkownika, wcale nie jest pamięcią fizyczną - wirtualna przestrzeń adresowa . Na przykład, jeśli proces rezerwuje na coś 10 MB, to jest to sekwencja adresów (wirtualnych), ale adresy te nie odpowiadają jeszcze pamięci fizycznej. Gdy taki adres jest dostępny, powoduje to błąd stronya następnie jądro próbuje zmapować je na prawdziwą pamięć, aby mogła przechowywać prawdziwą wartość. Procesy zwykle rezerwują znacznie więcej przestrzeni wirtualnej, niż faktycznie uzyskują dostęp, co pozwala jądru na najbardziej efektywne wykorzystanie pamięci RAM. Jednak pamięć fizyczna jest nadal zasobem skończonym, a gdy wszystko zostało zamapowane na wirtualną przestrzeń adresową, część wirtualnej przestrzeni adresowej należy wyeliminować, aby zwolnić trochę pamięci RAM.


5 Najpierw ostrzeżenie : jeśli spróbujesz tego vm.overcommit_memory=0, pamiętaj, aby najpierw zapisać swoją pracę i zamknąć wszystkie krytyczne aplikacje, ponieważ system zostanie zamrożony na około 90 sekund, a niektóre procesy umrą!

Chodzi o to, aby uruchomić bombę wideł, która wygaśnie po 90 sekundach, a widelce przydzielają miejsce, a niektóre z nich zapisują duże ilości danych do pamięci RAM, jednocześnie raportując do stderr.

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/time.h>
#include <errno.h>
#include <string.h>

/* 90 second "Verbose hungry fork bomb".
Verbose -> It jabbers.
Hungry -> It grabs address space, and it tries to eat memory.

BEWARE: ON A SYSTEM WITH 'vm.overcommit_memory=0', THIS WILL FREEZE EVERYTHING
FOR THE DURATION AND CAUSE THE OOM KILLER TO BE INVOKED.  CLOSE THINGS YOU CARE
ABOUT BEFORE RUNNING THIS. */

#define STEP 1 << 30 // 1 GB
#define DURATION 90

time_t now () {
    struct timeval t;
    if (gettimeofday(&t, NULL) == -1) {
        fprintf(stderr,"gettimeofday() fail: %s\n", strerror(errno));
        return 0;
    }
    return t.tv_sec;
}

int main (void) {
    int forks = 0;
    int i;
    unsigned char *p;
    pid_t pid, self;
    time_t check;
    const time_t start = now();
    if (!start) return 1;

    while (1) {
    // Get our pid and check the elapsed time.
        self = getpid();
        check = now();
        if (!check || check - start > DURATION) return 0;
        fprintf(stderr,"%d says %d forks\n", self, forks++);
    // Fork; the child should get its correct pid.
        pid = fork();
        if (!pid) self = getpid();
    // Allocate a big chunk of space.
        p = malloc(STEP);
        if (!p) {
            fprintf(stderr, "%d Allocation failed!\n", self);
            return 0;
        }
        fprintf(stderr,"%d Allocation succeeded.\n", self);
    // The child will attempt to use the allocated space.  Using only
    // the child allows the fork bomb to proceed properly.
        if (!pid) {
            for (i = 0; i < STEP; i++) p[i] = i % 256;
            fprintf(stderr,"%d WROTE 1 GB\n", self);
        }
    }
}                        

Skompiluj to gcc forkbomb.c -o forkbomb. Najpierw spróbuj z sysctl vm.overcommit_memory=2- prawdopodobnie dostaniesz coś takiego:

6520 says 0 forks
6520 Allocation succeeded.
6520 says 1 forks
6520 Allocation succeeded.
6520 says 2 forks
6521 Allocation succeeded.
6520 Allocation succeeded.
6520 says 3 forks
6520 Allocation failed!
6522 Allocation succeeded.

W tym środowisku taka widelecowa bomba nie dociera zbyt daleko. Zauważ, że liczba w „mówi N widelców” nie jest całkowitą liczbą procesów, jest to liczba procesów w łańcuchu / gałęzi prowadzących do tego.

Teraz spróbuj vm.overcommit_memory=0. Jeśli przekierujesz stderr do pliku, możesz później przeprowadzić zgrubną analizę, np .:

> cat tmp.txt | grep failed
4641 Allocation failed!
4646 Allocation failed!
4642 Allocation failed!
4647 Allocation failed!
4649 Allocation failed!
4644 Allocation failed!
4643 Allocation failed!
4648 Allocation failed!
4669 Allocation failed!
4696 Allocation failed!
4695 Allocation failed!
4716 Allocation failed!
4721 Allocation failed!

Tylko 15 procesom nie udało się przydzielić 1 GB - co pokazuje, że stan ma wpływ na heurystykę dla overcommit_memory = 0 . Ile tam było procesów? Patrząc na koniec tmp.txt, prawdopodobnie> 100 000. W jaki sposób można faktycznie korzystać z 1 GB?

> cat tmp.txt | grep WROTE
4646 WROTE 1 GB
4648 WROTE 1 GB
4671 WROTE 1 GB
4687 WROTE 1 GB
4694 WROTE 1 GB
4696 WROTE 1 GB
4716 WROTE 1 GB
4721 WROTE 1 GB

Osiem - co znowu ma sens, ponieważ miałem wtedy ~ 3 GB wolnej pamięci RAM i 6 GB wymiany.

Po wykonaniu tej czynności przejrzyj dzienniki systemu. Powinieneś zobaczyć wyniki raportowania zabójcy OOM (między innymi); przypuszczalnie dotyczy to oom_badness.

Złotowłosa
źródło
zamiana nie jest rozwiązaniem (ani nawet powiązanym) pamięci zamiast zaangażowania. Przydział pamięci (np .: malloc) polega na żądaniu rezerwacji pamięci wirtualnej, a nie fizycznej.
jlliagre
1
@jillagre: „zamiana nie jest rozwiązaniem (ani nawet powiązanym) pamięci nad zaangażowaniem” -> Tak, w rzeczywistości jest. Rzadko używane strony są zamieniane z pamięci RAM, pozostawiając więcej pamięci RAM dostępnej do radzenia sobie z błędami stron spowodowanymi stronicowaniem / alokacją popytu (co jest mechanizmem, który umożliwia przejęcie zobowiązań). Wymienione strony mogą w pewnym momencie wymagać powrotu z powrotem do pamięci RAM.
goldilocks 27.09.13
„Przydział pamięci (np .: malloc) dotyczy żądania rezerwacji pamięci wirtualnej, a nie fizycznej”. -> Racja, ale jądro może (i opcjonalnie powie) nie, gdy nie pozostaną żadne fizyczne mapowania . Z pewnością nie byłoby tak, ponieważ procesowi zabrakło wirtualnej przestrzeni adresowej (a przynajmniej nie zwykle, ponieważ jest to również możliwe, przynajmniej w systemach 32-bitowych).
goldilocks 27.09.13
Stronicowanie popytu nie jest tym, co umożliwia pamięć ponad zaangażowanie. Linux z pewnością przerasta pamięć w systemach bez obszaru wymiany. Być może mylisz pamięć ze stronicowaniem zobowiązań i popytu. Jeśli Linux powie „nie” mallocowi z procesem 64-bitowym, tj. Jeśli nie jest skonfigurowany tak, aby zawsze nadpisywać, byłoby to spowodowane uszkodzeniem pamięci lub sumą wszystkich rezerwacji pamięci (odwzorowanych lub nie na RAM lub dysk) przekracza próg progowy w zależności od konfiguracji. Nie ma to nic wspólnego z użyciem pamięci RAM, ponieważ może się to zdarzyć, nawet jeśli wciąż jest wolna pamięć RAM.
jlliagre
„Stronicowanie popytu nie jest tym, co umożliwia pamięć nad zobowiązaniami”. -> Być może lepiej byłoby powiedzieć, że jest to adresowanie wirtualne, które umożliwia zarówno stronicowanie popytu, jak i nadmierne zaangażowanie. „Linux z pewnością przerasta pamięć w systemach bez obszaru wymiany”. -> Oczywiście, ponieważ stronicowanie popytu nie wymaga wymiany; stronicowanie na żądanie z wymiany jest tylko specjalnym przykładem stronicowania na żądanie. Po raz kolejny, zamiana jest rozwiązaniem nadmiernego zaangażowania, nie w tym sensie, że rozwiązuje problem, ale w tym sensie, że pomoże zapobiec potencjalnym zdarzeniom związanym z nadmierną aktywnością.
goldilocks 27.09.13
16

To się nie stanie, jeśli kiedykolwiek załadujesz tylko 1G danych do pamięci. Co jeśli załadujesz znacznie więcej? Na przykład często pracuję z ogromnymi plikami zawierającymi miliony prawdopodobieństw, które należy załadować do R. To zajmuje około 16 GB pamięci RAM.

Uruchomienie powyższego procesu na moim laptopie spowoduje, że zacznie się zamieniać jak szalony, gdy tylko moje 8 GB pamięci RAM zostanie wypełnione. To z kolei spowolni wszystko, ponieważ odczyt z dysku jest znacznie wolniejszy niż odczyt z pamięci RAM. Co jeśli mam laptopa z 2 GB pamięci RAM i tylko 10 GB wolnego miejsca? Gdy proces zajmie całą pamięć RAM, zapełni również dysk, ponieważ pisze do zamiany, a ja nie mam już więcej pamięci RAM ani miejsca do zamiany (ludzie mają tendencję do ograniczania zamiany do dedykowanej partycji zamiast plik wymiany właśnie z tego powodu). Właśnie tam pojawia się zabójca OOM i rozpoczyna proces zabijania.

Tak więc w systemie może zabraknąć pamięci. Co więcej, silnie wymieniane systemy mogą stać się bezużyteczne na długo przed tym, po prostu z powodu powolnych operacji we / wy z powodu zamiany. Zasadniczo chce się unikać zamiany w jak największym stopniu. Nawet na wysokiej klasy serwerach z szybkimi dyskami SSD wyraźnie widać spadek wydajności. Na moim laptopie, który ma klasyczny dysk 7200 RPM, każda znacząca zamiana zasadniczo uniemożliwia korzystanie z systemu. Im więcej zamienia, tym wolniej się rozwija. Jeśli nie zabiję szybko obrażającego procesu, wszystko zawiesza się, dopóki nie wkroczy zabójca OOM.

terdon
źródło
5

Procesy nie są zabijane, gdy nie ma już pamięci RAM, są zabijane, gdy zostaną oszukane w ten sposób:

  • Jądro Linux zwykle pozwala procesom alokować (tj. Rezerwować) ilość pamięci wirtualnej, która jest większa niż w rzeczywistości (część pamięci RAM + cały obszar wymiany)
  • tak długo, jak procesy uzyskują dostęp tylko do podzbioru zarezerwowanych stron, wszystko działa poprawnie.
  • jeśli po pewnym czasie proces spróbuje uzyskać dostęp do strony, której jest właścicielem, ale nie ma już wolnych stron, nastąpi sytuacja braku pamięci
  • Zabójca OOM wybiera jeden z procesów, niekoniecznie ten, który zażądał nowej strony i po prostu zabija go, aby odzyskać pamięć wirtualną.

Może się to zdarzyć, nawet gdy system nie aktywnie zamienia, na przykład jeśli obszar wymiany jest wypełniony stronami pamięci demonów śpiących.

Tak się nigdy nie dzieje w systemach operacyjnych, które nie przepełniają pamięci. Dzięki nim żaden losowy proces nie jest zabijany, ale pierwszy proces proszący o pamięć wirtualną, gdy jest wyczerpana, ma błąd malloc (lub podobny). Dzięki temu ma szansę odpowiednio poradzić sobie z sytuacją. Jednak w tych systemach operacyjnych może się również zdarzyć, że w systemie zabraknie pamięci wirtualnej, podczas gdy wciąż jest wolna pamięć RAM, co jest dość mylące i ogólnie źle zrozumiane.

jlliagre
źródło
3

Gdy dostępna pamięć RAM zostanie wyczerpana, jądro zaczyna wymieniać fragmenty przetwarzania na dysk. W rzeczywistości jądro zaczyna zamieniać, gdy pamięć RAM jest prawie wyczerpana: zaczyna proaktywnie zamieniać, gdy ma wolny czas, aby być bardziej responsywnym, jeśli aplikacja nagle potrzebuje więcej pamięci.

Pamiętaj, że pamięć RAM nie jest używana tylko do przechowywania pamięci procesów. W typowym zdrowym systemie tylko około połowa pamięci RAM jest wykorzystywana przez procesy, a druga połowa jest wykorzystywana do buforowania dysku i buforów. Zapewnia to dobrą równowagę między uruchomionymi procesami a wejściami / wyjściami plików.

Przestrzeń wymiany nie jest nieskończona. W pewnym momencie, jeśli procesy będą nadal alokować coraz więcej pamięci, dane przelewu z pamięci RAM wypełnią swap. Kiedy tak się dzieje, procesy, które próbują zażądać więcej pamięci, odrzucają swoje żądania.

Domyślnie Linux przeciąża pamięć. Oznacza to, że czasami pozwala na uruchomienie procesu z pamięcią, która została zarezerwowana, ale nie została użyta. Głównym powodem nadmiernego zaangażowania jest sposób rozwidlania się . Kiedy proces uruchamia podproces, proces potomny funkcjonuje koncepcyjnie w replice pamięci rodzica - dwa procesy początkowo mają pamięć o tej samej treści, ale treść ta będzie się rozchodzić, gdy procesy wprowadzają zmiany w swoich własnych przestrzeniach. Aby w pełni to zaimplementować, jądro musiałoby skopiować całą pamięć rodzica. Spowodowałoby to spowolnienie rozwidlania, więc jądro ćwiczy kopiowanie przy zapisie: początkowo dziecko dzieli całą swoją pamięć z rodzicem; za każdym razem, gdy którykolwiek proces zapisuje na udostępnionej stronie, jądro tworzy kopię tej strony, aby przerwać udostępnianie.

Często dziecko pozostawia wiele stron nietkniętych. Jeśli jądro przydzieli wystarczającą ilość pamięci, aby zreplikować przestrzeń pamięci rodzica w każdym rozwidleniu, dużo pamięci zostanie zmarnowane na rezerwacje, których procesy potomne nigdy nie będą wykorzystywać. Stąd nadmierne zaangażowanie: jądro rezerwuje tylko część tej pamięci, na podstawie oszacowania, ile stron będzie potrzebne dziecko.

Jeśli proces próbuje przydzielić część pamięci i nie ma wystarczającej ilości pamięci, proces odbiera odpowiedź na błąd i radzi sobie z tym, co uzna za stosowne. Jeśli proces pośrednio żąda pamięci, pisząc na udostępnionej stronie, której należy udostępnić, to inna historia. Nie ma możliwości zgłoszenia tej sytuacji do aplikacji: uważa, że ​​ma tam zapisywalne dane, a nawet może je odczytać - po prostu pisanie wymaga nieco bardziej skomplikowanej operacji pod maską. Jeśli jądro nie jest w stanie zapewnić nowej strony pamięci, wszystko, co może zrobić, to zabić proces żądania lub inny proces, aby wypełnić pamięć.

W tym momencie możesz pomyśleć, że zabicie żądającego procesu jest oczywistym rozwiązaniem. Ale w praktyce nie jest tak dobrze. Proces ten może być ważnym, który akurat potrzebuje dostępu teraz tylko do jednej ze swoich stron, podczas gdy mogą być uruchomione inne, mniej ważne procesy. Jądro zawiera złożoną heurystykę, aby wybrać, które procesy zabić - (nie) znanego zabójcę OOM .

Gilles „SO- przestań być zły”
źródło
2

Aby dodać inny punkt widzenia z innych odpowiedzi, wielu VPS hostuje kilka maszyn wirtualnych na danym serwerze. Każda pojedyncza maszyna wirtualna będzie miała określoną ilość pamięci RAM na własny użytek. Wielu dostawców oferuje „seryjną pamięć RAM”, w której mogą korzystać z pamięci RAM przekraczającej wyznaczoną liczbę. Ma to być przeznaczone tylko do krótkotrwałego użytku, a ci, którzy przekroczą ten wydłużony czas, mogą zostać ukarani przez hosta zabijającego procesy w celu zmniejszenia ilości używanej pamięci RAM, aby inni nie cierpieli z powodu przeciążona maszyna hosta.

agweber
źródło
-1

Czas linux zajmuje zewnętrzną przestrzeń wirtualną. To jest partycja wymiany. Kiedy Ram jest wypełniony, linux zajmuje ten obszar wymiany, aby uruchomić proces o niskim priorytecie.

Bizzon
źródło
1
Żaden proces nie jest uruchamiany z wymiany. Pamięć wirtualna jest podzielona na równe wielkości odrębne jednostki zwane stronami. Po zwolnieniu pamięci fizycznej strony o niskim priorytecie są usuwane z pamięci RAM. Podczas gdy strony w pamięci podręcznej plików mają kopię zapasową systemu plików, anonimowe strony muszą być przechowywane w trybie wymiany. Priorytet strony nie jest bezpośrednio związany z priorytetem procesu, do którego należy, ale z tym, jak często jest używana. Jeśli uruchomiony proces próbuje uzyskać dostęp do strony spoza pamięci fizycznej, generowany jest błąd strony i proces jest wstrzymywany na korzyść innego procesu, podczas gdy potrzebne strony są pobierane z dysku.
Thomas Nyman