Post

Linux Bellek Yönetimi ve OOM Killer ile Yüzleşmek: Sinyaller, RAM ve İnfazlar

🇹🇷 Process'iniz aniden yok mu oldu? Loglarda sadece 'Killed' mi yazıyor? Linux sinyal mekanizmasını, 'kill -9'un tehlikelerini ve Kernel'ın seri katili OOM Killer'ın kimi neden seçtiğini, bir dedektif titizliğiyle inceliyoruz.

Linux Bellek Yönetimi ve OOM Killer ile Yüzleşmek: Sinyaller, RAM ve İnfazlar

Önceki yazımızda Linux’ta Blocking I/O ve Bekleme Sorunlarını Kernel Seviyesinde Anlamak başlığı altında, donan terminalleri, D durumundaki süreçleri ve select ile epoll arasındaki farkı incelemiştik.

Peki ya program beklemezse? Ya aniden, arkasında hiçbir iz bırakmadan, tek kelime etmeden yok olursa?

Sabah kahvenizi aldınız, terminale baktınız ve geceden beri çalışan kritik veri işleme servisinizi kontrol ettiniz. Servis yok. Log dosyası Segmentation Fault bile demeden aniden kesilmiş.

Siz: “Kim öldürdü bunu? Hata da vermemiş.”

Kernel: “Ben öldürdüm. Gemiyi kurtarmak için onu feda etmem gerekti.”

📚 Teknik Mola: RAM “Bitti” Ne Demek? Çoğu kişi free -h çıktısındaki free sütunu azalınca panikler. Yanlış! Linux, boş RAM’i “ziyan edilmiş kaynak” olarak görür ve Disk Cache için kullanır. Bakmanız gereken yer available sütunudur. Eğer available sıfıra yaklaşırsa, işte o zaman Linux’un “Seri Katili” (OOM Killer) tetiği çekmek üzere demektir.

Bu yazıda, Linux’un Cinayet Masası’na (Forensics) geçici görevle katılıyoruz. Programlar neden sessizce ölür? kill -9 kullanmak neden bir savaş suçudur? Ve modern Linux’ta “OOM Killer” yalnız mı çalışıyor?

📌 Bu Yazıda Ne Öğreneceksin?

Bu yazı, sadece bir hata ayıklama rehberi değil, Linux’un yaşam ve ölüm döngüsüne bir bakıştır. Okumayı bitirdiğinizde:

  • Ölümün Dili (Sinyaller): SIGTERM ile SIGKILL arasındaki farkı ve “nezaketle kovmak” ile “kafasına sıkmak” arasındaki ince çizgiyi öğreneceksiniz.
  • Seri Katilin Profili (OOM Score): Kernel’ın kurbanını neye göre seçtiğini (oom_score) ve root süreçlerine nasıl torpil geçtiğini göreceksiniz.
  • Modern Tuzaklar (Systemd & Java): systemd-oomd adlı yeni şerifin kurallarını ve Java uygulamalarının Container içindeki bellek yanılsamalarını anlayacaksınız.
  • Yürüyen Ölüler (Zombies): Öldürdüğünüz halde top listesinden silinmeyen süreçleri nasıl temizleyeceğinizi keşfedeceksiniz.
  • Ölümden Beteri (Swap Thrashing): Sistemin donarak ölmesinin, hızlıca ölmesinden neden daha kötü olduğunu göreceksiniz.

Olay yeri şeridini çekin. Cesedi incelemeye başlıyoruz.

📡 1. Ölümün Dili: Linux Sinyalleri (Signals)

Linux’ta bir sürecin (process) bitmesi için, eğer kodu bitip kendisi çıkmadıysa (exit()), dışarıdan bir dış müdahale gerekir. Bu müdahalenin teknik adı Sinyaldir. Sinyaller, Kernel’ın (veya yetkili diğer süreçlerin) bir uygulamaya gönderdiği kısa, net, asenkron ve acil durum SMS’leridir. Bu SMS’lerin içeriği metin değil, birer sayıdan ibarettir (1, 9, 15 gibi).

Terminalde kill -l yazarsanız, bu ölümcül listenin tamamını (64 adet) görürsünüz. Ama bir “Cinayet Dedektifi” olarak bizi ilgilendiren 3 büyük “Ölüm Emri” vardır:

graph TD
    User[Kullanıcı / Admin] -->|kill PID| Kernel{Linux Kernel}
    Kernel -->|Yetki Kontrolü| Security[Security Layer]
    Security -->|Onay| Delivery[Signal Delivery]
    Delivery -->|Sinyal: 15| Process{Hedef Süreç}
    Delivery -->|Sinyal: 9| SigKILL[Anında İnfaz]
    
    Process -->|Yakalarsa| Graceful[Temiz Kapanış]
    Process -->|Yakalamazsa| Default[Zorla Kapanış]
    SigKILL -->|Sorgusuz Sualsiz| Death[💀 Süreç Ölür]
    
    style SigKILL stroke:#f00,stroke-width:2px
    style Death stroke:#f9f,stroke-width:2px

A. SIGTERM (Signal 15) - “Lütfen Evi Boşalt”

  • Anlamı: “Termination Signal”. Nezaketle, usulüne uygun sonlandırma.
  • Analoji: Ev sahibinin kapıyı çalıp, yüzünde bir tebessümle “Hocam kontrat bitti, ay sonuna kadar evi toparlayıp, çöplerini atıp, anahtarı teslim edip çıkarsan sevinirim” demesidir.
  • Davranış: Program bu sinyali yakalayabilir (Catch edebilir).
    • Veritabanı bağlantılarını (Close Connection) kapatır.
    • Hafızada (RAM) tuttuğu ve henüz diske yazmadığı verileri (Flush Buffer) diske yazar. Veri kaybını önler.
    • Geçici dosyaları (/tmp) temizler.
    • Log dosyasına “Kapanıyorum, her şey için teşekkürler” yazar ve exit(0) ile çıkar (Graceful Shutdown).
  • Varsayılan Komut: Konsolda kill <PID> yazdığınızda, varsayılan olarak bu (15) sinyal gönderilir.

B. SIGINT (Signal 2) - “Yeter Artık”

  • Anlamı: “Interrupt”. Klavyeden donanım kesmesi.
  • Analoji: Biri hararetli bir şekilde konuşurken, elinizi havaya kaldırıp “Bir saniye, yeter, kes!” demeniz gibidir.
  • Davranış: Terminalde çalışan bir programa Ctrl+C yaptığınızda Kernel bu sinyali gönderir. Program kodunda bu sinyal (“Handler”) tanımlıysa, “İşlemi iptal ediyorum” diyerek temiz bir çıkış yapabilir. Tanımlı değilse, pat diye kapanır.

C. SIGKILL (Signal 9) - “Kafasına Sık”

  • Anlamı: “Kill Signal”. Kesin, Ani ve Tartışmasız Ölüm.
  • Analoji: Ev sahibinin kapıyı çalmadan, anahtarı istemeden, dozerle gelip evi, içindeki eşyalarla ve kiracıyla birlikte yıkmasıdır. Veya SWAT timinin camdan girmesidir.
  • Davranış: Bu sinyal yakalanamaz, engellenemez, bloke edilemez, göz ardı edilemez.
    • Programın “Dur bir log yazayım”, “Dosyayı kapatayım”, “Veritabanına commit edeyim” deme şansı SIFIRDIR.
    • Kernel, o milisaniyede programın CPU ile, RAM ile, Disk ile olan tüm ilişkisini bıçak gibi keser.
    • Masada açık kalan dosyalar bozulabilir (Corrupted Files).
    • Yarım kalan veriler sonsuza dek kaybolur.
    • Log dosyasında “Program durdu” bile yazmaz. Log aniden kesilir.

⚠️ Neden kill -9 Alışkanlık Yapmamalı? Acemi sistem yöneticileri, kapanmayan (veya yavaş kapanan) bir program görünce sabırsızlanıp refleks olarak kill -9 <PID> yapıştırır. Bu çok tehlikeli bir alışkanlıktır.

  • Veri Bütünlüğü: Eğer bir MySQL/PostgreSQL veritabanına kill -9 atarsanız, veritabanı dosyaları tutarsız hale gelebilir (“Index Corruption”) ve saatlerce “Recovery” yapmak zorunda kalabilirsiniz.
  • Yetimler: Eğer birçok alt süreç (“Child Process”) üreten bir ana sürece (Parent, örneğin Apache veya Chrome) kill -9 atarsanız, ana süreç anında öldüğü için çocuklarına “Siz de ölün” diyemez. Geride sistem kaynaklarını tüketen başıboş “Yetim Süreçler” (Orphan Zombies) bırakırsınız.

Altın Kural: Her zaman önce kill -15 (SIGTERM) gönderin. 10 saniye bekleyin. Program inatla kapanmıyorsa (Hung State), işte o zaman son çare olarak kill -9 kullanın.

🩸 2. OOM Killer: Kernel’ın İçindeki Seri Katil

Gelelim o meşhur, o gizemli Killed mesajının (eğer siz öldürmediyseniz) en büyük baş şüphelisine. Uygulamanız gayet güzel çalışıyordu. Kimse sisteme girip kill komutu yazmadı. Ama sabah geldiniz, süreç yok.

Sistem loglarını (dmesg -T veya /var/log/kern.log) açtınız ve şu korkunç, kırmızı satırları gördünüz:

1
2
[Tue Jan 12 04:00:01 2026] Out of memory: Killed process 15203 (java) total-vm:5120000kB, anon-rss:2048000kB, file-rss:0kB, shmem-rss:0kB
[Tue Jan 12 04:00:01 2026] oom_reaper: reaped process 15203 (java), now anon-rss:0kB

Tebrikler. OOM Killer (Out Of Memory Killer) tarafından ziyaret edildiniz. Kernel’ın özel timi, sunucunuzu kurtarmak için uygulamanızı infaz etti. (Tetiği çektikten sonra, temizliği “oom_reaper” isimli özel bir kernel thread’i yapar ve belleği sisteme geri kazandırır.)

Neden Olur? (Overcommit Yalanı)

Linux Kernel’ı, RAM yönetimi konusunda biraz “yalancıdır” (veya teknik tabirle “optimisttir”). Sisteminizde fiziksel olarak 8GB RAM var diyelim.

  1. Uygulama A: “Bana 4 GB yer ayır (malloc)” der. Kernel: “Tamam, al.” (Aslında vermez, sadece söz verir / Virtual Memory).
  2. Uygulama B: “Bana 6 GB yer ayır” der. Kernel: “Sana da tamam, dert etme.”

Toplamda 10GB söz verdi. Ama fizikselde 8GB var. Buna Memory Overcommit denir. Mantığı şudur: “Nasılsa herkes aynı anda, ayırttığı tüm alanın her baytını kullanmaz. Uçak şirketlerinin her uçuşta ‘Fazla Bilet’ (Overbooking) satması gibi.”

💀 Ölümden Beteri: Swap Thrashing OOM Killer bir “Merhamet Atışı”dır. Eğer sisteminizde Swap (Takas) alanı varsa, Kernel süreci öldürmeden önce belleği diske boşaltmaya çalışır. Disk yavaş olduğu için sistem dakikalarca donabilir (SSH bile yanıt vermez). Buna Swap Thrashing denir. Bazen sunucunun hızlıca hata verip kapanması (Fail Fast), 20 dakika boyunca donmasından iyidir. Bu yüzden vm.swappiness değerini Production sunucularda düşük tutmak (örneğin 10 veya 1) hayat kurtarır.

Ama gün gelir, Uygulama A ve B aynı anda o alanları gerçekten doldurmaya başlarsa (“Physical Write”), RAM biter. Swap alanı da biter. Kernel köşeye sıkışır. Ne yapacak? Kernel Panic (Mavi Ekranın Linux hali) geçirip tüm sunucuyu, işletim sistemini dondurmak mı? Yoksa “Sistemin (Geminin) yaşaması için birinin (Yükün) denize atılması gerekiyor” kararı mı? Kernel yaşamak ister. Ve OOM Killer’ı devreye sokar.

graph TD
    Start((RAM Bitti)) --> SwapCheck{Swap Dolu mu?}
    SwapCheck -- Hayır --> SwapUse[Swap'e Yaz]
    SwapUse --> Thrashing{Swap Thrashing?}
    Thrashing -- Evet --> Freeze[⏳ Sistem Felç Olur]
    SwapCheck -- Evet --> OOM[🚨 OOM Killer Tetiklenir]
    
    OOM --> Scan[Tüm Süreçleri Tara]
    Scan --> Calc{Puan Hesapla}
    Calc -->|RAM + Swap Kullanımı| Score[Badness Score]
    Calc -->|OOM Score Adj| Adjusted[Son Puan]
    
    Adjusted --> Victim{En Yüksek Puanlı?}
    Victim -- Evet --> Kill[🔫 SIGKILL Gönder]
    Kill --> Free[RAM Boşaldı]
    
    style OOM stroke:#bbf
    style Kill stroke:#f00

Kurban Nasıl Seçilir? (oom_score)

OOM Killer, eline silahı alıp rastgele ateş etmez. Bir mantığı, bir algoritması, soğukkanlı bir puanlama sistemi (badness score) vardır. Her süreç (PID) için bir karne hazırlar:

🧮 OOM Score Formülü (Basitleştirilmiş): Score = (RAM Kullanımı + Swap Kullanımı) + oom_score_adj

Kernel, her sürecin ne kadar bellek (RSS) ve Swap harcadığını toplar. Sonra buna sizin verdiğiniz torpil puanını (adj) ekler. Çıkan sonuç 0 ile 1000 arasındadır. 1000 Puan = %100 Bellek Kullanımı = Kesin Ölüm.

⚠️ Kritik Ayrım: Kernel, top komutundaki sanal VIRT belleğe değil, fiziksel RES (RSS - Resident Set Size) belleğe bakar. Bir sürecin 100GB sanal belleği olabilir, ama fizikselde 10MB yer kaplıyorsa OOM Killer için masumdur.

  1. RAM Kullanımı: En çok RAM tüketen (RSS + Swap), en büyük hedeftir. (Zenginler ilk ölür).
  2. Ömür: Kısa süredir çalışan süreçler, uzun süredir çalışan emektar süreçlere göre daha kolay harcanır.
  3. Kullanıcı: root ve sistem süreçleri (sshd, systemd) korunmaya çalışılır, normal kullanıcı süreçleri harcanır.

Bu puanı canlı olarak görebilirsiniz. cat /proc/<PID>/oom_score komutu size 0 ile 1000 arasında bir puan verir. Puan ne kadar yüksekse, namlu o sürece o kadar yakındır.

🛡️ Modern Linux Kullanıcılarına Uyarı: systemd-oomd Ubuntu 22.04+ veya Fedora kullanıyorsanız, Kernel’dan önce davranan systemd-oomd ile tanışın. Bu servis, RAM bitmese bile “Swap Basıncı” (PSI) artarsa uygulamaları öldürebilir. dmesg‘de iz bulamazsanız, journalctl -u systemd-oomd komutuna bakın. Gizli tetikçi odur.

graph LR
    subgraph Userspace[Userspace: systemd-oomd]
        PSI[PSI İzle] -->|Basınç Yüksek!| SystemdKill{Erken Müdahale}
        SystemdKill -->|SIGKILL| App1[Uygulama]
    end

    subgraph Kernel[Kernel Space: OOM Killer]
        Malloc[Malloc] -->|RAM Bitti!| KernelKill{Zorunlu İnfaz}
        KernelKill -->|SIGKILL| App2[Uygulama]
    end
    
    style Userspace stroke:#1565c0
    style Kernel stroke:#c62828

🕵️‍♂️ 3. Vaka Analizi: Kayıp Java Uygulaması ve Container Dünyası

Bir e-ticaret şirketinde, Production ortamındaki Java (Spring Boot) uygulamasının her 3 günde bir, düzensiz aralıklarla çöktüğü (“Killed” olduğu) raporlanıyor. Yazılımcılar hemen Heap Dump inceliyor: “Memory Leak yok, Heap boyutunu 2GB ile sınırladık (-Xmx2G). Uygulama taş çatlasa 2GB harcıyor.” Sysadmin htop‘a bakıyor: “Sunucuda (Host) 16GB RAM var, boş yer çok. RAM bitmiyor ki!”

Ama dmesg yalan söylemez. OOM Killer, Java’yı vurmuştur. Peki neden?

Analiz: Yazılımcı Heap’i 2GB sınırlamış olabilir. Güncel Java sürümleri (Java 10+ veya Java 8u191+), -XX:+UseContainerSupport ile container limitlerini otomatik algılar. Ancak çok eski sürümler veya yanlış yapılandırılmış JVM’ler, kendilerini hala 64GB RAM’e sahip Host makinede sanabilir. Ayrıca, Heap dışında bir de “Off-Heap” bellek vardır:

  • Metaspace: Class metadata’ları.
  • Thread Stacks: Her thread için (varsayılan 1MB) alan.
  • GC Overhead: Garbage Collector’ın çalışırken kullandığı alan.
  • Direct Buffers: (NIO) Hızlı I/O için kullanılan alanlar.

Bunlarla beraber Java süreci aslında 2.5GB veya 3GB RAM tüketebilir.

graph TD
    subgraph Container["Docker Container (Limit: 2GB)"]
        Heap[Java Heap: 2GB]
        OffHeap[Off-Heap: 500MB]
        Total[Toplam: 2.5GB]
        
        Heap --- OffHeap
        OffHeap --> Total
    end
    
    Total -->|Limit Aşıldı!| Cgroup[Cgroup OOM]
    Cgroup -->|SIGKILL| Container
    
    style OffHeap stroke:#333,color:white
    style Container stroke:#333,stroke-dasharray: 5 5

Daha da önemlisi, bu uygulama bir Docker Container içinde mi çalışıyor?

🐳 Cgroups v1 vs v2 Kargaşası: Dikkat! Cgroups v2 (Modern Kubernetes/Docker) sadece suçlu süreci öldürür. Ancak eski sistemlerdeki Cgroups v1, bazen tüm Container’ı komple durdurabilir. Eğer dmesg‘de “Memory cgroup out of memory” görüyor ama ölen süreci bulamıyorsanız, eski bir Kernel’ın azizliğine uğruyor olabilirsiniz. Modern dünyaya (v2) geçin.

Eğer Kubernetes YAML dosyasında limit: 2Gi verildiyse; Sizin uygulamanız (Heap + Off-Heap) 2.01 GiB olduğu anda, Host makinede 1 Terrabyte RAM olsa bile Cgroups (Container Runtime) devreye girer. “Sen sana ayrılan 2GB limitini aştın” der ve OOM Killer’ı sadece o container içine gönderir. Loglarda “Killed” görürsünüz. Host makinenin RAM’i boştur ama Container ölmüştür.

Çözüm:

  1. Doğru Limit: Java’nın -Xmx değeri, Container limitinin (Memory Limit) asla %100’ü olmamalı. %70-75’i olmalı (Örn: Container 4GB ise Heap 3GB). Geriye kalan %25, işletim sistemi ve Off-Heap için bırakılmalı.
  2. Skor Ayarı (Geçici Hack/Diplomatik Dokunulmazlık): Kritik bir süreciniz varsa (örneğin SSHD), Kernel’a “Buna dokunma” diyebilirsiniz. echo -1000 > /proc/<PID>/oom_score_adj. Bu komut Kernel’a “Bu sürece -1000 puan ver” der. Böylece puanı 0’ın altına düşer ve OOM Killer onu atlar (“Dokunulmazlık”). (Java uygulamaları için bu tavsiye edilmez, çünkü RAM gerçekten biterse Kernel Panic yaşanır. Ama SSHd gibi yönetim servisleri için hayat kurtarır).

    💡 Kalıcı Çözüm: echo komutu reboot sonrası uçar. Kalıcılık için servis dosyanıza (/etc/systemd/system/sshd.service.d/override.conf) şunu ekleyin: [Service] OOMScoreAdjust=-1000

4. “Segmentation Fault” (İntihar) vs “Killed” (Cinayet)

Bazen loglarda “Killed” yazmaz. Şunu görürsünüz: Segmentation fault (core dumped)

Bu bir cinayet değildir. Bu bir intihardır. OOM Killer veya bir başkası karışmamıştır. Program (Process), hafızada (RAM) kendisine ait olmayan, erişim izni olmayan bir adrese yazmaya veya okumaya çalışmıştır.

  • NULL pointer’a erişim (Olmayan bir nesneyi çağırmak).
  • Buffer Overflow (Dizinin sınırını taşmak).
  • RAM’in donanımsal olarak bozuk bir sektörüne denk gelme.

Bu durumda Kernel, “Hop, orası senin tarlan değil!” diyerek sürece SIGSEGV (Signal Segmentation Violation) sinyalini çakar. Process anında ölür ve geriye (ayarlıysa) bir “Core Dump” dosyası (hafızanın fotoğrafı) bırakır. Suçlu: %99 ihtimalle Yazılımcı (C/C++ kodu hatası, Pointer hatası). %1 ihtimalle bozuk RAM (Donanım).

📦 Container’da Core Dump Nereye Gider? Docker içinde “Core dumped” hatası alırsanız dosyayı muhtemelen bulamazsınız. Çünkü dump işlemi Host Kernel tarafından yönetilir ve varsayılan olarak container içine yazılmaz. Yakalamak için Host makinede /proc/sys/kernel/core_pattern ayarını özelleştirmeniz gerekir. Aksi takdirde delil (ceset) kaybolur.

🚑 5. Cinayeti Kim İşledi? (Auditd ile Takip)

Senaryo: OOM Killer masum (dmesg temiz). Segfault yok (Exit code 139 değil). Ama süreç “Killed” oldu (Exit code 137). Demek ki, etten kemikten bir İNSAN (veya bir script), manuel olarak kill komutunu çalıştırdı. Ya sıkılan bir Junior Sysadmin, ya hatalı yazılmış bir Crontab scripti, ya da bir CI/CD Deploy aracı süreci öldürdü.

“Ben yapmadım” diyen kullanıcıyı nasıl bulursunuz? history komutuna bakmak yetmez (silebilir). Çözüm: Auditd (Linux Audit System). Linux varsayılan olarak “kim kimi öldürdü” logunu tutmaz (çok log oluşur diye). Ama açabilirsiniz.

1
2
3
4
# audit kuralı ekle: "kill" sistem çağrısını (syscall) izle
# arch=b64: 64-bit sistem
# -k process_killer: Bu logu "process_killer" etiketiyle ara
sudo auditctl -a always,exit -F arch=b64 -S kill -k process_killer

Bu kuraldan sonra, birisi (veya bir script) kill komutunu kullandığında /var/log/audit/audit.log dosyasına şu düşer:

1
type=SYSCALL ... syscall=62 (kill) ... a0=1234 (hedef pid) a1=9 (sinyal=SIGKILL) ... auid=1000 (yapan user=ahmet) exe="/usr/bin/bash"

Çeviri: “Ahmet kullanıcısı (auid=1000), Bash terminalinden, 1234 PID’li sürece 9 numaralı sinyali (SIGKILL) gönderdi.”

Artık faili meçhul cinayet yoktur. Kanıt vardır.

🧟 6. Yürüyen Ölüler: Zombie (Z) ve Orphan Süreçler

Bir süreci öldürdünüz (kill -9). Ama top komutuna baktığınızda hala listede duruyor. Yanında Z veya <defunct> yazıyor. Tekrar ateş ediyorsunuz, yine ölmüyor. Neden? Çünkü zaten ölü. Ölü bir şeyi tekrar öldüremezsiniz.

Neden “Zombie” Olurlar?

Linux’ta bir süreç öldüğünde (exit), hemen yok olmaz. “Ben işimi bitirdim, çıkış kodum bu, beni kayıtlardan silin” demek için ebeveynini (Parent Process) bekler. Eğer ebeveyn süreç (Parent) sorumsuzsa ve bu bildirimi okumazsa (wait() sistem çağrısını yapmazsa), çocuk süreç RAM’den silinir ama Process Tablosunda bir “Zombi” olarak kalır.

  • Zararı: RAM tüketmezler (ceset çürümemiştir). Ama PID (Process ID) tüketirler. Linux’ta PID sayısı sınırlıdır (varsayılan 32768). Eğer binlerce zombi oluşursa, sistem “No more processes” hatası verir ve yeni hiçbir şey çalıştıramaz. Kilitlenir.

Nasıl Yok Edilir?

Zombiye kurşun işlemez (kill -9 <ZombiePID> işe yaramaz). Onu yok etmek için kafasını (Ebeveynini) vurmanız gerekir.

  1. Ebeveyni Bulun: ps -o ppid= -p <ZombiePID>
  2. Ebeveyni Öldürün: kill -15 <ParentPID>

Ebeveyn öldüğünde, ortada kalan zombiler Linux’un en büyük babası olan init (veya systemd, PID 1) sürecine evlatlık verilir. systemd çok sorumlu bir ebeveyndir, hemen wait() çağırır ve zombileri usulüne uygun şekilde gömer.

🛠️ Debugging Kontrol Listesi

Process’iniz sessizce öldüğünde panik yapmayın, şu sırayı takip edin:

  1. Manzara Kontrolü (free -h): Derinlere dalmadan önce free -h komutuyla genel duruma bakın. Swap tamamen dolu mu? Available sütunu kritik seviyede mi?

  2. Dmesg ve Journalctl (Hızlı Triage): dmesg -T zaman kayması yapabilir. Kesin saat için: journalctl -k --grep="Killed" veya journalctl -k -r (sondan başa). Kırmızı yazılar, OOM Killer izi, RAM miktarları var mı?

  3. Exit Code Analizi: Process’i systemd veya docker çalıştırıyorsa, “Exit Code” (Çıkış Kodu) en büyük ipucudur.
    • Exit Code 137 -> (128 + 9). Yani SIGKILL. Genelde OOM Killer veya manuel kill -9.
    • Exit Code 143 -> (128 + 15). Yani SIGTERM. Birisi kibarca “Dur” demiş (Deploy, Restart).
    • Exit Code 139 -> (128 + 11). Yani SIGSEGV. Segfault (Yazılım hatası, İntihar).
  4. Kernel Overcommit Ayarı (Dikkat!): Sunucunuz kritikse ve çok sık OOM oluyorsa, Kernel’ın “Hayal satmasını” engelleyebilirsiniz: sysctl vm.overcommit_memory=2.

    ⚠️ Kritik Uyarı: Bu ayarı yaparken vm.overcommit_ratio değerini kontrol etmezseniz (Default: %50), 16GB RAM’i olan sunucuyu “Elimde sadece 8GB var” moduna sokarsınız. Yarısını çöpe atmamak için oranı ihtiyacınıza göre (%100) ayarlayın.

  5. Auditd: Hala bulamadıysanız, auditd ile kill syscall’ını dinlemeye alın. Katil mutlaka bir iz bırakacaktır.

  6. Proaktif Takip (Hasta İzleme): Olay olduktan sonra (dmesg) bakmak kolaydır. Olay olmadan önce yakalamak için:
    • Modern Gözcü (PSI): /proc/pressure/memory dosyasını izleyin. (Çıktıdaki avg10=20.00, son 10 saniyede sistemin %20 oranında bellek beklediğini gösterir. Bu sayı artıyorsa fırtına yaklaşıyor demektir.)
    • pidstat -r 1: Hangi süreç RAM’i (“RSS”) şişiriyor, saniye saniye izleyin.
    • watch -n 1 cat /proc/<PID>/oom_score: Kritik sürecinizin OOM puanının yükselişini (bir at yarışı gibi) canlı izleyin.
    • Monitoring: Production ortamında dmesg başında beklenmez. Prometheus/Grafana ile node_memory_MemAvailable_bytes ve container_memory_usage_bytes metriklerine alarm kurun.

🔮 Sırada Kim Var? (Next in Line) Merak ediyorsanız, şu komutla OOM Killer’ın şu anki “Ölüm Listesi”ni görebilirsiniz:

1
2
# En yüksek OOM puanına sahip 5 süreci listele
for proc in $(find /proc -maxdepth 1 -regex '/proc/[0-9]+'); do printf "%2d %5d %s\n" "$(cat $proc/oom_score 2>/dev/null)" "$(basename $proc)" "$(cat $proc/cmdline 2>/dev/null | tr '\0' ' ' | head -c 50)"; done | sort -nr | head -n 5

En tepedeki süreç, RAM bittiği an ölecek ilk kişidir.

🔮 Bir Sonraki Adım: “Gürültülü Sessizlik”

Bellek sızıntılarını çözdünüz. OOM Killer ile barıştınız. “Killed” mesajının arkasındaki matematiği anladınız. Ama sunucudan fan sesleri geliyor. CPU %100 olmuş. top komutuna bakıyorsunuz, bir süreç (process) tepede çığlık atıyor. Hemen dedektifçilik oynamak için strace çalıştırıyorsunuz… ve hiçbir şey görmüyorsunuz. Ekran bomboş.

Bir program CPU’yu sömürürken neden sistem çağrısı yapmaz?

Bir sonraki yazıda, User Space dünyasına iniyoruz. strace‘in neden kör olduğunu, perf ile işlemcinin zihnine nasıl gireceğimizi ve %us ile %sy arasındaki kritik farkı inceliyoruz.

🚀 Serinin Devam Yazısı: Linux’ta CPU Profiling ve perf Kullanımı: strace’in Kör Noktası ve Zihin Okuma

This post is licensed under CC BY 4.0 by the author.