awk ile Sistem Kaynaklarını İzleme: /proc Dosyalarından CPU ve Bellek Kullanım Raporu Üretme

Sistem yöneticiliğinin belki de en can sıkıcı ama bir o kadar da kritik tarafı şu: bir şeyler patlamadan önce nerede patlamanın kaynağını bulmak. Yıllar önce bir production sunucusunda gece yarısı aldığım alarm hâlâ aklımda. top açtım, bir şeyler gördüm ama tam olarak ne olduğunu anlayamadım. O gece /proc dosyalarını elle okumayı ve awk ile işlemeyi öğrenmek zorunda kaldım. Şimdi o deneyimi sizinle paylaşmak istiyorum, çünkü grafana dashboard’ları ve fancy monitoring araçları harika, ama iş kriz anına gelince komut satırında ne kadar hızlı ve doğru davranabildiğiniz her şeyi belirliyor.

/proc Neden Bu Kadar Önemli?

Linux çekirdeği sistem hakkındaki her şeyi /proc sanal dosya sistemi üzerinden dışa aktarır. Bu bir disk dosyası değil, çekirdeğin anlık olarak ürettiği bir bilgi akışı. top, htop, vmstat, free gibi araçların hepsi arka planda bu dosyaları okuyor. Yani siz de okuyabilirsiniz, üstelik bu araçların size gösterdiğinden çok daha ham ve esnek bir şekilde.

/proc/stat, /proc/meminfo, /proc/loadavg, /proc/pid/stat gibi dosyalar ham veriyi düz metin olarak sunuyor. awk ise bu metni işlemek için biçilmiş kaftan. Neden Python veya Perl değil de awk diye sorabilirsiniz: çünkü awk her Linux sisteminde var, bağımlılık yok, script dosyası gerektirmiyor ve tek satırda ciddi iş yapabiliyor.

/proc/stat ile CPU Kullanımını Hesaplamak

CPU kullanımını gerçek anlamda ölçmek doğrudan değil. /proc/stat size anlık yüzde değil, boot’tan bu yana geçen jiffies (çekirdek zaman dilimleri) sayısını veriyor. Gerçek kullanım yüzdesini hesaplamak için iki okuma arasındaki farkı almanız gerekiyor.

Önce /proc/stat dosyasının yapısına bakalım:

cat /proc/stat | head -5

Çıktı şuna benzer:

cpu  245781 1024 89234 8923456 12034 0 3421 0 0 0
cpu0 62341 256 22134 2234567 3012 0 856 0 0 0
cpu1 61234 234 21987 2235123 3001 0 854 0 0 0
...

İlk satırdaki alanlar sırayla: user, nice, system, idle, iowait, irq, softirq, steal, guest, guest_nice.

CPU kullanım yüzdesini hesaplayan basit bir awk tek satırı:

awk '/^cpu / {
    idle = $5
    total = $2+$3+$4+$5+$6+$7+$8
    print "Toplam CPU Zamanı:", total, "| Idle:", idle
}' /proc/stat

Ama tek okuma size hiçbir şey söylemez. İki okuma arasındaki farkı hesaplamak için şu script’i kullanıyorum:

awk '
BEGIN {
    # İlk okuma
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu /) {
            split(line, a)
            idle1 = a[5]
            total1 = a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
            break
        }
    }
    close("/proc/stat")
    system("sleep 1")
    
    # İkinci okuma
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu /) {
            split(line, b)
            idle2 = b[5]
            total2 = b[2]+b[3]+b[4]+b[5]+b[6]+b[7]+b[8]
            break
        }
    }
    
    diff_idle = idle2 - idle1
    diff_total = total2 - total1
    cpu_usage = (1 - diff_idle/diff_total) * 100
    printf "CPU Kullanimi: %.2f%%n", cpu_usage
}
' /dev/null

Bu script 1 saniye bekleyip iki ölçüm arasındaki farktan yüzde hesaplıyor. Production’da kullandığınızda sleep 1 yerine sleep 5 veya sleep 10 daha sağlıklı sonuç veriyor.

Çekirdek Başına CPU Analizi

Tek sunucu değil, çok çekirdekli sistemlerde yük dengesizliği yaygın bir sorun. Bir çekirdek %100’deyken diğerleri boşta olabilir; top‘ta bunu kaçırabilirsiniz. Şu script tüm çekirdekleri tarar:

cat > /usr/local/bin/cpu_cores.awk << 'EOF'
BEGIN {
    print "Cekirdek Basina CPU Kullanim Raporu"
    print "====================================="
    
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu[0-9]/) {
            split(line, a)
            core = a[1]
            idle[core] = a[5]
            total[core] = a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
        }
    }
    close("/proc/stat")
    system("sleep 2")
    
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu[0-9]/) {
            split(line, b)
            core = b[1]
            diff_idle = b[5] - idle[core]
            diff_total = (b[2]+b[3]+b[4]+b[5]+b[6]+b[7]+b[8]) - total[core]
            
            if (diff_total > 0) {
                usage = (1 - diff_idle/diff_total) * 100
                bar = ""
                bars = int(usage / 5)
                for (i=0; i<bars; i++) bar = bar "#"
                for (i=bars; i<20; i++) bar = bar "."
                printf "%-6s [%s] %5.1f%%n", core, bar, usage
            }
        }
    }
}
EOF

awk -f /usr/local/bin/cpu_cores.awk /dev/null

Bu script her çekirdek için ASCII progress bar bile çiziyor. Gereksiz süslü bir özellik gibi görünebilir ama gece yarısı terminale bakıyorsanız görsel yoğunluk canınızı kurtarır.

/proc/meminfo ile Bellek Raporlama

Bellek analizi CPU’dan daha düz bir iş. /proc/meminfo anlık değerleri kilobyte cinsinden veriyor:

cat /proc/meminfo | head -10
MemTotal:       32768000 kB
MemFree:         4523412 kB
MemAvailable:   18234567 kB
Buffers:         1234567 kB
Cached:         12345678 kB
SwapCached:       123456 kB
...

Burada kritik bir nokta var: MemFree yanıltıcı. Linux belleği agresif olarak önbellekler, bu yüzden “boş” bellek düşük görünür ama bu sorun değil. Gerçekten kullanılabilir bellek MemAvailable alanında. Bunu bilmeden alarm kuranlar gereksiz gece uyandırmalar yaşıyor.

Kapsamlı bir bellek raporu:

awk '
/MemTotal/     { total = $2 }
/MemAvailable/ { available = $2 }
/MemFree/      { free = $2 }
/Buffers/      { buffers = $2 }
/^Cached/      { cached = $2 }
/SwapTotal/    { swap_total = $2 }
/SwapFree/     { swap_free = $2 }
/Dirty/        { dirty = $2 }
END {
    used = total - available
    used_pct = (used / total) * 100
    swap_used = swap_total - swap_free
    
    print "========== BELLEK RAPORU =========="
    printf "Toplam RAM   : %8.1f MBn", total/1024
    printf "Kullanilan   : %8.1f MB (%.1f%%)n", used/1024, used_pct
    printf "Kullanilabilir: %7.1f MBn", available/1024
    printf "Buffer/Cache : %8.1f MBn", (buffers+cached)/1024
    printf "Dirty Pages  : %8.1f MBn", dirty/1024
    print "-----------------------------------"
    if (swap_total > 0) {
        swap_pct = (swap_used / swap_total) * 100
        printf "Swap Toplam  : %8.1f MBn", swap_total/1024
        printf "Swap Kullanim: %8.1f MB (%.1f%%)n", swap_used/1024, swap_pct
        if (swap_pct > 20) print "UYARI: Swap kullanimi yuksek!"
    } else {
        print "Swap         : Devre disi"
    }
    print "==================================="
}
' /proc/meminfo

Dirty pages değeri özellikle I/O yoğun sistemlerde önemli. Yüksekse ya disk yavaş ya da flush mekanizması tıkanmış demek.

Süreç Bazında Kaynak Analizi

Hangi process en çok bellek yiyor? /proc/[pid]/status ve /proc/[pid]/stat dosyaları bunu söyler. Tüm çalışan süreçleri tarayıp rapor üretelim:

awk '
BEGIN {
    print "PID      RSS(MB)  Süreç Adı"
    print "--------------------------------"
    
    cmd = "ls /proc | grep -E "^[0-9]+$""
    while ((cmd | getline pid) > 0) {
        status_file = "/proc/" pid "/status"
        if ((getline < status_file) < 0) continue
        
        name = ""
        rss = 0
        
        while ((getline line < status_file) > 0) {
            if (line ~ /^Name:/) {
                split(line, a, "t")
                name = a[2]
            }
            if (line ~ /^VmRSS:/) {
                split(line, a)
                rss = a[2]
            }
        }
        close(status_file)
        
        if (rss > 0) {
            printf "%-8s %-8.1f %sn", pid, rss/1024, name | "sort -k2 -rn | head -15"
        }
    }
    close(cmd)
    close("sort -k2 -rn | head -15")
}
' /dev/null

Bu script çalışan tüm süreçlerin RSS (Resident Set Size, fiziksel bellek kullanımı) değerlerini toplayıp büyükten küçüğe sıralar. VSZ değil RSS’e bakın, VSZ sanal belleği gösteriyor ve çoğu zaman yanıltıcı derecede büyük.

Gerçek Dünya Senaryosu: Kapsamlı Sistem Raporu

Şimdi bunları birleştirerek gerçekten işe yarar bir script yazalım. Bu script birçok müşterimde monitoring çözümü olarak çalışıyor, özellikle Zabbix veya Prometheus kuramayacakları küçük ortamlarda:

cat > /usr/local/bin/sysreport.sh << 'SCRIPT'
#!/bin/bash
# Sistem Kaynak Raporu - awk tabanli
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
HOSTNAME=$(hostname)

echo "================================================"
echo "SISTEM RAPORU: $HOSTNAME"
echo "Zaman: $TIMESTAMP"
echo "================================================"

# Load Average
awk '{
    printf "Load Average (1/5/15 dk): %s / %s / %sn", $1, $2, $3
    printf "Calisan Surec: %sn", $4
}' /proc/loadavg

echo ""

# CPU Kullanimi
awk '
BEGIN {
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu /) {
            split(line, a)
            idle1=a[5]; total1=a[2]+a[3]+a[4]+a[5]+a[6]+a[7]+a[8]
        }
    }
    close("/proc/stat")
    system("sleep 1")
    while ((getline line < "/proc/stat") > 0) {
        if (line ~ /^cpu /) {
            split(line, b)
            idle2=b[5]; total2=b[2]+b[3]+b[4]+b[5]+b[6]+b[7]+b[8]
        }
    }
    diff_idle=idle2-idle1; diff_total=total2-total1
    usage=(1-diff_idle/diff_total)*100
    printf "CPU Kullanimi: %.1f%%", usage
    if (usage > 80) printf " [YUKSEK!]"
    printf "n"
}
' /dev/null

echo ""

# Bellek
awk '
/MemTotal/     { total=$2 }
/MemAvailable/ { avail=$2 }
/SwapTotal/    { st=$2 }
/SwapFree/     { sf=$2 }
END {
    used=total-avail
    pct=(used/total)*100
    printf "Bellek: %.1f/%.1f GB (%.1f%% kullaniliyor)n",
        used/1048576, total/1048576, pct
    if (pct > 85) print "UYARI: Bellek kullanimi kritik seviyede!"
    if (st > 0 && sf < st) {
        swap_used_pct=((st-sf)/st)*100
        printf "Swap: %.1f%% kullaniliyorn", swap_used_pct
    }
}
' /proc/meminfo

echo ""
echo "En Cok Bellek Kullanan 5 Surecler:"

find /proc -maxdepth 2 -name status 2>/dev/null | awk '
{
    name=""; rss=0
    while ((getline line < $0) > 0) {
        if (line ~ /^Name:/) { split(line,a,"t"); name=a[2] }
        if (line ~ /^VmRSS:/) { split(line,a); rss=a[2] }
    }
    close($0)
    if (rss > 1024) printf "%.1f MBt%sn", rss/1024, name
}
' | sort -rn | head -5

echo "================================================"
SCRIPT

chmod +x /usr/local/bin/sysreport.sh

Bu script’i cron’a ekleyip loglamak için:

# Her 5 dakikada bir çalıştır, logla
echo "*/5 * * * * root /usr/local/bin/sysreport.sh >> /var/log/sysreport.log 2>&1" >> /etc/cron.d/sysreport

# Log dosyasını logrotate ile yönet
cat > /etc/logrotate.d/sysreport << 'EOF'
/var/log/sysreport.log {
    daily
    rotate 7
    compress
    missingok
    notifempty
}
EOF

Threshold Tabanlı Alarm Mekanizması

Rapor üretmek yeterli değil, kritik değerlerde alarm gerecek bir mekanizma da lazım. Şu awk bloğu basit ama etkili bir eşik kontrolü yapıyor:

awk '
BEGIN {
    CPU_WARN=70; CPU_CRIT=90
    MEM_WARN=80; MEM_CRIT=90
    
    # Bellek kontrolü
    while ((getline line < "/proc/meminfo") > 0) {
        if (line ~ /MemTotal/)     { split(line,a); total=a[2] }
        if (line ~ /MemAvailable/) { split(line,a); avail=a[2] }
    }
    close("/proc/meminfo")
    
    mem_pct = ((total-avail)/total)*100
    
    if (mem_pct >= MEM_CRIT)
        printf "KRITIK: Bellek kullanimi %.1f%% (esik: %d%%)n", mem_pct, MEM_CRIT
    else if (mem_pct >= MEM_WARN)
        printf "UYARI: Bellek kullanimi %.1f%% (esik: %d%%)n", mem_pct, MEM_WARN
    else
        printf "OK: Bellek kullanimi %.1f%%n", mem_pct
}
' /dev/null

Bu çıktıyı mail veya curl ile bir Slack webhook’una gönderebilirsiniz. Basit ama işlevsel.

İpuçları ve Dikkat Edilmesi Gerekenler

awk ile /proc dosyalarını işlerken öğrendiğim bazı pratik notlar:

  • /proc dosyaları her okumada farklı değer döndürebilir, bu yüzden aynı script içinde aynı dosyayı birden fazla açıp kapatıyorsanız close() çağrısını unutmayın. Yoksa stale cache’den okursunuz.
  • getline hata yönetimi şart, /proc/[pid]/ dosyaları process ölünce anında kayboluyor. -1 dönüşünü kontrol etmezseniz script garip davranır.
  • Jiffies frekansı sistem bağımlı, genellikle 100 Hz ama bazı gömülü sistemlerde farklı. getconf CLK_TCK ile kontrol edin.
  • MemAvailable Linux 3.14’te eklendi, eski kernel’larda yoktur. Bunu hesaplamanız gerekirse MemFree + Buffers + Cached kullanın, yakın bir tahmin verir.
  • awk ile arithmetic overflow dikkat, büyük sistemlerde jiffies sayıları çok büyüyebilir. Mümkünse gawk kullanın, integer aralığı daha geniş.
  • Çok sık okuma yapma, /proc/stat okumak ucuz bir operasyon ama döngü içinde binlerce sürecin status dosyasını okumak yavaşlayabilir. 5-10 saniye aralıklar production için makul.

Performans Karşılaştırması

Merak edenler için: bu awk tabanlı yaklaşım python3 ile yazılmış eşdeğerine göre genellikle 3-5x daha hızlı başlıyor (interpreter yükleme maliyeti). Uzun çalışan süreçlerde fark kapanıyor ama monitoring script’leri kısa ömürlüdür, startup hızı önemli.

/proc tabanlı okuma vs. ps aux parse etme konusunda da /proc kazanıyor. ps zaten /proc‘u okuyor ama formatlama ve process spawn maliyeti var. Direkt okumak her zaman daha verimli.

Sonuç

awk ve /proc kombinasyonu, sistem yöneticisinin kutusundaki en hafif ama en güçlü araçlardan biri. Fancy monitoring platformları olmayan ortamlarda, container içinde araç kısıtlı olduğunda, ya da gece yarısı “neden yavaş?” sorusuna hızlı cevap bulmak gerektiğinde bu bilgi altın değerinde.

Buradaki script’leri olduğu gibi kullanmayın: kendi ortamınıza göre threshold değerlerini ayarlayın, kendi log formatınızı belirleyin ve log rotation’ı mutlaka kurun. Monitoring’in kendisi kaynak tüketmemeli.

Son bir not: bu script’leri yazdıktan sonra bir yere belgeleyin. Altı ay sonra siz bile ne yaptığınızı unutabilirsiniz, gece yarısı bunu anlamaya çalışmak istemezsiniz.

Bir yanıt yazın

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