Tüm eğitimler
TEKNİK REHBER GÖMÜLÜ LİNUX KERNEL BELLEK 2026

SLUB Allocator
Linux Kernel Bellek Yöneticisi

Linux kernel'ın varsayılan slab tahsis yöneticisi SLUB'ın iç mimarisi, gömülü sistemlerde bellek optimizasyonu ve hata ayıklama teknikleri.

00 SLUB neden var — slab allocator evrimi

Linux kernel'ın bellek yönetimi katmanları arasında slab katmanı, küçük ve orta boyutlu nesnelerin verimli tahsisini sağlar. SLUB bu katmanın günümüzdeki varsayılan uygulamasıdır.

Slab Allocator Neden Gerekli?

Buddy allocator sayfa (4 KB) granülaritesinde çalışır. Bir task_struct yaklaşık 9 KB, bir inode ise yaklaşık 600 bayttır. Her küçük nesne için tam sayfa tahsis etmek hem bellek israfına hem de TLB baskısına yol açar. Slab katmanı bu boşluğu kapatır: sayfaları önbelleğe alır ve aynı türden nesneleri bu sayfalar içinde paketler.

Uygulama / Surucu
       |
  kmalloc / kmem_cache_alloc
       |
   SLUB Katmani  <-- nesneleri onbellekte tutar
       |
  Buddy Allocator  <-- sayfa bazli tahsis
       |
  Fiziksel Bellek

Tarihsel Süreç: SLAB, SLUB ve SLOB

İlk slab uygulaması SLAB, Solaris kernel'ından Jeff Bonwick tarafından Linux'a adapte edildi (1994). Verimli nesne önbelleklemesi sağlasa da zamanla karmaşık veri yapıları, yüksek metadata alanı ve NUMA ile ölçekleme sorunları ortaya çıktı.

SLUB (2007, Christoph Lameter), SLAB'ın yerini almak üzere tasarlandı. Temel hedefleri şunlardı: daha az metadata, daha basit kod yolu, NUMA farkındalığı ve per-CPU freelist. Linux 2.6.22'de tanıtıldı ve 2011'den itibaren varsayılan allocator oldu.

SLOB ise küçük gömülü sistemler (CONFIG_EMBEDDED) için tasarlanmış minimal bir uygulamadır. Metadata overhead'i en düşük seviyededir ancak performansı düşüktür ve büyük sistemlerde kullanılmaz.

ÖzellikSLABSLUBSLOB
Varsayılan mı?Hayır (eski varsayılan)Evet (Linux 3.x+)Hayır (CONFIG_EMBEDDED)
Metadata alanıYüksekDüşük (sayfa içi)Çok düşük
NUMA desteğiSınırlıTamYok
Per-CPU freelistVar (karmaşık)Var (sade)Yok
Hata ayıklamaOrtaGüçlüMinimal
Hedef platformGenelGenel + SunucuKüçük gömülü

Kernel Konfigürasyonu

Kullanılacak allocator kernel derleme zamanında seçilir. Çalışma zamanında hangi allocator aktif olduğu aşağıdaki komutlarla doğrulanabilir:

# .config dosyasında SLUB seçimi (varsayılan)
CONFIG_SLUB=y
# CONFIG_SLAB is not set
# CONFIG_SLOB is not set

# Calısma zamanında hangi allocator aktif:
cat /proc/slabinfo | head -5

# dmesg çıktısında allocator adı:
dmesg | grep -i "slub\|slab allocator"

01 SLUB mimarisi — temel veri yapıları

SLUB'ın verimliliği, akıllıca tasarlanmış veri yapılarından kaynaklanır. Metadata'yı sayfa içinde saklayarak ayrı metadata dizileri yerine sıfır ek bellek kullanır.

kmem_cache Yapısı

Her slab önbelleği bir kmem_cache örneğiyle temsil edilir. Bu yapı, nesne boyutu, hizalama, NUMA eşleme, per-CPU veriler ve istatistikler gibi tüm önbellek meta-bilgilerini barındırır.

/* Basitleştirilmiş kmem_cache yapısı (include/linux/slub_def.h) */
struct kmem_cache {
    struct kmem_cache_cpu __percpu *cpu_slab; /* Per-CPU aktif slab */
    unsigned long flags;         /* Önbellek bayrakları (SLAB_POISON vb.) */
    unsigned long min_partial;   /* Partial listede minimum slab sayısı */
    int size;                    /* Nesne boyutu (padding dahil) */
    int object_size;             /* Gerçek nesne boyutu */
    int offset;                  /* Freelist pointer ofseti */
    int cpu_partial;             /* Per-CPU partial list boyutu */
    struct kmem_cache_order_objects oo; /* Sayfa düzeni bilgisi */
    gfp_t allocflags;            /* Buddy'den sayfa alırken kullanılan bayraklar */
    int refcount;                /* Referans sayısı */
    void (*ctor)(void *);        /* Nesne kurucu fonksiyonu */
    const char *name;            /* Önbellek adı (/sys/kernel/slab'da görünür) */
    struct list_head list;       /* Tüm kmem_cache listesine bağlantı */
    struct kmem_cache_node *node[MAX_NUMNODES]; /* NUMA node listeleri */
};

Per-CPU Slab Yapısı

kmem_cache_cpu, her CPU için ayrı tutulan yerel slab durumunu içerir. Kilit gerektirmeden hızlı tahsis ve serbest bırakma bu yapı sayesinde mümkündür.

struct kmem_cache_cpu {
    void **freelist;    /* Sonraki boş nesneye işaret eden pointer */
    unsigned long tid;  /* İşlem kimliği — ABA sorununu önler */
    struct page *page;  /* Aktif slab sayfası */
    struct page *partial; /* Kısmen dolu sayfalar listesi */
};

Slab Sayfası ve Freelist Mekanizması

SLUB, nesne meta-bilgilerini ayrı bir veri yapısında tutmaz; bunun yerine sayfa descriptor'ının struct page alanlarını yeniden kullanır. Boş nesneler, her nesnenin ilk birkaç baytında gömülü bir freelist zinciriyle birbirine bağlıdır.

struct page (slab sayfası):
  .freelist -----> [Nesne 0] -> [Nesne 3] -> [Nesne 7] -> NULL
                    (bos)         (bos)         (bos)
  .objects = 8  [Nesne 1] [Nesne 2] [Nesne 4] [Nesne 5] [Nesne 6]
  .inuse   = 5   (dolu)    (dolu)    (dolu)    (dolu)    (dolu)

Üç Seviyeli Liste Yapısı

SLUB her NUMA node için üç farklı slab kategorisi tutar:

Aktif Slab (cpu_slab->page) Per-CPU olarak tutulan, kilit gerektirmeden tahsis yapılan mevcut slab sayfası. Her CPU'nun kendi aktif sayfası vardır.
Partial List (node->partial) Hem dolu hem boş nesneler içeren sayfalar. CPU'sunun aktif sayfası tükendiğinde buradan yeni sayfa alınır. NUMA node başına ayrı listedir.
Full List Tüm nesneleri dolu sayfalar. CONFIG_SLUB_DEBUG açık olduğunda takip edilir; normal modda ayrıca izlenmez.

Tahsis Akışı

kmem_cache_alloc(cache, flags)
    |
    v
CPU freelist bos degil?  ---Evet--> freelist'ten nesne al (hizli yol, kilit yok)
    |
   Hayir
    |
    v
cpu_slab->page bos degil?  ---Evet--> page freelist'ten al, cpu freelist'i doldur
    |
   Hayir
    |
    v
node->partial listesi bos degil?  ---Evet--> partial sayfayi cpu_slab'a aktar
    |
   Hayir
    |
    v
Buddy'den yeni sayfa tahsis et (alloc_pages), yeni slab olustur

02 kmem_cache API — oluşturma ve tahsis

Sürücü veya kernel alt sistemi kendi nesne önbelleğini oluşturmak istediğinde kmem_cache_create ile başlar. Bu API, nesne boyutu ve hizalama gereksinimlerine göre optimize edilmiş bir önbellek yapısı kurar.

kmem_cache_create Prototipi

#include <linux/slab.h>

struct kmem_cache *kmem_cache_create(
    const char *name,      /* /sys/kernel/slab'da görünecek isim */
    unsigned int size,     /* Nesne boyutu (bayt) */
    unsigned int align,    /* Minimum hizalama (0 = doğal hizalama) */
    slab_flags_t flags,    /* Davranış bayrakları */
    void (*ctor)(void *)   /* Nesne kurucu (NULL = yok) */
);
/* Dönüş: başarıda kmem_cache *, hata durumunda NULL */

Önemli Bayraklar

SLAB_HWCACHE_ALIGN Nesneyi CPU donanım önbelleği satırı sınırına (genellikle 64 bayt) hizalar. False sharing'i önler; performans kritik önbelleklerde kullanılır.
SLAB_POISON Tahsis edilen nesneleri 0x5a, serbest bırakılanları 0x6b deseniyle doldurur. Use-after-free ve başlatılmamış bellek okuma hatalarını tespit etmeye yarar.
SLAB_RED_ZONE Nesnenin başına ve sonuna "red zone" eklenir. Sınır dışı yazma (out-of-bounds write) erken tespit edilir.
SLAB_ACCOUNT Bu önbellekten yapılan tahsisler cgroup bellek sayacına dahil edilir. memcg entegrasyonu için gereklidir.
SLAB_PANIC Önbellek oluşturulamazsa kernel panic verir. Kritik sistem önbelleklerinde (task_struct, inode vb.) kullanılır; NULL kontrolü gereksiz kılınır.
SLAB_TYPESAFE_BY_RCU RCU grace period dolana kadar slab sayfaları serbest bırakılmaz. Kilit olmadan güvenli nesne erişimi için kullanılır.

Tam Kullanım Örneği — Kernel Modülünde Özel Önbellek

#include <linux/module.h>
#include <linux/slab.h>
#include <linux/init.h>

/* Önbelleğe alınacak nesne tipi */
struct my_sensor_data {
    u32  sensor_id;
    s32  temperature;   /* 1/1000 derece cinsinden */
    u64  timestamp_ns;
    u8   flags;
    /* SLAB_HWCACHE_ALIGN 64 byte'a tamamlar */
};

static struct kmem_cache *sensor_cache;

static int __init sensor_cache_init(void)
{
    sensor_cache = kmem_cache_create(
        "sensor_data",                   /* isim */
        sizeof(struct my_sensor_data),   /* boyut */
        0,                               /* hizalama: doğal */
        SLAB_HWCACHE_ALIGN | SLAB_PANIC, /* bayraklar */
        NULL                             /* kurucu yok */
    );
    /* SLAB_PANIC: başarısızsa panic, NULL dönmez */
    pr_info("sensor_cache olusturuldu, boyut=%zu\n",
            kmem_cache_size(sensor_cache));
    return 0;
}

static struct my_sensor_data *alloc_sensor(void)
{
    struct my_sensor_data *sd;
    sd = kmem_cache_alloc(sensor_cache, GFP_KERNEL);
    if (!sd)
        return NULL;
    memset(sd, 0, sizeof(*sd));
    return sd;
}

static void free_sensor(struct my_sensor_data *sd)
{
    kmem_cache_free(sensor_cache, sd);
}

static void __exit sensor_cache_exit(void)
{
    kmem_cache_destroy(sensor_cache);
}

module_init(sensor_cache_init);
module_exit(sensor_cache_exit);

Hizalama Hesabı

SLUB, size parametresini birden fazla gereksinime göre yukarı yuvarlar. Bu hesap derleme zamanında gerçekleşir ve çalışma zamanı overhead'i yoktur:

/* Gerçek nesne boyutu hesabı (basitleştirilmiş) */
size = ALIGN(size, sizeof(void *));        /* freelist pointer için */
if (flags & SLAB_HWCACHE_ALIGN)
    size = ALIGN(size, cache_line_size()); /* donanım önbellek satırı */
if (flags & SLAB_RED_ZONE)
    size += sizeof(unsigned long long);    /* red zone eklenir */

/* Bir sayfaya kaç nesne sığar? */
objects_per_slab = (PAGE_SIZE << order) / size;

03 kmalloc ve kmem_cache ilişkisi

kmalloc, SLUB üzerinde inşa edilmiş genel amaçlı kernel bellek tahsis fonksiyonudur. İstenen boyutu en yakın boyut sınıfına yuvarlar ve ilgili kmem_cache'den nesne alır.

kmalloc_caches Dizisi

Kernel başlangıcında (kmem_cache_init), 8 bayttan başlayıp genellikle 8 MB'a kadar uzanan ikinin kuvvetleri ve ara boyutlar için sabit bir dizi kmem_cache oluşturulur. Bu diziye kmalloc_caches adı verilir.

/* Kernel içi tanım (include/linux/slab.h — basitleştirilmiş) */
extern struct kmem_cache *
    kmalloc_caches[NR_KMALLOC_TYPES][KMALLOC_SHIFT_HIGH + 1];

/* Boyut sınıfları (KMALLOC_NORMAL tipi):
 *  Index 0:    8 bayt    (kmalloc-8)
 *  Index 1:   16 bayt    (kmalloc-16)
 *  Index 2:   32 bayt    (kmalloc-32)
 *  Index 3:   64 bayt    (kmalloc-64)
 *  Index 4:  128 bayt    (kmalloc-128)
 *  Index 5:  256 bayt    (kmalloc-256)
 *  Index 6:  512 bayt    (kmalloc-512)
 *  Index 7: 1024 bayt    (kmalloc-1k)
 *  Index 8: 2048 bayt    (kmalloc-2k)
 *  Index 9: 4096 bayt    (kmalloc-4k)
 */

__kmalloc Akışı

kmalloc(size, flags)
    |
    v
size <= KMALLOC_MAX_CACHE_SIZE?
    |
   Evet                      Hayir
    |                          |
    v                          v
kmalloc_index(size)       __kmalloc_large_node()
boyut sinifini hesapla     (buddy allocator dogrudan)
    |
    v
kmalloc_caches[type][index]
    |
    v
kmem_cache_alloc_trace()
    |
    v
SLUB per-CPU freelist'ten nesne dondur

GFP Bayrakları

GFP_KERNEL Standart kernel tahsisi. Bellek baskısı altında bekleme (uyku) yapabilir. Interrupt context'te kullanılamaz.
GFP_ATOMIC Interrupt handler, spinlock ve benzeri atomik bağlamlarda kullanılır. Uyku yapmaz; başarısız olursa NULL döner.
GFP_NOWAIT GFP_ATOMIC benzeri ama acil bellek rezervini (emergency pool) kullanmaz. Kademeli geriye dönüş senaryoları için.
GFP_DMA DMA uyumlu (genellikle 16 MB altı) bellek bölgesinden tahsis. Eski ISA DMA için; modern sistemlerde genellikle gerekmez.
__GFP_ZERO Tahsis edilen belleği sıfırlar. kzalloc() bu bayrağı örtük olarak ekler.

Boyut Sınıfı Seçimi Örneği

/* 100 bayt istendiğinde */
idx = kmalloc_index(100);   /* idx = 7 => 128 baytlık önbellek seçilir */
cache = kmalloc_caches[KMALLOC_NORMAL][7];  /* kmalloc-128 */

/* Atık: 128 - 100 = 28 bayt iç fragmantasyon */

/* kzalloc: sıfırlanmış bellek */
void *ptr = kzalloc(sizeof(struct my_obj), GFP_KERNEL);

/* kfree: hangi boyut sınıfı olduğunu sayfa metadata'sından otomatik anlar */
kfree(ptr);

Performans İpucu: kmalloc ve kmem_cache_alloc Karşılaştırması

Tekrar eden aynı boyuttaki tahsisler için doğrudan kmem_cache_create ve kmem_cache_alloc tercih edilmelidir. kmalloc boyut yuvarlaması nedeniyle daha fazla iç fragmentasyon oluşturabilir; ayrıca boyut bilgisini bulana kadar hafif bir indeks hesabı yapar. Kendi önbelleğiniz bu ek maliyeti ortadan kaldırır ve önbellek adını /sys/kernel/slab/ altında izlemenizi sağlar.

04 Gömülü sistemlerde SLUB tuningleri

RAM kısıtlı gömülü platformlarda SLUB'ın varsayılan davranışı optimal olmayabilir. Kernel komut satırı parametreleri ve derleme zamanı seçenekleriyle bellek kullanımını ince ayarlamak mümkündür.

Kernel Komut Satırı Parametreleri

ParametreVarsayılanAçıklama
slub_min_order=N0Slab sayfası için minimum buddy order. 0 = tek sayfa (4 KB). Büyük değerler daha az sayfa başlığı overhead'i ama daha fazla bellek israfı.
slub_max_order=N1 (ARM) / 3 (x86)Slab sayfası için maksimum buddy order. Gömülü sistemlerde 0 veya 1 ile sınırlamak çok büyük parçalar kullanılmasını engeller.
slub_min_objects=N0 (otomatik)Bir slabda en az kaç nesne olmalı. SLUB, bu değeri sağlamak için gerekirse order'ı artırır.
slab_max_order=NDeğişkenkmalloc büyük tahsisleri için buddy order üst sınırı.
# Gömülü sistem bootargs örneği (256 MB RAM, ARMv7)
# U-Boot ortam değişkeni
setenv bootargs "console=ttyS0,115200 root=/dev/mmcblk0p2 rw \
    slub_max_order=0 slub_min_objects=4"

# /proc/cmdline ile doğrulama:
cat /proc/cmdline

CONFIG_SLUB_CPU_PARTIAL

Bu seçenek per-CPU partial list'i etkinleştirir. Partial slab'ların CPU'ya yerel tutulması NUMA sistemlerde büyük kazanç sağlar; ancak küçük gömülü sistemlerde (tek çekirdek veya 2 çekirdek) bu yapı gereksiz bellek harcar. Devre dışı bırakmak RAM tasarrufu sağlar:

# Küçük gömülü sistemler için (tek çekirdek, 256 MB RAM ve altı)
# CONFIG_SLUB_CPU_PARTIAL is not set

# Çok çekirdekli, 512 MB RAM üstü sistemlerde açık tutun:
CONFIG_SLUB_CPU_PARTIAL=y

Per-CPU Partial Limit Ayarı

CONFIG_SLUB_CPU_PARTIAL etkinse, her önbelleğin per-CPU partial listesinde tutulacak maksimum nesne sayısı çalışma zamanında sysfs üzerinden izlenebilir ve belirli ölçüde denetlenebilir:

# Çalışma zamanı gözlem:
cat /sys/kernel/slab/kmalloc-256/cpu_partial
cat /sys/kernel/slab/kmalloc-256/cpu_slabs

# Tüm önbelleklerde cpu_partial değerlerini listele:
for d in /sys/kernel/slab/*/; do
    name=$(basename $d)
    val=$(cat $d/cpu_partial 2>/dev/null)
    echo "$name: $val"
done

NUMA Bağlantısız Sistemlerde min_partial

Tek NUMA nodlu gömülü sistemlerde min_partial değerini düşürmek, partial listede fazladan slab sayfaları tutulmasını önler. Bu sayede bellek geri dönüşümü daha agresif olur:

# Çalışma zamanında sysfs üzerinden:
cat /sys/kernel/slab/my_cache/min_partial

# Düşürme (geliştirme/test ortamı):
echo 3 > /sys/kernel/slab/my_cache/min_partial

SLOB ile Karşılaştırma: Ne Zaman SLOB Tercih Edilmeli?

RAM'in 32 MB'ın altında olduğu ve çok fazla farklı nesne türü olmayan sistemlerde SLOB tercih edilebilir. SLUB'ın per-CPU yapıları ve partial listeleri bu ölçekte belleğin anlamlı bir bölümünü tüketir. Ancak SLOB hata ayıklama desteğinden yoksundur; bu, geliştirme sürecini zorlaştırır. Geliştirme aşamasında SLUB kullanılıp ürün imajında SLOB'a geçmek yaygın bir yaklaşımdır.

05 SLUB hata ayıklama teknikleri

Slab katmanındaki hatalar — use-after-free, double-free, heap buffer overflow — kernel'ın en tehlikeli ve tespit edilmesi en güç sorunları arasındadır. SLUB'ın yerleşik hata ayıklama altyapısı bu sorunları derleme ve çalışma zamanında yakalamayı kolaylaştırır.

CONFIG_SLUB_DEBUG

Temel hata ayıklama altyapısını etkinleştirir. Red zone, poison, track, owner gibi ek özellikler bu seçenek açık olduğunda kullanılabilir hale gelir. Üretim gömülü imajlarında kapalı tutmak, hem bellek hem CPU açısından avantajlıdır.

# Derleme zamanı:
CONFIG_SLUB_DEBUG=y
CONFIG_SLUB_DEBUG_ON=y  # Tüm önbelleklerde varsayılan olarak debug açık

# Yalnızca belirli bir önbellek için açmak (daha az overhead):
# Kernel komut satırında:
slub_debug=FZP,my_cache_name
# F = sanity check, Z = red zone, P = poison

slabinfo Kullanımı

# /proc/slabinfo çıktısını görüntüle:
cat /proc/slabinfo | head -3

# En büyük bellek tüketen önbellekler:
awk 'NR > 2 { printf "%s %d KB\n", $1, ($3 * $2) / 1024 }' \
    /proc/slabinfo | sort -k2 -rn | head -10

# Belirli bir önbelleğin tahsis istatistikleri:
cat /sys/kernel/slab/kmalloc-256/alloc_calls
cat /sys/kernel/slab/kmalloc-256/free_calls

/sys/kernel/slab/ Arayüzü

DosyaAçıklama
object_sizeGerçek nesne boyutu (bayt)
slab_sizeSlab sayfası boyutu (padding dahil)
objs_per_slabBir slabdaki nesne sayısı
orderSlab için kullanılan buddy order
slabsToplam aktif slab sayfası sayısı
objectsToplam nesne kapasitesi
objects_partialPartial slablardaki nesne sayısı
alloc_fastpathPer-CPU freelist'ten yapılan tahsis sayısı
alloc_slowpathYavaş yoldan (kilitli) yapılan tahsis sayısı
free_fastpathHızlı yoldan serbest bırakma sayısı

Use-After-Free Tespiti

CONFIG_SLUB_DEBUG + SLAB_POISON kombinasyonu serbest bırakılan nesneyi 0x6b deseniyle doldurur. Nesne serbest bırakıldıktan sonra bu alana yazma veya okuma yapılırsa, sonraki sanity check'te bozulma tespit edilir:

/* Hatalı kod örneği: use-after-free */
struct my_obj *obj = kmem_cache_alloc(my_cache, GFP_KERNEL);
kmem_cache_free(my_cache, obj);
obj->field = 42;  /* YANLIS: serbest bırakıldı! */

/* SLUB_DEBUG + SLAB_POISON açıksa, sonraki sanity check'te:
 * BUG kmalloc-128 (Not tainted): Poison overwritten
 * INFO: 0xffff... has been poisoned: 0x6b vs 0x2a */

Validate Komutu ile Elle Doğrulama

# Belirli bir önbelleği elle doğrula (CONFIG_SLUB_DEBUG gerektirir):
echo 1 > /sys/kernel/slab/my_cache/validate

# Kernel log'unda bozulma bulunursa:
# BUG: KASAN: slab-out-of-bounds in ...
# Write of size N at addr ... by task .../PID

# Tüm önbellekleri sistematik olarak tara:
for cache in /sys/kernel/slab/*/; do
    echo 1 > "$cache/validate" 2>/dev/null
done
dmesg | grep "BUG\|KASAN" | tail -20

06 Bellek baskısı ve shrinker API

Gömülü sistemlerde RAM kıtlığı altında çalışırken, slab katmanının elindeki fazla belleği geri vermesi kritik önem taşır. Shrinker API bu mekanizmanın temel taşıdır.

Shrinker API Genel Bakış

Linux kernel'ında bellek baskısı oluştuğunda (düşük su işareti WMARK_LOW altına düşüldüğünde) kswapd, tescil edilmiş tüm shrinker'ları çağırır. Her slab önbelleği kendi shrinker'ını sunabilir ya da alt sistem kendi özel mantığını uygular.

#include <linux/shrinker.h>

/* Shrinker yapısı */
struct shrinker {
    unsigned long (*count_objects)(struct shrinker *,
                                   struct shrink_control *sc);
    unsigned long (*scan_objects)(struct shrinker *,
                                  struct shrink_control *sc);
    long batch;     /* Tek seferinde serbest bırakılacak nesne sayısı */
    int seeks;      /* Nesneyi yeniden oluşturma maliyeti */
    unsigned flags;
    struct list_head list;
};

Özel Shrinker Kaydı

static unsigned long my_count_objects(struct shrinker *shrinker,
                                      struct shrink_control *sc)
{
    return atomic_read(&my_cache_free_count);
}

static unsigned long my_scan_objects(struct shrinker *shrinker,
                                     struct shrink_control *sc)
{
    unsigned long freed = 0;
    while (freed < sc->nr_to_scan && !list_empty(&my_idle_list)) {
        struct my_obj *obj = list_first_entry(&my_idle_list,
                                              struct my_obj, list);
        list_del(&obj->list);
        kmem_cache_free(my_cache, obj);
        freed++;
        atomic_dec(&my_cache_free_count);
    }
    return freed;
}

static struct shrinker my_shrinker = {
    .count_objects = my_count_objects,
    .scan_objects  = my_scan_objects,
    .seeks         = DEFAULT_SEEKS,
};

/* Modül init'inde kaydet: */
register_shrinker(&my_shrinker, "my-driver-shrinker");

/* Modül exit'inde sil: */
unregister_shrinker(&my_shrinker);

kmem_cache_shrink

kmem_cache_shrink, belirli bir önbellekteki tüm boş slab sayfalarını hemen buddy allocator'a geri verir. Partial slab'lara dokunmaz. Bellek baskısı olmasa bile çağrılabilir:

/* Acil bellek geri alma — tüm bos slabları serbest bırak */
int ret = kmem_cache_shrink(my_cache);
/* ret: 0 = tamamen bos, pozitif = hala partial/full slab var */

# Kullanıcı alanından tüm slab önbelleklerini küçültme:
echo 2 > /proc/sys/vm/drop_caches   /* sadece slab */
echo 3 > /proc/sys/vm/drop_caches   /* sayfa önbelleği + slab */

Gömülü Düşük Bellek Senaryosu

RAM kritik seviyenin altina dusuyor (WMARK_LOW)
        |
        v
kswapd uyaniyor
        |
        v
do_shrink_slab() -- kayitli tum shrinker'lari cagir
        |
        +--> driver shrinker: bos nesne havuzunu bosalt
        +--> inode/dentry shrinker: kullanilmayan VFS nesneleri
        +--> netfs shrinker: ag tampon onbellekleri
        |
        v
kmem_cache_shrink() -- bos slab sayfalarini buddy'e iade et
        |
        v
Buddy allocator bos sayfalari geri aliyor -- RAM basinc azaliyor

memcg ile Slab Muhasebesi

cgroup v2 kullanılan sistemlerde slab tahsisleri bellek cgroup'larına atfedilebilir. SLAB_ACCOUNT bayrağı ve CONFIG_MEMCG_KMEM bu özelliği etkinleştirir. Gömülü konteyner tabanlı mimarilerde kaynak izolasyonu sağlar:

CONFIG_MEMCG=y
CONFIG_MEMCG_KMEM=y

/* Önbellek oluştururken: */
cache = kmem_cache_create("net_skb", sizeof(struct sk_buff), 0,
                          SLAB_HWCACHE_ALIGN | SLAB_ACCOUNT, NULL);

# cgroup bellek kullanımını izle:
cat /sys/fs/cgroup/my_container/memory.kmem.usage_in_bytes

07 KASAN ile SLUB entegrasyonu

Kernel Address Sanitizer (KASAN), SLUB ile derin entegrasyon kurarak heap bellek hatalarını donanım desteği olmadan yazılım düzeyinde tespit eder. Gömülü geliştirme ortamlarında vazgeçilmez bir araçtır.

KASAN Çalışma Prensibi

KASAN, her 8 baytlık "uygulama belleği" için 1 bayt "gölge bellek" kullanır. Gölge bellek, ilgili adresin erişilebilir olup olmadığını kodlar. Derleyici, her bellek erişiminden önce otomatik olarak gölge bellek kontrolü ekler.

Gölge bellek bayt anlamları:
  0x00 = 8 baytın tamamı erişilebilir
  0x01-0x07 = yalnızca ilk N bayt erişilebilir
  0xFC = slab redzone (sınır dışı erişim)
  0xFB = slab freelist poison (use-after-free)
  0xF1 = stack sol redzone
  0xF3 = stack sag redzone

Konfigürasyon Seçenekleri

CONFIG_KASAN=y KASAN'ı etkinleştirir. CONFIG_SLUB_DEBUG ile birlikte kullanılması önerilir.
CONFIG_KASAN_GENERIC Yazılım tabanlı, compiler-instrumented mod. Her bellek erişiminde ekstra kontrol kodu çalışır. Yavaş ama taşınabilir.
CONFIG_KASAN_OUTLINE Kontrol kodu ayrı bir fonksiyon çağrısı olarak derlenir. Daha küçük text boyutu, ancak daha yavaş. Gömülü sistemlerde flash alanından tasarruf sağlar.
CONFIG_KASAN_INLINE Kontrol kodu doğrudan inline eklenir. Daha hızlı ancak kernel text boyutu önemli ölçüde artar.
CONFIG_KASAN_SW_TAGS Pointer tag tabanlı KASAN (ARMv8.5 MTE'nin yazılım öykünmesi). Daha düşük bellek overhead'i; gömülü ARM64 için iyi seçenek.

Gömülü Sistemde KASAN Konfigürasyonu

# Gömülü ARM64 için önerilen KASAN konfigürasyonu:
CONFIG_KASAN=y
CONFIG_KASAN_GENERIC=y
CONFIG_KASAN_OUTLINE=y      # Flash boyutu için INLINE yerine
# CONFIG_KASAN_INLINE is not set
CONFIG_SLUB_DEBUG=y
CONFIG_STACKTRACE=y          # Hata raporunda stack trace için

# Overhead tahmini:
# - Bellek: yaklasik 1/8 ekstra RAM (shadow memory)
# - Hız: genellikle yuzde 20-40 yavaslamaayıklama
# - Flash: OUTLINE modda minimum artis

KASAN Hata Raporu Örneği

==================================================================
BUG: KASAN: slab-out-of-bounds in sensor_write+0x4c/0x80 [sensor_drv]
Write of size 4 at addr ffff000012345678 by task kworker/0:1/42

CPU: 0 PID: 42 Comm: kworker/0:1 Not tainted 6.6.0 #1
Hardware name: My Embedded Board (DT)
Call trace:
 dump_backtrace+0x0/0x1c0
 show_stack+0x18/0x24
 dump_stack_lvl+0x68/0x84
 kasan_report+0xac/0x1c0
 sensor_write+0x4c/0x80 [sensor_drv]
 ...

Allocated by task 42:
 kmem_cache_alloc+0x88/0x200
 sensor_alloc+0x30/0x60 [sensor_drv]

The buggy address ffff000012345678 is located 8 bytes to the right
of 32-byte region [ffff000012345658, ffff000012345678)
==================================================================

Shadow Byte Haritası ile SLUB Entegrasyonu

SLUB nesne tahsis ettiğinde, ilgili gölge bellek baytları 0x00 (erişilebilir) olarak işaretlenir. Nesne serbest bırakıldığında ise 0xFB (slab freelist zehiri) olarak güncellenir. Red zone bölgeleri 0xFC ile işaretlidir. Derleyici tarafından eklenen her yük ve saklama talimatı öncesi bu değer kontrol edilir; uyumsuzluk KASAN raporunu tetikler.

08 Performans ölçümü ve optimizasyon

SLUB'ın performansını analiz etmek; allocator darboğazlarını, fragmentasyon sorunlarını ve önbellek etkinliğini anlamayı gerektirir. Bu bölüm, ölçüm araçlarını ve gömülü sistemlere özel optimizasyon önerilerini kapsar.

perf kmem ile Tahsis Profili

# Kernel slab tahsislerini kayıt altına al (5 saniye):
perf record -e kmem:kmalloc,kmem:kmem_cache_alloc \
     -a --sleep 5

# Raporu göster — hangi fonksiyon en çok tahsis yapıyor:
perf report --stdio | head -30

# Boyut dağılımı analizi:
perf kmem --slab stat --sort=bytes | head -20

# Örnek çıktı:
# Callsite        Total_alloc/Per | Total_req/Per | Hit | Frag
# sensor_alloc    16384/128       | 12800/100     | 128 | 21.88%

slabstats Betiği

#!/bin/sh
# /proc/slabinfo'dan anlamlı istatistik çıkaran basit betik

echo "=== En Büyük Slab Önbellekleri ==="
awk 'NR > 2 {
    name = $1; active = $2; total = $3; obj_size = $4
    mem_kb = (total * obj_size) / 1024
    printf "%-30s %8d KB (%d nesne, %d bayt)\n",
           name, mem_kb, total, obj_size
}' /proc/slabinfo | sort -k2 -rn | head -15

echo ""
echo "=== Fragmantasyon Analizi ==="
awk 'NR > 2 && $3 > 0 {
    frag = 100 - ($2 * 100 / $3)
    if (frag > 20)
        printf "YUKSEK FRAG: %-25s %5.1f%%\n", $1, frag
}' /proc/slabinfo

Fragmantasyon Analizi

Slab fragmantasyonu, toplam nesnelerin yalnızca bir kısmının kullanımda olması durumudur. Yüksek fragmantasyon, belleğin atıl kalması anlamına gelir:

# Aktif nesne oranı (kullanım yoğunluğu):
# active_objs / num_objs > 0.8 idealdir
awk 'NR > 2 {
    printf "%-30s kullanim=%.0f%%\n", $1, ($2/$3)*100
}' /proc/slabinfo | sort -t= -k2 -n | head -10

# Ayrıntılı fragmantasyon raporu:
for cache in /sys/kernel/slab/kmalloc-*; do
    name=$(basename $cache)
    slabs=$(cat $cache/slabs 2>/dev/null)
    objs=$(cat $cache/objects 2>/dev/null)
    partial=$(cat $cache/objects_partial 2>/dev/null)
    echo "$name: slabs=$slabs objects=$objs partial=$partial"
done

Gömülü Optimizasyon Önerileri

1. Özel kmem_cache kullanın Sık tahsis edilen aynı boyuttaki nesneler için kmalloc yerine kmem_cache_create tercih edin. Boyut yuvarlamasını ortadan kaldırır, fragmantasyonu azaltır.
2. SLAB_HWCACHE_ALIGN uygulayın Cache-line false sharing'i önler. Çok çekirdekli gömülü SoC'larda (Cortex-A55 quad-core gibi) somut performans kazancı sağlar.
3. Nesne havuzu tasarımı yapın Gerçek zamanlı görevlerde tahsis gecikmesini ortadan kaldırmak için başlangıçta nesne havuzu doldurun, çalışma zamanında yalnızca havuzdan alın ve iade edin.
4. slub_max_order=0 deneyin Slab sayfalarını tek sayfa (4 KB) ile sınırlamak buddy allocator fragmantasyonunu azaltır. Küçük nesnelerde overhead artabilir; profil alarak karar verin.
5. CONFIG_SLUB_CPU_PARTIAL değerini değerlendirin Tek çekirdekli veya düşük RAM'li sistemlerde devre dışı bırakmak bellek tasarrufu sağlar. Çok çekirdekli sistemlerde ise throughput'u artırır.

Benchmark: kmalloc ve kmem_cache_alloc Karşılaştırması

/* Basit kıyaslama kernel modülü (yalnızca geliştirme ortamı için) */
#include <linux/time.h>

#define ITER 100000

static void bench_kmalloc(void)
{
    int i;
    ktime_t t0 = ktime_get();
    for (i = 0; i < ITER; i++) {
        void *p = kmalloc(128, GFP_KERNEL);
        kfree(p);
    }
    ktime_t t1 = ktime_get();
    pr_info("kmalloc-128: %lld ns / tahsis\n",
            ktime_to_ns(ktime_sub(t1, t0)) / ITER);
}

static void bench_cache_alloc(struct kmem_cache *cache)
{
    int i;
    ktime_t t0 = ktime_get();
    for (i = 0; i < ITER; i++) {
        void *p = kmem_cache_alloc(cache, GFP_KERNEL);
        kmem_cache_free(cache, p);
    }
    ktime_t t1 = ktime_get();
    pr_info("kmem_cache_alloc: %lld ns / tahsis\n",
            ktime_to_ns(ktime_sub(t1, t0)) / ITER);
}
/* Tipik sonuç: kmem_cache_alloc genellikle yuzde 5-15 daha hızlıdır */

Sonuç: SLUB'ı Doğru Kullanmak

SLUB, Linux kernel'ının bellek tahsis katmanının sağlam ve verimli temelidir. Gömülü sistemlerde maksimum fayda için: sık tahsis edilen nesnelere özel önbellek açın, DEBUG modunu yalnızca geliştirme imajlarında kullanın, slub_max_order ve cpu_partial parametrelerini platform RAM'ine göre ayarlayın ve düzenli olarak /proc/slabinfo ile fragmentasyon durumunu izleyin.