Dlaczego mój system Linux zacina się, dopóki nie upuszczę pamięci podręcznej?

3

W ciągu ostatnich kilku miesięcy miałem bardzo irytujący problem z moim systemem Linux: zacina się przy odtwarzaniu dźwięku Firefoksa, ruchu myszy itp., Z malejącym sub-sekundowym (ale wciąż zauważalnym) zacinaniem co kilka sekund. Problem pogarsza się, gdy pamięć podręczna jest zapełniana lub gdy uruchomione są programy intensywnie wykorzystujące pamięć dyskową / pamięć (np. Oprogramowanie do tworzenia kopii zapasowych) restic ). Jednak gdy pamięć podręczna nie jest pełna (np. Przy bardzo małym obciążeniu), wszystko działa bardzo płynnie.

Patrząc przez perf top wyjście, widzę to list_lru_count_one ma wysokie koszty ogólne (~ 20%) w tych okresach opóźnienia. htop także pokazuje kswapd0 przy użyciu 50-90% procesora (choć wydaje się, że wpływ jest znacznie większy). W czasach ekstremalnego opóźnienia htop Miernik procesora jest często zdominowany przez użycie procesora jądra.

Jedyne obejście, które znalazłem, to wymuszenie na jądrze zachowania wolnej pamięci ( sysctl -w vm.min_free_kbytes=1024000 ) lub do ciągłego upuszczania pamięci podręcznych echo 3 > /proc/sys/vm/drop_caches. Żadne z nich nie jest oczywiście idealne, ani też nie rozwiązuje go całkowicie; to tylko sprawia, że ​​jest rzadsze.

Czy ktoś ma jakieś pomysły na to, dlaczego tak się dzieje?

Informacja o systemie

  • i7-4820k z 20 GB (niedopasowanej) pamięci RAM DDR3
  • Reprodukowany na Linuksie 4.14-4.18 na NixOS niestabilny
  • Uruchamia kontenery Docker i Kubernetes w tle (co, jak sądzę, nie powinno tworzyć mikrosterowania?)

Co już próbowałem

  • Zmiana harmonogramów I / O (bfq) za pomocą wielozakresowych harmonogramów I / O
  • Używając -ck patchset Con Kolivasa (nie pomógł)
  • Wyłączanie wymiany, zmiana swapowania, używając zram

EDYTOWAĆ : Dla jasności, oto zdjęcie htop i perf podczas takiego opóźnienia. Zwróć uwagę na wysokość list_lru_count_one Obciążenie procesora i kswapd0 + wysokie użycie procesora jądra.

htop and perf output

Pneumaticat
źródło
Dostrój cpu / io za pomocą cgroup. Daj więcej priorytetów zadaniom interaktywnym.
Ipor Sircer
Tak samo dzieje się w moim systemie (bez kontenerów). Podejrzewam, że jakiś błąd blokuje pamięć podręczną, ale nie wiem. Byłbym bardzo zainteresowany odpowiedzią. Próbowałem również zmienić harmonogramy I / O, nie pomogłem. System zatrzymuje się, nawet gdy jest bardzo lekko załadowany, więc priorytet dla zadań interaktywnych na pewno nie pomoże.
dirkt
Czy to samo dzieje się, gdy używasz tylko jednego z kijków barana? Wspominasz, że dzieje się to nawet przy lekkim obciążeniu, więc jest to testowalne. Może istnieć subtelny czas / problem elektryczny z innej pamięci RAM
Christopher Hostage
@ChristopherHostage, którego właśnie wypróbowałem z zaledwie 2/4 pamięci RAM, i nadal wykazywał jąkanie.
Pneumaticat
@IporSircer Nie sądzę, żeby to była kwestia priorytetyzacji procesora; jąkania pokrywają się z wysokim jądro Użycie procesora, a nie korzystanie z innych programów. Popraw mnie, jeśli się mylę.
Pneumaticat

Odpowiedzi:

2

Wygląda na to, że wypróbowałeś już wiele rzeczy, które zasugerowałbym na początku (poprawianie konfiguracji wymiany, zmiana harmonogramów I / O itp.).

Oprócz tego, co już próbowałeś zmienić, zasugerowałbym zmianę nieco domyślnych ustawień domyślnych dla zachowania zapisu wstecznego maszyny wirtualnej. Jest to zarządzane przez sześć następujących wartości sysctl:

  • vm.dirty_ratio: Kontroluje, ile zapisów musi być oczekujących na zapis przed wysłaniem. Obsługuje zapisywanie zwrotne na pierwszym planie (na proces) i jest wyrażone jako całkowity procent pamięci RAM. Domyślnie 10% pamięci RAM
  • vm.dirty_background_ratio: Kontroluje, ile zapisów musi być oczekujących na zapis przed wysłaniem. Obsługuje zapisywanie zwrotne w tle (w całym systemie) i jest wyrażone jako całkowita liczba RAM. Domyślnie 20% pamięci RAM
  • vm.dirty_bytes: Taki sam jak vm.dirty_ratio, z wyjątkiem wyrażonej jako całkowita liczba bajtów. Albo to lub vm.dirty_ratio zostanie użyty, w zależności od tego, co zostało zapisane jako ostatnie.
  • vm.dirty_background_bytes: Taki sam jak vm.dirty_background_ratio, z wyjątkiem wyrażonej jako całkowita liczba bajtów. Albo to lub vm.dirty_background_ratio zostanie użyty, w zależności od tego, co zostało zapisane jako ostatnie.
  • vm.dirty_expire_centisecs: Ile setnych części sekundy musi upłynąć, zanim rozpocznie się oczekujący zapis zwrotny, gdy powyższe cztery wartości sysctl nie uruchomiłyby go. Domyślnie 100 (jedna sekunda).
  • vm.dirty_writeback_centisecs: Jak często (w setnych częściach sekundy) jądro ocenia brudne strony pod kątem zapisu wstecznego. Domyślnie 10 (jedna dziesiąta sekundy).

Zatem przy wartościach domyślnych, co dziesiąte sekundy, jądro wykona następujące czynności:

  • Zapisz zmodyfikowane strony w trwałym magazynie, jeśli zostały one ostatnio zmodyfikowane ponad sekundę temu.
  • Zapisz wszystkie zmodyfikowane strony dla procesu, jeśli całkowita ilość zmodyfikowanej pamięci, która nie została zapisana, przekracza 10% pamięci RAM.
  • Zapisz wszystkie zmodyfikowane strony w systemie, jeśli całkowita ilość zmodyfikowanej pamięci, która nie została zapisana, przekracza 20% pamięci RAM.

Tak więc powinno być całkiem łatwo zobaczyć, dlaczego wartości domyślne mogą powodować problemy, ponieważ system może próbować zapisać do 4 gigabajty danych do pamięci trwałej co dziesiąty sekundy.

Ogólnym konsensu w tych dniach jest dostosowanie vm.dirty_ratio 1% pamięci RAM i vm.dirty_background_ratio 2%, co w przypadku systemów z mniej niż 64 GB pamięci RAM powoduje zachowanie równoważne z pierwotnie zamierzonym.

Kilka innych rzeczy, na które należy zwrócić uwagę:

  • Spróbuj zwiększyć vm.vfs_cache_pressure trochę sysctl. Kontroluje to jak agresywnie jądro odzyskuje pamięć z pamięci podręcznej systemu plików, gdy potrzebuje pamięci RAM. Domyślna wartość to 100, nie obniżaj jej do poniżej 50 (ty będzie uzyskasz naprawdę złe zachowanie, jeśli przejdziesz poniżej 50, w tym warunki OOM), i nie podnosisz tego do znacznie więcej niż około 200 (dużo wyżej, a jądro będzie marnować czas na próby odzyskania pamięci, której naprawdę nie może). Odkryłem, że uderzanie go do 150 w rzeczywistości wyraźnie poprawia szybkość reakcji, jeśli masz dość szybkie przechowywanie.
  • Spróbuj zmienić tryb nadpisywania pamięci. Można to zrobić, zmieniając wartość vm.overcommit_memory sysctl. Domyślnie, jądro użyje heurystycznego podejścia, aby spróbować przewidzieć, ile pamięci RAM może faktycznie przeznaczyć na zatwierdzenie. Ustawienie tego na 1 wyłącza heurystykę i mówi jądru, aby działało tak, jakby miało nieskończoną pamięć. Ustawienie tego na 2 powoduje, że jądro nie zobowiązuje się do zapisywania większej ilości pamięci niż całkowita ilość miejsca wymiany w systemie plus procent rzeczywistej pamięci RAM (kontrolowanej przez vm.overcommit_ratio ).
  • Spróbuj ulepszyć vm.page-cluster sysctl. Kontroluje to, ile stron jest wymienianych lub wymienianych jednocześnie (jest to logarytmiczna wartość base-2, więc domyślnie 3 tłumaczy na 8 stron). Jeśli faktycznie wymieniasz, może to poprawić wydajność wymiany stron.
Austin Hemmelgarn
źródło
Dziękujemy za szczegółowe sugestie! Niestety, żaden z nich nie działa dla mnie. Próbowałem zmienić współczynnik brudny, nacisk pamięci podręcznej vfs i klaster stron bez skutku. vm.overcommit_memory = 1 nie zmieniło się, a = 2 zasadniczo spowodowało awarię mojego systemu, więc to też nie ma sensu.
Pneumaticat
Czy masz włączoną funkcję KSM lub THP? Jeśli tak, sugerowałbym ich wyłączenie i zobaczenie, czy to w ogóle pomaga.
Austin Hemmelgarn
Nie, żadna z nich nie jest włączona. THP jest faktycznie wyraźnie wyłączony transparent_hugepage=never, ale próbowałem go ponownie włączyć i nie miało to znaczenia.
Pneumaticat
1

Problem został znaleziony!

Okazuje się, że jest to problem wydajnościowy w odzyskiwaniu pamięci w Linuksie, gdy istnieje duża liczba kontenerów / grup pamięci. (Zastrzeżenie: moje wyjaśnienie może być błędne, nie jestem deweloperem jądra.) Problem został rozwiązany w 4.19-rc1 + w ten zestaw poprawek :

Ten zestaw patcha rozwiązuje problem przy powolnym występowaniu shrink_slab ()   maszyny mające wiele obkurczaczy i grup pamięci (tj. z wieloma   pojemniki). Problemem jest złożoność shrink_slab () to O (n ^ 2) i   rośnie zbyt szybko wraz ze wzrostem liczby pojemników.

Miejmy 200 kontenerów, a każdy kontener ma 10 mocowań i 10   cgroups. Wszystkie zadania kontenerowe są izolowane i nie dotykają obcych   uchwyty do kontenerów.

W przypadku odzyskania globalnego zadania należy wykonać iterację po wszystkich memcgs i   zadzwonić do wszystkich skurczarek memcg świadomych dla wszystkich. To znaczy   zadanie musi odwiedzić 200 * 10 = 2000 skurczów dla każdego memcg i od tego czasu   jest 2000 memcgów, całkowita liczba wywołań do_shrink_slab () wynosi 2000 *   2000 = 4000000.

Mój system został szczególnie mocno uderzony, ponieważ uruchomiłem dużą liczbę kontenerów, co prawdopodobnie powodowało pojawienie się problemu.

Moje kroki związane z rozwiązywaniem problemów, na wypadek gdyby były pomocne dla każdego, kto ma podobne problemy:

  1. Ogłoszenie kswapd0 zużywa tonę procesora, gdy mój komputer się zacina
  2. Spróbuj zatrzymać pojemniki Docker i ponownie zapełnić pamięć → komputer się nie jąka!
  3. Biegać ftrace (następujący Wspaniały blog wyjaśniający Julii Evan ) aby uzyskać ślad, zobacz to kswapd0 ma tendencję do utknięcia shrink_slab, super_cache_count, i list_lru_count_one.
  4. Google shrink_slab lru slow, znajdź patchset!
  5. Przełącz się na Linux 4.19-rc3 i sprawdź, czy problem został rozwiązany.
Pneumaticat
źródło