Bir gece yarısı alarm çaldığında ve sunucunuza bağlandığınızda ps aux çıktısında onlarca etiketi görmek… Bu sahneyi yaşayan her sysadmin bilir o tuhaf hissi. Ne tam anlamıyla ölü, ne de yaşıyor. İşte bu yüzden “zombie” diyoruz onlara. Linux’ta zombie süreçler, yeni başlayan sistem yöneticilerinin kafasını karıştıran ama aslında bir kez anlaşıldığında oldukça mantıklı bir kavram. Bu yazıda zombie süreçlerin ne olduğunu, neden oluştuklarını, nasıl tespit edeceğinizi ve nasıl temizleyeceğinizi gerçek dünya senaryolarıyla birlikte ele alacağız.
Zombie Süreç Nedir?
Linux’ta her sürecin bir yaşam döngüsü vardır. Süreç çalışır, işini bitirir ve sonlanır. Ancak bir süreç sonlandığında, kernel hemen tüm kaynaklarını serbest bırakmaz. Önce sürecin çıkış durumunu (exit status) bir yerde saklar çünkü ebeveyn sürecin bu bilgiye ihtiyacı olabilir.
Ebeveyn süreç, çocuk sürecin çıkış durumunu wait() veya waitpid() sistem çağrısıyla alana kadar, o çocuk süreç zombie (zombi) durumunda kalır. Yani süreç teknik olarak bitmiştir, CPU kullanmaz, bellek tüketmez; ama process table’da bir giriş olarak durmaya devam eder.
Bunu şöyle düşünebilirsiniz: Bir eleman işten ayrılmış ama İK departmanı çıkış evraklarını henüz imzalamamış. Adam gitmişi gitmiş ama sistemde hâlâ “aktif” görünüyor.
Zombie süreçlerin temel özellikleri:
- CPU kullanmazlar
- Bellek tüketmezler (neredeyse hiç)
- Process table’da yer kaplarlar
- PID’lerini tutarlar
- Durumları
psçıktısındaZveyaolarak görünür
Neden Önemli?
“Madem kaynak tüketmiyor, ne önemi var?” diye düşünebilirsiniz. Haklısınız, bir iki tane zombie süreç gerçekten sorun değil. Ama şu senaryoyu düşünün: Hatalı kodlanmış bir uygulama sürekli çocuk süreçler yaratıyor ve bunların hiçbirini toplamıyor. Zamanla process table dolmaya başlar.
Linux’ta sistem genelinde maksimum süreç sayısı /proc/sys/kernel/pid_max ile sınırlıdır. Bu değer dolduğunda yeni süreç oluşturamazsınız. fork() çağrıları başarısız olmaya başlar. Web sunucunuz yeni bağlantı kabul edemez hale gelir. Veritabanı yeni sorgu thread’i açamaz. Sistem çalışıyor görünse de aslında kilitlenmiş gibi davranır.
Gerçek bir senaryo: Bir müşterimizin PHP-FPM sunucusunda her gün belirli saatlerde servis yanıtsız kalıyordu. Araştırdığımızda göründü ki hatalı bir cron job, her dakika bir shell script çalıştırıyor; script PHP süreçleri yaratıyor ama ebeveyn script bu süreçleri beklemiyor. Günler içinde binlerce zombie birikmişti.
Zombie Süreçleri Tespit Etmek
ps Komutu ile Tespit
En klasik yöntem ps komutudur. Zombie süreçler çıktıda Z durum koduyla veya etiketiyle görünür.
ps aux | grep Z
Bu komut her Z içeren satırı getirir ama “grep” kelimesinin kendisi de Z içerdiğinden biraz gürültülü olabilir. Daha temiz bir çıktı için:
ps aux | awk '$8 == "Z"'
Burada $8 sütunu process state’i temsil eder. Sadece durumu Z olan satırları filtreler.
Daha detaylı bilgi için:
ps -el | grep Z
Bu komutun çıktısında şöyle satırlar görürsünüz:
# Örnek çıktı:
# Z 5 27 18342 18300 0 80 0 - 0 exit ? 00:00:00 [php] <defunct>
Burada 18342 zombie sürecin PID’i, 18300 ise ebeveyn sürecin PID’idir (PPID).
top ve htop ile Gerçek Zamanlı İzleme
top komutunun özet satırında zombie sayısını görebilirsiniz:
top
Çıktının üst kısmında şuna benzer bir satır görürsünüz:
Tasks: 245 total, 1 running, 244 sleeping, 0 stopped, 3 zombie
O 3 zombie rakamı sizi uyarır. top çalışırken o tuşuna basıp S=Z filtresi ekleyerek sadece zombie süreçleri listeleyebilirsiniz.
htop daha görsel bir deneyim sunar. F6 ile sütunlara göre sıralayabilir, STATE sütununa göre filtreleyebilirsiniz:
htop
htop içinde / tuşuyla arama yapıp defunct yazarsanız zombie süreçleri hızla bulursunuz.
/proc Dosya Sistemi ile Manuel İnceleme
Her sürecin /proc/[PID]/status dosyasında durum bilgisi bulunur:
cat /proc/18342/status | grep State
Çıktı şöyle görünür:
State: Z (zombie)
Tüm zombie PID’leri bulmak için:
for pid in /proc/[0-9]*/status; do
state=$(grep "^State:" "$pid" | awk '{print $2}')
if [ "$state" = "Z" ]; then
echo "Zombie PID: $(echo $pid | cut -d'/' -f3)"
cat "$pid" | grep -E "Name:|Pid:|PPid:|State:"
echo "---"
fi
done
Bu script size her zombie sürecin adını, PID’ini ve PPID’ini gösterir.
Zombie Sayısını Anlık İzlemek
Sisteminizdeki zombie sayısını tek komutla öğrenmek için:
ps aux | awk '$8=="Z" {count++} END {print "Zombie count:", count}'
Ya da çok daha kısa:
ps -eo stat | grep -c Z
Bunu bir monitoring scripti olarak kullanabilir, belirli eşiğin üzerine çıktığında alert alabilirsiniz.
Ebeveyn Süreci Bulmak
Zombie süreci silmek için doğrudan kill komutu işe yaramaz çünkü süreç zaten ölü. Asıl yapmanız gereken ebeveyn süreci bulmak ve ona SIGCHLD sinyali göndermek ya da onu yeniden başlatmak.
Zombie sürecin ebeveynini bulmak:
# Zombie PID'in PPID'ini öğren
ps -o ppid= -p 18342
Ya da daha kapsamlı:
ps -el | awk '$8=="Z" {print "Zombie PID: "$4, "Parent PID: "$5}'
Ebeveyn süreci bulduktan sonra onun ne olduğunu öğrenmek için:
ps -p 18300 -o pid,ppid,cmd,stat
Zombie Süreçleri Temizlemek
Yöntem 1: Ebeveyne SIGCHLD Göndermek
En nazik yöntem budur. SIGCHLD sinyali, ebeveyn sürece “çocuklarını topla” mesajı gönderir. Ebeveyn bu sinyale doğru yanıt veriyorsa zombie süreçler temizlenir.
kill -SIGCHLD 18300
18300, ebeveyn sürecin PID’i. Sinyal gönderdikten sonra zombie’nin kaybolup kaybolmadığını kontrol edin:
ps aux | awk '$8=="Z"'
Yöntem 2: Ebeveyn Süreci Yeniden Başlatmak
Ebeveyn süreç SIGCHLD sinyaline yanıt vermiyorsa veya bu sinyali handle etmiyorsa, bir üst seçenek servis yöneticisinden süreci yeniden başlatmaktır:
# systemd ile
systemctl restart uygulama-servisi
# Klasik yöntemle
kill -HUP 18300
SIGHUP genellikle bir süreci konfigürasyonunu yeniden yüklemesi için kullanılır ve pek çok uygulama buna yanıt olarak child süreçlerini düzgünce temizler.
Yöntem 3: Ebeveyn Süreci Öldürmek
Eğer ebeveyn süreç tamamen hatalıysa ve yeniden başlatma işe yaramıyorsa, onu öldürmek en doğru çözümdür. Ebeveyn ölünce tüm zombie çocuklar init (PID 1) veya systemd tarafından evlat edinilir ve temizlenir.
kill -9 18300
Bu noktada dikkatli olun. Ebeveyn süreç kritik bir servisin ana süreci olabilir. Önce ne olduğunu iyice anlayın.
Yöntem 4: Tüm Zombie’lerin Ebeveynlerini Toplu Temizlemek
Birden fazla zombie varsa şu script işe yarar:
#!/bin/bash
echo "Zombie süreçler aranıyor..."
zombie_pids=$(ps aux | awk '$8=="Z" {print $2}')
if [ -z "$zombie_pids" ]; then
echo "Zombie süreç bulunamadı."
exit 0
fi
echo "Bulunan zombie PID'ler: $zombie_pids"
for zpid in $zombie_pids; do
ppid=$(ps -o ppid= -p $zpid 2>/dev/null | tr -d ' ')
if [ -n "$ppid" ] && [ "$ppid" != "1" ]; then
echo "Zombie PID: $zpid -> Ebeveyn PID: $ppid'e SIGCHLD gönderiliyor"
kill -SIGCHLD $ppid
fi
done
sleep 2
remaining=$(ps aux | awk '$8=="Z"' | wc -l)
echo "Kalan zombie sayısı: $remaining"
Bu scripti çalıştırdığınızda zombie ebeveynlerine SIGCHLD gönderir ve 2 saniye bekledikten sonra durumu raporlar.
Gerçek Dünya Senaryoları
Senaryo 1: Hatalı Python Script
Bir data pipeline uygulaması düşünün. Python ile yazılmış, multiprocessing modülü kullanıyor ama join() çağrısı eksik:
# HATALI KOD - zombie üretir
import multiprocessing
import time
def worker():
time.sleep(1)
while True:
p = multiprocessing.Process(target=worker)
p.start()
# p.join() eksik! Zombie üretir
time.sleep(0.5)
Bu kodu birkaç saat çalıştırdığınızda sisteminizi zombie’ye boğar. Doğru kullanım p.join() veya p.daemon = True ayarlamaktır.
Bunu tespit etmek için:
# En çok zombie çocuğa sahip ebeveyni bul
ps -el | awk '$8=="Z" {print $5}' | sort | uniq -c | sort -rn | head -5
Senaryo 2: Apache/PHP Ortamında
Web sunucularında özellikle CGI script’leri veya shell exec çağrıları zombie üretebilir. Şüphelendiğinizde:
# Apache worker'larının zombie durumunu kontrol et
ps aux | grep apache | head -20
# PHP-FPM zombie kontrolü
ps aux | grep php-fpm | awk '$8=="Z"'
# Sistemdeki tüm defunct süreçleri ebeveynleriyle göster
ps axo stat,ppid,pid,comm | awk '$1~/Z/ {print "PPID:"$2, "PID:"$3, "CMD:"$4}'
Senaryo 3: Docker Container İçinde
Docker container’larında zombie sorunu sıkça görülür çünkü container’larda genellikle PID 1’i alan uygulama, init görevlerini üstlenmez yani zombie’leri toplamaz.
# Container içinde zombie kontrolü
docker exec -it container_adi ps aux | awk '$8=="Z"'
# Tüm container'larda zombie ara
for container in $(docker ps -q); do
count=$(docker exec $container ps aux 2>/dev/null | awk '$8=="Z"' | wc -l)
if [ $count -gt 0 ]; then
name=$(docker inspect --format='{{.Name}}' $container)
echo "Container: $name -> Zombie: $count"
fi
done
Docker’da çözüm için --init flagini kullanabilirsiniz:
docker run --init your-image
Ya da tini gibi bir init süreç yöneticisi kullanabilirsiniz. Bu, container içinde düzgün bir PID 1 sağlar ve zombie’leri otomatik toplar.
Monitoring ve Alert Kurulumu
Zombie süreçleri proaktif izlemek için basit bir bash monitoring scripti:
#!/bin/bash
THRESHOLD=10
LOG_FILE="/var/log/zombie_monitor.log"
ALERT_EMAIL="[email protected]"
zombie_count=$(ps -eo stat | grep -c "^Z")
timestamp=$(date '+%Y-%m-%d %H:%M:%S')
echo "$timestamp - Zombie count: $zombie_count" >> $LOG_FILE
if [ $zombie_count -gt $THRESHOLD ]; then
echo "$timestamp - UYARI: $zombie_count zombie süreç tespit edildi!" >> $LOG_FILE
# Detaylı bilgi kaydet
echo "=== Zombie Detayları ===" >> $LOG_FILE
ps axo stat,ppid,pid,comm | awk '$1~/Z/' >> $LOG_FILE
# Mail gönder (mailutils kurulu olmalı)
echo "Sunucuda $zombie_count zombie süreç tespit edildi. Log: $LOG_FILE" |
mail -s "UYARI: Zombie Süreç Alarmı" $ALERT_EMAIL
fi
Bu scripti cron’a ekleyin:
# Her 5 dakikada bir kontrol
*/5 * * * * /usr/local/bin/zombie_monitor.sh
Kernel ve Sistem Parametreleri
Zombie sorunuyla sık karşışıyorsanız bazı kernel parametrelerini incelemeniz faydalı olabilir:
# Maksimum process sayısını kontrol et
cat /proc/sys/kernel/pid_max
# Anlık süreç sayısını öğren
ps aux | wc -l
# Thread sayısı sınırını öğren
cat /proc/sys/kernel/threads-max
# Gerekirse pid_max değerini artır (kalıcı için sysctl.conf'a yaz)
echo "kernel.pid_max = 4194304" >> /etc/sysctl.conf
sysctl -p
Ancak pid_max artırmak asıl sorunu çözmez. Sadece biraz zaman kazandırır. Asıl çözüm zombie üreten uygulamayı düzeltmektir.
Zombie Oluşmasını Önlemek
Sistem yöneticisi olarak yapabilecekleriniz sınırlıdır çünkü zombie sorunu çoğunlukla uygulama kodundan kaynaklanır. Ama şunları yapabilirsiniz:
- Uygulama geliştiricilerini bilgilendirin:
wait(),waitpid()çağrılarının önemi hakkında. - Docker kullanıyorsanız
--initflag ekleyin: Her container’da otomatik. - systemd servislerinde
KillMode=control-groupkullanın: Servis sonlandığında tüm çocuk süreçler de temizlenir. - Periyodik monitoring yapın: Yukarıdaki script gibi araçlarla zombie sayısını izleyin.
nohupvedisownkullanımına dikkat edin: Ebeveynden koparılan süreçler bazen beklenmedik davranışlar sergileyebilir.
Geliştiricilere yönelik temel öneri şudur: Her fork() çağrısından sonra mutlaka wait() çağrısı yapılmalı ya da SIGCHLD sinyal handler kurulmalıdır. Daemon process’lerde ise double-fork tekniği kullanılarak zombie riski minimize edilebilir.
Sonuç
Zombie süreçler, çoğu zaman gereksiz yere panik yaratır ama temelde anlaşılması ve yönetilmesi zor değildir. Birkaç tane zombie sisteminize zarar vermez; ama yüzlerce, binlerce zombie birikimine izin verirseniz işler sarpa sarabilir.
Özetle yapmanız gerekenler şunlardır: ps aux | awk '$8=="Z"' ile zombie’leri tespit edin, ebeveyn PID’i bulun, önce SIGCHLD deneyin, işe yaramazsa ebeveyn süreci yeniden başlatın veya öldürün. Uzun vadede ise monitoring kurun ve uygulamanızdaki sorunu kökten çözün.
En güzel yanı şu: Zombie süreçlerle savaşmak aslında sistemin sizi bir şey hakkında uyardığı anlamına gelir. Bir uygulama kötü davranıyor, bir servis hatalı çalışıyor. Bu sinyali doğru okursanız, daha büyük bir felaket olmadan önce müdahale edebilirsiniz. İyi sysadmin’lik de zaten bu değil mi?