EADDRINUSE ve Görünmez Prosesler
Yerel yazılım geliştirmede veya otomatik entegrasyon testlerinde en sık karşılaşılan can sıkıcı çalışma zamanı hatalarından biri 'Error: listen EADDRINUSE: address already in use :::3000' istisnasıdır. Geliştirici refleksi genellikle hızlıca terminali açmak, lsof -i :3000 veya netstat -ano komutlarıyla portu dinleyen PID değerini bulmak ve ardından 'kill -9 <pid>' çalıştırmaktır. Ancak modern web mimarilerinde bu körlemesine sonlandırma hamlesi çoğu zaman sonuçsuz kalır; uygulama yeniden başlatıldığında ya birkaç saniye sonra tekrar aynı hatayla çöker ya da port işletim sistemi katmanında kilitli kalmaya devam eder.
Bu durumun arkasında modern araç zincirlerinin karmaşık proses mimarileri yer alır. Günümüzde web sunucuları neredeyse hiçbir zaman izole tekil prosesler olarak çalışmaz; nodemon, PM2, Vite, Next.js veya IDE hata ayıklayıcıları gibi denetleyici (supervisor) katmanlarının altında barınırlar. Bir geliştirici yalnızca soketi açan alt iş parçacığını (worker PID) sonlandırdığında, üst denetleyici proses soket dosya tanımlayıcısını (file descriptor) elinde tutabilir veya çöken alt prosesin yerine milisaniyeler içinde yenisini başlatarak soketi hemen geri rezerve eder. TCP yaşam döngüsündeki TIME_WAIT durumu ve eksik SO_REUSEADDR yapılandırmaları da soketin çekirdek düzeyinde askıda kalmasına neden olabilir.
İkinci yaygın tuzak, ebeveyni çökmüş veya kontrolsüz sonlandırılmış zombi ve yetim proseslerdir. Ebeveyn proses çöktüğünde işletim sistemi alt prosesi PID 1 (init veya systemd) altına taşır ve bu sahipsiz proses arka planda soketi dinlemeyi sürdürür. Üçüncü katman ise konteyner sınırlarıdır. Docker Desktop veya Podman kullanıldığında ana makinede 0.0.0.0:3000 portuna bağlanan varlık docker-proxy prosesidir; asıl web sunucusu ise izole bir Linux ağ ad alanında (network namespace) çalışır. Port çakışmasını kökten çözebilmek için tek bir proses kimliğine odaklanmak yetersizdir; soketten başlayıp tepe noktadaki denetleyiciye kadar uzanan tüm proses ağacını şeffafça görmek gerekir.
# EADDRINUSE runtime crash during server boot:
Error: listen EADDRINUSE: address already in use :::3000
at Server.setupListenHandle [as _listen2] (node:net:1904:16)
at listenInCluster (node:net:1961:12)
at Server.listen (node:net:2063:7)
# The classic blind kill pattern and why it fails:
kill -9 41280
# Socket remains occupied or child respawns under supervisor PID 41200Sıfır Bağımlılıkla Soket ve Ağaç Çözümlemesi
PortTree'yi geliştirirken temel aldığım en kritik mimari prensip sıfır üçüncü parti bağımlılık ilkesiydi. Python ekosisteminde proses ve sistem durumunu denetlemek için psutil endüstri standardı olarak kabul edilir; ancak psutil derlenmiş C uzantılarına (C-extensions) sıkı sıkıya bağlıdır. Bu durum hafifletilmiş Alpine Linux konteynerlerinde, kısıtlı CI/CD ajanlarında ya da gcc, musl-dev gibi derleme araçlarının bulunmadığı kurumsal ortamlarda tekerlek (wheel) derleme hatalarına, güvenlik kısıtlamalarına ve kabul edilemez paket indirme gecikmelerine neden olur. PortTree, hiçbir dış Python paketi kurmadan doğrudan işletim sisteminin yerel çekirdek arayüzleriyle konuşur.
Linux üzerinde PortTree, çekirdeğin sunduğu /proc sanal dosya sistemini doğrudan ayrıştırır. /proc/net/tcp ve /proc/net/tcp6 dosyalarını okuyarak onaltılık biçimde tutulan yerel IP adreslerini, port numaralarını ve ilgili soketin inode (indeks düğümü) değerini hesaplar. IPv4 ile eşlenmiş IPv6 (IPv4-mapped IPv6) formatlarını da destekler. Ardından çalışan her prosesin dosya tanımlayıcılarını barındıran /proc/[pid]/fd/* sembolik bağlantılarını tarayarak eşleşen soket inode'unu bulur ve soketin asıl sahibini tespit eder. Sonrasında /proc/[pid]/stat dosyasındaki PPID (ebeveyn proses ID) değerini takip ederek ağacı özyinelemeli olarak kurar.
Windows platformunda ise Python'ın yerleşik ctypes modülü kullanılarak işletim sisteminin iphlpapi.dll kütüphanesine bağlanılır. GetExtendedTcpTable API'si TCP_TABLE_OWNER_PID_ALL parametresiyle çağrılarak çekirdek TCP tablosu doğrudan okunur. Proses hiyerarşisi ise kernel32.dll üzerinden CreateToolhelp32Snapshot, Process32First ve Process32Next API'leriyle anlık görüntü alınarak çıkarılır. macOS üzerinde ise harici shell komutları çalıştırmak yerine Darwin çekirdeğinin libproc ve sysctl yapıları doğrudan bellekten okunur. Bu sayede her platformda mikro saniyeler seviyesinde deterministik analiz sağlanır.
# Linux /proc parsing & Windows ctypes bindings in PortTree:
from ctypes import Structure, c_ulong, sizeof, windll, byref
class MIB_TCPROW_OWNER_PID(Structure):
_fields_ = [
("dwState", c_ulong),
("dwLocalAddr", c_ulong),
("dwLocalPort", c_ulong),
("dwRemoteAddr", c_ulong),
("dwRemotePort", c_ulong),
("dwOwningPid", c_ulong),
]
# Zero third-party C-extensions or compilation wheels required.Terminalde Denetim ve Temizlik
PortTree'nin komut satırı arayüzü, geliştiricinin terminaldeki odak süresini korumak ve otomasyon script'lerine kolayca entegre edilmek üzere dizayn edildi. PyPI üzerinden 'python -m pip install porttree==1.0.0' komutuyla hiçbir ek derleme adımı olmadan saniyeler içinde kurulabilir veya ortamı kirletmemek adına 'pipx run porttree 3000' şeklinde geçici olarak koşturulabilir. Temel 'porttree 3000' komutu, portu dinleyen yerel IP arayüzünü (örneğin 127.0.0.1 ile 0.0.0.0 farkı güvenlik açısından kritiktir), proses adını, PID numarasını ve tam başlatma parametrelerini özetleyen okunaklı bir tablo sunar.
Çok katmanlı mimarilerde asıl farkı yaratan özellik '--tree' bayrağıdır. 'porttree 3000 --tree' çalıştırıldığında araç, portu tutan alt prosesten başlayıp tüm ebeveyn denetleyicileri ASCII ağaç grafiği olarak ekrana çizer. Bu görünüm sayesinde portun tek başına koşan bir Node prosesi tarafından mı, yoksa bir npm run dev script'i, nodemon gözlemcisi, IDE hata ayıklayıcısı veya docker-proxy köprüsü tarafından mı kilitlendiği anında teşhis edilir. Her düğümde proses kimliği, bellek kullanımı ve komut satırı açıkça listelenir.
Tespit edilen proses grubunu temizlemek için '--kill' seçeneği kademeli ve güvenli bir sonlandırma protokolü işletir. PortTree doğrudan kör bir SIGKILL göndermek yerine, ağaçtaki tüm proseslere öncelikle SIGTERM ileterek açık veritabanı bağlantılarını ve dosya kilitlerini düzgünce kapatmaları için süre tanır; eğer prosesler tanımlanan zaman aşımında portu serbest bırakmazsa SIGKILL devreye girer. CI boru hatları ve özel script'ler için '--format json' seçeneği ise jq gibi araçlarla filtrelenebilir yapılandırılmış veri üretir.
# Install PortTree from PyPI (zero dependencies):
python -m pip install porttree==1.0.0
# Inspect which process owns port 3000:
porttree 3000
# View full process hierarchy from socket owner up to supervisor:
porttree 3000 --tree
# Gracefully terminate the process tree occupying the port:
porttree 3000 --kill
# Structured JSON output for CI pipeline assertions:
porttree 3000 --format jsonSınırlar ve CI/CD Denetimi
Güvenilir bir sistem mühendisliği aracı, kendi sınırlarını ve işletim sistemi kısıtlamalarını şeffafça belgelemelidir. UNIX sistemlerinde 1024'ün altındaki ayrıcalıklı (privileged) portlar (80, 443 gibi) ile başka kullanıcılara ait sistem servislerinin proses tanımlayıcıları normal kullanıcı yetkileriyle sorgulanamaz. PortTree bu gibi kısıtlamalarda sessizce boş dönmek veya hata yutmak yerine açık bir izin uyarısı (EPERM / PermissionError) vererek sudo veya Administrator ayrıcalıklarının gerektiğini geliştiriciye açıkça bildirir.
Konteyner ad alanları bir diğer sınır alanını oluşturur. Bir Docker konteyneri içinde koşan PortTree, ana makine ağını göremez; yalnızca kendi izole ağ ad alanındaki portları tarayabilir. Ana makinede koşan PortTree ise köprülenmiş konteyner portunu docker-proxy olarak tespit eder; ancak konteyner içindeki proses detaylarını görebilmek için Docker daemon soketine erişim gerekir. Bu ayrım, ağ ad alanı sınırlarının doğru anlaşılmasını ve araçtan gerçekçi beklentiler kurulmasını sağlar.
Sürekli entegrasyon (CI/CD) hatlarında PortTree, test koşumları öncesinde deterministik bir ön denetim kapısı (pre-flight check) olarak görev yapar. Paralel test suitelerinde sabit portları (PostgreSQL 5432, Redis 6379, Mock API 8080) kullanan servisler başlamadan önce 'porttree 3000 --check-free' çağrılır. Port boşsa 0, doluysa 1 çıkış kodu dönerek testlerin beklenmedik port kilitleri yüzünden saatlerce zaman aşımına uğramasını önler ve CI kaynaklarını verimli korur.
# GitHub Actions workflow: Pre-flight port availability assertion
- name: Verify clean port state for test runner
run: |
python -m pip install porttree==1.0.0
# Returns exit code 0 if port 3000 is free, 1 if occupied
porttree 3000 --check-free || {
echo "Port 3000 is unexpectedly held prior to integration tests"
porttree 3000 --format json
exit 1
}Kaynak: PortTree: GitHub Deposu
Kaynak: PortTree: PyPI Paketi
Kaynak: Umut Güngör: Hakkımda
