Sunucuda bir şeyler ters gidiyor: sistemin fanları deliler gibi dönüyor, uygulama yanıt vermekte gecikiyor ve SSH bağlantısı bile kasıyor. Deneyimli bir sysadmin olarak bu belirtileri görünce aklıma ilk gelen soru şu oluyor: “Hangi süreç CPU’yu yiyor?” Bu soruyu yanıtlamak hem hızlı hem de doğru yapılması gereken bir iş. Yanlış süreci kapatırsanız production ortamını çökertebilirsiniz, analizi yüzeysel bırakırsanız sorun birkaç saat sonra tekrar karşınıza çıkar.
Bu yazıda gerçek dünya senaryoları üzerinden ilerliyoruz. Sadece komutları listelemekle kalmayacağız, o komutların çıktılarını nasıl okuyacağınızı ve bir sonraki adımda ne yapmanız gerektiğini de konuşacağız.
İlk Bakış: top ve htop ile Durumu Değerlendirmek
Sisteme bağlandığınızda ilk refleksiniz top komutu olmalı. Evet, herkes biliyor, ama çıktıyı doğru okumak önemli.
top
top açıldığında önce özet satırlarına bakın. Şöyle bir şey göreceksiniz:
top - 14:32:11 up 47 days, 3:21, 2 users, load average: 8.45, 7.23, 5.12
Tasks: 312 total, 3 running, 309 sleeping, 0 stopped, 0 zombie
%Cpu(s): 94.2 us, 3.1 sy, 0.0 ni, 1.8 id, 0.7 wa, 0.0 hi, 0.2 si
MiB Mem : 32048.0 total, 1243.2 free, 28654.4 used, 2150.4 buff/cache
Burada dikkat edilmesi gerekenler:
- load average: 8 çekirdekli bir sistemde bu değerlerin 8’in üzerinde olması sisteminizin kapasitesini aştığı anlamına gelir
- 94.2 us: CPU’nun yüzde 94’ü kullanıcı alanı proseslerine gidiyor, bu ciddi bir rakam
- 0.7 wa: I/O bekleme süresi düşük, yani sorun disk değil CPU kaynaklı
- 1.8 id: CPU’nun sadece yüzde 1.8’i boşta, sistem gerçekten sıkışmış durumda
top içinde P tuşuna basarak CPU kullanımına göre sıralayabilirsiniz. M belleğe göre sıralar. k tuşuyla PID girerek doğrudan süreç sonlandırabilirsiniz ama bunu yapmadan önce analizi tamamlayın.
htop kuruluysa çok daha rahat bir deneyim sunar:
htop -d 5
-d 5 parametresi 0.5 saniyelik güncelleme aralığı belirler. htop‘ta F6 ile sıralama kriterini değiştirebilir, F9 ile sinyal gönderebilirsiniz.
ps ile Detaylı Süreç Analizi
top anlık durumu gösterir ama ps ile o anki süreç listesinin daha ayrıntılı bir fotoğrafını çekebilirsiniz. Özellikle scriptlerde ve otomatik raporlamada vazgeçilmez.
ps aux --sort=-%cpu | head -20
Bu komut CPU kullanımına göre azalan sırada ilk 20 süreci listeler. Çıktı şöyle görünür:
USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND
www-data 4821 87.3 12.4 2345678 409856 ? R 09:14 142:31 /usr/bin/php-fpm7.4
mysql 1203 34.2 8.1 1876543 267890 ? S 08:01 89:23 /usr/sbin/mysqld
root 9987 8.4 0.3 45678 12345 ? S 13:45 2:12 python3 /opt/backup.py
Buradaki TIME sütunu çok önemli: 142:31 ifadesi bu sürecin toplam 142 dakika 31 saniye CPU süresi tükettiğini gösteriyor. Eğer süreç 15 dakika önce başladıysa ve zaten 142 dakika CPU tüketmişse, bu anormal bir duruma işaret eder.
Belirli bir süreci daha detaylı incelemek için:
ps -p 4821 -o pid,ppid,user,stat,pcpu,pmem,comm,args
-p 4821: Sadece bu PID’i göster -o: Çıktı formatını belirle ppid: Parent process ID, hangi sürecin başlattığını gösterir stat: Süreç durumu (R=Running, S=Sleeping, D=Disk wait, Z=Zombie) args: Tam komut satırı argümanlarıyla birlikte
/proc Dizini: Sürecin İçine Bakmak
Bir süreci gerçekten anlamak istiyorsanız /proc dizini altındaki dosyalara bakmanız gerekiyor. Bu Linux’un güçlü yanlarından biri: her çalışan süreç için /proc/[PID]/ altında detaylı bilgiler mevcut.
# Sürecin açık dosyalarına bak
ls -la /proc/4821/fd | wc -l
# Sürecin kullandığı kütüphaneler
cat /proc/4821/maps | awk '{print $6}' | sort -u | grep -v '^$'
# Süreç başlatılırken kullanılan tam komut
cat /proc/4821/cmdline | tr '' ' '
# Sürecin environment değişkenleri
cat /proc/4821/environ | tr '' 'n' | grep -E 'PATH|HOME|USER'
# CPU ve bellek kullanım detayları
cat /proc/4821/status
/proc/[PID]/status çıktısından şunlara bakın:
- VmRSS: Gerçek bellek kullanımı
- VmSwap: Swap’a alınan bellek miktarı
- Threads: Kaç thread çalışıyor
- voluntary_ctxt_switches: İşlemciden gönüllü olarak ne sıklıkta vazgeçiyor
Yüksek voluntary_ctxt_switches değeri sürecin I/O ağırlıklı çalıştığını gösterir. Düşük değer ise CPU bağımlı (CPU-bound) bir süreç olduğunu işaret eder.
strace ile Sürecin Ne Yaptığını Anlamak
Çalışan bir süreci strace ile izleyebilirsiniz. Bu komut sürecin yaptığı sistem çağrılarını gerçek zamanlı gösterir.
strace -p 4821 -c -f 2>&1 | head -50
-p 4821: Bu PID’e bağlan -c: İstatistik modunda çalış, en çok hangi sistem çağrısının yapıldığını gösterir -f: Fork’lanan child process’leri de takip et
Birkaç saniye bekleyip Ctrl+C ile durdurun. Çıktıda şöyle bir şey göreceksiniz:
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
89.23 4.523421 123 36782 1234 futex
6.12 0.310234 45 6891 0 read
2.34 0.118543 89 1332 0 write
Eğer futex başı çekiyorsa bu genellikle thread senkronizasyon sorununa işaret eder. Yüksek read/write sistematik bir I/O döngüsünü gösterir. mmap veya brk hakimse bellek yönetimi sorunu olabilir.
Anlık olarak ne yaptığını görmek için:
strace -p 4821 -T -e trace=network 2>&1 | tail -20
-T: Her sistem çağrısının ne kadar sürdüğünü göster -e trace=network: Sadece ağ ile ilgili sistem çağrılarını izle
lsof ile Açık Dosya ve Bağlantı Analizi
Sürecin hangi dosyaları ve ağ bağlantılarını kullandığını lsof ile görebilirsiniz:
lsof -p 4821
Bu çıktı çok uzun olabilir. Sadece ağ bağlantılarına bakın:
lsof -p 4821 -i -n
-i: Sadece ağ soketlerini listele -n: DNS çözümleme yapma, IP adreslerini ham göster
Eğer süreç sürekli yeni bağlantı açıp kapatıyorsa veya binlerce bağlantısı varsa, bu anormal bir davranışa işaret eder. Bir PHP-FPM işleminin 500 farklı dış IP’ye bağlantı açması kötü bir şey.
Aynı zamanda kaç dosya açık olduğuna bakın:
lsof -p 4821 | wc -l
Sistem dosya tanımlayıcı limitini kontrol edin:
cat /proc/4821/limits | grep "open files"
Eğer limit 1024 ve açık dosya 1020 ise, süreç yakında “Too many open files” hatası alacak demektir.
perf ile Derinlemesine CPU Profilleme
Sorunun kaynağına gerçekten inmek istiyorsanız perf araçları şart. Bu araç Linux kernel ile entegre çalışır ve CPU’nun tam olarak neyle meşgul olduğunu gösterir.
# Sürecin CPU profil verisini 30 saniye topla
perf record -g -p 4821 -- sleep 30
# Sonuçları raporla
perf report --stdio | head -60
-g: Call graph bilgisini de topla –stdio: Terminal çıktısı olarak göster
Çıktıda en üstte yer alan fonksiyonlar CPU’nun en çok zaman harcadığı yerlerdir. Eğer bir web uygulamasındaki bir fonksiyon yüzde seksenini alıyorsa, sorunun kodu bu fonksiyon içinde aramanız gerekir.
Tüm sistem için anlık CPU kullanımına bakmak isterseniz:
perf top -p 4821
Bu top gibi ama fonksiyon düzeyinde çalışır. Sürecin içinde hangi fonksiyon ne kadar CPU yiyor, canlı olarak görebilirsiniz.
Gerçek Dünya Senaryosu: Runaway PHP-FPM Süreci
Diyelim ki bir e-ticaret sitesi yönetiyorsunuz. Sabah 09:00’da müşterilerden “site yavaş” şikayetleri gelmeye başladı. SSH ile bağlandınız ve şu adımları izlediniz:
# 1. Genel duruma bak
top -b -n 1 | head -30
# 2. En çok CPU tüketen süreci bul
ps aux --sort=-%cpu | head -5
# Çıktı: PID 4821, www-data, %87 CPU, /usr/bin/php-fpm7.4 worker
# 3. Bu sürecin ne zamandır çalıştığına bak
ps -p 4821 -o lstart,etime,time
# 4. PHP-FPM loglarına bak
tail -f /var/log/php7.4-fpm.log
# 5. Nginx access logunda hangi endpoint çağrılıyor?
tail -n 1000 /var/log/nginx/access.log | awk '{print $7}' | sort | uniq -c | sort -rn | head -10
Bu sorgu şunu gösteriyor:
892 /api/products/search?q=laptop&filter=all&sort=price&page=1
445 /api/products/search?q=laptop&filter=all&sort=price&page=2
234 /api/products/search?q=
Ürün arama API’si anormal trafik alıyor. Ya bir bot saldırısı var, ya da frontend’de bir döngü var. Süreç içinden strace ile bakalım:
strace -p 4821 -e trace=read,write,sendto,recvfrom -T 2>&1 | head -30
Sürecin çok hızlı MySQL sorguları gönderip aldığını gördünüz. MySQL slow query loguna bakın:
tail -n 50 /var/log/mysql/mysql-slow.log
Orada 0.001 saniyede tamamlanan ama saniyede yüzlerce kez çağrılan bir sorgu görüyorsunuz. Sorun N+1 query problemi: her ürün için ayrı ayrı kategori sorgusu yapılıyor. Bu bir kod sorunu, süreci kapatmak sadece geçici rahatlama sağlar.
Süreç Ağacını Anlamak: pstree ve pgrep
Bir süreç anormal davranıyorsa parent-child ilişkisini anlamak kritik:
pstree -p 4821
Bu komut o süreci başlatan parent ve onun başlattığı child süreçleri görsel olarak gösterir.
Belirli bir uygulamaya ait tüm süreçleri bulmak için:
pgrep -la php-fpm
-l: Sadece PID değil, komut adını da göster -a: Tam argümanları göster
# Tüm PHP-FPM worker'larının CPU kullanımı
ps -o pid,%cpu,stat,start_time,command $(pgrep php-fpm) | sort -k2 -rn
Bir worker diğerlerine göre çok daha yüksek CPU kullanıyorsa, o worker’a gelen özel bir istek var demektir.
vmstat ve iostat ile Sistem Geneli Analiz
Bazen yüksek CPU bir sürecin işlemci döngülerini tüketmesinden değil, sistemin genel durumundan kaynaklanır:
vmstat 2 10
Bu komut 2 saniye aralıklarla 10 kez sistem istatistiklerini gösterir. Dikkat edilecekler:
- r (run queue): Çalışmayı bekleyen süreç sayısı. Çekirdek sayısından yüksekse sorun var
- b (blocked): I/O bekleyen süreçler. Yüksekse disk sorunu
- si/so: Swap in/out. Bu sıfır olmayan değerler gösteriyorsa bellek yetersiz
- us: User space CPU kullanımı
- sy: Kernel space CPU kullanımı. Bu yüksekse kernel düzeyinde bir sorun var
# CPU çekirdek başına kullanım
mpstat -P ALL 2 5
Eğer sadece bir çekirdek yüzde yüz doluyken diğerleri boşta duruyorsa, bu single-threaded bir uygulamayı işaret eder. Ölçeklendirme için paralel işlem yapmaya ihtiyaç var.
sar ile Tarihsel Veri Analizi
Sorun şu an aktif değilse ama gece bir ara yaşandıysa ne yapacaksınız? sar komutu tarihsel sistem istatistiklerini saklar:
# Bugünkü CPU kullanım geçmişi
sar -u 1 10
# Dün gece 02:00 ile 04:00 arası
sar -u -s 02:00:00 -e 04:00:00 -f /var/log/sysstat/sa$(date -d yesterday +%d)
Eğer sysstat paketi kuruluysa bu veriler otomatik toplanmaktadır. Yoksa:
apt install sysstat
# veya
yum install sysstat
Gece yarısında CPU spike’ını gördünüz:
02:14:23 AM CPU %user %nice %system %iowait %idle
02:14:23 AM all 92.34 0.00 4.12 0.23 3.31
Saat 02:14’te ne çalıştığına bakın:
crontab -l
cat /etc/cron.d/*
Backup scripti, log rotation, veritabanı optimizasyonu gibi zamanlanmış görevlerden biri CPU’yu beklenenden fazla tüketiyor olabilir.
nice ve renice ile Süreç Önceliği Yönetimi
Sorunlu süreci hemen öldürmek her zaman doğru değil, özellikle kritik bir backup işlemiyse. Bunun yerine önceliğini düşürebilirsiniz:
# Çalışan süreci daha düşük önceliğe al
renice +15 -p 4821
Değer aralığı -20 ile +19 arasında. +19 en düşük öncelik, -20 en yüksek. Normal kullanıcı sadece pozitif değer verebilir, negatif değer için root gerekir.
Yeni bir süreci düşük öncelikle başlatmak için:
nice -n 15 /opt/backup.sh
Ağır backup veya encoding işlerini her zaman nice ile başlatın. Böylece sistem gerçek production trafiğine öncelik verebilir.
Süreç İzleme İçin Pratik Script
Günlük hayatta kullanabileceğiniz, yüksek CPU’yu tespit edip log’a yazan basit bir script:
#!/bin/bash
THRESHOLD=80
LOGFILE="/var/log/cpu-monitor.log"
DATE=$(date '+%Y-%m-%d %H:%M:%S')
while true; do
# CPU kullanımı eşiği aşan süreçleri bul
HIGH_CPU=$(ps aux --sort=-%cpu | awk -v threshold="$THRESHOLD" 'NR>1 && $3>threshold {print $0}')
if [ -n "$HIGH_CPU" ]; then
echo "[$DATE] YUKSEK CPU ALGILANDI:" >> "$LOGFILE"
echo "$HIGH_CPU" >> "$LOGFILE"
# PID'i al ve detaylı bilgi logla
PID=$(echo "$HIGH_CPU" | awk '{print $2}' | head -1)
echo "--- PID $PID DETAY ---" >> "$LOGFILE"
cat /proc/$PID/status >> "$LOGFILE" 2>/dev/null
# Stack trace al
cat /proc/$PID/stack >> "$LOGFILE" 2>/dev/null
echo "---" >> "$LOGFILE"
fi
sleep 30
done
Bu scripti systemd servisi olarak çalıştırabilir ya da cron’a ekleyebilirsiniz. Kritik sistemlerde bu log’lar bir sonraki soruşturmada altın değerinde olur.
Zombie Süreçlerle Başa Çıkmak
Zaman zaman Z statüsünde zombie süreçler görürsünüz. Bunlar aslında CPU tüketmez ama süreç tablosunu doldurur ve sistemi yorar:
# Zombie süreçleri listele
ps aux | awk '$8 == "Z" {print $0}'
# Zombie'nin parent'ını bul
ps -o ppid= -p [ZOMBIE_PID]
# Parent'a SIGCHLD gönder, zombieyi temizlemesini iste
kill -SIGCHLD [PARENT_PID]
Eğer parent da sorunluysa ve SIGCHLD işe yaramazsa, parent’ı yeniden başlatmanız gerekebilir. Parent öldüğünde zombie’nin parent’ı init (PID 1) olur ve init otomatik olarak zombie’yi temizler.
Sonuç
Yüksek CPU sorunlarını çözmek sistematik bir yaklaşım gerektirir: önce genel durumu değerlendirin, sonra şüpheli süreci izole edin, ardından o sürecin davranışını derinlemesine analiz edin. top ve ps ile başlayıp /proc, strace, lsof ve perf ile derinleşen bu yolculuk çoğunlukla sizi üç farklı sonuca götürür: kod hatası, yanlış konfigürasyon veya yetersiz donanım kapasitesi.
En önemli nokta şu: süreci körü körüne kill -9 ile kapatmayın. Bu geçici bir çözüm sağlar ama asıl sorunu gizler. Log dosyası mevcut mu, veritabanı bağlantısı düzgün kapandı mı, yarım kalan işlem var mı? Bunları kontrol etmeden yapılan bir kill işlemi, sistemi farklı bir sorunun içine sürükleyebilir.
Tarihsel veri toplamak için sysstat paketini her sunucunuza kurun. Sorun anında değil, sorun olmadan önce toplanmış veriler, incident sonrası analizi çok daha kolaylaştırır. Ve son olarak, kritik sistemler için bir baseline oluşturun: normal çalışmada CPU nasıl görünüyor, hangi süreçler ne kadar tüketiyor? Bu baseline olmadan “anormal” olanı tanımlamak mümkün değil.