procstat ve /proc/PID/status ile Çalışan Süreçlerin Detaylı Durum Bilgilerini Okuma

Üretim ortamında bir süreç aniden yavaşladığında ya da beklenmedik miktarda bellek tüketmeye başladığında, ilk içgüdün muhtemelen top veya htop açmak olur. Ama bu araçlar sana sadece anlık bir fotoğraf verir. Bir sürecin gerçekten ne yaptığını, hangi dosya tanımlayıcılarını tuttuğunu, bellek haritasının nasıl göründüğünü veya sinyal durumunun ne olduğunu anlamak için çok daha derine inmek gerekiyor. İşte tam bu noktada /proc/PID/status dosyası ve procstat aracı devreye giriyor.

/proc Dosya Sistemi Hakkında Temel Bir Not

Linux çekirdeği, çalışan her süreç için /proc altında dinamik bir dizin oluşturur. Bu dizinler sabit diskten okunmuyor; çekirdek bunları bellekte tutuyor ve sen okumaya çalıştığında gerçek zamanlı olarak üretiyor. Yani /proc/1234/status dosyasını okuduğunda, disk I/O yok, sadece çekirdekten direkt veri akışı var.

Bu mimariyi anlamak önemli çünkü bu dosyaları okumak son derece hafif bir işlem. Binlerce süreci izleyen bir monitoring scripti yazarken bile /proc dosyalarını okumak, harici bir araç çağırmaktan çok daha az overhead yaratır.

/proc/PID/status Dosyasını Okumak

En basit haliyle bir sürecin durum bilgisine bakmak için:

cat /proc/$(pgrep nginx | head -1)/status

Çıktı uzun ve yoğun olacak. Satır satır incelemeye değer.

# Belirli bir PID için okuma
cat /proc/4521/status

# grep ile belirli alanları filtreleme
grep -E "^(Name|State|Pid|VmRSS|VmSize|Threads)" /proc/4521/status

Çıktıdan göreceğin temel alanların ne anlama geldiğini bilmek gerekiyor:

  • Name: Sürecin çekirdek tarafından görünen adı, maksimum 15 karakter
  • State: Sürecin anlık durumu. S uyku, R çalışıyor, D kesilemez uyku (bu önemli!), Z zombie, T durdurulmuş
  • Tgid: Thread grup ID’si, çok threadli uygulamalarda ana thread’in PID’i
  • Pid: Sürecin process ID’si
  • PPid: Parent process ID’si
  • VmPeak: Sürecin hayatı boyunca ulaştığı maksimum sanal bellek boyutu
  • VmSize: Anlık toplam sanal bellek kullanımı
  • VmRSS: Resident Set Size, fiziksel RAM’de gerçekten tutulan miktar
  • VmSwap: Swap’a atılmış bellek miktarı, sıfırdan büyükse dikkat et
  • Threads: Sürecin toplam thread sayısı
  • voluntary_ctxt_switches: Sürecin kendi isteğiyle yaptığı context switch sayısı
  • nonvoluntary_ctxt_switches: Çekirdek tarafından zorla yapılan context switch sayısı

Bellek Durumunu Analiz Etmek

Bir Java uygulamasının bellek sorununu araştırdığını düşün. VmRSS ile VmSize arasındaki fark sana önemli bir şey söyler:

# Bellek alanlarını izole et
grep "^Vm" /proc/$(pgrep java | head -1)/status

Eğer VmSize çok büyük ama VmRSS görece küçükse, uygulama büyük mmap bölgeleri rezerve etmiş ama henüz kullanmıyor demektir. Java heap’in lazy allocation yaptığı durumlarda bu çok yaygın. Ama VmSwap sıfırdan büyükse, o zaman gerçek bir bellek baskısı var ve OS sürecin bir kısmını swap’a attı.

# VmSwap değerini izle, sıfırdan büyükse alarm ver
SWAP=$(grep VmSwap /proc/4521/status | awk '{print $2}')
if [ "$SWAP" -gt "0" ]; then
    echo "UYARI: PID 4521 swap kullanıyor! ${SWAP} kB"
fi

State D Durumu: Kesilemez Uyku

State: D gördüğünde kıl dikkat. Bu durum, sürecin bir I/O işlemini beklerken kesilemez uyku modunda olduğunu gösterir. Normal şartlarda bu durum çok kısa sürer. Ama eğer bir süreç uzun süre D state’de kalıyorsa, bu ciddi bir problem işareti:

  • NFS mount noktasında timeout
  • Blok cihazında bozulma veya erişim problemi
  • Çekirdek bug’ı
  • Donmuş storage backend
# D state'deki süreçleri bul
for pid in /proc/[0-9]*/status; do
    state=$(grep "^State" $pid 2>/dev/null | awk '{print $2}')
    name=$(grep "^Name" $pid 2>/dev/null | awk '{print $2}')
    if [ "$state" = "D" ]; then
        echo "D State: $name (${pid%/status})"
    fi
done

Bu scripti cron’a koyup loglamak, intermittent storage problemlerini yakalamak için oldukça işe yarar bir yöntem.

Thread ve Sinyal Bilgileri

# Sinyal bilgilerini oku
grep -E "^(Sig|Cap)" /proc/4521/status

Çıktıdaki SigBlk, SigIgn, SigCgt değerleri hexadecimal sinyal maskeleri. Hangi sinyalin hangi bite karşılık geldiğini görmek için:

# SigCgt değerini decode et (caught signals)
SIGCGT=$(grep SigCgt /proc/4521/status | awk '{print $2}')
python3 -c "
import signal
mask = int('$SIGCGT', 16)
caught = [i for i in range(1, 65) if mask & (1 << (i-1))]
print('Yakalanan sinyaller:', caught)
"

Bu analiz, bir uygulamanın SIGTERM’i gerçekten işleyip işlemediğini anlamak için kullanışlı. Eğer SigCgt içinde SIGTERM (15) yoksa, kill -15 gönderdiğinde uygulama düzgün kapanmayacak demektir.

procstat ile BSD/FreeBSD Tarafı

FreeBSD ya da macOS üzerinde çalışıyorsan (veya her iki dünyada da süreç yönetimi yapıyorsan), procstat senin /proc/PID/status muadildin. Linux’ta procstat paketi de mevcut ama orijinal habitat’ı BSD tarafı.

FreeBSD’de procstat kurulumu için ek bir şey yapmana gerek yok, sistem ile birlikte geliyor:

# FreeBSD üzerinde temel kullanım
procstat -a          # Tüm süreçleri listele
procstat 1234        # Belirli bir PID için bilgi
procstat -v 1234     # Virtual memory map
procstat -f 1234     # Açık dosya tanımlayıcıları
procstat -s 1234     # Sinyal durumu
procstat -t 1234     # Thread bilgileri
procstat -k 1234     # Kernel stack

procstat -k özellikle ilginç. Bir sürecin neden D state’de olduğunu anlamak istediğinde kernel stack çıktısı sana tam olarak hangi kernel fonksiyonunda beklendiğini gösterir:

# FreeBSD'de donmuş bir süreci analiz et
procstat -k $(pgrep problematic_app)

Çıktıda nfs_biosread veya vn_lock gibi fonksiyon adları görüyorsan, storage katmanında bir problem olduğunu çok net anlarsın.

Linux’ta Daha Derine Gitmek: /proc/PID Alt Dosyaları

/proc/PID dizini sadece status dosyasından ibaret değil. Gerçek analiz için diğer dosyalara da bakmak gerekiyor:

# Sürecin açık dosyalarını listele
ls -la /proc/4521/fd/

# Dosya tanımlayıcı sayısını öğren
ls /proc/4521/fd/ | wc -l

# FD limitini status ile karşılaştır
cat /proc/4521/limits | grep "open files"

Eğer bir uygulama yavaşça bellek leak yapıyorsa, açık FD sayısını izlemek de önemli. Bazı leak’ler aslında FD leak’idir:

# FD sayısını logla
while true; do
    FD_COUNT=$(ls /proc/4521/fd 2>/dev/null | wc -l)
    echo "$(date): PID 4521 FD count: $FD_COUNT"
    sleep 30
done

/proc/PID/maps ve smaps ile Bellek Haritası

Status dosyası genel bellek bilgisi verir, ama hangi kütüphanenin ne kadar yer kapladığını görmek için maps veya daha detaylı smaps dosyasına bakman gerekiyor:

# Bellek haritasını oku
cat /proc/4521/maps

# smaps ile detaylı bellek analizi
cat /proc/4521/smaps | grep -A 20 "heap"

# Her mapping için RSS kullanımını hesapla
awk '/^[0-9a-f]/{name=$NF} /^Rss/{rss+=$2} END{print name, rss"kB"}' 
    /proc/4521/smaps

/proc/PID/io ile I/O İstatistikleri

# Sürecin I/O istatistiklerini oku
cat /proc/4521/io

Buradaki read_bytes ve write_bytes alanları, sürece atfedilen gerçek disk I/O’sunu gösterir. rchar ve wchar ise sistem çağrılarından geçen toplam byte sayısı, bunlar page cache üzerinden de olabilir.

# İki anlık okuma ile I/O hızını hesapla
read_bytes_1=$(grep "^read_bytes" /proc/4521/io | awk '{print $2}')
sleep 5
read_bytes_2=$(grep "^read_bytes" /proc/4521/io | awk '{print $2}')
io_rate=$(( (read_bytes_2 - read_bytes_1) / 5 ))
echo "Okuma hızı: ${io_rate} bytes/saniye"

Gerçek Dünya Senaryosu: Yavaşlayan Bir Web Uygulaması

Bir prodüksiyon ortamında web sunucusunun response time’ı aniden artmaya başladığını düşün. İşte adım adım nasıl yaklaşırsın:

# 1. Önce nginx worker'larının state'ini kontrol et
for pid in $(pgrep nginx); do
    state=$(grep "^State" /proc/$pid/status | awk '{print $2}')
    threads=$(grep "^Threads" /proc/$pid/status | awk '{print $2}')
    vmrss=$(grep "^VmRSS" /proc/$pid/status | awk '{print $2}')
    echo "PID: $pid | State: $state | Threads: $threads | RSS: ${vmrss}kB"
done
# 2. Context switch oranlarına bak, yüksek nonvoluntary değer CPU contention işareti
for pid in $(pgrep nginx); do
    vol=$(grep "voluntary_ctxt" /proc/$pid/status | head -1 | awk '{print $2}')
    nonvol=$(grep "nonvoluntary_ctxt" /proc/$pid/status | awk '{print $2}')
    echo "PID: $pid | Gönüllü: $vol | Zorla: $nonvol"
done

Eğer nonvoluntary_ctxt_switches değerleri çok yüksekse, CPU’da aşırı yük var ve scheduler süreçleri zorla kesip yeniden planlamak zorunda kalıyor. Bu, CPU core sayısını arttırmanın zamanı gelmiş olabileceğinin işareti.

# 3. Sürecin upstream bağlantılarına bak
ls -la /proc/$(pgrep nginx | head -1)/fd | grep socket | wc -l

Açık socket sayısı beklenenden çok fazlaysa, upstream bağlantıların kapanmadığını düşünebilirsin. Bu nokta ss veya netstat ile derinleştirilmeli.

Monitoring Script’i Bir Araya Getirmek

Yukarıdaki bilgileri birleştiren, üretimde kullanılabilir bir izleme scripti:

#!/bin/bash
# proc_monitor.sh - Süreç sağlık kontrolü

PROCESS_NAME=${1:-"nginx"}
SWAP_THRESHOLD_KB=51200     # 50MB
FD_THRESHOLD=1000
D_STATE_MAX_COUNT=3

D_STATE_COUNT=0
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')

for pid in $(pgrep "$PROCESS_NAME" 2>/dev/null); do
    [ ! -d "/proc/$pid" ] && continue

    NAME=$(grep "^Name" /proc/$pid/status 2>/dev/null | awk '{print $2}')
    STATE=$(grep "^State" /proc/$pid/status 2>/dev/null | awk '{print $2}')
    VMRSS=$(grep "^VmRSS" /proc/$pid/status 2>/dev/null | awk '{print $2}')
    VMSWAP=$(grep "^VmSwap" /proc/$pid/status 2>/dev/null | awk '{print $2:-0}')
    THREADS=$(grep "^Threads" /proc/$pid/status 2>/dev/null | awk '{print $2}')
    FD_COUNT=$(ls /proc/$pid/fd 2>/dev/null | wc -l)
    NONVOL=$(grep "nonvoluntary_ctxt" /proc/$pid/status 2>/dev/null | awk '{print $2}')

    echo "[$TIMESTAMP] PID=$pid NAME=$NAME STATE=$STATE RSS=${VMRSS}kB SWAP=${VMSWAP}kB THREADS=$THREADS FD=$FD_COUNT NONVOL_CTXT=$NONVOL"

    if [ "$STATE" = "D" ]; then
        D_STATE_COUNT=$((D_STATE_COUNT + 1))
        echo "KRITIK: PID $pid D state'de!"
    fi

    if [ "${VMSWAP:-0}" -gt "$SWAP_THRESHOLD_KB" ]; then
        echo "UYARI: PID $pid $VMSWAP kB swap kullanıyor!"
    fi

    if [ "$FD_COUNT" -gt "$FD_THRESHOLD" ]; then
        echo "UYARI: PID $pid $FD_COUNT açık FD'ye sahip!"
    fi
done

if [ "$D_STATE_COUNT" -gt "$D_STATE_MAX_COUNT" ]; then
    echo "KRITIK: $D_STATE_COUNT süreç D state'de, storage problemi olabilir!"
fi

Bu scripti cron ile her 5 dakikada bir çalıştırıp log dosyasına yönlendirdiğinde, zaman içinde eğilimleri görebilir hale gelirsin.

/proc/PID/status Okurken Dikkat Edilecekler

Birkaç pratik not:

  • /proc/PID/status dosyasını okurken süreç sonlanmışsa dosya kaybolur. Scriptlerde her zaman hata kontrolü yap.
  • Aynı sürecin birden fazla threadini izliyorsan, /proc/PID/task/TID/status formatını kullan.
  • VmRSS değeri shared memory’yi de içerir. Gerçek private kullanım için smaps içindeki Private_Dirty toplamına bak.
  • Çekirdek thread’leri (isim köşeli parantez içinde görünenler, [kworker] gibi) için bazı alanlar her zaman mevcut olmayabilir.
# Thread seviyesinde status okuma
cat /proc/4521/task/4521/status   # Ana thread
cat /proc/4521/task/4522/status   # Başka bir thread
ls /proc/4521/task/               # Tüm thread'leri listele

Sonuç

/proc/PID/status ve procstat araçları, bir sürecin iç dünyasına bakmanın en doğrudan yolu. top ve ps gibi araçlar kullanışlı, ama bunlar aslında /proc dosyalarından okuduklarını işleyip sunan sarmalayıcılar. Kaynağa direkt gitmek, hem daha fazla kontrol hem de daha az overhead demek.

Özellikle D state takibi, VmSwap izleme ve context switch analizi, prodüksiyon problemlerini erken yakalamak için güçlü üçlü. Bu verileri düzenli olarak loglayan, eşik değerlerini aşınca alarm üreten bir yapı kurduğunda, “neden yavaşladı” sorusunu olaydan sonra sormak yerine, “neden yavaşlıyor” sorusunu olay sırasında cevaplayabilir hale gelirsin.

Monitoring katmanını ne kadar zenginleştirirsen zenginleştir, sonunda ham verilere dönmek zorunda kalacaksın. /proc tam da orada, her zaman hazır.

Bir yanıt yazın

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