If you have been following this free Linux kernel development course, you already know that the buddy allocator hands out memory in whole pages — 4KB, 8KB, 16KB and so on. But most kernel objects, like an inode or a task_struct, are only a few hundred bytes. Handing out a full 4KB page for a 200-byte object would waste over 90% of that page. This is exactly the problem the Linux slab allocator was built to solve, and in this lecture of our free embedded Linux course we will understand it from the ground up, then write a real kernel module using it.
kmem_cache
SLUB allocator
free linux kernel development course
free linux device drivers course
free embedded systems course
What You Will Learn
- Why the buddy allocator alone is not enough
- What a slab, a cache, and an object are
- Empty, partial, and full slab states
- The kmem_cache API: create, alloc, free, destroy
- Why SLAB was removed and SLUB is now the only allocator
- Writing an original kmem_cache driver, ep_slab_cache_demo
- Reading live cache stats with slabtop and /proc/slabinfo
Prerequisites
Before this lecture, you should be comfortable with:
- Basic kernel module build/insmod/rmmod workflow
- kmalloc() and vmalloc() basics (previous lecture)
- The page allocator and buddy algorithm (previous lecture)
- A Linux VM or board running kernel 6.x with root/sudo access
Why the Linux Slab Allocator Exists
The page allocator (buddy system) only gives out memory in powers of two, starting at one page. The kernel, however, constantly creates and destroys small, fixed-size structures — file objects, network buffers, process descriptors — thousands of times per second. Asking the buddy allocator for a fresh page every single time would be slow and wasteful. The linux slab allocator sits on top of the page allocator and specializes in exactly this job: carving pages into small, same-sized, reusable chunks.
Core Linux Slab Allocator Terms
Before touching any code, let’s define the three words you will see everywhere in slab allocator discussions:
| Term | Meaning |
|---|---|
| Slab | A small, contiguous chunk of physical memory (one or more page frames) divided into equal-sized object slots. |
| Cache | A collection of slabs that all store the same type of object, represented by struct kmem_cache. |
| Object | One usable slot inside a slab, sized to fit exactly one instance of the structure the cache was made for. |
Slab States in the Linux Slab Allocator
Every slab inside a cache is always in one of three states, and the SLUB allocator tracks this automatically:
Empty
All objects free
Partial
Some used, some free
Full
All objects used
When code asks for an object, the slab allocator first looks in a partial slab, then an empty slab, and only asks the page/buddy allocator for a brand-new slab if none is available. When an object is freed, it simply goes back to its slab in an initialized state — no page allocator call is needed at all in the common case. This reuse is exactly why the linux slab allocator is so much faster than calling the page allocator directly for every small object.
SLAB vs SLUB: A Modernization Note
Older books and tutorials describe the original “SLAB” allocator, which kept three separate lists per cache (per-CPU, shared, per-NUMA-node) to reduce lock contention. This added a lot of bookkeeping overhead. A simpler design called SLUB (the “unqueued slab allocator”) replaced it as the kernel default back in 2.6.23, and current mainline kernels have gone further:
| Allocator | Status in modern kernel |
|---|---|
| SLOB | Removed in Linux 6.4 (was for tiny/no-MMU systems) |
| SLAB | Deprecated in 6.5, fully removed in Linux 6.8 |
| SLUB | The only slab allocator left; it is what kmalloc() and kmem_cache_* use today |
So on any kernel 6.8 or newer, when you hear “slab allocator” it always means SLUB under the hood, even though the API is still called kmem_cache_* for historical reasons.
The kmem_cache API
Drivers rarely build raw slabs by hand. Instead, you create a cache for your own structure type and let the slab allocator manage objects for you.
| Function | Purpose |
|---|---|
kmem_cache_create() |
Creates a new cache for a fixed-size object type |
kmem_cache_alloc() |
Allocates one object from the cache |
kmem_cache_free() |
Returns an object to the cache |
kmem_cache_destroy() |
Destroys the cache and frees all its slabs |
Hands-On: ep_slab_cache_demo Driver
Let’s write an original kernel module that creates its own cache for a small structure, allocates a few objects from it, and frees them — a minimal but complete tour of the linux slab allocator API.
// ep_slab_cache_demo.c
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
struct ep_sensor_reading {
int channel;
int value;
char label[16];
};
static struct kmem_cache *ep_reading_cache;
static int __init ep_slab_cache_demo_init(void)
{
struct ep_sensor_reading *r1, *r2;
ep_reading_cache = kmem_cache_create("ep_sensor_reading",
sizeof(struct ep_sensor_reading),
0,
SLAB_HWCACHE_ALIGN,
NULL);
if (!ep_reading_cache) {
pr_err("ep_slab_cache_demo: cache creation failed\n");
return -ENOMEM;
}
r1 = kmem_cache_alloc(ep_reading_cache, GFP_KERNEL);
r2 = kmem_cache_alloc(ep_reading_cache, GFP_KERNEL);
if (!r1 || !r2) {
pr_err("ep_slab_cache_demo: object alloc failed\n");
return -ENOMEM;
}
r1->channel = 0;
r1->value = 512;
strscpy(r1->label, "temp", sizeof(r1->label));
r2->channel = 1;
r2->value = 998;
strscpy(r2->label, "humidity", sizeof(r2->label));
pr_info("ep_slab_cache_demo: r1 ch=%d val=%d label=%s\n",
r1->channel, r1->value, r1->label);
pr_info("ep_slab_cache_demo: r2 ch=%d val=%d label=%s\n",
r2->channel, r2->value, r2->label);
kmem_cache_free(ep_reading_cache, r1);
kmem_cache_free(ep_reading_cache, r2);
pr_info("ep_slab_cache_demo: objects freed back to cache\n");
return 0;
}
static void __exit ep_slab_cache_demo_exit(void)
{
kmem_cache_destroy(ep_reading_cache);
pr_info("ep_slab_cache_demo: cache destroyed\n");
}
module_init(ep_slab_cache_demo_init);
module_exit(ep_slab_cache_demo_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("EmbeddedPathashala");
MODULE_DESCRIPTION("Linux slab allocator kmem_cache demo");
Build and Run
# Makefile
obj-m += ep_slab_cache_demo.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
$ make
$ sudo insmod ep_slab_cache_demo.ko
$ dmesg | tail -5
$ sudo rmmod ep_slab_cache_demo
Expected dmesg Output
[ 1234.001] ep_slab_cache_demo: r1 ch=0 val=512 label=temp
[ 1234.001] ep_slab_cache_demo: r2 ch=1 val=998 label=humidity
[ 1234.001] ep_slab_cache_demo: objects freed back to cache
[ 1240.045] ep_slab_cache_demo: cache destroyed
Inspecting Live Caches: slabtop and /proc/slabinfo
While your module is loaded, you can see your cache alongside every other kernel cache on the system:
$ sudo cat /proc/slabinfo | grep ep_sensor_reading
ep_sensor_reading 2 X 48 X X : tunables ...
$ sudo slabtop -o | head -15
The columns show active objects, total objects, and object size for your cache — a quick way to confirm your kmem_cache_create() call actually registered a cache and to watch it grow or shrink as your driver allocates and frees objects.
Real-World Use Cases
Where the kernel itself uses slab caches
- task_struct cache for every process/thread
- inode and dentry caches in every filesystem
- sk_buff (network packet) caches in the network stack
- Driver-private object pools (USB requests, block I/O requests)
Common Mistakes
| Mistake | Why it’s a problem |
|---|---|
| Freeing with kfree() instead of kmem_cache_free() | Corrupts the cache’s internal bookkeeping |
| Not checking kmem_cache_create() for NULL | Leads to a crash on the first alloc call |
| Destroying a cache while objects are still allocated | Triggers a kernel warning and can leak or corrupt memory |
| Using GFP_KERNEL inside interrupt context | Can sleep where sleeping is not allowed — use GFP_ATOMIC instead |
Best Practices
- Create one cache per object type, never mix types in one cache
- Always pair every kmem_cache_alloc() with a matching kmem_cache_free()
- Destroy the cache in your module’s exit function
- Use SLAB_HWCACHE_ALIGN for objects accessed on hot paths
Performance Considerations
Because the slab allocator keeps freed objects ready to reuse, repeated alloc/free cycles for the same structure are far cheaper than repeatedly calling the page allocator. SLUB’s per-CPU “current slab” design also avoids most locking on the fast path, which matters a lot on multi-core embedded boards handling high-frequency interrupts or packets.
Security Considerations
Slab reuse means a freed object’s old contents can still be present in memory. For security-sensitive structures, use kzalloc()/kmem_cache_zalloc() or the SLAB_POISON/hardened usercopy debug options to avoid leaking stale kernel data through recycled objects.
Summary / Key Takeaways
- The linux slab allocator sits above the page allocator and specializes in small, fixed-size objects
- A cache holds one or more slabs; slabs are empty, partial, or full
- SLAB was removed in kernel 6.8; SLUB is now the only slab allocator
- kmem_cache_create/alloc/free/destroy is the standard driver-facing API
- slabtop and /proc/slabinfo let you inspect caches live
Conclusion
The linux slab allocator is what makes frequent small-object allocation fast and predictable inside the kernel. By building on top of the buddy/page allocator and reusing freed objects instead of returning pages every time, it avoids both the fragmentation and the speed penalty that plain page allocation would cause. With SLAB now gone from the kernel and SLUB as the single implementation, understanding the kmem_cache API is the most practical way to work with this layer as a driver author. In the next lecture of this free linux device drivers course, we will go one level deeper into kmalloc() size classes and how SLUB decides which cache backs a given kmalloc() request.
FAQ
What is the linux slab allocator used for?
It manages fast, reusable allocation of small, fixed-size kernel objects like inodes, task structures, and driver-private structures, sitting on top of the page allocator.
Is SLAB still available in the Linux kernel?
No. SLAB was deprecated in kernel 6.5 and fully removed in kernel 6.8. SLUB is now the only general-purpose slab allocator in mainline Linux.
What is the difference between kmalloc() and kmem_cache_alloc()?
kmalloc() uses generic, pre-existing size-class caches for arbitrary allocations, while kmem_cache_alloc() draws from a cache you created yourself for one specific structure type.
What are the three slab states?
Empty (all objects free), partial (some used, some free), and full (all objects used). The allocator prefers partial slabs first to keep memory tightly packed.
Why not just use the page allocator for everything?
The page allocator only gives out whole pages, so small objects would waste most of each page. The slab allocator subdivides pages into right-sized chunks instead.
How do I see slab cache usage on a running system?
Use slabtop for a live view or read /proc/slabinfo directly to see object counts and sizes per cache.
Do I need to zero memory from kmem_cache_alloc()?
Not automatically. Use kmem_cache_zalloc() or pass __GFP_ZERO if you need zeroed memory, since reused objects can contain old data.
Can kmem_cache_alloc() be called from interrupt context?
Yes, but use GFP_ATOMIC instead of GFP_KERNEL, since GFP_KERNEL allocations are allowed to sleep and interrupt context cannot sleep.
Continue the Free Linux Kernel Development Course
Next up: kmalloc() size classes and SLUB internals.
