- Linux oferuje polecenia, takie jak free, vmstat, ps i top, które umożliwiają sprawdzenie faktycznego wykorzystania pamięci i procesora, rozróżniając pamięć używaną, wolną i buforowaną.
- Narzędzia takie jak GDB i Valgrind umożliwiają wykrywanie błędów segmentacji, wycieków pamięci, nielegalnego dostępu i niezainicjowanych zmiennych w programach natywnych.
- W środowiskach .NET Core połączenie dotnet-dump, LLDB i SOS ułatwia szczegółową analizę zrzutów pamięci zarówno w systemie Linux, jak i Windows.
- Uporządkowany obieg prac diagnostycznych, który łączy metryki systemowe, logi i analizę zrzutów, znacznie skraca czas lokalizowania i rozwiązywania problemów z pamięcią.
Gdy coś idzie nie tak na serwerze Linux, przyczyną często jest sposób wykorzystania pamięci i narzędzia debugowania . Dopóki jednak nie wiesz, na co zwracać uwagę, wszystko wydaje się chaotycznym zbiorem liczb i procesów. Zrozumienie, co dzieje się w pamięci RAM, jak zachowują się Twoje programy i jak zapisywać zrzuty pamięci, jest kluczem do uniknięcia ślepego rozwiązywania problemów za każdym razem, gdy pojawia się dziwny błąd lub błąd segmentacji.
W tym artykule szczegółowo i w przystępny sposób omówimy, jak sprawdzać pamięć, debugować programy i analizować zrzuty w systemie Linux za pomocą poleceń systemowych (free, vmstat, /proc/meminfo), debugerów takich jak GDB, analizatorów pamięci takich jak Valgrind oraz narzędzi specyficznych dla .NET i innych środowisk. Celem jest, abyś po tym artykule dysponował praktycznym arsenałem narzędzi do diagnozowania problemów z pamięcią i procesorem w serwerach i profesjonalnych środowiskach programistycznych.
Przegląd: Pamięć w systemie Linux i dlaczego nie należy przejmować się 100%
Na serwerze Linux pamięć RAM działa jak szybki magazyn danych i kodu, które system będzie wkrótce ponownie wykorzystywać. Dostęp do pamięci RAM jest znacznie szybszy niż do dysku, dlatego jądro wykorzystuje to, aby wykorzystać ją jako pamięć podręczną dla plików, buforów i stron pamięci, które mogą być wkrótce ponownie potrzebne.
W kontekście serwera WWW z systemem Apache lub Nginx albo serwera bazy danych, takiego jak MySQL lub MariaDB, wiedza o tym, ile pamięci jest używane, ile jest wolne, a ile jest buforowane, jest kluczowa dla prawidłowego doboru rozmiaru usług oraz zrozumienia przestrzeni nazw i grup c . W rzeczywistości narzędzia optymalizacyjne, takie jak MySQLTuner , wykorzystują te dane (i inne informacje) do sugerowania ustawień bufora, pamięci podręcznej i połączenia.
Ważne jest, aby zrozumieć koncepcję, która często na początku zaskakuje: pamięć RAM jest zaprojektowana do użytku . 90%, a nawet 100% wykorzystania niekoniecznie stanowi problem; Linux wypełnia pamięć RAM dyskową pamięcią podręczną, aby przyspieszyć działanie systemu. Prawdziwy niepokój pojawia się, gdy pojawiają się błędy braku pamięci , procesy zawieszają się z komunikatem OOM (Out Of Memory) lub usługi zawieszają się, a nie tylko wtedy, gdy wykres pokazuje „wysokie wykorzystanie”.
Przejście do agresywnej optymalizacji ma sens tylko wtedy, gdy zauważysz, że serwer staje się niestabilny, powolny lub zatrzymuje usługi z powodu nadmiernego wykorzystania pamięci, lub gdy wiesz, że zbliżają się skoki ruchu, które mogą przytłoczyć obecne zasoby.
Podstawowe polecenia do sprawdzania pamięci w systemie Linux

Pierwszym krokiem w rozwiązywaniu problemów z pamięcią jest zrozumienie całkowitej ilości pamięci RAM i jej podziału na pamięć używaną, wolną, pamięć podręczną i pamięć wymiany. Linux oferuje kilka narzędzi do tego celu, każde o innym poziomie szczegółowości.
polecenie free: szybki przegląd pamięci RAM i swapu
Najczęściej używanym poleceniem do szybkiego podglądu jest za darmo, szczególnie z parametrem -h Aby wyświetlić rozmiary w czytelnym formacie: Aby uzyskać więcej informacji na temat interpretacji i optymalizacji tych metryk, zobacz optymalizacja statystyk pamięci.
free -h
Typowy wynik pokazuje w kilku wierszach całkowitą, używaną, wolną, współdzieloną, buforową i podręczną pamięć RAM i przestrzeń wymiany. Zobaczysz coś takiego:
Łączna liczba wykorzystanych wolnych buforów współdzielonych w pamięci podręcznej Pamięć: 2.0 G 1.5 G 470 M 300 M 0 B 414 M -/+ bufory/pamięć podręczna: 1.1 G 885 M Wymiana: 0 B 0 B 0 B
Linia -/+ buffers/cache Jest to szczególnie przydatne, ponieważ pokazuje pamięć faktycznie wykorzystywaną przez procesy (z wyłączeniem buforów/pamięci podręcznej) oraz pamięć potencjalnie dostępną dla nowych aplikacji. Wiele osób widzi niewiele „wolnej pamięci” w pierwszym wierszu i wpada w panikę, podczas gdy w rzeczywistości znaczna jej część jest po prostu buforowana i może być… ponownie wykorzystane bez dramatów.
vmstat: aktywna, bezczynna pamięć i metryki systemowe
Jeśli potrzebujesz czegoś bardziej szczegółowego, vmstat zapewnia bardziej techniczny obraz systemu. Dzięki:
vmstat -s -S M
Otrzymasz listę zawierającą całkowitą ilość pamięci, pamięć używaną, pamięć wolną, pamięć aktywną, pamięć nieaktywną, bufory, pamięć wymiany itd. Na przykład:
2048 M pamięci całkowitej 1582 M pamięci użytej 1124 M pamięci aktywnej 406 M pamięci nieaktywnej 465 M pamięci wolnej 0 M pamięci buforowej 407 M pamięci podręcznej wymiany 0 M całkowitej wymiany 0 M używanej wymiany 0 M wolnej wymiany
Koncepcja pamięci bezczynnej jest interesująca: to pamięć, która jest uznawana za używaną, ale nie jest powiązana z aktywnymi procesami, więc w praktyce zachowuje się jak pamięć, którą system może odzyskać . To pomaga wyjaśnić, dlaczego Linux może sprawiać wrażenie, że „zajmuje całe miejsce”, ale w rzeczywistości ma miejsce do zwolnienia w razie potrzeby.
/proc/meminfo: surowe szczegóły jądra
Jeśli interesują Cię wszystkie szczegóły obsługiwane przez jądro, możesz zawsze zajrzeć do pliku /proc/meminfo :
cat /proc/meminfo
Plik ten zawiera dziesiątki pól związanych z zarządzaniem pamięcią wewnętrzną : całkowita pamięć, wolna pamięć, bufory, pamięć podręczna, bloki, hugepages, dostępna pamięć itd. Jest on bardzo przydatny dla narzędzi automatycznych i skryptów monitorujących, chociaż jego bezpośrednie odczytanie jest nieco mniej przyjazne dla użytkownika niż „free” lub „vmstat” i wymaga dokładnego zrozumienia znaczenia każdego pola.
Panele sterowania i ich „równoległa rzeczywistość”
Jeśli zarządzasz serwerem za pomocą paneli sterowania, takich jak cPanel, Plesk, VestaCP lub Webmin , zauważysz, że zazwyczaj wyświetlają one wykresy wykorzystanej, wolnej i buforowanej pamięci za pośrednictwem interfejsu webowego. Dane te są wygodne, ale należy pamiętać, że są one okresowo aktualizowane i czasami nie odzwierciedlają dokładnego stanu w czasie rzeczywistym. Do dokładnej diagnostyki i precyzyjnego dostrajania zawsze najlepiej jest użyć terminala i jego natywnych poleceń.
Debugowanie procesów i pamięci za pomocą narzędzi systemowych
Gdy już wiesz, ile pamięci jest dostępne i jak jest ona ogólnie wykorzystywana, kolejnym krokiem jest zlokalizowanie procesów, które nieprawidłowo zużywają zasoby , i przeanalizowanie ich zachowania. W tym miejscu do gry wchodzą klasyczne narzędzia, takie jak ps, top, htop, strace i ltrace, a także polecenia sieciowe i dyskowe, które pomagają wyeliminować wąskie gardła.
ps, top i htop: prześwietlenie procesów i procesora
Polecenie `ps` wyświetla aktywne procesy. W połączeniu z filtrami i sortowaniem pozwala szybko zlokalizować procesy obciążające procesor.
ps aux --sort=-%cpu | head
Pokazuje procesy posortowane według procentowego wykorzystania procesora , pamięci, użytkownika i polecenia. Idealnie nadaje się jako migawka do identyfikacji głównego podejrzanego, gdy serwerowi brakuje zasobów; znajomość terminala pozwala na szybkie działanie.
Z drugiej strony, `top` i jego bardziej wizualna odmiana `htop` oferują podgląd procesów w czasie rzeczywistym, użycia procesora, użycia pamięci, stanu (R, S, D itd.) oraz średniego obciążenia . `top` zaczyna się od:
top -o %CPU
Możesz skupić się na tym, które procesy najbardziej obciążają procesor. Poza liczbami, ważne jest, aby szukać wzorców :
- Procesy w stanie R (uruchomione) które pozostają na górze przez długi czas.
- Nagłe zmiany średniego obciążenia które wskazują na ostatnie szczyty obciążenia.
- Przydział procesora w %us, %sy i %wa (czas użytkownika, czas systemowy i oczekiwanie na wejście/wyjście).
- Jeśli problematyczny proces to usługa systemowa, konkretny użytkownik lub demon.
- Jeżeli konsumpcja jest stabilna lub Porusza się skokowo w górę i w dół., co może wskazywać na zadania wsadowe lub nieregularny ruch.
Bardzo wysoka wartość %wa (oczekiwanie na wejście/wyjście) może być wskazówką, że wąskie gardło może nie znajdować się w procesorze, lecz w dysku lub podsystemie pamięci masowej.
Przeglądanie wątków w procesie: góra -H
W przypadku aplikacji wielowątkowych (Java, Python, Node itp.) nie wystarczy wiedzieć, który proces zużywa procesor; trzeba zobaczyć, który konkretny wątek wymyka się spod kontroli . W przypadku:
top -H -p PID
Otrzymujesz szczegółowe informacje o każdym wątku w procesie. Jeśli jeden z nich wyświetla się jako 100%, a pozostałe są bezczynne, prawdopodobnie występuje nieskończona pętla, dziwny problem z blokowaniem lub źle zaprojektowana sekcja krytyczna w tej części kodu.
Priorytet procesu: nice i renice
Jeśli proces intensywnie wykorzystujący zasoby jest uzasadniony, ale chcesz zapobiec jego przeciążeniu reszty systemu, możesz dostosować jego priorytet (tzw. niceness) . Polecenia `nice` i `renice` pozwalają uruchamiać procesy z niższym priorytetem lub dostosować priorytet istniejącego procesu, dzięki czemu jądro przydziela im mniej czasu procesora w porównaniu z innymi krytycznymi zadaniami.
Inne kluczowe polecenia: pidof, kill, strace i ltrace
Kiedy system jest pod presją, przydatna jest możliwość szybkiej lokalizacji i kontroli procesów:
- pidof:pobiera PID procesu na podstawie jego nazwy, idealne rozwiązanie w przypadku skryptów lub szybkich interwencji.
- zabić: wysyła sygnały do procesów (TERM, KILL itp.), aby zostały wyłączone w sposób uporządkowany lub aby przerwały pracę, jeśli utknęły.
- strace:pokazuje wywołania systemowe który wykonuje proces; idealny do sprawdzenia, na czym się opiera (odczyty z dysku, gniazda itp.).
- śladPodobny do strace, ale skupiony na telefony do księgarni, przydatne do debugowania problemów związanych z korzystaniem ze współdzielonego interfejsu API.
Narzędzia te są przydatne nie tylko do wykrywania wycieków pamięci lub awarii, ale są również szeroko stosowane w cyberbezpieczeństwie, analizie kryminalistycznej i debugowaniu dziwnego zachowania plików binarnych .
Polecenia dla plików i dysków: lsof, df, du, locate
Gdy podejrzewasz, że coś jest nie tak z pamięcią masową (np. wiele otwartych plików lub pełne dyski), do gry wchodzą inne polecenia:
- lsof:wymień pliki otwarte przez proces, przydatne do znajdowania wycieków deskryptorów lub plików, które uniemożliwiają odmontowanie systemu plików.
- df:pokazuje wolne miejsce na dysku według systemu plików, niezbędne jest wykluczenie 100% dysków powodujących kaskadowe awarie.
- du:obliczyć przestrzeń używana przez katalogi, bardzo przydatne do lokalizowania folderów, które rozrastają się w sposób niekontrolowany (logi, kopie zapasowe itp.).
- zlokalizować: umożliwia wyszukiwanie plików według nazwy przy użyciu indeksowanej bazy danych, co w wielu sytuacjach jest szybsze niż polecenie find.
Polecenia sieciowe do diagnostyki: nc, netstat, ab, tcpdump, wireshark, nmap
Często problemom z wydajnością i pamięcią towarzyszą wąskie gardła sieciowe lub ataki , dlatego warto mieć pod ręką kilka podstawowych narzędzi:
- nc (netcat):prawdziwy scyzoryk szwajcarski w dziedzinie sieci; otwiera gniazda, wysyła i odbiera dane, pozwala na testowanie usług, a nawet tworzenie improwizowanych tuneli.
- netstat: wyświetla aktywne połączenia, gniazda nasłuchujące i statystyki sieciowe. Przydatne, gdy chcemy sprawdzić, czy do danego portu nawiązano dużo połączeń.
- ab (test porównawczy Apache):generuje obciążenie serwera WWW w celu zmierzenia czasu reakcji i zachowania pod obciążeniem.
- tcpdump i Wireshark: przechwytywanie pakietów sieciowych do dogłębnie analizuj ruch; tcpdump w konsoli, Wireshark z graficznym interfejsem.
- nmapSkaner portów i usług, powszechnie stosowany zarówno do audytów bezpieczeństwa, jak i sprawdzania powierzchnia ekspozycji serwera.
GDB: klasyczny debugger błędów pamięci i awarii środowiska wykonawczego
Gdy program w systemie Linux ulega awarii z powodu błędu segmentacji lub innego poważnego błędu, kolejnym logicznym krokiem jest użycie dobrego debugera. W świecie GNU sztandarowym narzędziem jest GDB (GNU Debugger) , który pozwala na sprawdzenie stanu wewnętrznego programu, zatrzymanie go w dowolnym momencie, sprawdzenie zmiennych i zużycia pamięci oraz śledzenie wykonywania krok po kroku.
GDB został pierwotnie zaprojektowany dla programów w językach C i C++, ale został rozszerzony na inne języki, takie jak Rust, a nawet assembler , oraz na obsługę ścieżek bibliotecznych, takich jak LD_LIBRARY_PATH . Jest on fundamentalny w programowaniu, ale także w cyberbezpieczeństwie, gdzie jest wykorzystywany do inżynierii wstecznej, wyszukiwania luk w zabezpieczeniach oraz tworzenia i debugowania exploitów.
Uruchom GDB i uruchom program
Aby użyć GDB z plikiem wykonywalnym, zaleca się skompilowanie programu za pomocą informacje debugowania korzystając z opcji -g z GCC:
gcc -g -Wall programa.c -o programa
Następnie uruchamiasz go za pomocą:
gdb programa
W GDB program nie jest jeszcze uruchomiony. Możesz go uruchomić za pomocą:
- biegaćUruchom program z dowolnymi argumentami.
- początek: to samo co run, ale zatrzymuje się na początku funkcji
main.
Jeśli masz plik wykonywalny o nazwie np. SUMA A jeśli chcesz zobaczyć, jak przetwarza swoje argumenty, przejdź do GDB i użyj run 3 5 aby uruchomić go z tymi parametrami z debugera.
Punkty przerwania i punkty obserwacyjne: wstrzymywanie programu w razie potrzeby
Punkty przerwania to punkty zatrzymania w kodzie źródłowym: gdy wykonywanie do nich dotrze, program zatrzymuje się, umożliwiając sprawdzenie jego stanu wewnętrznego. W GDB definiuje się je za pomocą polecenia :
break lugar
gdzie „lokalizacja” może być nazwą funkcji, numerem wiersza lub plikiem:wierszem . Typowy przykład:
break main
Dzięki temu GDB zatrzymuje się dokładnie w momencie wejścia do funkcji głównej. Gdy program zatrzyma się w punkcie przerwania, możesz wyświetlić kod za pomocą l (lista), sprawdzić zmienne i poruszać się po stosie wywołań.
Punkty obserwacyjne działają inaczej: zamiast zatrzymywać się w określonym miejscu kodu, zatrzymują się, gdy zmienia się wartość zmiennej . Są bardzo przydatne do wykrywania uszkodzeń pamięci lub nieoczekiwanych modyfikacji struktur krytycznych bez konieczności wypełniania kodu poleceniami print.
Precyzyjna kontrola przepływu: krok, dalej, kontynuuj, zakończ
Po zatrzymaniu programu w punkcie przerwania możesz postępować na kilka sposobów:
- krok: wykonuje następujący wiersz i wprowadza wywoływane funkcje.
- Następny: wykonuje następujący wiersz bez wprowadzania funkcji (uruchamia je „wszystkie naraz”).
- kontynuować: wznawia wykonywanie do następnego punktu przerwania lub do zakończenia programu.
- koniec: kontynuuje działanie do momentu powrotu bieżącej funkcji i powrotu do punktu, z którego została wywołana.
Pozwala to na inspekcję podejrzanego kodu krok po kroku, bez konieczności „połykania” całego programu. Przydatna sztuczka: w GDB, Naciśnięcie Enter powtarza ostatnie polecenieidealny do robienia wielu next o step a potem mniej pisania.
Stos wywołań i ramki: zobacz, jak do tego doszliśmy
Aby zrozumieć, dlaczego w danym momencie wystąpiła awaria, kluczowe jest sprawdzenie, które funkcje zostały wywołane przed tym momentem. Struktura przechowująca te informacje to stos wywołań . GDB oferuje kilka poleceń do zarządzania nim:
- bt (backtrace): pokazuje kompletny stos z listą zagnieżdżonych funkcji i numerami ramek.
- up:przesuwa się o jeden poziom wyżej w stosie, do punktu, w którym wywołano bieżącą funkcję.
- na dół: schodzi o jeden poziom niżej, do funkcji, która była uruchomiona.
- fr (ramka): wyświetla aktualną ramkę lub umożliwia zmianę na inną poprzez wskazanie jej numeru.
Dzięki temu możesz umiejscowić się na różnych poziomach wywołania (na przykład przechodząc z funkcji wewnętrznej do main ) i zobaczyć, jak zmienne znajdowały się w każdym miejscu.
Interakcja z pamięcią: set, return, backtrace yx
Jedną z największych zalet GDB jest możliwość odczytu i modyfikacji pamięci podczas zatrzymania programu. Oto kilka przydatnych operacji:
- zestaw: zmienia wartość zmiennej w trakcie wykonywania, idealne do testowania scenariuszy bez konieczności ponownej kompilacji.
- wartość zwracana: zmusza bieżącą funkcję do zwrócenia określonej wartości, bez dalszego wykonywania jej kodu.
- ślad wstecz:już widziane, aby pokazać bieżący stos wywołań.
- x: bada zawartość pamięci zaczynając od adresu, przy użyciu różnych formatów i rozmiarów (na przykład,
x/4xw 0x7fffffffe000(aby zobaczyć cztery słowa w systemie szesnastkowym).
Za pomocą tych opcji można replikować błędy, sprawdzać, czy niebezpieczne dane wejściowe powodują przepełnienie, lub badać złożone struktury danych na stosie lub stercie.
Debugowanie post mortem z użyciem zrzutów pamięci
Linux może wygenerować plik core , gdy proces ulega awarii z powodu sygnału takiego jak SIGSEGV. Plik ten zawiera pełny stan procesu w momencie awarii: pamięć, rejestry, stos itp. Aby go aktywować w sesji, wystarczy:
ulimit -c unlimited
Aby włączyć je na stałe dla użytkowników, możesz edytować plik /etc/security/limits.conf i dodać wiersz taki jak ten:
* soft core unlimited
Gdy program ulega awarii, generuje plik, zazwyczaj o nazwie „core”, w katalogu roboczym. Można go następnie przeanalizować za pomocą GDB w następujący sposób:
gdb ejecutable core
Umożliwia to przeprowadzenie analizy post mortem, niemal tak jakby program wciąż działał. Jest to idealne rozwiązanie, gdy awarii nie da się łatwo odtworzyć na żądanie.
Valgrind: polowanie na wycieki pamięci i błędy
W przypadku wielu problemów z pamięcią w C/C++ (i podobnych językach), GDB nie wystarcza, ponieważ nie jest zaprojektowane do automatycznego wykrywania nielegalnych odczytów, wycieków ani niezainicjowanego użycia pamięci . Tu właśnie pojawia się Valgrind – system debugowania i profilowania, który emuluje procesor i monitoruje każdy dostęp do pamięci.
Valgrind zawiera kilka narzędzi, ale najczęściej używanym jest Memcheck , który zastępuje standardowego menedżera pamięci języka C własnym, zawierającym strefy ochronne wokół przydzielonych bloków . Pozwala to na wykrywanie:
- Niezainicjowane użycie pamięci (zmienne używane przed przypisaniem im wartości).
- Czytania/pisma po wykonaniu zajęć wolnych (używać po zwolnieniu).
- Dostęp poza granicami bloku pamięci.
- Wycieki pamięci (malloc bez wolnych, utraconych wskaźników, itp.).
Przykład bezbłędny: „czysty” program w Memcheck
Załóżmy, że kompilujesz krótki program „hello world” z informacjami debugowania:
gcc -Wall -gstabs valgrind_hello_good.c -o valgrind_hello_good
Jeśli uruchomisz go pod Valgrindem, używając:
valgrind --tool=memcheck --leak-check=full -v ./valgrind_hello_good
Otrzymasz ślad, w którym na końcu PODSUMOWANIE BŁĘDÓW wskazuje 0 błędów i brak wycieków. To potwierdza, że przynajmniej z punktu widzenia Memchecka, program nie wykazuje anomalii pamięci.
Wykrywanie wycieków pamięci
Jeśli zmodyfikujesz kod, aby wykonać operację malloc bez zwalniania pamięci (na przykład rezerwując bajt i nigdy go nie zwalniając), a następnie przekompilujesz:
gcc -Wall -gstabs valgrind_hello_bad.c -o valgrind_hello_bad
podczas biegania:
valgrind --tool=memcheck --leak-check=full -v ./valgrind_hello_bad
Valgrind wyświetli PODSUMOWANIE STUDENTA z wartością różną od zera „in use at exit”, wskazującą ilość niewykorzystanej pamięci. Dodatkowo sekcja „definitywnie utracona” pokaże liczbę wyciekniętych bajtów i bloków, wraz ze śladem wywołań, które spowodowały wyciek (funkcja, plik i wiersz kodu).
W przypadku bardziej złożonych przypadków, w których stosuje się np. filtry, 100 bajtów w funkcji leak()Wyjście z Valgrind wyraźnie wskaże Ci drogę malloc problematyczne, pozwalające na dodanie free odpowiadające i sprawdź później, czy podsumowanie powróci do zerowego poziomu wycieków.
Nielegalne czyny i dostęp poza zasięgiem
Innym typowym błędem wykrywanym przez Memcheck jest zapis pod nieprawidłowe adresy , na przykład zapis do komórki pamięci 0 (NULL) lub poza koniec tablicy. Jeśli skompilujesz program, który wykonuje niedozwolony zapis:
gcc -gstabs -Wall valgrind_illegal_read_write.c -o valgrind_illegal_read_write
i uruchamiasz go z:
valgrind -v ./valgrind_illegal_read_write
Zobaczysz wiadomości takie jak:
Nieprawidłowy zapis rozmiaru 4 w 0x80483C4: main (valgrind_illegal_read_write.c:8) Adres 0x0 nie jest umieszczony w stosie, nie jest przydzielony do specjalnej lokalizacji ani (ostatnio) nie został zwolniony
Dzięki temu dowiesz się dokładnie, gdzie wystąpił nielegalny zapis i jak duży był tekst, co w normalnych warunkach zwykle objawia się jako prosty „błąd segmentacji” bez żadnych dalszych wskazówek.
Niezainicjowane zmienne i zależności między nimi
Memcheck powiadamia Cię również, gdy Decyzja sterująca zależy od niezainicjowanej zmiennejJeśli kompilujesz program ze zmienną lokalną, która nigdy nie jest inicjowana i jest używana w if:
gcc -gstabs -Wall valgrind_unitialized.c -o valgrind_unitialized
i uruchamiasz to za pomocą:
valgrind -v --track-origins=yes ./valgrind_unitialized
Wynik będzie zawierał coś takiego:
Warunkowy skok lub ruch zależy od niezainicjowanych wartości w 0x80483F2: main (valgrind_unitialized.c:9) Niezainicjowana wartość została utworzona przez alokację stosu w 0x80483EA: main (valgrind_unitialized.c:7)
Parametr –track-origins=yes pomaga zlokalizować miejsce, w którym niezainicjowana zmienna została utworzona po raz pierwszy, co znacznie przyspiesza poprawianie kodu.
Nadużywanie wolnych zasobów i wykrywanie błędów w stercie
Valgrind weryfikuje również, że free jest wywoływane tylko w przypadku prawidłowych wskaźnikówJeśli spróbujesz zwolnić pamięć, która nie pochodzi z mallocAlbo klikając dwukrotnie na ten sam blok otrzymasz komunikaty takie jak ten:
Nieprawidłowe free() / delete / delete[] / realloc() w 0x402B06C: free (...) przez 0x8048449: main (valgrind_illegal_free.c:11)
W wielu przypadkach informuje Cię nawet o adresie, z którego dzwonisz free Znajduje się bezpośrednio za blokiem, który został już zwolniony, co umożliwia identyfikację błędy w obliczeniach wskaźników lub podwójne zwolnienia.
Podsumowując, Valgrind to narzędzie niezbędne w przypadku podejrzenia wycieków pamięci, użycia pamięci po zwolnieniu lub uszkodzenia pamięci w programach natywnych, a jego połączenie z GDB zapewnia bardzo szczegółowy obraz stanu wewnętrznego aplikacji.
Zrzuty pamięci i analiza w systemach Linux i Windows
W nowoczesnych aplikacjach, zwłaszcza w środowisku .NET Core , analiza złożonych problemów z pamięcią lub wydajnością często wiąże się z przechwytywaniem zrzutów pamięci w celu przeprowadzenia dokładnej analizy na innym komputerze. Zrzuty te zamrażają stan procesu w określonym momencie, podobnie jak tradycyjny zrzut pamięci.
Analiza zrzutów .NET w systemie Linux za pomocą dotnet-dump i LLDB
W środowiskach .NET Core w systemie Linux jednym z zalecanych narzędzi jest dotnet-dump . Po przechwyceniu zrzutu można rozpocząć analizę za pomocą:
dotnet-dump analyze <dump-file>
Ważne jest, aby analizę przeprowadzić na maszynie o tej samej architekturze i dystrybucji Linuksa , co środowisko, w którym wygenerowano zrzut. dotnet-dump jest ukierunkowany na zarządzany kod .NET. Aby analizować również kod natywny (C/C++), bardziej odpowiednie jest użycie LLDB wraz z rozszerzeniem SOS.
LLDB umożliwia debugowanie zarówno kodu zarządzanego, jak i natywnego, a za pomocą dotnet-sos można zainstalować rozszerzenie SOS, które udostępnia specyficzne polecenia dla platformy .NET. Aby poprawnie załadować zrzut .NET Core, LLDB i SOS wymagają określonych plików binarnych ze środowiska, w którym zrzut został utworzony:
- libmscordaccore.so
- libcoreclr.so
- DotNet (host użyty do uruchomienia aplikacji)
Te pliki binarne można zazwyczaj pobrać za pomocą narzędzia dotnet-symbol . Jeśli jest to kompilacja prywatna lub nie znajdują się one na serwerze symboli, zawsze można je skopiować z oryginalnego komputera. Jeśli pliki binarne nie są dołączone do pliku zrzutu, można użyć następującego polecenia w LLDB/SOS:
setclrpath <ruta>aby wskazać lokalizację plików binarnych .NET Core.setsymbolserver -directory <ruta>aby określić lokalizację symboli.
Gdy już masz wszystko, czego potrzebujesz, załaduj zrzut do LLDB, określając host dotnet jako plik wykonywalny do debugowania:
lldb --core <dump-file> <host-program>
Tutaj To droga do wysypiska i Zwykle jest binarny. dotnet, chyba że jest to aplikacja niezależna. W takim przypadku będzie to osobny plik wykonywalny aplikacji (bez rozszerzenia .dll).
W ramach bazy danych LLDB często zachodzi konieczność skonfigurowania prawidłowego serwera symboli setsymbolserver -ms aby skorzystać z serwera Microsoft lub setsymbolserver -directory <ruta> dla katalogu lokalnego i uruchom loadsymbols aby załadować symbole natywne. Stamtąd możesz użyć standardowych poleceń SOS do analizy zarządzanego stosu, wątków, wyjątków itp.
Analiza zrzutu Linuksa z systemu Windows
Zrzuty generowane w systemie Linux nie ograniczają się do analizy w tym samym systemie; można je również otwierać w systemie Windows za pomocą narzędzi takich jak Visual Studio, WinDbg czy dotnet-dump. Możliwości są różne:
- visual Studio:umożliwia czyszczenie wysypisk śmieci mieszanką kod natywny i zarządzanyz bardzo przyjaznym dla użytkownika interfejsem graficznym. Zaleca się zapoznanie się z konkretnym przewodnikiem. Debugowanie zrzutów pamięci programu Visual Studio.
- WinDbgZaawansowane wsparcie dla zrzutów użytkowników, w tym Zrzuty Linuksa Z przepływem pracy podobnym do systemu Windows. Do zrzutów z systemów Linux x64 lub Arm64 należy używać wersji x64 WinDbg, a do zrzutów x86 wersji x86.
- dotnet-dump W systemie Windows: używa się go w ten sam sposób, co w systemie Linux, z
dotnet-dump analyzePamiętaj, aby wybrać wersję x64 lub x86 narzędzia w zależności od architektury zrzutu.
Profesjonalna diagnoza wysokiego wykorzystania procesora i pamięci
Poza konkretnymi narzędziami, czyszczenie pamięci w Linuksie zazwyczaj wymaga ustrukturyzowanego procesu , aby uniknąć przypadkowych działań. Praktyczny schemat dla serwerów wygląda następująco:
- Patrzeć na Top w celu wykrywania podejrzanych wzorców i procesów ładowania.
- Potwierdź za pomocą ps procesy wykorzystujące najwięcej procesora i pamięci.
- Sprawdź, czy zużycie jest logiczne (kompilacja, wsad, indeksowanie), czy też przypomina nietypowe zachowanie.
- Analizuj wątki za pomocą góra -H -p PID jeśli chodzi o procesy wielowątkowe.
- Sprawdzić dzienniki serwisowe z journalctl lub tail.
- Sprawdź, czy problem rzeczywiście jest związany z procesorem, czy też jest ukryty w... wejście/wyjście dysku używając iostat.
Aby ustalić, czy wąskie gardło występuje w procesorze czy na dysku, bardzo pomocne jest polecenie iostat -xz 1 3 : jeśli widzisz dyski o wysokim obciążeniu i długim czasie oczekiwania, a także wysokim %wa w top , problem prawdopodobnie nie leży w procesorze, lecz w pamięci masowej.
Dzięki zastosowaniu tego przepływu pracy łatwiej jest diagnozować takie przypadki, jak:
- Boty lub złośliwy ruch wyzwalanie procesów php-fpm i przeciążanie serwera WWW.
- Un Mikrousługa Java z wątkiem w pętli nieskończonej pochłaniając cały rdzeń.
- Proces Pythona, który wydaje się być ograniczony przez procesor, ale w rzeczywistości jest zablokowany, czekam na dysk w każdej iteracji pętli.
Łącząc narzędzia systemowe (ps, top, vmstat, free), debugery (GDB, LLDB, dotnet-dump), analizatory pamięci (Valgrind) oraz polecenia sieciowe i dyskowe, można stworzyć sposób pracy, w którym problemy z pamięcią i wydajnością w systemie Linux przestaną być czarną skrzynką , a staną się czymś, co przy odrobinie metody i cierpliwości można zidentyfikować i naprawić z dużą dokładnością.