Kafayı yiyecektim dostlar. Uzun süre çalışan bir otomasyon botu yazmıştım. Ana işlem, belirli aralıklarla subprocess.Popen kullanarak harici bir veri işleme programı (data_processor.exe) çalıştırıyordu. Her şey harika gidiyor gibiydi, ta ki botu manuel olarak durdurup (Ctrl+C), sistem monitörüme bakana kadar. Gördüğüm manzara içler acısıydı.
Task Manager'da (Linux'ta htop) onlarca data_processor işleminin zombie (defunct) olarak sistemde takılı kaldığını gördüm. Ana programım ölmüştü ama onun çocukları yaşamaya devam ediyordu! Hafızayı tıkıyor, PID'leri işgal ediyorlardı. StackOverflow'da bile direkt çözüm bulamadım, herkes communicate() veya wait() diyordu ama benim senaryomda bunlar bloklayıcı oluyordu.
Meğerse sorun şuradaymış: Ben Popen'ı çağırıp, dönen process objesini bir kenara atıyordum. İşletim sistemi, ana process (benim botum) sonlandığında, onun çocuk process'lerinin durumunu raporlayacak bir ebeveyn bulamadığı için onları zombie statüsünde bırakıyormuş. Şaka gibi ama gerçek.
Çözüm için iki önemli adım attım. İlk olarak, tüm başlattığım process'leri bir listeye (processes = [] ) eklemeye başladım. Ana program sonlanırken (ister normal bitiş, ister KeyboardInterrupt ile), bu liste üzerinde dolaşıp her process'i kontrol etmem ve öldürmem (terminate()) gerekiyordu.
İkinci ve daha temiz çözüm, signal kütüphanesi ile bir handler yazmak oldu. Programımın ölüm sinyalini (SIGTERM, SIGINT) yakalayıp, açık kalan tüm process'leri temizliyorum.
Python:
import subprocess
import signal
import sys
child_processes = []
def cleanup(signum, frame):
for proc in child_processes:
if proc.poll() is None: # Hala çalışıyorsa
proc.terminate()
proc.wait() # Zombi olmaması için bekle
sys.exit(0)
signal.signal(signal.SIGINT, cleanup)
signal.signal(signal.SIGTERM, cleanup)
# Process başlatma örneği
proc = subprocess.Popen(['data_processor.exe', 'arg1'])
child_processes.append(proc)
# ... botun diğer işleri
Bu küçük değişiklikle zombi istilasından kurtuldum. Aslında basit bir atexit modülü de iş görebilirdi ama sinyal handler'ı daha kapsamlı oldu.
Ben bu yöntemle çözdüm ama belki sizin daha şık, daha Pythonic bir çözümünüz vardır. Uzun ömürlü programlarda subprocess yönetimi için hangi pattern'leri kullanıyorsunuz? Hiç siz de benzer bir zombi kabusu yaşadınız mı?