pmap Komutu ile Süreç Bellek Haritasını Analiz Etme

Bir üretim sunucusunda bellek kullanımı aniden yükselmeye başladığında, ilk refleks genellikle top veya htop açmak oluyor. Ama bu araçlar size sürecin toplam bellek kullanımını gösterir; asıl soru şu: o bellek nerede kullanılıyor, hangi kütüphane ne kadar yer kaplıyor, paylaşılan segment mi, özel mi? İşte tam bu noktada pmap devreye giriyor.

pmap, bir sürecin sanal bellek haritasını adres aralıkları, boyutlar, izinler ve eşleme kaynakları ile birlikte listeleyen bir araç. /proc//maps dosyasının okunabilir ve analiz edilebilir hali diyebiliriz buna. Yıllarca bu komutu yüzeysel kullanan sysadmin’ler gördüm; oysa doğru kullanıldığında memory leak tespitinden kütüphane bağımlılığı analizine kadar ciddi işler yapıyor.

pmap Nedir ve Ne İşe Yarar

pmap, procps paketinin bir parçası olarak gelir. Çoğu Linux dağıtımında varsayılan olarak kurulu bulunur. Temel işlevi, bir process ID’sine ait bellek eşlemelerini (memory mappings) insan okunabilir biçimde sunmak.

Bir süreç çalışırken işletim sistemi ona sanal bir adres alanı tahsis eder. Bu alan; kod segmentini, yığını (heap), yığıt (stack), paylaşılan kütüphaneleri ve anonim eşlemeleri içerir. pmap tüm bu parçaları tek bir çıktıda birleştirir.

Günlük sysadmin hayatında şu senaryolarda kullanıyorum:

  • Bellek sızıntısı şüphesi olan bir Java veya Python uygulamasını incelemek
  • Hangi paylaşılan kütüphanelerin yüklendiğini doğrulamak
  • Bir servisin neden beklenenin çok üzerinde RSS kullandığını anlamak
  • Containerized uygulamalarda bellek ayak izini optimize etmek

Temel Kullanım

Bir sürecin PID’ini bulduktan sonra en basit kullanım şu:

pmap 1234

Bu komut, PID 1234’e ait bellek haritasını standart çıktıya basar. Daha işe yarar olan -x (extended) veya -X (daha ayrıntılı extended) modlarıdır.

# Genişletilmiş format - en sık kullandığım mod
pmap -x 1234

# Daha detaylı format, kernel sürümüne göre farklı kolonlar gösterebilir
pmap -X 1234

# Tüm süreçlere aynı anda bakmak için (dikkatli kullanın)
pmap -x $(pgrep nginx | head -5)

pmap -x çıktısını ilk gördüğünüzde biraz bunaltıcı gelebilir. Kolonları tanımak önemli:

  • Address: Eşlemenin başlangıç sanal adresi
  • Kbytes: Eşlemenin toplam boyutu (kilobayt)
  • RSS: Resident Set Size, fiziksel RAM’de gerçekten bulunan kısım
  • Dirty: Değiştirilmiş ama henüz diske yazılmamış sayfalar
  • Mode: Okuma/yazma/çalıştırma izinleri
  • Mapping: Eşlemenin kaynağı (dosya adı veya [anon], [heap], [stack])

Gerçek Dünya Senaryosu 1: Memory Leak Tespiti

Bir müşteri ortamında bir Node.js uygulaması vardı, her gün yaklaşık 200MB büyüyordu ve kimse nedenini bulamıyordu. Klasik yöntemle PID’i alıp pmap -x çıktısını zaman içinde kaydetmeye başladım:

# Belirli aralıklarla anlık görüntü almak için basit bir döngü
for i in $(seq 1 10); do
    echo "=== Snapshot $i - $(date) ===" >> /tmp/memory_snapshots.txt
    pmap -x $(pgrep node) >> /tmp/memory_snapshots.txt
    sleep 300
done

Daha sonra anonim eşlemelerin (anon) toplam boyutunu karşılaştırdım:

# Belirli bir PID'in anonim eşleme toplamını çıkar
pmap -x 4821 | grep anon | awk '{sum += $2} END {print sum " KB"}'

Günler içinde [anon] alanlarının büyüdüğünü, kütüphane alanlarının sabit kaldığını gördüm. Bu, kütüphane kaynaklı değil, uygulama heap’inde büyüyen bir durum olduğunu gösteriyordu. Geliştirici ekibine bu veriyi gösterince önce inanmadılar, ama sayılar konuştu.

Gerçek Dünya Senaryosu 2: Paylaşılan Kütüphane Analizi

Bir PHP-FPM kurulumunda worker process’lerin neden bu kadar fazla RAM yediğini araştırıyordum. Aynı kütüphaneler her worker tarafından yükleniyor muydu, paylaşılıyor muydu?

# PHP-FPM worker'larından birinin PID'ini alalım
PHPFPM_PID=$(pgrep -f "php-fpm: worker" | head -1)
pmap -x $PHPFPM_PID | grep ".so" | sort -k2 -rn | head -20

Bu çıktı bana hangi .so dosyalarının en çok yer kapladığını gösterdi. Önemli nokta: Kütüphane eşlemeleri genellikle r-xp (read-execute, private/copy-on-write) olarak işaretlenir. RSS değerinin düşük olması, o kütüphane sayfalarının çoğunun paylaşıldığı anlamına gelir; bu iyi bir işaret.

Ama aynı kütüphane birden fazla worker’da Dirty sütununda yüksek değer gösteriyorsa, COW (copy-on-write) tetiklenmiş demektir ve her worker aslında kendi kopyasını RAM’de tutuyor. Bu PHP durumunda OPcache ayarlarıyla çözülebilir bir sorundu.

pmap -d ile Cihaz Formatı

-d bayrağı biraz farklı bir format sunar ve bazı durumlarda daha temiz bir genel bakış sağlar:

pmap -d 1234

Bu format çıktının sonunda şu satırı ekler:

mapped: 245760K    writeable/private: 18432K    shared: 204800K

Bu özet satır çok değerlidir. Özellikle:

  • mapped: Toplam eşlenmiş sanal adres alanı
  • writeable/private: Sürece özel, yazılabilir bölgeler (heap, stack, COW tetiklenmiş sayfalar)
  • shared: Paylaşılan eşlemeler (genellikle kütüphaneler)

Bir sürecin “şiştiğini” hızlıca anlamak için writeable/private değerine bakıyorum. Bu değer zamanla artıyorsa ciddi bir sorun var demektir.

Toplu Süreç Analizi

Birden fazla süreç üzerinde çalışmak gerektiğinde, mesela tüm Apache worker’larını veya Java instance’larını karşılaştırmak istediğimde şunu kullanıyorum:

# Tüm Java process'lerinin bellek özetini göster
for pid in $(pgrep java); do
    echo -n "PID $pid: "
    pmap -d $pid | tail -1
done
# Belirli bir uygulamanın tüm worker'larını heap boyutuna göre sırala
for pid in $(pgrep -f "gunicorn"); do
    heap_kb=$(pmap -x $pid | grep "[heap]" | awk '{print $2}')
    echo "$pid $heap_kb KB"
done | sort -k2 -rn

Bu ikinci örnek özellikle işe yarıyor. Aynı konfigürasyondaki worker’ların heap boyutları birbirinden çok farklıysa, bazıları bellek tutuyordur ve restart edilmeleri gerekebilir.

/proc/maps ile Karşılaştırma

pmap aslında /proc//maps ve /proc//smaps dosyalarını okuyor. Bazen ham veriyle çalışmak daha fazla esneklik sağlar:

# /proc/maps dosyasını direkt okumak
cat /proc/1234/maps

# smaps daha detaylı bilgi verir - pmap -X'in kaynağı
cat /proc/1234/smaps | grep -A 15 "heap"

smaps dosyası her eşleme için Pss (Proportional Set Size) değerini de verir. Bu değer, paylaşılan sayfaları paylaşan süreç sayısına bölerek hesaplar. Bir sürecin gerçek bellek maliyetini ölçmek istiyorsanız RSS yerine PSS bakın.

# smaps_rollup ile özet PSS değeri
cat /proc/1234/smaps_rollup | grep Pss

Bu, containerized ortamlarda kaynak limiti hesaplarken kritik bir fark yaratıyor.

pmap ile Stack Boyutu İnceleme

Çok iş parçacıklı uygulamalarda thread stack’leri ciddi bellek tüketimine yol açabilir. Her thread kendi stack’ine sahiptir ve bu [stack] veya [stack:] olarak görünür:

# Tüm thread stack'lerini listele
pmap -x 5678 | grep stack

# Stack'lerin toplam boyutu
pmap -x 5678 | grep stack | awk '{sum += $2} END {print "Total stack: " sum " KB"}'

Bir keresinde bir Java uygulamasında 800’den fazla thread olduğunu ve her birinin 512KB stack tuttuğunu keşfettim. Toplam 400MB sadece stack’lerden geliyordu. Uygulama kodunda thread pool yönetimi yoktu, her istek yeni bir thread açıyordu. pmap bunu görünür kılmadan önce kimse farkında değildi.

Bellek İzinleri ve Güvenlik Analizi

pmap çıktısındaki Mode kolonu güvenlik açısından da incelenebilir. rwx iznine sahip bir eşleme (hem yazılabilir hem çalıştırılabilir) şüphe uyandırmalıdır:

# rwx izinli eşlemeleri ara - potansiyel güvenlik sorunu
pmap -x 1234 | grep "rwx"

# Tüm running process'lerde rwx eşleme ara
for pid in /proc/[0-9]*/maps; do
    if grep -q "rwx" "$pid" 2>/dev/null; then
        echo "Şüpheli: $pid"
        grep "rwx" "$pid"
    fi
done

Normal bir uygulamada rwx görmeniz nadir olmalı. JIT kullanan uygulamalar (Java HotSpot, V8, vb.) istisna olabilir ama yine de belgelenmiş olması gerekir.

Otomatik Bellek İzleme Scripti

Üretim ortamlarında kullandığım basit ama etkili bir izleme scripti:

#!/bin/bash
# memory_watch.sh - Belirli bir process'i izler ve anomali tespit eder

PROCESS_NAME="${1:-nginx}"
THRESHOLD_KB="${2:-1048576}"  # Varsayılan 1GB
LOG_FILE="/var/log/memory_watch_${PROCESS_NAME}.log"
CHECK_INTERVAL=60

echo "$(date): İzleme başladı - Process: $PROCESS_NAME, Eşik: ${THRESHOLD_KB}KB" >> "$LOG_FILE"

while true; do
    for pid in $(pgrep -x "$PROCESS_NAME" 2>/dev/null); do
        # pmap -d ile özet al
        summary=$(pmap -d "$pid" 2>/dev/null | tail -1)
        writeable=$(echo "$summary" | grep -oP 'writeable/private: K[0-9]+')
        
        if [ -z "$writeable" ]; then
            continue
        fi
        
        echo "$(date) PID $pid: writeable/private=${writeable}KB" >> "$LOG_FILE"
        
        if [ "$writeable" -gt "$THRESHOLD_KB" ]; then
            echo "$(date) UYARI: PID $pid eşiği aştı! ${writeable}KB > ${THRESHOLD_KB}KB" >> "$LOG_FILE"
            # Anlık görüntü al
            pmap -x "$pid" >> "/var/log/pmap_snapshot_${pid}_$(date +%Y%m%d%H%M%S).log"
            # Buraya alert mekanizması eklenebilir
        fi
    done
    sleep "$CHECK_INTERVAL"
done

Bu scripti systemd servisi olarak veya screen/tmux içinde çalıştırabiliriz. Eşik aşıldığında otomatik olarak tam pmap -x çıktısını kaydetmesi, incident sonrası analiz için çok değerli.

Yaygın Hatalar ve Dikkat Edilmesi Gerekenler

pmap kullanırken birkaç şeyi aklınızda tutun:

  • Kbytes ile RSS karıştırma: Kbytes sanal adres alanını gösterir, RSS ise fiziksel RAM’de ne kadar olduğunu. Yüksek Kbytes ama düşük RSS normal olabilir; swap’a düşmüş veya henüz erişilmemiş sayfalar olabilir.
  • Root yetkisi bazen gerekli: Başka kullanıcıların süreçlerine pmap ile bakabilmek için genellikle root veya ptrace yetkisi gerekir. sudo pmap kullanın ya da CAP_SYS_PTRACE capability’sini verin.
  • Kısa ömürlü process’ler: pmap çalıştırdığınız an süreç sonlanmışsa “No such process” hatası alırsınız. Script yazarken bu durumu handle edin.
  • Çok iş parçacıklı process’lerde: pmap PID’e göre çalışır. Bir thread’in TID’ini verirseniz ana sürecin haritasını göreceksiniz zaten, Linux’ta thread’ler aynı adres alanını paylaşır.
  • smaps ile pmap farkı: pmap -X daha doğru veri sunar ama daha yavaştır. Canlı sistemde çok sayıda süreç üzerinde döngü kuruyorsanız bu gecikmeyi hesaba katın.

Alternatif Araçlarla Birlikte Kullanım

pmap tek başına yeterli olmayabilir. Genellikle şu araçlarla birlikte kullanıyorum:

  • valgrind memcheck: Leak tespiti için, özellikle geliştirme ortamında
  • heaptrack: Heap allocationları izlemek için daha modern bir araç
  • smem: PSS tabanlı bellek kullanımını raporlar, sistem geneli değerlendirme için iyi
  • perf: Bellek erişim kalıplarını anlamak için
# smem ile pmap karşılaştırması - hangi process gerçekten ne kadar yer tutuyor
smem -r -p -k | head -20

# Belirli bir process'in PSS değerini hesapla
cat /proc/$(pgrep -n nginx)/smaps | awk '/^Pss:/{sum += $2} END {print sum " KB PSS"}'

Sonuç

pmap, Linux sistem yönetiminde haksız yere göz ardı edilen bir araç. top ile başlayıp pmap ile derinleşen bir analiz akışı, çoğu bellek sorununu ciddi ölçüde kısaltılmış sürede çözmenizi sağlar.

Pratik özet olarak: Bir sürecin bellek kullanımı şüpheli görünüyorsa önce pmap -d ile writeable/private değerine bakın. Bu değer zamanla artıyorsa leak var demektir. Sonra pmap -x ile hangi eşleme kaynaklı olduğunu tespit edin. [anon] artıyorsa heap leak, belirli bir .so artıyorsa kütüphane kaynaklı bir sorun arayın.

Komut basit görünüyor ama verdiği bilgi derinliği ciddi. Bir de üretim ortamında panik halindeyken pmap çıktısını okumayı öğrenmiş olmak, o anı çok daha sakin geçirmenizi sağlıyor.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir