Czy tak powinien wyglądać stronicowanie Linuksa?


26

Kiedy mój system Linux zbliża się do stronicowania (tj. W moim przypadku 16 GB pamięci RAM jest prawie pełny, 16 GB zamiany jest całkowicie pusty), jeśli nowy proces X próbuje przydzielić część pamięci, system całkowicie się blokuje. To znaczy, dopóki nieproporcjonalnie duża liczba stron (wrt całkowity rozmiar i szybkość żądań alokacji pamięci X) zostały zamienione. Zauważ, że nie tylko gui całkowicie przestaje odpowiadać, ale nawet podstawowe usługi, takie jak sshd, są całkowicie zablokowane.

Są to dwa fragmenty kodu (co prawda surowe), których używam do wywołania tego zachowania w bardziej „naukowy” sposób. Pierwsza pobiera dwie liczby x, y z wiersza poleceń i przechodzi do przydzielania i inicjowania wielu porcji y bajtów, dopóki nie zostanie przydzielonych więcej niż x bajtów ogółem. A potem po prostu śpi w nieskończoność. Będzie to wykorzystane do doprowadzenia systemu na skraj stronicowania.

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

int main(int argc, char** argv) {
   long int max = -1;
   int mb = 0;
   long int size = 0;
   long int total = 0;
   char* buffer;

   if(argc > 1)
     {
       max = atol(argv[1]);
       size = atol(argv[2]);
     }
   printf("Max: %lu bytes\n", max);
   while((buffer=malloc(size)) != NULL && total < max) {
       memset(buffer, 0, size);
       mb++;
       total=mb*size;
       printf("Allocated %lu bytes\n", total);       
   }      
   sleep(3000000);
   return 0;
}

Drugi fragment kodu robi dokładnie to samo, co pierwszy, z wyjątkiem tego, że ma sleep(1);zaraz po printf(nie powtórzę całego kodu). Ten będzie używany, gdy system jest na krawędzi stronicowania, aby wymieniać strony w „łagodny” sposób, tj. Powoli żądając przydziału nowych fragmentów pamięci (tak, aby system z pewnością był w stanie wymieniać strony i nadążaj za nowymi żądaniami).

Tak więc, po skompilowaniu dwóch fragmentów kodu, nazwijmy odpowiedni exes fasteater i powolny, zróbmy to:

1) uruchom swoje ulubione GUI (oczywiście nie jest to absolutnie konieczne)

2) uruchom miernik mem / swap (np. watch -n 1 free)

3) uruchom wiele instancji, fasteater x ygdzie x jest rzędu gigabajtów, a y jest rzędu megabajtów. Rób to, aż prawie wypełnisz barana.

4) uruchom jedno wystąpienie sloweater x y, gdzie x jest rzędu gigabajtów, a y jest rzędu megabajtów.

Po kroku 4), co powinno się zdarzyć (i zawsze tak się dzieje w moim systemie) jest to, że zaraz po wyczerpaniu pamięci RAM, system zablokuje się całkowicie. GUI jest zablokowane sshd jest zablokowane itp. ALE, nie na zawsze! Po zakończeniu żądania przydzielenia przez system Sloweater system powróci do życia (po minutach blokowania, a nie sekundach ...) w tej sytuacji:

a) baran jest prawie pełny

b) zamiana jest również pełna (pamiętaj, że na początku była pusta)

c) brak interwencji zabójcy.

I zauważ, że partycja wymiany znajduje się na dysku SSD. Wygląda więc na to, że system nie jest w stanie stopniowo przenosić stron z pamięci ram do swap (prawdopodobnie z fasteaterów, które właśnie śpią), aby zrobić miejsce dla powolnych (i zaledwie kilku megabajtów) żądań wolnej wody.

Teraz ktoś mnie poprawi, jeśli się mylę, ale nie wydaje się, że tak powinien wyglądać nowoczesny system w tym otoczeniu. Wygląda na to, że zachowuje się jak stare systemy (z powrotem waaaaay), kiedy brak było stronicowania, a system pamięci wirtualnej po prostu zamienił całą przestrzeń pamięci jakiegoś procesu zamiast kilku stron.

Czy ktoś może to również przetestować? A może ktoś, kto ma również system BSD.

AKTUALIZACJA 1 Postępowałem zgodnie z radą Marka Plotnicka w komentarzach i zacząłem vmstat 1 >outprzed przystąpieniem do testu stronicowania. Możesz zobaczyć wynik poniżej (przecinam całą początkową część, w której RAM jest wypełniony bez udziału zamiany):

procs -----------memory---------- ---swap-- -----io---- -system-- ------cpu-----
r  b   swpd   free   buff  cache   si   so    bi    bo   in   cs us sy id wa st
0  0   6144 160792      8 272868    0    0     0     0  281 1839  1  0 99  0  0
0  0   6144 177844      8 246096    0    0     0     0  425 2300  1  1 99  0  0
0  0   6144 168528      8 246112    0    0    16     0  293 1939  1  0 99  0  0
0  0   6144 158320      8 246116    0    0     0     0  261 1245  0  0 100  0  0
2  0  10752 161624      8 229024    0 4820 17148  4820  845 3656  1  2 97  0  0
2  0  10752 157300      8 228096    0    0 88348     0 2114 8902  0  5 94  1  0
0  0  10752 176108      8 200052    0    0 108312     0 2466 9772  1  5 91  3  0
0  0  10752 170040      8 196780    0    0 17380     0  507 1895  0  1 99  0  0
0 10  10752 160436      8 191244    0    0 346872    20 4184 17274  1  9 64 26  0
0 29 12033856 152888      8 116696 5992 15916880 1074132 15925816 819374 2473643  0 94  0  6  0
3 21 12031552 295644      8 136536 1188    0 11348     0 1362 3913  0  1 10 89  0
0 11 12030528 394072      8 151000 2016    0 17304     0  907 2867  0  1 13 86  0
0 11 12030016 485252      8 158528  708    0  7472     0  566 1680  0  1 23 77  0
0 11 12029248 605820      8 159608  900    0  2024     0  371 1289  0  0 31 69  0
0 11 12028992 725344      8 160472 1076    0  1204     0  387 1381  0  1 33 66  0
0 12 12028480 842276      8 162056  724    0  3112     0  357 1142  0  1 38 61  0
0 13 12027968 937828      8 162652  776    0  1312     0  363 1191  0  1 31 68  0
0  9 12027456 1085672      8 163260  656    0  1520     0  439 1497  0  0 30 69  0
0 10 12027200 1207624      8 163684  728    0   992     0  411 1268  0  0 42 58  0
0  9 12026688 1331492      8 164740  600    0  1732     0  392 1203  0  0 36 64  0
0  9 12026432 1458312      8 166020  628    0  1644     0  366 1176  0  0 33 66  0

Jak widać, jak tylko zaangażowana jest zamiana, następuje masowa zamiana 15916880 kilobajtów naraz, która, jak sądzę, trwa przez cały czas zawieszenia systemu. Wszystko to jest najwyraźniej spowodowane procesem (wolniejszym), który prosi o 10 MB co sekundę.

AKTUALIZACJA 2: Zrobiłem szybką instalację FreeBSD i powtórzyłem ten sam schemat alokacji jak w Linuksie ... i był tak płynny, jak powinien. FreeBSD zamieniał strony stopniowo, podczas gdy wolniejszy komputer przydzielał wszystkie swoje 10 MB fragmentów pamięci. Ani jednego zaczepu ... WTF tu się dzieje ?!

AKTUALIZACJA 3: Złożyłem błąd w module śledzenia błędów jądra. Wygląda na to, że przyciąga uwagę, więc ... kciuki ...


2
Jak wspomniałem, wszystko jest zamknięte. Próbowałem ssh'ing z innego systemu, który właśnie przekroczył limit czasu.
John Terragon,

2
Jeśli uruchomię vmstat 1 ze standardowym wyjściem, myślę, że się zawiesi. Ale masz rację, mógłbym po prostu uruchomić vmstat 1>somefilebezpośrednio z systemu, a następnie zobaczyć, co zgłasza po przywróceniu systemu. Spróbuję tego.
John Terragon

2
Użyłem vmstat. Wyniki w powyższej aktualizacji.
John Terragon

3
swappinessjest domyślną wartością 60 (nie to, że jej zmiana daje lepszy wynik). Jądro używane z vmstaturuchomieniem to 4.14.35, ale próbowałem 4.15, 4.16 i nawet wróciłem do serii 4.0 (!): Zawsze takie samo zachowanie. I to nie jest tak, że używam dziwnej dystrybucji, to tylko debian. Nie używam obrazów jądra z Debiana (nie że moje mają nietypowe konfiguracje), ale próbowałem jednego z tych ... takich samych zachowań.
John Terragon

2
Bardzo interesująca dyskusja na temat błędu jądra! I wygląda na to, że odizolowałeś ten problem, aby zamienić partycję zaszyfrowaną za pomocą LUKS. Możesz zredagować swoją odpowiedź lub ewentualnie opublikować odpowiedź samodzielnie (ze znanymi dotychczas obejściami i być może aktualizować ją, gdy dyskusja na temat LKML osiągnie bardziej rozstrzygające wyniki). Naprawdę imponujące, aby zobaczyć działającą społeczność jądra Linuksa! 😁
filbranden

Odpowiedzi:


1

Właśnie po to istnieje ochrona przed thrashem.

Ciągle monitoruje stan zamiany, a gdy coś przypadkowo zaczyna zajmować dużo pamięci RAM, tymczasowo zawiesza procesy zachłanne na pamięć RAM, więc jądro ma czas na zamianę pamięci, nie powodując braku reakcji całego systemu.


-3

Przydzielasz tylko pamięć - tak naprawdę nic w niej nie wkładasz. „Normalny” program przydzieli fragment, a następnie zacznie go używać. Przydział nie jest tym samym, co użycie pamięci.


3
Witamy w wysyłaniu postów na Unix StackExchange. Umieszcza w nim dane, które akurat są zerowe. Zobacz memset (). Jądro Linux zapewnia fizyczną stronę pamięci RAM, gdy tylko napiszesz na stronie wirtualnej; nie patrzy na konkretną zapisaną wartość.
sourcejedi

Właściwie skompilowałem i uruchomiłem to na pulpicie, zaczynając od 2 GB używanego, 6 GB wolnego. Początkowo zamieniał się w wolnym tempie i tylko kiedy osiągnął limit, zamienił się agresywnie - co spowodowało, że różne działania GUI zaczęły działać.
Jeremy Boden,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.