How Does ksize() Measure Allocated Memory? – – Free Linux Device Drivers Course in Hyderabad


Linux Kernel Memory Allocation Tutorial: kmalloc(), ksize() and the Slab Allocator Explained
A beginner-friendly, up-to-date lecture from our free Linux kernel programming course

If you are searching for a clear, practical Linux kernel memory allocation tutorial, this lecture is written for you. Kernel-space memory management works very differently from the malloc() you use in ordinary C programs, and understanding it properly is one of the first real milestones in any serious study of the Linux kernel. In this lesson, part of our free Linux kernel programming course, we break down exactly how the kernel hands out small chunks of memory to drivers and subsystems, why some of that memory is wasted, and how you can measure it yourself.

This tutorial is written and verified against current, actively maintained kernels. Older books and tutorials often describe the SLAB allocator as the kernel default — that has not been true for a long time, and the old SLAB implementation was removed from the kernel entirely in version 6.8. We will explain what actually runs on your machine today.

What You Will Learn

  • Why the kernel cannot simply use one giant memory allocator for everything
  • The difference between the page allocator and the slab allocator
  • How kmalloc() actually picks the memory it gives you, using SLUB internals
  • How to use ksize() to discover how much memory you truly received
  • How to calculate and interpret memory wastage in a Linux kernel memory allocation tutorial style walkthrough
  • A safe, modern kernel module example you can build and run yourself
  • Common mistakes, best practices, and security considerations around kernel allocation

Prerequisites

This lecture assumes you are comfortable with basic C programming and have followed along with earlier lessons in this free Linux kernel programming course covering kernel modules and printk(). If you are new here, we’d recommend starting from the beginning of the free Linux device drivers course, since memory allocation concepts build directly on module basics.

  • A Linux machine or VM running a kernel version 6.4 or newer (Ubuntu 24.04 LTS, Debian 12, or Fedora are all fine)
  • Kernel headers installed for your running kernel
  • Basic familiarity with writing and inserting a kernel module (insmod/rmmod)
  • Comfort reading dmesg output

Why Kernel Memory Allocation Works Differently

In user-space, malloc() hides almost everything from you. In the kernel, there is no libc, no heap the way you know it, and allocation failures must be checked and handled every single time — a driver cannot just crash. On top of that, kernel memory itself is a scarce, non-swappable resource in most cases, so efficiency matters far more than in an ordinary application.

To manage this, the kernel actually layers two allocators on top of each other. Understanding this layering is the real key to any solid Linux kernel memory allocation tutorial, so let’s look at it visually before touching any code.

How a kmalloc() Request Flows Through the Kernel
Your Driver
calls kmalloc(size)
→
Slab Allocator
SLUB picks nearest cache
→
Page Allocator
Buddy system, whole pages
→
Physical RAM

When a driver asks for memory smaller than a page, the request goes to the slab allocator first. The slab allocator itself gets its raw material — whole pages — from the page allocator underneath it, then slices those pages into small, reusable objects. This is the layering the kernel uses to solve two very different problems at once.

Page Allocator vs Slab Allocator

Aspect Page Allocator (Buddy System) Slab Allocator (SLUB)
Allocation unit Whole pages, in powers of two Small objects, carved out of pages
Best for Large, page-aligned buffers Objects smaller than one page
Typical API alloc_pages(), __get_free_pages() kmalloc(), kzalloc(), kfree()
Internal fragmentation Can be severe for small requests Minimised by fixed-size object caches

Inside the Modern Slab Allocator: SLUB

Older Linux kernel memory allocation tutorials often mention three competing slab implementations: SLAB, SLUB, and SLOB. On a current kernel, you only need to care about one of them. SLOB, the tiny allocator meant for memory-constrained embedded boards with no MMU, was removed in kernel 6.4. SLAB, the original and more complex implementation, was removed in kernel 6.8. SLUB has been the sole slab allocator in mainline Linux since then, and it had already been the default for most distributions for well over a decade before that.

SLUB keeps things fast by giving each CPU its own “current” slab to allocate from, avoiding locking on the common fast path.

SLUB Per-CPU Fast Path (Simplified)
CPU 0
Active Slab
free objects → handed out directly
CPU 1
Active Slab
its own freelist, no shared lock

Each CPU only touches its own slab on the common path, which is why kernel memory allocation stays fast even under heavy driver activity.

The kmalloc-N Size Classes

kmalloc() does not allocate an exact number of bytes. It rounds your request up to the nearest available cache size. This single fact is the root cause of almost all the “wastage” you will see later in this tutorial.

Cache Name Object Size Used When You Call
kmalloc-8 8 bytes kmalloc(1..8, …)
kmalloc-16 16 bytes kmalloc(9..16, …)
kmalloc-32 32 bytes kmalloc(17..32, …)
kmalloc-64 64 bytes kmalloc(33..64, …)
kmalloc-128 128 bytes kmalloc(97..128, …)
kmalloc-256 … kmalloc-8k up to 8192 bytes Larger power-of-two-ish requests

Note: On a current kernel you will also see kmalloc-cg-* caches, which are memcg-accounted variants used when a driver allocates with __GFP_ACCOUNT, and — if CONFIG_RANDOM_KMALLOC_CACHES is enabled — several randomised duplicate caches per size, used as a hardening measure against heap-shaping attacks. You can list every active cache and its live object counts on your own machine at any time with sudo cat /proc/slabinfo.

Kernel Memory Allocation APIs You Should Know

Function Purpose
kmalloc(size, flags) General-purpose allocation, contents uninitialised
kzalloc(size, flags) Same as kmalloc(), memory zeroed out
kcalloc(n, size, flags) Zeroed array allocation with overflow checking
krealloc(ptr, size, flags) Resize an existing allocation
ksize(ptr) Reports the actual usable size of an allocation
kfree(ptr) Releases memory back to its slab cache

Measuring Real Wastage: A Safe Kernel Module Example

Rather than reproducing decades-old sample code, let’s write a small, safe kernel module from scratch for this Linux kernel memory allocation tutorial. Instead of looping forever until an allocation fails (which can trigger kernel warnings), we will test a fixed, realistic set of sizes that a real driver might request — buffer sizes for things like a small config struct, a network packet buffer, and a DMA-safe block.

// alloc_efficiency.c
// A minimal kernel module that reports how much memory
// kmalloc() actually hands back for a range of realistic sizes.

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

static const size_t requested_sizes[] = {
    24, 50, 100, 250, 600, 1000, 3000, 6000
};

static void report_allocation(size_t want)
{
    void *block;
    size_t got, waste;

    block = kmalloc(want, GFP_KERNEL);
    if (!block) {
        pr_alert("alloc_efficiency: allocation of %zu bytes failed\n", want);
        return;
    }

    got = ksize(block);
    waste = got - want;

    pr_info("requested=%6zu bytes | actual=%6zu bytes | wasted=%5zu bytes (%3zu%%)\n",
            want, got, waste, (waste * 100) / want);

    kfree(block);
}

static int __init alloc_efficiency_init(void)
{
    int i;

    pr_info("alloc_efficiency: module loaded, testing %zu sizes\n",
            ARRAY_SIZE(requested_sizes));

    for (i = 0; i < ARRAY_SIZE(requested_sizes); i++)
        report_allocation(requested_sizes[i]);

    return 0;
}

static void __exit alloc_efficiency_exit(void)
{
    pr_info("alloc_efficiency: module unloaded\n");
}

module_init(alloc_efficiency_init);
module_exit(alloc_efficiency_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("Reports kmalloc/ksize wastage for a fixed set of sizes");

Build and load it the usual way:

$ make
$ sudo insmod alloc_efficiency.ko
$ dmesg | tail -n 12
$ sudo rmmod alloc_efficiency

On a typical x86_64 machine you will see a pattern like this: requesting 24 bytes lands you in kmalloc-32 (8 bytes wasted, 33%), requesting 100 bytes lands in kmalloc-128 (28 bytes wasted, 28%), and requesting exactly 1000 bytes lands in kmalloc-1k (only 24 bytes wasted, about 2%). The closer your request sits to the top of a size class, the lower your wastage percentage — and the closer it sits just above a size class boundary, the higher it climbs.

The Wastage Formula

The relationship shown by ksize() is always this simple calculation:

actual_allocated = ksize(pointer);
wasted_bytes      = actual_allocated - requested_bytes;
wasted_percent    = (wasted_bytes * 100) / requested_bytes;
Wastage Shrinks as You Approach a Cache Boundary

Requested 24B in kmalloc-32

33% wasted

Requested 100B in kmalloc-128

28% wasted

Requested 1000B in kmalloc-1k

2%

Real-World Use Cases

  • Network drivers sizing socket buffer headers close to a kmalloc boundary to minimise per-packet waste at high throughput.
  • Character and block drivers allocating small per-open context structures with kzalloc() so fields start predictably at zero.
  • Filesystem code using dedicated kmem_cache_create() caches instead of generic kmalloc() when the same object type is allocated and freed extremely often, to avoid repeated size-class rounding entirely.
  • Security-sensitive subsystems relying on randomised kmalloc caches to make heap-shaping attacks harder for exploit authors.

Common Mistakes and Troubleshooting

  • Calling ksize() on page-allocator memory. ksize() only works on memory returned by the slab allocator (kmalloc()/kzalloc()); it is not valid on memory from alloc_pages() or vmalloc().
  • Assuming kmalloc() zeroes memory. It does not — use kzalloc() or kcalloc() whenever uninitialised memory would be a bug or a security risk.
  • Ignoring the return value. Every kmalloc() family call can return NULL under memory pressure; always check before use.
  • Over-relying on ksize() for bonus space. Technically you may write up to the ksize() value, but doing so ties your code to allocator internals and can break if cache sizes change — treat it as a diagnostic tool, not a design feature.
  • Forgetting kfree(). Kernel memory leaks are far more damaging than user-space leaks, since there is no process exit to clean them up automatically.

Best Practices

  • Match your struct sizes to size-class boundaries where it is cheap to do so (padding a struct by a few bytes can sometimes reduce wastage significantly).
  • Use kzalloc() by default unless you have a measured, specific reason to skip zeroing.
  • For high-frequency, fixed-size objects, create a dedicated kmem_cache with kmem_cache_create() instead of generic kmalloc().
  • Use /proc/slabinfo and slabtop during development to spot unexpectedly large or growing caches.

Performance Considerations

The per-CPU fast path in SLUB means allocation and free are usually just a few lockless instructions. Performance problems tend to appear when a driver crosses NUMA nodes frequently, or when slab merging combines your cache with an unrelated one under memory pressure, changing cache-line locality. Profiling with perf alongside /proc/slabinfo is the most reliable way to catch these issues early.

Security Considerations

Modern kernels harden the slab allocator against heap exploitation in several ways: CONFIG_SLAB_FREELIST_HARDENED protects in-object freelist pointers and detects double-frees, CONFIG_SLAB_FREELIST_RANDOM randomises object order within a new slab, and CONFIG_RANDOM_KMALLOC_CACHES creates multiple randomised copies of each kmalloc size class so an attacker cannot reliably predict which cache a given allocation will land in. As a driver author, you benefit from these automatically — but you should still avoid manual pointer arithmetic on kmalloc() results, since that is exactly the kind of bug these protections are designed to catch.

Summary / Key Takeaways

  • kmalloc() memory comes from the slab allocator, which itself sits on top of the page allocator.
  • SLUB is the only slab allocator in current mainline Linux; SLOB was removed in 6.4, SLAB in 6.8.
  • Every kmalloc() request is rounded up to the nearest kmalloc-N cache size, which is the source of allocation wastage.
  • ksize() tells you the true, actual size of a slab allocation and is the right tool for measuring this wastage.
  • Choosing sizes close to a cache boundary, using dedicated caches for hot paths, and always checking for NULL are the practical habits that matter most.

Conclusion

This Linux kernel memory allocation tutorial covered the full path a small allocation takes inside the kernel — from your driver’s kmalloc() call down through the SLUB slab allocator and, ultimately, the page allocator. You have seen how to measure real allocation efficiency with ksize(), why the numbers you get make sense once you understand size classes, and how modern kernels have both simplified (down to a single slab allocator) and hardened this code since older tutorials were written. If you’re following our free Linux kernel programming course, the next lecture will build directly on these ideas as we start writing dedicated slab caches with kmem_cache_create().

Frequently Asked Questions

1. What does kmalloc() actually return in the Linux kernel?

It returns a pointer to a block of physically contiguous kernel memory taken from the SLUB slab allocator’s nearest matching size-class cache, such as kmalloc-64 or kmalloc-256.

2. Is SLAB or SLUB the default allocator today?

SLUB is the only slab allocator available in current mainline Linux. SLAB was fully removed in kernel version 6.8, so any tutorial still describing SLAB as the default is out of date.

3. What is ksize() used for?

ksize() returns the actual usable size of a slab allocation, which is usually larger than what you originally requested with kmalloc(). It is primarily a diagnostic and educational tool.

4. Why is there wastage when I call kmalloc()?

Because kmalloc() always rounds your requested size up to the next available kmalloc-N cache size. The difference between what you asked for and what you got is the wastage.

5. Can I use ksize() on memory from vmalloc() or alloc_pages()?

No. ksize() is only valid on memory returned by the slab allocator family (kmalloc(), kzalloc(), and similar). Using it on page-allocator or vmalloc() memory is undefined behaviour.

6. Does kzalloc() cost more than kmalloc()?

Slightly — it zeroes the memory after allocation, which takes a small, usually negligible amount of extra time. For most drivers, the safety benefit is well worth it.

7. What replaced SLOB in embedded kernels?

SLOB was removed in kernel 6.4 because modern embedded boards generally have enough RAM to use SLUB, which is faster and better maintained.

8. How can I see live slab cache usage on my own system?

Run sudo cat /proc/slabinfo or use the slabtop command to see every active cache, its object size, and how many objects are currently allocated.

9. Is this free Linux kernel programming course suitable for beginners?

Yes. This lecture and the rest of our free Linux kernel programming and Linux device drivers course are written to take you from basic kernel modules through to real driver-level memory management concepts, one topic at a time.

10. Should I always use a dedicated kmem_cache instead of kmalloc()?

Not always — kmalloc() is perfectly fine for occasional or general-purpose allocations. Dedicated caches are worth the extra setup only when you allocate and free the same fixed-size object very frequently.

Continue the Free Linux Kernel Programming Course

More lectures on kernel memory management, Linux device drivers, and embedded systems are on the way at EmbeddedPathashala.

Browse the Course
Subscribe for Updates

Leave a Reply

Your email address will not be published. Required fields are marked *