embedded-deck
TEKNİK REHBER GÖMÜLÜ LİNUX MEMORY MANAGEMENT 2026

Linux Memory Management
Derin Bakış.

Sanal adres uzayından fiziksel sayfa tablolarına, slab allocator'dan OOM killer'a, huge pages'ten NUMA politikalarına ve embedded cihazlarda memory leak tespitine kadar Linux bellek yönetiminin eksiksiz rehberi.

00 Linux VM mimarisi

Linux, her process için bağımsız bir sanal adres uzayı sunar. ARM64 mimarisinde 4 seviyeli sayfa tablosu (4-level paging), 48-bit sanal adres uzayı sağlar.

Sanal adres uzayı düzeni (ARM64)

0xFFFF_FFFF_FFFF_FFFF ┐
        Kernel Space   │  128 TB (TTBR1_EL1)
        (tüm process   │  Kernel kodu, veri, modul
         paylaşır)     │  vmalloc, ioremap, kasan
0xFFFF_0000_0000_0000 ┘
          [Boşluk — erişim → SIGSEGV]
0x0000_FFFF_FFFF_FFFF ┐
        User Space     │  128 TB (TTBR0_EL1)
        (her process   │  Stack (yüksek adres)
         izole)        │  mmap / shared libs
                       │  Heap (brk büyür)
                       │  BSS / Data / Text
0x0000_0000_0000_0000 ┘
    

4-level paging (ARM64 / x86-64)

PGD (Page Global Dir)Seviye 0 — 48-bit VA'nın [47:39] bitleri, TTBR register'ı işaret eder
PUD (Page Upper Dir)Seviye 1 — [38:30] bitleri, 1 GB'lık blok haritalama mümkün (huge page)
PMD (Page Middle Dir)Seviye 2 — [29:21] bitleri, 2 MB'lık blok haritalama (THP için kullanılır)
PTE (Page Table Entry)Seviye 3 — [20:12] bitleri, 4 KB'lık fiziksel sayfa adresi + izin bitleri
Page offset[11:0] bitleri — 4 KB sayfa içinde byte ofseti

TLB (Translation Lookaside Buffer)

Her VA → PA çevirisi sayfa tablosunda 4 bellek erişimi gerektirir. TLB bu çevirileri önbellekler. Context switch'te TLB flush (ASID tagging ile kısmen kaçınılabilir) gecikmeye yol açar.

# TLB istatistiklerini perf ile izle
perf stat -e dTLB-load-misses,iTLB-load-misses ./myapp

# ARM64 ASID tagging — TLB flush olmadan context switch
# kernel/arch/arm64/mm/context.c içinde asid_generation

# Sayfa tablosunu bir process için incele
cat /proc/$(pgrep myapp)/smaps | grep -E "Size|Rss|Pss|Dirty"
NOT

ARM64'te 4 KB, 16 KB ve 64 KB sayfa boyutları desteklenir. Raspberry Pi (BCM2837/BCM2711) 4 KB granule kullanır. Büyük sayfa granule TLB miss sayısını düşürür fakat daha fazla bellek israfına yol açabilir.

01 /proc/meminfo anatomisi

/proc/meminfo, sistemdeki bellek kullanımının anlık fotoğrafını sunar. Her satırın ne anlama geldiğini bilmek, bellek sorunlarını teşhis etmek için kritiktir.

Temel alanlar

cat /proc/meminfo
# MemTotal:        8057072 kB   ← Toplam fiziksel RAM (kernel + user)
# MemFree:          312548 kB   ← Hiç kullanılmayan RAM
# MemAvailable:    3214088 kB   ← Yeni proses için kullanılabilir tahmin
# Buffers:          184736 kB   ← Blok cihaz metadata önbelleği
# Cached:          2891220 kB   ← Page cache (dosya içerikleri)
# SwapCached:        12340 kB   ← Swap'ta ama aynı zamanda RAM'de
# Active:          3104568 kB   ← Son kullanılan sayfalar (LRU head)
# Inactive:        1892340 kB   ← Uzun süredir kullanılmayan (LRU tail)
# Active(anon):    1284000 kB   ← Anonim aktif (heap, stack)
# Inactive(anon):   384000 kB   ← Anonim inaktif (swap adayı)
# Active(file):    1820568 kB   ← Dosya aktif page cache
# Inactive(file):  1508340 kB   ← Dosya inaktif page cache
# SwapTotal:       2097148 kB   ← Toplam swap alanı
# SwapFree:        1843200 kB   ← Kullanılmayan swap
# Dirty:             14528 kB   ← Değiştirilmiş ama henüz yazılmamış
# Writeback:             0 kB   ← Aktif olarak diske yazılan
# SReclaimable:     452000 kB   ← Slab'ın geri alınabilir kısmı (cache)
# SUnreclaim:        89000 kB   ← Slab'ın zorunlu (kernel struct) kısmı
# KernelStack:       24576 kB   ← Kernel stack sayfaları
# PageTables:        48000 kB   ← Sayfa tablosu belleği
# HugePages_Total:       0      ← Statik huge page sayısı
# HugePages_Free:        0

MemAvailable formülü

ÖNEMLİ

MemAvailable, MemFree + reclaimable Cached + reclaimable Slab değerinden hesaplanır. MemFree sıfıra yakın olsa bile yüzlerce MB Cached geri alınabildiği için sistem normalde çalışmaya devam eder. Gerçek baskı MemAvailable çok düşük olduğunda başlar.

AlanTanımKritik Eşik
MemFreeTamamen boş sayfaTek başına anlamsız
MemAvailableGerçek kullanılabilir tahmin<%10 → baskı başlar
DirtyYazılmamış page cacheÇok yüksek → I/O latency
SUnreclaimSerbest bırakılamaz kernel bellekSürekli artarsa → leak
SwapFree → 0Swap dolduOOM killer yaklaşıyor

02 Page cache

Page cache, disk I/O'yu hızlandırmak için dosya içeriklerini RAM'de tutar. Linux mevcut boş belleği agresif biçimde page cache'e atar; gerektiğinde geri verir.

Dosya okuma akışı

read() syscall
    |
  VFS Layer
    |
  Page Cache kontrol ── HIT ──→ doğrudan kullanıcıya kopyala
    |
   MISS
    |
  Block Layer (bio submit)
    |
  Disk / eMMC / NVMe
    |
  Veri sayfa cachee yüklenir
    |
  Kullanıcıya kopyalanır
    

Dirty page ve writeback

# Dirty page kontrolü
cat /proc/sys/vm/dirty_ratio          # %10 → toplam RAM'in %10'u → sync writeback
cat /proc/sys/vm/dirty_background_ratio # %5 → arka plan writeback başlar
cat /proc/sys/vm/dirty_writeback_centisecs # 500 = 5 sn writeback periyodu
cat /proc/sys/vm/dirty_expire_centisecs    # 3000 = 30 sn sonra dirty page zorunlu flush

# Anlık dirty/writeback miktarı
grep -E "Dirty|Writeback" /proc/meminfo

# Tüm dirty page'leri zorla flush et
sync

# kswapd ve writeback thread durumu
ps aux | grep -E "kswapd|writeback|kworker"

Page cache temizleme

# Page cache'i temizle (test/benchmark öncesi)
sync
echo 1 > /proc/sys/vm/drop_caches   # sadece page cache

# Slab + page cache
sync
echo 2 > /proc/sys/vm/drop_caches   # sadece slab
echo 3 > /proc/sys/vm/drop_caches   # slab + page cache

# UYARI: production'da kullanmayın — I/O spike'ına yol açar!

vmstat ile page cache izleme

# 1 sn aralıklı bellek istatistikleri
vmstat 1 10
# procs  --------memory--------  ---swap-- -----io---- -system-- ----cpu----
#  r  b  swpd   free   buff  cache   si  so    bi    bo   in   cs  us sy id wa
#  1  0     0 312548 184736 2891220  0   0    12     8  245 891   5  2 93  0

# si/so: swap in/out — sıfır değilse bellek baskısı var
# bi/bo: block in/out — sayfa yükleme I/O
# buff: buffer cache  cache: page cache
EMBEDDED NOT

Embedded sistemlerde RAM kısıtlı olduğu için dirty_ratio ve dirty_background_ratio'yu düşürmek beklenmedik I/O spike'larını azaltır. eMMC flash için sık writeback ömrü kısaltabileceğinden denge kurulmalıdır.

03 Slab allocator

Slab allocator, kernel içindeki küçük ve sık kullanılan nesnelerin (inode, dentry, task_struct...) verimli tahsisini sağlar. Parçalanmayı önler, önbellekleme ile tahsis hızını artırır.

Allocator varyantları

AllocatorAçıklamaKullanım Durumu
SLABOrijinal tasarım, debug dostu, karmaşıkBüyük sunucular, eski kernellar
SLUBVarsayılan (2.6.23+), basit, hızlı, düşük overheadModern Linux (ARM/x86)
SLOBÇok küçük footprint, minimal overheadÇok kısıtlı embedded (<32 MB)

kmalloc / kfree

/* Kernel modülünde bellek tahsisi */
#include <linux/slab.h>

/* Küçük tahsis (slab önbelleğinden) */
void *buf = kmalloc(256, GFP_KERNEL);
if (!buf)
    return -ENOMEM;
/* ... kullan ... */
kfree(buf);

/* Sıfırlanmış tahsis */
void *zbuf = kzalloc(sizeof(struct mydata), GFP_KERNEL);

/* Array tahsisi — overflow-safe */
void *arr = kcalloc(count, sizeof(struct item), GFP_KERNEL);

/* GFP bayrakları:
 * GFP_KERNEL  — uyku izinli, normal context
 * GFP_ATOMIC  — interrupt/atomic context, uyku yok
 * GFP_DMA     — DMA erişilebilir bölge (< 16 MB)
 * __GFP_ZERO  — sıfırla */

/proc/slabinfo ve slabtop

# Slab önbelleklerini listele
cat /proc/slabinfo | head -20
# slabinfo - version: 2.1
# # name       <active_objs> <num_objs> <objsize> <objperslab> <pagesperslab>
# kmalloc-256      4096       4096       256         16          1
# kmalloc-128      8192       8192       128         32          1
# dentry           25000      30000      192         21          1
# inode_cache      12000      15000      608          6          1

# Gerçek zamanlı izleme (htop benzeri)
slabtop

# Belirli cache'i filtrele
cat /proc/slabinfo | awk '/dentry/ {print $1, $2, $3, $4}'

# SLUB debug (CONFIG_SLUB_DEBUG)
cat /sys/kernel/slab/kmalloc-256/alloc_calls 2>/dev/null

Bellek parçalanması

# Harici fragmentation durumu
cat /proc/buddyinfo
# Node 0, zone   Normal    120  95  64  38  20  8  4  2  1  0  0
# Sütunlar: order 0 (4KB) → order 10 (4MB) blok sayısı

# Compaction tetikle (fragmentasyonu azalt)
echo 1 > /proc/sys/vm/compact_memory

# Kernel memory fragmentation istatistikleri
grep -E "compact|pgmigrate" /proc/vmstat

04 OOM killer

Out-Of-Memory killer, kullanılabilir bellek tükendiğinde sistemi kurtarmak için bir veya daha fazla process'i sonlandırır. Hangi process'in öldürüleceği oom_score ile belirlenir.

oom_score hesaplama

# Bir process'in OOM skoru (0-1000)
cat /proc/$(pgrep myapp)/oom_score
# 742  ← yüksek = öldürülme olasılığı yüksek

# OOM skoru ayarlama (-1000 = asla öldürme, +1000 = önce öldür)
echo -900 > /proc/$(pgrep sshd)/oom_score_adj
echo 500  > /proc/$(pgrep myapp)/oom_score_adj

# Systemd servis için OOM ayarı:
# /etc/systemd/system/myapp.service
# [Service]
# OOMScoreAdjust=-500

Overcommit politikası

overcommit_memory=0Varsayılan — sezgisel kontrol: saçma istekler reddedilir, normal aşırı commit kabul edilir
overcommit_memory=1Her zaman kabul et — malloc asla NULL dönemez, OOM olasılığı yüksek
overcommit_memory=2Kesin sınır — swap + RAM*ratio kadar toplam commit, aşınca malloc başarısız olur
# Mevcut overcommit politikası
cat /proc/sys/vm/overcommit_memory
cat /proc/sys/vm/overcommit_ratio   # mode=2 için RAM yüzdesi (varsayılan 50)

# Toplam commit limiti ve kullanımı
grep -E "Committed_AS|CommitLimit" /proc/meminfo

dmesg OOM log analizi

# OOM killer tetiklenmiş mi?
dmesg | grep -i "oom\|killed process\|out of memory"

# Tipik OOM log örneği:
# [12345.678] Out of memory: Kill process 1234 (myapp) score 742 or sacrifice child
# [12345.679] Killed process 1234 (myapp) total-vm:524288kB, anon-rss:498688kB
# [12345.680]  oom_kill_process+0x...

# oom_kill_process çağrıldığında kernel şunu basar:
# - Tüm processlerin anlık bellek kullanımı (task dump)
# - vm stats, oom_score, oom_score_adj değerleri
EMBEDDED STRATEJİ

Embedded sistemlerde kritik daemon'lara (udhcpd, sshd, watchdog) OOMScoreAdjust=-1000 verin. Ana uygulama process'i varsayılan bırakın ya da +100..+300 verin. overcommit_memory=2 ile deterministik davranış sağlanabilir fakat tüm bellek taleplerinin peşin hesaplanması gerekir.

05 Huge pages

Huge pages, TLB miss sayısını dramatik şekilde azaltarak büyük bellek kullanan uygulamalarda (veritabanı, ML inference, DSP) performans kazanımı sağlar.

Statik huge pages

# Mevcut huge page durumu
cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages   # 2 MB
cat /sys/kernel/mm/hugepages/hugepages-1048576kB/nr_hugepages # 1 GB (ARM64)

grep Huge /proc/meminfo
# HugePages_Total:   128   ← ayrılan sayfa sayısı
# HugePages_Free:    112   ← kullanılmayan
# HugePages_Rsvd:      4   ← rezerve edilmiş
# Hugepagesize:     2048 kB

# Kernel parametresi ile ayarla
echo 64 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# veya boot parametresi:
# hugepages=128 hugepagesz=2M

# hugetlbfs mount et
mkdir /mnt/huge
mount -t hugetlbfs nodev /mnt/huge

THP (Transparent Huge Pages)

# THP modu kontrol
cat /sys/kernel/mm/transparent_hugepage/enabled
# [always] madvise never

# Modlar:
# always   = agresif, tüm anonim bellek için dene
# madvise  = sadece madvise(MADV_HUGEPAGE) ile işaretlenmiş
# never    = THP kapalı

# Embedded sistemlerde genellikle kapalı tutulur:
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled

# THP istatistikleri
grep -i huge /proc/vmstat | grep -v "nr_anon\|file"

madvise ile THP kullanımı (C)

#include <sys/mman.h>

/* Büyük buffer için huge page iste */
void *buf = mmap(NULL, 256 * 1024 * 1024,   /* 256 MB */
                 PROT_READ | PROT_WRITE,
                 MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);

/* THP kullan — 2 MB bloklar olarak haritalansın */
madvise(buf, 256 * 1024 * 1024, MADV_HUGEPAGE);

/* İşlem bitince geri ver */
madvise(buf, 256 * 1024 * 1024, MADV_NOHUGEPAGE);
munmap(buf, 256 * 1024 * 1024);

06 Memory pressure ve PSI

PSI (Pressure Stall Information), Linux 4.20'den itibaren CPU, memory ve I/O kaynaklarının ne kadar zamanda baskı altında olduğunu ölçer. Memory pressure, OOM olmadan önce erken uyarı sağlar.

/proc/pressure/memory

cat /proc/pressure/memory
# some avg10=0.52 avg60=0.18 avg300=0.06 total=1234567
# full avg10=0.00 avg60=0.00 avg300=0.00 total=0

# "some": En az bir task memory bekliyor (stall)
#   avg10  = son 10 saniye ortalama % stall süresi
#   avg60  = son 60 saniye ortalama
#   avg300 = son 5 dakika ortalama
#   total  = toplam microsaniye (monotonic)
#
# "full": TÜM runnable task'lar memory bekliyor (sistem donuk)
#   full > 0 → ciddi bellek baskısı

# Diğer PSI dosyaları
cat /proc/pressure/cpu
cat /proc/pressure/io

cgroup memory.pressure

# cgroup v2 ile konteyner/servis düzeyinde PSI
cat /sys/fs/cgroup/system.slice/myservice.service/memory.pressure
# some avg10=1.23 avg60=0.45 avg300=0.12 total=5678900

# cgroup memory limiti ayarla
echo "512M" > /sys/fs/cgroup/myapp/memory.max
echo "400M" > /sys/fs/cgroup/myapp/memory.high   # soft limit

# memory.stat — detaylı cgroup bellek istatistikleri
cat /sys/fs/cgroup/myapp/memory.stat

PSI threshold trigger (event notification)

# PSI tabanlı bellek baskı monitörü scripti
#!/bin/bash
THRESHOLD="some 200000 1000000"   # 200ms / 1 sn window

# PSI poll ile trigger (kernel 5.2+)
exec 3<> /proc/pressure/memory
echo "$THRESHOLD" >&3

while true; do
    if read -t 5 -u 3 line; then
        echo "$(date): MEMORY PRESSURE TRIGGER: $line"
        # uyarı gönder, cache temizle, vs.
    fi
done
KULLANIM

systemd, PSI'yi cgroup bazlı hafıza baskısı tespiti için kullanır. MemoryPressureWatch=yes ile bir servis, kendi memory baskısına göre hafıza düşürme (memory.reclaim) yapabilir. Embedded sistemlerde özel bir watchdog daemon PSI'yi izleyerek kritik süreçleri yeniden başlatabilir.

07 NUMA

NUMA (Non-Uniform Memory Access), çok işlemcili sistemlerde her CPU'nun kendi yerel belleğine daha hızlı eriştiği mimaridir. Doğru NUMA politikası belirgin performans farkı yaratır.

NUMA topolojisi

# NUMA node'larını listele
numactl --hardware
# available: 2 nodes (0-1)
# node 0 cpus: 0 1 2 3 4 5 6 7
# node 0 size: 32128 MB
# node 0 free: 18234 MB
# node 1 cpus: 8 9 10 11 12 13 14 15
# node 1 size: 32128 MB
# node 1 free: 20100 MB
# node distances:
# node   0   1
#   0:  10  21
#   1:  21  10

# NUMA istatistikleri
numastat
numastat -p $(pgrep myapp)

NUMA bellek politikaları

defaultYerel node tercih edilir — process nerede çalışıyorsa oradan tahsis
bindSadece belirtilen node'lardan tahsis — başka node kullanılmaz, OOM olur
preferredTercih edilen node, dolunca diğerlerine geç
interleaveBelleği node'lara round-robin dağıt — bant genişliği kazanımı
# Node 0 CPU'da, node 0 RAM'inde çalıştır
numactl --cpunodebind=0 --membind=0 ./myapp

# Node 0 ve 1 arası interleave
numactl --interleave=0,1 ./myapp

# Tercih: node 1, dolunca node 0'a geç
numactl --preferred=1 ./myapp

# Process'in numa_maps'ini incele
cat /proc/$(pgrep myapp)/numa_maps | head -20
# 7f8a00000000 default anon=1024 dirty=1024 N0=800 N1=224 kernelpagesize_kB=4

# numad — otomatik NUMA optimizasyon daemon
systemctl start numad

08 Pratik: embedded cihazda memory leak bulma

Embedded Linux'ta memory leak tespiti — valgrind, /proc/PID/smaps analizi, OOM tuning ve page cache monitoring için adım adım pipeline.

Adım 1: /proc/PID/smaps ile leak izleme

#!/bin/bash
# memory_watch.sh — process bellek büyümesini izle
PID=$(pgrep myapp)
INTERVAL=10
LOG=/tmp/mem_watch.log

while kill -0 $PID 2>/dev/null; do
    TIMESTAMP=$(date +%s)
    RSS=$(grep VmRSS /proc/$PID/status | awk '{print $2}')
    HEAP=$(grep "\[heap\]" /proc/$PID/maps | awk '{print $1}')
    ANON=$(awk '/^Anonymous:/ {sum+=$2} END {print sum}' /proc/$PID/smaps)
    echo "$TIMESTAMP RSS=${RSS}kB Heap=$HEAP Anon=${ANON}kB" >> $LOG
    sleep $INTERVAL
done

echo "Process $PID sonlandı, log: $LOG"

Adım 2: valgrind ile heap analizi

# Valgrind memcheck (cross-compile için valgrind ARM64 binary)
valgrind \
    --tool=memcheck \
    --leak-check=full \
    --show-leak-kinds=all \
    --track-origins=yes \
    --log-file=/tmp/valgrind.log \
    ./myapp --test-mode

# Valgrind çıktısı örneği:
# ==1234== 4096 bytes in 1 blocks are definitely lost in loss record 2 of 3
# ==1234==    at 0x4C2FB0F: malloc (vg_replace_malloc.c:309)
# ==1234==    by 0x10856B: init_buffer (myapp.c:142)
# ==1234==    by 0x10899F: main (myapp.c:87)

# Massif ile heap profiling
valgrind --tool=massif --pages-as-heap=yes ./myapp
ms_print massif.out.* | head -50

Adım 3: smaps_rollup ile hızlı özet

# Kernel 4.14+ — smaps_rollup tüm sayfaların toplamı
cat /proc/$(pgrep myapp)/smaps_rollup
# Rss:              248576 kB  ← gerçek fiziksel kullanım
# Pss:              210234 kB  ← orantılı paylaşımlı
# Private_Clean:     12800 kB  ← paylaşılmayan temiz
# Private_Dirty:    198400 kB  ← paylaşılmayan kirli (heap/stack)
# Shared_Clean:      32000 kB  ← paylaşılan (SO dosyaları)
# Anonymous:        198400 kB  ← dosya arkası olmayan (heap)

# Private_Dirty artıyorsa → heap leak şüpheli

Adım 4: OOM tuning — embedded sistemde

# /etc/sysctl.d/99-memory.conf
vm.overcommit_memory = 2
vm.overcommit_ratio  = 80        # RAM'in %80'i + swap
vm.panic_on_oom      = 0         # OOM killer çalışsın, kernel paniklik etme
vm.oom_dump_tasks    = 1         # OOM sırasında tüm task bellek dökümü
vm.dirty_ratio       = 20
vm.dirty_background_ratio = 5
vm.swappiness        = 10        # swap'ı azalt, page cache tercih et

sysctl -p /etc/sysctl.d/99-memory.conf

Adım 5: page cache monitoring scripti

#!/bin/bash
# page_cache_monitor.sh
while true; do
    FREE=$(grep MemFree /proc/meminfo | awk '{print $2}')
    AVAIL=$(grep MemAvailable /proc/meminfo | awk '{print $2}')
    CACHED=$(grep "^Cached:" /proc/meminfo | awk '{print $2}')
    DIRTY=$(grep "^Dirty:" /proc/meminfo | awk '{print $2}')
    PSI_SOME=$(awk '/^some/ {print $2}' /proc/pressure/memory | cut -d= -f2)

    printf "[%s] Free:%6d kB  Avail:%7d kB  Cache:%7d kB  Dirty:%6d kB  PSI:%.2f%%\n" \
        "$(date +%H:%M:%S)" $FREE $AVAIL $CACHED $DIRTY $PSI_SOME

    if (( AVAIL < 51200 )); then   # < 50 MB kullanılabilir
        echo "UYARI: Kritik bellek baskısı! MemAvailable=$AVAIL kB" >&2
        sync && echo 1 > /proc/sys/vm/drop_caches
    fi
    sleep 5
done
ÖZET

Embedded Linux'ta bellek yönetimi; VM mimarisi, page cache writeback, slab allocator, OOM killer tuning ve NUMA politikalarının bütüncül anlaşılmasını gerektirir. Memory leak tespiti için smaps_rollup periyodik izleme + valgrind kombinasyonu en güvenilir yöntemdir. PSI ile proaktif bellek baskısı tespiti, OOM'dan önce önlem almayı sağlar.