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.
| Özellik | SLAB | SLUB | SLOB |
|---|---|---|---|
| Varsayılan mı? | Hayır (eski varsayılan) | Evet (Linux 3.x+) | Hayır (CONFIG_EMBEDDED) |
| Metadata alanı | Yüksek | Düşük (sayfa içi) | Çok düşük |
| NUMA desteği | Sınırlı | Tam | Yok |
| Per-CPU freelist | Var (karmaşık) | Var (sade) | Yok |
| Hata ayıklama | Orta | Güçlü | Minimal |
| Hedef platform | Genel | Genel + Sunucu | Küçü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:
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
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ı
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
| Parametre | Varsayılan | Açıklama |
|---|---|---|
| slub_min_order=N | 0 | Slab 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=N | 1 (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=N | 0 (otomatik) | Bir slabda en az kaç nesne olmalı. SLUB, bu değeri sağlamak için gerekirse order'ı artırır. |
| slab_max_order=N | Değişken | kmalloc 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ü
| Dosya | Açıklama |
|---|---|
| object_size | Gerçek nesne boyutu (bayt) |
| slab_size | Slab sayfası boyutu (padding dahil) |
| objs_per_slab | Bir slabdaki nesne sayısı |
| order | Slab için kullanılan buddy order |
| slabs | Toplam aktif slab sayfası sayısı |
| objects | Toplam nesne kapasitesi |
| objects_partial | Partial slablardaki nesne sayısı |
| alloc_fastpath | Per-CPU freelist'ten yapılan tahsis sayısı |
| alloc_slowpath | Yavaş yoldan (kilitli) yapılan tahsis sayısı |
| free_fastpath | Hı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
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
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.