The Kernel Thread and kthreadd: How the Kernel Runs Code That Has No User-Space

2026-07-01

Every process on Linux descends from PID 1 (init) — except the ones that don't. Look at ps -ef and you'll see dozens of entries in square brackets: [kthreadd], [rcu_sched], [migration/0]. These are kernel threads: schedulable entities with no user-space address space, no file descriptors you can inherit, and no execve() in their history. They exist entirely inside the kernel's address space, executing kernel functions directly.

The ancestor of every kernel thread is kthreadd (PID 2), spawned during boot right after PID 1. It sits in a loop waiting on a work list. When any subsystem calls kthread_create(), the request is enqueued and kthreadd wakes up, calls kernel_thread(), and forks a new task whose mm pointer is NULL — meaning it borrows the previous task's page tables lazily (this is why they're called "anonymous" or "lazy TLB" tasks). The new thread's entry point isn't _start; it's a plain C function pointer you passed in.

Why go through kthreadd instead of forking directly? Context. Whoever calls kthread_create() might be holding locks, running in interrupt context, or executing in a doomed process about to die. kthreadd runs in a clean, schedulable, sleep-safe context — the one place where the heavy machinery of task creation (allocating a task_struct, a kernel stack, a PID) is always safe to invoke.

Concrete example: when you insert a USB drive, the kernel's USB subsystem needs to probe the device — a slow operation involving I/O waits. It can't do this in the interrupt handler that fired when the device was plugged in. Instead it calls kthread_run(usb_probe_thread, ...), which asks kthreadd to spawn a worker. That worker sleeps on I/O, negotiates with the device, and eventually exits. You'll briefly see it in ps as something like [usb-storage].

Rule of thumb for reading ps: a name in [brackets] means kernel thread — its /proc/PID/exe symlink is broken, /proc/PID/maps is empty, and killing it with SIGKILL does nothing (kernel threads ignore signals unless they explicitly check with kthread_should_stop()). If you want it gone, you unload the module that created it.

Kernel threads are also cheap — no user pages, no VMAs, no signal machinery to speak of. A modern server easily runs 200+ of them. Each one costs roughly 16KB of kernel stack plus the task_struct (~10KB), so 200 threads is about 5MB of RAM — a rounding error compared to what they enable: asynchronous, sleepable, per-CPU kernel work that would otherwise pile up in softirqs or interrupt handlers.

Key Takeaway: kthreadd is the kernel's own init — a PID 2 loop that spawns bracketed, mm-less tasks so subsystems can run sleepable work in a clean context instead of stuck in interrupt handlers.

All newsletters