Błąd defragmentacji pamięci RAM / OOM

11

To pytanie jest dość długie, więc zadam pytania u góry, a następnie przejdę do mojej metody dochodzenia do pytań:

  1. Czy rm (oparty na Busybox) nie wykonał się, ponieważ nie było wystarczającej ilości ciągłej pamięci RAM?
  2. Jeśli tak, to czy istnieje lekka metoda defragmentacji DMA - bez uciekania się do restartu systemu?
  3. Jeśli nie, co go spowodowało? Jak mogę temu zapobiec w przyszłości?

Po tym, jak nasz system testowy działał dość intensywnie przez ostatnie kilka dni - telnet połączyłem się z systemem i sprawdziłem wyniki testu. Kiedy przyszedłem usunąć niektóre dane, system zwrócił wiersz poleceń (tak jakby polecenie wykonało się poprawnie). Kiedy przyszedłem sprawdzić katalog pod kątem innego zestawu wyników, zobaczyłem, że plik nadal istnieje (używając ls).

Po tym zauważyłem, że coraz więcej moich poleceń powłoki nie działa zgodnie z oczekiwaniami.

Zacznę od wyjścia z dmesg po tym, jak rm nie wykonał poprawnie:

Przydział długości 61440 z procesu 6821 (rm) nie powiódł się

DMA na jednostkę centralną:

CPU 0: hi: 0, btch: 1 usd: 0

Aktywny_anon: 0 Aktywny_plik: 1 Nieaktywny_anon: 0 Nieaktywny_plik: 0 Niewykrywalny: 6 Brudny: 0 Zapis zwrotny: 0 Niestabilny: 0 Wolny: 821 Płyta: 353 Odwzorowany: 0 Pagetable: 0 Odbicie: 0

DMA wolny: 3284kB min: 360kB niski: 448kB wysoki: 540kB aktywny_anon: 0kB nieaktywny_anon: 0kB aktywny_plik: 4kB nieaktywny_plik: 0kB nieunikniony: 24kB obecny: 8128kB skanowany stron: 0 all_creclaimable? Nie

lowmem_reserve []: 0 0 0

DMA: 31 * 4kB 47 * 8kB 42 * 16kB 64 * 32kB 1 * 64kB 0 * 128kB 0 * 256kB 0 * 512kB 0 * 1024kB 0 * 2048kB 0 * 4096kB = 3284kB

14 wszystkich stron pagecache

Nie można przydzielić pamięci RAM na dane procesowe, errno 12

Początkowo myślałem, że nie jestem w stanie uruchomić programu w największej części ciągłej pamięci. To znaczy, że DMA był zbyt rozdrobniony i musiałbym znaleźć sposób na zmusienie systemu do defragmentacji pamięci.

Potem zrobiłem szybką kontrolę matematyki / rozsądku i zdałem sobie sprawę, że program powinien być w stanie działać w jedynym ciągłym gnieździe pamięci 64kB. Rm zażądał 61440 bajtów (60 kB).

Zrobiłem starą dobrą „ręczną defragmentację” i ponownie uruchomiłem system. Po ponownym uruchomieniu systemu wyprowadzam / proc / buddyinfo:

Node 0, zone DMA 2 8 3 12 0 1 0 1 0 1 0

Podejrzewam, że mapa do:

  • 2 x 4 kB
  • 8 x 8 kB
  • 3 x 16 kB
  • 12 x 32 kB
  • 1 x 128 kB
  • 1 x 512 kB

Ale jeśli sumuje się powyższą listę wartości, nie zgadza się ona z danymi wyjściowymi / proc / meminfo :

MemTotal:           6580 kB
MemFree:            3164 kB
Buffers:               0 kB
Cached:              728 kB
SwapCached:            0 kB
Active:              176 kB
Inactive:            524 kB
Active(anon):          0 kB
Inactive(anon):        0 kB
Active(file):        176 kB
Inactive(file):      524 kB`
Unevictable:           0 kB
Mlocked:               0 kB
MmapCopy:            844 kB
SwapTotal:             0 kB
SwapFree:              0 kB
Dirty:                 0 kB
Writeback:             0 kB
AnonPages:             0 kB
Mapped:                0 kB
Slab:               1268 kB
SReclaimable:        196 kB
SUnreclaim:         1072 kB
PageTables:            0 kB
NFS_Unstable:          0 kB
Bounce:                0 kB
WritebackTmp:          0 kB
CommitLimit:        3288 kB
Committed_AS:          0 kB
VmallocTotal:          0 kB
VmallocUsed:           0 kB
VmallocChunk:          0 kB

Podsumowując, moje pytania to:

  1. Czy polecenie rm nie zostało wykonane, ponieważ nie było wystarczającej ilości ciągłej pamięci RAM?
  2. Jeśli tak, to czy istnieje lekka metoda defragmentacji DMA - bez uciekania się do restartu systemu?
  3. Jeśli nie, co go spowodowało? Jak mogę temu zapobiec w przyszłości?

Korzystam z Lantronix XPort Pro (8 MB, Linux OS) z systemem uClinux w wersji 2.6.30. Używana powłoka jest cicha.

OldTinfoil
źródło
Drobny punkt: pominąłeś 1 x 2048 kB z listy fragmentów pamięci. Jeśli to uwzględnisz, suma wynosi 3192 kB, co jest bardzo zbliżone do 3164 kB wymienionego w / proc / meminfo.
Alex Selby,

Odpowiedzi:

11

Na twoje pytanie 2 (defragmentacja pamięci), cytowanie z https://www.kernel.org/doc/Documentation/sysctl/vm.txt :

compact_memory

Dostępne tylko po ustawieniu CONFIG_COMPACTION. Po zapisaniu 1 w pliku wszystkie strefy są kompaktowane, dzięki czemu dostępna jest wolna pamięć w ciągłych blokach, o ile to możliwe. Może to być ważne na przykład przy alokacji ogromnych stron, chociaż procesy będą również bezpośrednio kompaktować pamięć w razie potrzeby.

oznacza to, że następująca komenda (wykonywana z uprawnieniami roota i jeśli włączona została wspomniana powyżej opcja jądra)

echo 1 > /proc/sys/vm/compact_memory

powinien poinformować jądro, aby próbowało jak najbardziej defragmentować pamięć. Uwaga: np. W niektórych wersjach RHEL6 może to spowodować awarię jądra ...

Andre Holzner
źródło
1
Dziękujemy za poświęcenie czasu na powrót i skomentowanie starego pytania!
OldTinfoil
7

Zajęło to trochę czasu, ale pomyślałem, że wstrzymam się od odpowiedzi, dopóki nie otrzymam odpowiedzi na wszystkie 3 moje pytania cząstkowe.

Zanim zacznę, wspomnę, że poprawne określenie „rozpakowywania” pamięci roboczej odnosi się do „pracy kompaktowej”.

1. Czy rm nie wykonał się, ponieważ nie było wystarczającej ilości ciągłej pamięci RAM?

Mój wniosek był poprawny - rm nie wykonał się, ponieważ nie było wystarczającej ciągłej pamięci RAM. System pozyskiwał pamięć RAM i dzielił ją na fragmenty, przez co nie można było jej odzyskać.

2. Jeśli tak, to czy istnieje lekka metoda defragmentacji DMA - bez uciekania się do restartu systemu?

Okazuje się, że nie ma sposobu na kompaktowanie pamięci, poza ponownym uruchomieniem systemu osadzonego. W przypadku systemu bez MMU zapobieganie to nazwa gry.

Część mnie zastanawia się, czy można zhakować jądro Linuksa, aby emulować MMU w oprogramowaniu. Myślę, że gdyby to było możliwe, ktoś już by to zrobił. Nie mogę sobie wyobrazić, że to zupełnie nowa koncepcja;)

3. Jak mogę temu zapobiec w przyszłości?

W tym projekcie korzystałem z crona, aby ręcznie inicjować program za każdym razem, gdy był wymagany. Znacznie lepszym sposobem na to jest wywołanie programu przy uruchamianiu, a następnie zmusienie programu do uśpienia, aż będzie to konieczne. W ten sposób pamięć nie musi być przydzielana przy każdym użyciu. W ten sposób zmniejsza się fragmentację.

Podczas pierwszej iteracji projektu polegaliśmy na moich wywołaniach skryptów powłoki do wykonywania funkcji krytycznych (takich jak rm). Nie widzieliśmy potrzeby ponownego wynalezienia koła, jeśli nie byłoby takiej potrzeby.

Zalecam jednak unikanie powłoki tam, gdzie to możliwe, w przypadku systemu bez MMU -

( Pytanie , co się stanie, jeśli wykonasz ls -la /path/to/directory/ | grep file-i-seek?)

( Odpowiedź : uruchamia nowy podproces)

Jeśli potrzebujesz zaimplementować niektóre podstawowe funkcje skryptu powłoki w swoim programie C, polecam sprawdzenie kodu źródłowego używanego w BusyBox . Prawdopodobnie będziesz używać C we wbudowanym systemie.

OldTinfoil
źródło
Dziękujemy za poświęcenie czasu na powrót i podzielenie się swoimi odkryciami.
Caleb,
3
[Zdaję sobie sprawę, że to jest stare] Emulacja MMU jest trudna ... Bez MMU każdy program bezpośrednio korzysta z fizycznych adresów wyświetlanych na szynie pamięci. Możesz emulować jeden, ale będziesz musiał przechwytywać każdy dostęp do pamięci (tak jak robi to rzeczywista MMU). Wydajność byłaby okropna. Alternatywnie możesz użyć wskaźników pośrednich (tak jak zrobił to Mac OS Classic, nazywając je „uchwytami”), ale wtedy masz zupełnie trudny interfejs API i bardzo trudny w obliczu uprzedzenia (Mac OS Classic używał wielozadaniowości kooperacyjnej) .
derobert
Dziękujemy za powrót i poświęcenie czasu na napisanie odpowiedzi. Nie wiedziałem, że MacOS Classic to zrobił.
OldTinfoil