Demon zarządzania pamięcią

Android 17 i nowszy obsługuje demona zarządzania pamięcią (mmd), czyli demona systemowego, który obsługuje konfigurację demona, parametry i bieżące zadania związane z wymianą lub ZRAM.

Tło

Przed wprowadzeniem mmd konfiguracje ZRAM na Androidzie były rozproszone i oferowały ograniczone możliwości dostosowywania. mmd rozwiązuje ten problem, centralizując zarządzanie ZRAM, umożliwiając bardziej zaawansowaną logikę konfiguracji i upraszczając dodawanie nowych funkcji oraz ulepszeń architektury. mmd zapewnia też wyraźne rozdzielenie zadań między procesem opartym na Javie a system_serverzarządzaniem pamięcią lub wymianą na poziomie jądra.

Architektura i zarządzanie ZRAM

Po zakończeniu rozruchu (czyli gdy sys.boot_completed=1) mmd_setup próbuje skonfigurować ZRAM z określonymi parametrami. Po zakończeniu konfiguracji ZRAM system włącza usługę mmd, która obsługuje bieżące zadania konserwacyjne.

W projekcie mmd operacje konserwacyjne są inicjowane z system_server przez wysyłanie żądań Binder do mmd za pomocą interfejsu IMmd. mmd wykonuje zadania związane z konserwacją, takie jak zapisywanie zwrotne ZRAM, ponowna kompresja i zapisywanie zwrotne dla poszczególnych procesów, na podstawie własnego wewnętrznego mechanizmu zasad. Zarówno harmonogram z ActivityManagerService, jak i zasady konserwacji ZRAM można skonfigurować za pomocą właściwości systemu.

Integracja serwera systemu (system_server)

Proces system_server oparty na Javie określa, kiedy jest wywoływany proces mmd. Proces ten oddziela globalne czyszczenie pamięci od ukierunkowanych optymalizacji pamięci w poszczególnych aplikacjach.

Normalna konserwacja po przetworzeniu

Globalną konserwacją ZRAM zarządza usługa ActivityManagerService za pomocą funkcji com.android.server.memory.ZramMaintenance.

zram-maintenance

Rysunek 1. Przepływ planowania konserwacji ZRAM.

  • Silnik planowania: ZramMaintenance rejestruje okresowe zadanie w tle w usłudze JobScheduler Androida.
  • Ograniczenia zadania: aby zapobiec przeskokom interfejsu użytkownika na pierwszym planie lub rywalizacji o procesor, zadanie jest wyraźnie skonfigurowane za pomocą setRequiresDeviceIdle(true)setRequiresBatteryNotLow(true).
  • Uruchamianie przez Binder: gdy algorytm szeregowania uruchamia onStartJob(), system_server, wywołuje mmd.doZramMaintenanceAsync(). Jest to jednokierunkowe wywołanie asynchroniczne interfejsu Binder. Funkcja system_server nie blokuje oczekiwania na zakończenie skanowania konserwacyjnego. mmd umieszcza to w kolejce wątku roboczego w tle, aby wykonać ponowną kompresję i zapisanie zwrotne w kolejności.

Zapisywanie zwrotne na poziomie procesu

Ukierunkowane usuwanie pamięci z poszczególnych procesów jest zarządzane przez usługę ActivityManagerService za pomocą parametru com.android.server.am.CachedAppOptimizer.

mmd-writeback

Rysunek 2. Przepływ zapisu zwrotnego mmd w przypadku poszczególnych procesów.

Gdy proces przechodzi w stan buforowania w tle, ActivityManager przeprowadza kompresję pamięci. Jeśli błąd LMK (low memory kill) procesu byłby widoczny dla użytkownika, tzn. proces hostuje aktywność, a zapisywanie zwrotne ZRAM na proces spowodowałoby zmniejszenie wykorzystania pamięci procesu do niemal zera, system wykonuje te czynności:

  1. Po kompresji CachedAppOptimizer wysyła opóźnioną wiadomość (ZRAM_WRITEBACK_MSG) do wewnętrznego modułu obsługi kompresji (opóźnioną o mZramWritebackWaitSeconds).
  2. Po upływie czasu opóźnienia ActivityManager otwiera bezpieczny deskryptor pliku procesu pidfd.
  3. Serwer systemowy wywołuje mmd.asyncWritebackProcessZramMemory(pfd, callback).
  4. mmd wykonuje ioctl zapisu zwrotnego dla poszczególnych procesów i przesyła raport za pomocą IMmdProcessWritebackCallback. Jeśli się to uda, ActivityManager oznaczy rekord procesu (setIsZramWrittenBack(app, true)), aby zwiększyć jego oom_score_adj, i zarejestruje dane w FrameworkStatsLog.ZRAM_WRITEBACK_EVENT.

Pobieranie z wyprzedzeniem dla każdego procesu

Gdy użytkownik ponownie uruchomi wcześniej buforowaną aplikację (odmrożoną z powodu UNFREEZE_REASON_ACTIVITY), ActivityManager minimalizuje opóźnienie uruchamiania aplikacji spowodowane przez poważne błędy strony z pamięci zapasowej:

  1. CachedAppOptimizer przechwytuje zdarzenie odblokowania i wywołuje prefetchZram(app).
  2. Serwer systemowy wysyła pidfd aplikacji za pomocą interfejsu Binder, używając mmd.asyncPrefetchProcessZramMemory(pfd). mmd wysyła ioctl, instruując jądro, aby asynchronicznie pobierało wstępnie zamienione strony z powrotem do pamięci RAM, podczas gdy główny wątek interfejsu aplikacji jest inicjowany.ZRAM_ANDROID_IOC_PROCESS_PREFETCH

Omówienie zadań konserwacyjnych i zadań przetwarzania końcowego

W tej sekcji opisujemy operacje konserwacyjne w tle i zadania przetwarzania końcowego, które mmd wykonuje w celu optymalizacji przestrzeni wymiany i pamięci systemowej.

Konserwacja w mmd

mmd konserwacja oznacza zaplanowane, działające w tle procesy konserwacyjne, które optymalizują wykorzystanie przestrzeni wymiany i pamięci fizycznej bez wpływu na wydajność aktywnych użytkowników. Zamiast wykonywać ciągłe, synchroniczne skanowanie (które powodowałoby częste wybudzanie procesora i zacinanie się interfejsu), konserwacja jest przeprowadzana asynchronicznie:

  1. system_server okresowo wysyła doZramMaintenanceAsync() przez Binder.

  2. mmd umieszcza żądanie w kolejce zadań w tleLowPrioWorkItem::ZramMaintenance.

  3. mmd jest jeden wątek roboczy, który zarządza kolejką o wysokim priorytecie i kolejką o niskim priorytecie. Elementy robocze o wysokim priorytecie (np. wstępne pobieranie danych dla poszczególnych procesów) są przetwarzane w pierwszej kolejności i mogą przerywać przetwarzanie elementów roboczych o niskim priorytecie. Konserwacja i zapisywanie zwrotne w przypadku poszczególnych procesów działają jako zadania o niskim priorytecie. Po wywołaniu wątek instancji roboczej wykonuje kolejno 2 podstawowe operacje konserwacyjne:

    • Ponowna kompresja ZRAM: skanuje istniejące strony wymiany i ponownie kompresuje nieużywane strony za pomocą algorytmu kompresji wtórnej o wyższym współczynniku, np. zstd.

    • Zapisywanie do ZRAM: skanuje nieużywane strony i całkowicie usuwa je z pamięci RAM do pamięci flash, która jest urządzeniem pętli z pliku na /data.

Zadania przetwarzania końcowego w pamięci ZRAM

W module ZRAM jądra Linuksa i architekturze mmd zadania przetwarzania końcowego to asynchroniczne przekształcenia stosowane do stron pamięci po ich zamianie przez standardowe ścieżki odzyskiwania jądra (kswapd lub kompresja).

Gdy strona jest początkowo zamieniana, system priorytetowo traktuje szybkość: używa szybkiego podstawowego algorytmu kompresji (np. lz4) i przechowuje skompresowaną stronę w pamięci RAM. Z czasem jednak wiele stron zamienionych na pamięć podręczną staje się nieaktywnych lub bezczynnych, np. aplikacje w pamięci podręcznej w tle, które nie są wznawiane przez wiele godzin. Pozostawianie nieużywanych stron w szybkiej, lekko skompresowanej pamięci ZRAM jest nieefektywne.

Potok przetwarzania końcowego

mmd wdraża wieloetapowy cykl życia przetwarzania końcowego, aby zoptymalizować te strony:

mmd-page-lifecycle

Rysunek 3. mmd cykl życia strony.

  1. Etap 1. Wstępna wymiana (szybka kompresja): pamięć jest najpierw odzyskiwana za pomocą kswapd lub kompresji aplikacji. Zwykle pierwsze odzyskiwanie jest wykonywane przy użyciu szybkiego algorytmu kompresji, takiego jak lz4, a zawartość jest przechowywana w pamięci RAM.

  2. Etap 2. Oznaczanie stanu bezczynności (starzenie i śledzenie): mmd śledzenie stanu bezczynności uzyskuje dostęp do śledzenia pamięci jądra (CONFIG_ZRAM_TRACK_ENTRY_ACTIME) lub używa własnego znacznika bezczynności oprogramowania, aby śledzić, jak długo strony pozostają nietknięte.

  3. Etap 3. Przetwarzanie końcowe 1 – ponowna kompresja (odzyskiwanie pamięci): strony, które osiągną wiek bezczynności ponownej kompresji (min_idle_secondsmax_idle_seconds), są ponownie kompresowane. mmd zapisuje w /sys/block/zram0/recompress, aby poinstruować jądro, aby zdekompresowało stronę lz4 i ponownie skompresowało ją za pomocą zstd. Pozwala to odzyskać fizyczną pamięć RAM bez zużywania pamięci flash.

  4. Etap 4. Przetwarzanie końcowe 2 – zapisywanie zwrotne (usuwanie z pamięci flash): jeśli obciążenie pamięci się utrzymuje, a strony osiągną wiek bezczynności zapisu zwrotnego (zwykle 20 godzin lub więcej), mmd wywołuje zapis zwrotny. mmd zapisuje /sys/block/zram0/idle/sys/block/zram0/writeback, aby całkowicie usunąć skompresowaną stronę z pamięci RAM do pamięci flash.

Konfiguracja ZRAM

mmd wczytuje i przetwarza te właściwości konfiguracji ZRAM:

Właściwość Użyj Domyślny
mmd.zram.enabled Czy konfiguracja mmd ZRAM jest włączona. false
mmd.zram.num_devices Liczba urządzeń ZRAM do skonfigurowania. W przypadku liczby N urządzenia zram0zram<N-1> muszą być obecne, zanim system ustawi wartość sys.boot_completed=1. Właściwości na liście urządzeń z pamięcią ZRAM można konfigurować na poziomie poszczególnych urządzeń. 1
mmd.zram.device_priority Wartości priorytetu do przekazania podczas wywoływania funkcji swapon. Nie ustawiono
mmd.zram.comp_algorithm Algorytm kompresji ZRAM. Jeśli nie zostanie określony, używany jest domyślny algorytm kompresji jądra. Nie ustawiono
mmd.zram.size Rozmiar urządzenia ZRAM w bajtach lub procent rozmiaru pamięci RAM urządzenia, np.75%. 50%
mmd.zram.writeback.enabled Określa, czy włączyć zapisywanie zwrotne ZRAM. false
mmd.zram.writeback.device_size Rozmiar urządzenia zapisu zwrotnego w bajtach lub jako procent partycji danych. Rzeczywisty rozmiar urządzenia można dostosować do dostępnego miejsca na partycji danych. 1073741824 (1 GiB)
mmd.zram.writeback.min_free_space_mib Minimalna ilość wolnego miejsca w MiB, która musi być dostępna po skonfigurowaniu urządzenia zapisu zwrotnego. 1536 (1,5 GiB)
mmd.zram.writeback.use_nr_tags_prop Gdy true używa wartości w mmd.zram.writeback.nr_tags do skonfigurowania głębokości kolejki urządzenia pętli obsługującego zapisywanie zwrotne ZRAM. Jest to obejście w sytuacjach, w których nie można skonfigurować zasad SELinux dostawcy, aby umożliwić mmd bezpośrednie odczytywanie nr_tags urządzenia blokowego obsługującego /data. false
mmd.zram.writeback.nr_tags Zobacz mmd.zram.writeback.use_nr_tags_prop. Nie ustawiono
mmd.zram.recompression.enabled Określa, czy włączyć funkcję ponownej kompresji ZRAM. false
mmd.zram.recompression.algorithm Dodatkowy algorytm ponownej kompresji ZRAM. zstd

Właściwości urządzenia ZRAM

Gdy wartość mmd.zram.num_devices jest większa niż 1, poszczególne właściwości można opcjonalnie skonfigurować dla każdego urządzenia ZRAM, ustawiając właściwość na wartość rozdzieloną przecinkami, która zawiera dokładnie mmd.zram.num_devices elementów. Obejmują one:

  • mmd.zram.size
  • mmd.zram.comp_algorithm
  • mmd.zram.device_priority
  • mmd.zram.recompression.enabled
  • mmd.zram.recompression.huge_idle.enabled
  • mmd.zram.recompression.idle.enabled
  • mmd.zram.recompression.huge.enabled
  • mmd.zram.recompression.threshold_bytes
  • mmd.zram.recompression.algorithm
  • mmd.zram.writeback.device_size
  • mmd.zram.writeback.huge_idle.enabled
  • mmd.zram.writeback.idle.enabled
  • mmd.zram.writeback.huge.enabled

Wycofanie istniejącej konfiguracji ZRAM

Chociaż swapon_all jest nadal dostępny na Androidzie do konfigurowania ZRAM i przestrzeni wymiany opartej na dysku, mmd jest preferowanym podejściem do zarządzania ZRAM, ponieważ ułatwia konfigurację i zapewnia zaawansowane funkcje, takie jak ponowna kompresja ZRAM.

Gdy mmdkonfiguracja ZRAM jest włączona przez mmd.zram.enabled:

  • Konfiguracja ZRAM w swapon_all staje się bezczynna.
  • Istniejące konfiguracje ZRAM, takie jak config_zramWriteback w pliku nakładkiconfig.xmlro.zram.* właściwości systemu zapisu zwrotnego, są ignorowane.

Ustawienia konserwacji ZRAM

Konserwacja ZRAM powinna działać od razu po wyjęciu z pudełka. Możesz ją dodatkowo dostroić za pomocą właściwości systemu w tej sekcji.

Planowanie konserwacji ZRAM

Te właściwości określają, jak i kiedy zadania konserwacyjne ZRAM są planowane przez system_server.

Właściwość Użyj Domyślny
mm.zram.maintenance.first_delay_seconds Opóźnienie przed rozpoczęciem pierwszej konserwacji ZRAM. 3600 (1 godzina)
mm.zram.maintenance.periodic_delay_seconds Opóźnienie między kolejnymi harmonogramami konserwacji ZRAM. 3600 (1 godzina)
mm.zram.maintenance.require_device_idle Określa, czy konserwację ZRAM należy inicjować tylko wtedy, gdy urządzenie jest bezczynne. true
mm.zram.maintenance.require_battery_not_low Określa, czy przed rozpoczęciem konserwacji ZRAM bateria nie powinna być rozładowana. true

Zasady zapisu zwrotnego ZRAM

Te parametry określają, kiedy i jaki typ pamięci jest zapisywany na urządzeniu zapasowym:

Właściwość Użyj Domyślny
mmd.zram.writeback.backoff_seconds Czas oczekiwania od ostatniej operacji zapisu zwrotnego. 600 (10 minut)
mmd.zram.writeback.min_idle_seconds W połączeniu z mmd.zram.writeback.max_idle_seconds służy do obliczania czasu bezczynności strony, aby kwalifikowała się ona do zapisu zwrotnego na podstawie ułamka wykorzystania pamięci. Obliczony czas bezczynności jest interpolowany wykładniczo między tymi dwoma parametrami, aby zminimalizować obciążenie, gdy nie występuje brak pamięci. 72000 (20 godzin)
mmd.zram.writeback.max_idle_seconds Maksymalna liczba sekund używana do dynamicznego obliczania wieku nieaktywnej strony na podstawie wykorzystania pamięci. 90000 (25 godzin)
mmd.zram.writeback.huge.enabled Czy włączyć zapisywanie zmian na stronie HUGE. false
mmd.zram.writeback.idle.enabled Czy włączyć zapisywanie zmian na stronie IDLE. true
mmd.zram.writeback.huge_idle.enabled Czy włączyć zapisywanie zmian na stronie HUGE_IDLE. true
mmd.zram.writeback.min_bytes Minimalna liczba bajtów do zapisania w jednej rundzie zapisu zwrotnego w stanie bezczynności. 5242880 (5 MiB)
mmd.zram.writeback.max_bytes Maksymalna liczba bajtów do zapisania w jednej rundzie zapisu zwrotnego w stanie bezczynności. 314572800 (300 MiB)
mmd.zram.writeback.max_bytes_per_day Maksymalna liczba bajtów do zapisania w ciągu 24 godzin. 25769803776 (24 GiB)
mmd.zram.writeback.limit.enabled Określa, czy włączyć rozliczanie limitu budżetu dziennego zapisu zwrotnego. true

Zasady ponownej kompresji ZRAM

Te parametry określają, kiedy i jakiego typu pamięć jest ponownie kompresowana:

Właściwość Użyj Domyślny
mmd.zram.recompression.backoff_seconds Czas do ponowienia od ostatniej ponownej kompresji. 1800 (30 minut)
mmd.zram.recompression.min_idle_seconds W połączeniu z mmd.zram.recompression.max_idle_seconds służy do obliczania czasu bezczynności strony, aby kwalifikowała się ona do ponownej kompresji na podstawie ułamka wykorzystania pamięci. Obliczony czas bezczynności jest interpolowany wykładniczo między tymi dwoma parametrami, aby zminimalizować obciążenie, gdy nie występuje brak pamięci. 7200 (2 godziny)
mmd.zram.recompression.max_idle_seconds Maksymalna liczba sekund używana do dynamicznego obliczania wieku nieaktywnej strony. 14400 (4 godziny)
mmd.zram.recompression.threshold_bytes Minimalny rozmiar w bajtach stron ZRAM, które są brane pod uwagę podczas ponownej kompresji. 1024 (1 KiB)
mmd.zram.recompression.huge.enabled Czy włączyć ponowną kompresję strony HUGE. true
mmd.zram.recompression.idle.enabled Czy włączyć ponowną kompresję strony IDLE. true
mmd.zram.recompression.huge_idle.enabled Czy włączyć ponowną kompresję strony HUGE_IDLE. true

Śledzenie nieużywanych stron ZRAM

mmd Konserwacja ZRAM oznacza strony ZRAM jako nieaktywne na podstawie tego, jak długo nie były używane. Ta funkcja wymaga włączenia konfiguracji jądra CONFIG_ZRAM_TRACK_ENTRY_ACTIME lub CONFIG_ZRAM_MEMORY_TRACKING. CONFIG_ZRAM_TRACK_ENTRY_ACTIME jest domyślnie włączona w przypadku jąder GKI w wersji 6.18 i nowszych. W starszych wersjach jądra wiąże się to z dodatkowym obciążeniem pamięci i nie jest domyślnie włączone.

Jeśli konfiguracja jądra nie jest włączona, mmd konserwacja ZRAM wraca do logiki zastępczej oprogramowania, aby śledzić nieużywane strony ZRAM:

  1. Oznaczanie wszystkich stron ZRAM jako nieużywanych podczas uruchamiania mmd.

  2. Pomiń kolejne czynności konserwacyjne ZRAM, dopóki nie upłynie wymagany okres wycofania.

  3. Zapisywanie zwrotne lub ponowne kompresowanie nieużywanych stron w pamięci ZRAM. Jeśli z powodu limitów zapisu pozostały nieaktywne strony, mmd kontynuuje zapisywanie stron podczas następnej konserwacji bez oznaczania nowych stron jako nieaktywnych (pomijając krok 4).

  4. Jeśli wszystkie nieaktywne strony zostaną zapisane, ponownie oznacz wszystkie strony ZRAM jako nieaktywne i wróć do kroku 2. Jeśli zapisywanie zwrotne ZRAM jest wyłączone, mmd oznacza wszystkie strony ZRAM jako nieaktywne, gdy po upływie czasu nieaktywności ponownej kompresji następuje ponowna kompresja ZRAM.

Wskazówki dotyczące rozwiązywania problemów i weryfikacji

Aby zweryfikować i zdiagnozować działanie mmd i ZRAM, wykonaj te czynności weryfikacyjne i procedury rozwiązywania problemów.

Weryfikowanie konfiguracji ZRAM

Aby sprawdzić, czy mmd prawidłowo skonfigurował ZRAM podczas uruchamiania:

  1. Sprawdź aktywny algorytm kompresji i rozmiar dysku:

    cat /sys/block/zram0/comp_algorithm
    cat /sys/block/zram0/disksize
    
  2. Sprawdź mmd właściwości systemu i stan uruchomionej usługi:

    getprop | grep mmd.zram
    dumpsys -l | grep mmd
    

Weryfikacja konserwacji i zapisu zwrotnego ZRAM

Sprawdź, czy zadania konserwacyjne związane z zapisywaniem zwrotnym i ponowną kompresją ZRAM działają prawidłowo:

  1. Sprawdź stan urządzenia blokowego:

    cat /sys/block/zram0/bd_stat
    
  2. Sprawdź skuteczność ponownej kompresji, monitorując /sys/block/zram0/mm_stat. Zmiany rozmiarów skompresowanych danych powinny pojawić się po cyklach konserwacji.

Weryfikowanie zapisu zwrotnego w przypadku poszczególnych procesów

Aby sprawdzić, czy zapisywanie zwrotne w przypadku poszczególnych procesów działa prawidłowo, możesz użyć tych metod:

  • Sprawdź adb logcat -s mmd, aby znaleźć logi udanych zapisów zwrotnych lub diagnostykę błędów.

Typowe problemy i diagnostyka

Poniżej znajdziesz typowe sytuacje, w których użytkownik może napotkać błędy:

  • WritebackDailyLimitExceeded: ten błąd oznacza, że osiągnięto limitmmd.zram.writeback.max_bytes_per_day. W takiej sytuacji mmd wstrzymuje zapisywanie zwrotne w stanie bezczynności do czasu, aż przesunie się 24-godzinne okno ruchome.
  • Process prefetch or writeback failed: ten błąd może pojawić się w logcat, gdy nie powiedzie się wywołanie ioctl. Częste przyczyny:
    • EBADF lub ESRCH: proces docelowy zakończył się, zanim mmd mógł wysłać pidfd do jądra.
    • ENOSPC: partycja pamięci zapasowej jest pełna lub kolejka urządzenia pętli jest wyczerpana.
  • Nie skonfigurowano ZRAM: jeśli mmd nie uda się skonfigurować ZRAM podczas uruchamiania, może to być spowodowane tym, że starsze skrypty inicjujące swapon_all lub skrypty dostawcy zablokowały /dev/block/zram0, zanim mmd mogło się wykonać.